4

I need to load, alter and write the code in a mix.exs file. I want to be able to load the file, write the dependencies and write the file.

I start with:

defmodule Elixir_2ndTest.Mixfile do
  use Mix.Project

  def project do
    [app: :elixir_2nd_test,
     version: "0.0.1",
     elixir: "~> 1.2",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     description: description(),
     deps: deps]
  end

  def application do
    [applications: [:logger]]
  end

  defp deps do
    []
  end
end

And I need to end up with (the only difference is in the deps fun):

defmodule Elixir_2ndTest.Mixfile do
  use Mix.Project

  def project do
    [app: :elixir_2nd_test,
     version: "0.0.1",
     elixir: "~> 1.2",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     description: description(),
     deps: deps]
  end

  def application do
    [applications: [:logger]]
  end

  defp deps do
    [{:httpoison, "~> 0.8.3"}]
  end
end

The dependencies come from a different build system (I cannot use hex directly form the public internet, so I use it in OFFLINE mode and drop the dependencies in .hex/

I know what teh depenencies and what the versions are an need to insert them in the deps function (in this case httpoison 0.8.3).

If my understanding is correct this should be possible by loading the file, quoting, altering, unquoting.

This is what I have up until this point:

{:ok, body} = File.read("mix.exs")
{:ok, ast} = Code.string_to_quoted(body)

Any pointer on how I can alter the ast and write it back would be appreciated.

2
  • I looked into this a little bit in hopes of adding something like mix install similar to npm install, you'll basically have to parse and lex the file. I haven't had a chance to dig into that. If you do it would be great, but its definitely non-trivial since deps could be a function, or multiple functions, or a list in the project function. Commented Jul 1, 2016 at 22:18
  • I know what the deps are. I just need to drop them in there. Anything that's in there would get replaced. Commented Jul 1, 2016 at 22:34

2 Answers 2

1

It won't look exactly the same, but you can use Macro.to_string to convert the ast back to elixir code.

I was playing around with using my library PhStTransform to modify the ast and convert it back to code. Here's a very simple example from the PhStTransform test library.

test "transform quote do output" do
  data = quote do: Enum.map(1..3, fn(x) -> x*x end)
  data_transform = quote do: Enum.map(1..3, fn(y) -> y*y end)
  replace_x = fn(a, _d ) ->
    case a do
      :x -> :y
      atom -> atom
    end
  end
  potion = %{ Atom => replace_x }
  assert PhStTransform.transform(data, potion) == data_transform
end

What that does is convert all references to :x in the ast into :y. You'd need to be a bit more clever with writing the potion for PhStTransform, but I think it should be possible. PhStTransform is in hex.pm.

https://hex.pm/packages/phst_transform

Sign up to request clarification or add additional context in comments.

Comments

1

I'm not an Elixir expert, but I know about transforming source code; see my bio.

If you have access to the AST as a data structure, you can always write procedural code to climb over it and hack at where you want something different. I assume if Elixir will give you the AST, it will give you access/modification procedures for working with it. This is compiler 101.

That's usually NOT pretty code to write or maintain. And, it may not be enough: you often need more than just the AST to do serious analysis and transformation. See my essay on Life After Parsingl. Think of this as compiler 102.

One the first stumbling blocks is regenerating text from the AST. Here is my SO discussion on how to prettyprint an AST, and why it is harder than it looks: https://stackoverflow.com/a/5834775/120163

(Sounds like Fred the Magic Wonder Dog didn't think what Elixir offered was enough and is inventing his own extensions to make this easier.).

3 Comments

yeah. sort of get how this is supposed to work, and wondering if there is something that can be done for Elixir.
Elixir AST is an Elixir data structure so it's very easy to transform. There aren't a lot of tools out there yet targeted to manipulating the AST, but it doesn't require special parsing/etc...
My point is that parsing/AST production is the easiest part. Everything gets harder after that. The question is, do you want to reinvent all the machinery required to do transformations, or do you want use foundations where this is built in and you can work on your problem?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.