Search code examples
macrosmetaprogrammingelixirhygiene

Injecting variables into a quoted expression


I have a quoted expression such as this one:

ast = quote do
  IO.puts x
end

And I wish to inject a variable into the expression so that x has a value, and the expression won't raise a CompileError due to x being undefined when evaluated like so:

Code.eval_quoted ast, [x: "Hello, world!"]

This AST is passed to me, and I cannot change the code it is generated with. I can modify the AST after I've been passed it though.

I thought to do this I could transform the AST to resemble this quoted expression:

ast = quote do
  x = var!(x)
  IO.puts x
end

After attempting this my AST looks like so:

{:__block__, [],
[{:=, [],
  [{:x, [], Elixir}, {:var!, [context: Check.Runner], [{:x, [], Elixir}]}]},
  {{:., [], [{:__aliases__, [alias: false], [:IO]}, :puts]}, [],
  [{:x, [], Check.RunnerTest}]}]}

This doesn't work. I thought that this might be due to having an incorrect value for the context of var!.

Resolution: I now realise that the variables were in the incorrect scope, and I need an AST like this:

{:__block__, [],
[{:=, [],
  [{:x, [], Check.RunnerTest},
    {:var!, [context: Check.Runner], [{:x, [], Check.Runner}]}]},
  {{:., [], [{:__aliases__, [alias: false], [:IO]}, :puts]}, [],
  [{:x, [], Check.RunnerTest}]}]}

Solution

  • Your code should just work. In fact, you have written:

    ast = quote do
      x = var!(x)
      IO.puts x
    end
    

    But you showed the AST for some other code, one where you assigned 1 to var!(x). In other words, there is nothing wrong with the code above.