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
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
Functions
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
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
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.
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
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.
Examples
iex> Macro.to_string(quote do: foo.bar(1, 2, 3))
"foo.bar(1, 2, 3)"
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.
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)
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.
Unescapes the given tokens according to the given map.
Check unescape_tokens/1
and unescape_string/2
for more information.
Breaks a pipeline expression into a list.
Raises if the pipeline is ill-formed.
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, [], []}
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.
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}