Copyright © 2010-2014 Richard Carlsson
Authors: Richard Carlsson (carlsson.richard@gmail.com).
Merl is a more user friendly interface to the erl_syntax module in
the syntax_tools application, making it easy both to build new ASTs
(abstract syntax trees) from scratch and to match and decompose existing
ASTs. For details that are outside the scope of Merl itself, please see the
documentation of erl_syntax.
-include_lib("merl/include/merl.hrl").
Then, you can use the ?Q(Text) macros in your code to create ASTs or match
on existing ASTs. For example:
Tuple = ?Q("{foo, 42}"),
?Q("{foo, _@Number}") = Tuple,
Call = ?Q("foo:bar(_@Number)")
Calling merl:print(Call) will then print the following code:
foo:bar(42)
The ?Q macros turn the quoted code fragments into ASTs, and lifts
metavariables such as _@Tuple and _@Number to the level of your Erlang
code, so you can use the corresponding Erlang variables Tuple and Number
directly. This is the most straightforward way to use Merl, and in many
cases it's all you need.
?Q macros as patterns. For example:
case AST of
?Q("{foo, _@Foo}") -> handle(Foo);
?Q("{bar, _@Bar}") when erl_syntax:is_integer(Bar) -> handle(Bar);
_ -> handle_default()
end
These case switches only allow ?Q(...) or _ as clause patterns, and the
guards may contain any expressions, not just Erlang guard expressions.
MERL_NO_TRANSFORM is defined before the merl.hrl header
file is included, the parse transform used by Merl will be disabled, and in
that case, the match expressions ?Q(...) = ..., case switches using
?Q(...) patterns, and automatic metavariables like _@Tuple cannot be
used in your code, but the Merl macros and functions still work. To do
metavariable substitution, you need to use the ?Q(Text, Map) macro, e.g.:
Tuple = ?Q("{foo, _@bar, _@baz}", [{bar, Bar}, {baz,Baz}])
The text given to a ?Q(Text) macro can be either a single string, or a
list of strings. The latter is useful when you need to split a long
expression over multiple lines, e.g.:
?Q(["case _@Expr of",
" {foo, X} -> f(X);",
" {bar, X} -> g(X)",
" _ -> h(X)"
"end"])If there is a syntax error somewhere in the text (like the missing semicolon in the second clause above) this allows Merl to generate an error message pointing to the exact line in your source code. (Just remember to comma-separate the strings in the list, otherwise Erlang will concatenate the string fragments as if they were a single string.)
@, for example '@foo' or '@Foo'_@, for example _@bar or _@Bar"'@, for example "'@File"9091 or 909123_ or 0 characters may be used to
indicate "lifting" of the variable one or more levels, and after that, a @
or 9 character indicates a glob metavariable (matching zero or more
elements in a sequence) rather than a normal metavariable. For example:
'@_foo' is lifted one level, and _@__foo is lifted two
levels_@@bar is a glob variable, and _@_@bar is a lifted glob
variable90901 is a lifted variable,90991 is a glob variable, and 9090091
is a glob variable lifted two levels
(Note that the last character in the name is never considered to be a lift
or glob marker, hence, _@__ and 90900 are only lifted one level, not
two. Also note that globs only matter for matching; when doing
substitutions, a non-glob variable can be used to inject a sequence of
elements, and vice versa.)
If the name after the prefix and any lift and glob markers is _ or 0,
the variable is treated as an anonymous catch-all pattern in matches. For
example, _@_, _@@_, _@__, or even _@__@_.
_@Foo or _@_@Foo, it will become a
variable on the Erlang level, and can be used to easily deconstruct and
construct syntax trees:
case Input of
?Q("{foo, _@Number}") -> ?Q("foo:bar(_@Number)");
...
We refer to these as "automatic metavariables". If in addition the name ends
with @, as in _@Foo@, the value of the variable as an Erlang term will
be automatically converted to the corresponding abstract syntax tree when
used to construct a larger tree. For example, in:
Bar = {bar, 42},
Foo = ?Q("{foo, _@Bar@}")
(where Bar is just some term, not a syntax tree) the result Foo will be a
syntax tree representing {foo, {bar, 42}}. This avoids the need for
temporary variables in order to inject data, as in
TmpBar = erl_syntax:abstract(Bar),
Foo = ?Q("{foo, _@TmpBar}")
If the context requires an integer rather than a variable, an atom, or a
string, you cannot use the uppercase convention to mark an automatic
metavariable. Instead, if the integer (without the 909-prefix and
lift/glob markers) ends in a 9, the integer will become an Erlang-level
variable prefixed with Q, and if it ends with 99 it will also be
automatically abstracted. For example, the following will increment the
arity of the exported function f:
case Form of
?Q("-export([f/90919]).") ->
Q2 = erl_syntax:concrete(Q1) + 1,
?Q("-export([f/909299]).");
...
?Q("f(_@Arg)") = Expr
but if you want to match on something like the name of a function, you have
to use an atom as metavariable:
?Q("'@Name'() -> _@@_." = Function
(note the anonymous glob variable _@@_ to ignore the function body).
In some contexts, only a string or an integer is allowed. For example, the
directive -file(Name, Line) requires that Name is a string literal and
Line an integer literal:
?Q("-file(\"'@File\", 9090).") = ?Q("-file(\"foo.erl\", 42).")).
This will extract the string literal "foo.erl" into the variable Foo.
Note the use of the anonymous variable 9090 to ignore the line number. To
match and also bind a metavariable that must be an integer literal, we can
use the convention of ending the integer with a 9, turning it into a
Q-prefixed variable on the Erlang level (see the previous section).
?Q("{_@@Elements}") = ?Q({a, b, c})
will bind Elements to the list of individual syntax trees representing the
atoms a, b, and c. This can also be used with static prefix and suffix
elements in the sequence. For example:
?Q("{a, b, _@@Elements}") = ?Q({a, b, c, d})
will bind Elements to the list of the c and d subtrees, and
?Q("{_@@Elements, c, d}") = ?Q({a, b, c, d})
will bind Elements to the list of the a and b subtrees. You can even use
plain metavariables in the prefix or suffix:
?Q("{_@First, _@@Rest}") = ?Q({a, b, c})
or
?Q("{_@@_, _@Last}") = ?Q({a, b, c})(ignoring all but the last element). You cannot however have two globs as part of the same sequence.
?Q("-export([_@@Name]).")
to match out all name/arity pairs in the export list, or to insert a list of
exports in a declaration, because the Erlang parser only allows elements on
the form A/I (where A is an atom and I an integer) in the export list.
A variable like the above is not allowed, but neither is a single atom or
integer, so '@@Name' or 909919 wouldn't work either.
?Q("-export(['@_@Name'/0]).")
This causes the variable to be lifted (after parsing) to the next higher
level in the syntax tree, replacing that entire subtree. In this case, the
'@_@Name'/0 will be replaced with '@@Name', and the /0
part was just used as dummy notation and will be discarded.
?Q("-export(['@__Name'/0]).")
using two underscores, but with no glob marker this time. This will make the
entire ['@__Name'/0] part be replaced with '@Name'.
zero() -> 0.
consists of the name (the atom zero), and a list of clauses containing the
single clause () -> 0. The clause consists of an argument list (empty), a
guard (empty), and a body (which is always a list of expressions) containing
the single expression 0. This means that to match out the name and the
list of clauses of any function, you'll need to use a pattern like
?Q("@Name'() -> _@_@Body.")', using a dummy clause whose body is a glob
lifted one level.
merl:show(T), which prints a summary. For example, entering
merl:show(merl:quote("inc(X, Y) when Y > 0 -> X + Y."))
in the Erlang shell will print the following (where the + signs separate
groups of subtrees on the same level):
function: inc(X, Y) when ... -> X + Y.
atom: inc
+
clause: (X, Y) when ... -> X + Y
variable: X
variable: Y
+
disjunction: Y > 0
conjunction: Y > 0
infix_expr: Y > 0
variable: Y
+
operator: >
+
integer: 0
+
infix_expr: X + Y
variable: X
+
operator: +
+
variable: Y
This shows another important non-obvious case: a clause guard, even if it's
as simple as Y > 0, always consists of a single disjunction of one or more
conjunctions of tests, much like a tuple of tuples. Thus:
"when _@Guard ->" will only match a guard with exactly one
test"when _@@Guard ->" will match a guard with one or more
comma-separated tests (but no semicolons), binding Guard to the list
of tests"when _@_Guard ->" will match just like the previous pattern, but
binds Guard to the conjunction subtree"when _@_@Guard ->" will match an arbitrary nonempty guard,
binding Guard to the list of conjunction subtrees"when _@__Guard ->" will match like the previous pattern, but
binds Guard to the whole disjunction subtree"when _@__@Guard ->" will match any clause,
binding Guard to [] if the guard is empty and to [Disjunction]
otherwise"(_@Args) when _@__@Guard -> _@Body"
Generated by EDoc