Elixir v1.1.0 Macro

Conveniences for working with macros.

Custom Sigils

To create a custom sigil, define a function with the name sigil_{identifier} that takes two arguments. The first argument will be the interpolated string, the second will be a char list containing any modifiers.

Valid modifiers include only lower and upper case letters. Other characters will cause a syntax error.

The module containing the custom sigil must be imported before the sigil syntax can be used.

Summary

Types

Expr node (remaining ones are literals)

t()

Abstract Syntax Tree (AST)

Functions

Decomposes a local or remote call into its remote part (when provided), function name and argument list

Recursively escapes a value so it can be inserted into a syntax tree

Receives an AST node and expands it until it can no longer be expanded

Receives an AST node and expands it once

Pipes expr into the call_args at the given position

Performs a depth-first, post-order traversal of quoted expressions

Performs a depth-first, post-order traversal of quoted expressions using an accumulator

Performs a depth-first, pre-order traversal of quoted expressions

Performs a depth-first, pre-order traversal of quoted expressions using an accumulator

Converts the given expression to a binary

Unescapes the given chars

Unescapes the given chars according to the map given

Unescapes the given tokens according to the default map

Unescapes the given tokens according to the given map

Breaks a pipeline expression into a list

Applies the given function to the node metadata if it contains one

Validates the given expressions are valid quoted expressions

Generates an AST node representing the variable given by the atoms var and context

Types

expr :: {expr | atom, Keyword.t, atom | [t]}

Expr node (remaining ones are literals)

t ::
  expr |
  {t, t} |
  atom |
  number |
  binary |
  pid |
  (... -> any) |
  [t]

Abstract Syntax Tree (AST)

Functions

decompose_call(ast)

Specs

decompose_call(Macro.t) ::
  {atom, [Macro.t]} |
  {Macro.t, atom, [Macro.t]} |
  :error

Decomposes a local or remote call into its remote part (when provided), function name and argument list.

Returns :error when an invalid call syntax is provided.

Examples

iex> Macro.decompose_call(quote do: foo)
{:foo, []}

iex> Macro.decompose_call(quote do: foo())
{:foo, []}

iex> Macro.decompose_call(quote do: foo(1, 2, 3))
{:foo, [1, 2, 3]}

iex> Macro.decompose_call(quote do: Elixir.M.foo(1, 2, 3))
{{:__aliases__, [], [:Elixir, :M]}, :foo, [1, 2, 3]}

iex> Macro.decompose_call(quote do: 42)
:error
escape(expr, opts \\ [])

Specs

escape(term, Keyword.t) :: Macro.t

Recursively escapes a value so it can be inserted into a syntax tree.

One may pass unquote: true to escape/2 which leaves unquote statements unescaped, effectively unquoting the contents on escape.

Examples

iex> Macro.escape(:foo)
:foo

iex> Macro.escape({:a, :b, :c})
{:{}, [], [:a, :b, :c]}

iex> Macro.escape({:unquote, [], [1]}, unquote: true)
1
expand(tree, env)

Receives an AST node and expands it until it can no longer be expanded.

This function uses expand_once/2 under the hood. Check it out for more information and examples.

expand_once(ast, env)

Receives an AST node and expands it once.

The following contents are expanded:

  • Macros (local or remote)
  • Aliases are expanded (if possible) and return atoms
  • Pseudo-variables (__ENV__, __MODULE__ and __DIR__)
  • Module attributes reader (@foo)

If the expression cannot be expanded, it returns the expression itself. Notice that expand_once/2 performs the expansion just once and it is not recursive. Check expand/2 for expansion until the node can no longer be expanded.

Examples

In the example below, we have a macro that generates a module with a function named name_length that returns the length of the module name. The value of this function will be calculated at compilation time and not at runtime.

Consider the implementation below:

defmacro defmodule_with_length(name, do: block) do
  length = length(Atom.to_char_list(name))

  quote do
    defmodule unquote(name) do
      def name_length, do: unquote(length)
      unquote(block)
    end
  end
end

When invoked like this:

defmodule_with_length My.Module do
  def other_function, do: ...
end

The compilation will fail because My.Module when quoted is not an atom, but a syntax tree as follow:

{:__aliases__, [], [:My, :Module]}

That said, we need to expand the aliases node above to an atom, so we can retrieve its length. Expanding the node is not straight-forward because we also need to expand the caller aliases. For example:

alias MyHelpers, as: My

defmodule_with_length My.Module do
  def other_function, do: ...
end

The final module name will be MyHelpers.Module and not My.Module. With Macro.expand/2, such aliases are taken into consideration. Local and remote macros are also expanded. We could rewrite our macro above to use this function as:

defmacro defmodule_with_length(name, do: block) do
  expanded = Macro.expand(name, __CALLER__)
  length   = length(Atom.to_char_list(expanded))

  quote do
    defmodule unquote(name) do
      def name_length, do: unquote(length)
      unquote(block)
    end
  end
end
pipe(expr, call_args, position)

