🎓 A1Learner
🔍
0 of 46 lessons completed

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?