Elixir AST Manipulation
AST manipulation is rewriting the tree form of code before it runs. An abstract syntax tree is the nested form Elixir uses to hold a piece of code.
Quote shows the tree form
The tree for 1 + 2 holds the operator first. Its operands follow as a list.
ast = quote do: 1 + 2
IO.puts(elem(ast, 0))
IO.inspect(elem(ast, 2))Prewalk can change an operator
The walk swaps addition for multiplication. The changed tree multiplies to 2, and the original tree adds to 3.
ast = quote do: 1 + 2
changed = Macro.prewalk(ast, fn {:+, meta, args} -> {:*, meta, args}; node -> node end)
IO.puts(elem(Code.eval_quoted(changed), 0))
IO.puts(elem(Code.eval_quoted(ast), 0))Prewalk can count parts of a tree
The walk counts each whole number it meets. The tree holds three whole numbers.
ast = quote do: 10 + 20 + 30
{_tree, count} = Macro.prewalk(ast, 0, fn node, acc when is_integer(node) -> {node, acc + 1}; node, acc -> {node, acc} end)
IO.puts(count)Example
ast = quote do: 2 * 3 + 4
changed = Macro.prewalk(ast, fn {:*, meta, args} -> {:+, meta, args}; node -> node end)
IO.puts(elem(Code.eval_quoted(ast), 0))
IO.puts(elem(Code.eval_quoted(changed), 0))Key points
- Syntax tree: a syntax tree is the nested form of a piece of code.
- Quote: quote turns code into its tree form without running it.
- Node: a node is one piece of the tree, such as an operator or a number.
- Prewalk: Macro.prewalk visits every node of a tree and builds a new tree from the results.
- Evaluation: Code.eval_quoted runs a tree and gives back a pair holding its value.
In this example, the original tree multiplies first and gives 10, and the rewritten tree adds every number and gives 9.
Note
a tree holds operators and operands apart, so a rewrite can change the operator and keep the operands.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
What does Macro.prewalk do to a tree?