Specs

pipe(Macro.t, Macro.t, integer) ::
  Macro.t |
  no_return

Pipes expr into the call_args at the given position.

postwalk(ast, fun)

Specs

postwalk(t, (t -> t)) :: t

Performs a depth-first, post-order traversal of quoted expressions.

postwalk(ast, acc, fun)

Specs

postwalk(t, any, (t, any -> {t, any})) :: {t, any}

Performs a depth-first, post-order traversal of quoted expressions using an accumulator.

prewalk(ast, fun)

Specs

prewalk(t, (t -> t)) :: t

Performs a depth-first, pre-order traversal of quoted expressions.

prewalk(ast, acc, fun)

Specs

prewalk(t, any, (t, any -> {t, any})) :: {t, any}

Performs a depth-first, pre-order traversal of quoted expressions using an accumulator.

to_string(tree, fun \\ fn _ast, string -> string end)

Specs

to_string(Macro.t, (Macro.t, String.t -> String.t)) :: String.t

Converts the given expression to a binary.

Examples

iex> Macro.to_string(quote do: foo.bar(1, 2, 3))
"foo.bar(1, 2, 3)"
unescape_string(chars)

Specs

unescape_string(String.t) :: String.t

Unescapes the given chars.

This is the unescaping behaviour used by default in Elixir single- and double-quoted strings. Check unescape_string/2 for information on how to customize the escaping map.

In this setup, Elixir will escape the following: \0, \a, \b, \d, \e, \f, \n, \r, \s, \t and \v. Unicode codepoints can be given as hexadecimals via \xNN and \x{NN...} escapes.

This function is commonly used on sigil implementations (like ~r, ~s and others) which receive a raw, unescaped string.

Examples

iex> Macro.unescape_string("example\\n")
"example\n"

In the example above, we pass a string with \n escaped and return a version with it unescaped.

unescape_string(chars, map)

Specs

unescape_string(String.t, (non_neg_integer -> non_neg_integer | false)) :: String.t

Unescapes the given chars according to the map given.

Check unescape_string/1 if you want to use the same map as Elixir single- and double-quoted strings.

Map

The map must be a function. The function receives an integer representing the codepoint of the character it wants to unescape. Here is the default mapping function implemented by Elixir:

def unescape_map(?0), do: ?0
def unescape_map(?a), do: ?\a
def unescape_map(?b), do: ?\b
def unescape_map(?d), do: ?\d
def unescape_map(?e), do: ?\e
def unescape_map(?f), do: ?\f
def unescape_map(?n), do: ?\n
def unescape_map(?r), do: ?\r
def unescape_map(?s), do: ?\s
def unescape_map(?t), do: ?\t
def unescape_map(?v), do: ?\v
def unescape_map(?x), do: true
def unescape_map(e),  do: e

If the unescape_map function returns false. The char is not escaped and \ is kept in the char list. Hexadecimals will be escaped if the map function returns true for ?x. ## Examples Using the unescape_map function defined above is easy: Macro.unescape_string “example\n”, &unescape_map(&1)

unescape_tokens(tokens)

Specs

unescape_tokens([Macro.t]) :: [Macro.t]

Unescapes the given tokens according to the default map.

Check unescape_string/1 and unescape_string/2 for more information about unescaping.

Only tokens that are binaries are unescaped, all others are ignored. This function is useful when implementing your own sigils. Check the implementation of Kernel.sigil_s/2 for examples.

unescape_tokens(tokens, map)

Specs

unescape_tokens([Macro.t], (non_neg_integer -> non_neg_integer | false)) :: [Macro.t]

Unescapes the given tokens according to the given map.

Check unescape_tokens/1 and unescape_string/2 for more information.

unpipe(expr)

Specs

unpipe(Macro.t) :: [Macro.t]

Breaks a pipeline expression into a list.

Raises if the pipeline is ill-formed.

update_meta(quoted, fun)

Specs

update_meta(t, (Keyword.t -> Keyword.t)) :: t

Applies the given function to the node metadata if it contains one.

This is often useful when used with Macro.prewalk/2 to remove information like lines and hygienic counters from the expression for either storage or comparison.

Examples

iex> quoted = quote line: 10, do: sample()
{:sample, [line: 10], []}
iex> Macro.update_meta(quoted, &Keyword.delete(&1, :line))
{:sample, [], []}
validate(expr)

Specs

validate(term) :: :ok | {:error, term}

Validates the given expressions are valid quoted expressions.

Check the type:Macro.t for the specification of a valid quoted expression.

var(var, context)

Specs

var(var, context) :: {var, [], context} when var: atom, context: atom

Generates an AST node representing the variable given by the atoms var and context.

Examples

In order to build a variable, a context is expected. Most of the times, in order to preserve hygiene, the context must be __MODULE__:

iex> Macro.var(:foo, __MODULE__)
{:foo, [], __MODULE__}

However, if there is a need to access the user variable, nil can be given:

iex> Macro.var(:foo, nil)
{:foo, [], nil}