View Source merlin (merlin v3.0.0)
Parse transform helper library
The main function is transform/3
, which let's you easily traverse an ast()
, optionally modify it, and carry a state.
There are a few helper functions that provides easy access to commonly needed information. For example the current set of bindings and the MFA for every function call.
Finally, when you're done transformingreturn/1
the result in the format expected by erl_lint
(or else you crash the compiler).
Summary
Types
Represents the action to take after calling the transformer()
callback.
erl_syntax:syntaxTree()
also includes the vanilla AST and dialyser doesn't always approve of that.erl_lint:error_info()
.parse_transform
behaviour.transform/3
.Represents the return value from the transformer()
callback.
erl_lint:error_info()
.Functions
Returns the result from transform/3
, or just the final forms, to an erl_lint
compatible format.
Returns the given node or nodes in erl_parse
format.
Transforms the given Forms
using the given Transformer
with the given State
.
Types
-type action() :: continue | delete | return | exceptions.
Represents the action to take after calling the transformer()
callback.
-type ast() :: erl_syntax_ast() | erl_parse() | erl_syntax:syntaxTree() | {eof, erl_anno:anno()}.
-type erl_parse() :: erl_parse:abstract_clause() | erl_parse:abstract_expr() | erl_parse:abstract_form() | erl_parse:abstract_type() | erl_parse:form_info() | erl_parse:af_binelement(term()) | erl_parse:af_generator() | erl_parse:af_remote_function().
-type erl_syntax_ast() :: {tree, Type :: atom(), erl_syntax:syntaxTreeAttributes(), Data :: term()} | {wrapper, Type :: atom(), erl_syntax:syntaxTreeAttributes(), Tree :: erl_parse()}.
erl_syntax:syntaxTree()
also includes the vanilla AST and dialyser doesn't always approve of that.
-type error_marker() :: {error, marker_with_file()}.
erl_lint:error_info()
.
-type exceptions_grouped_by_file() :: [{File :: string(), [erl_lint:error_info()]}].
-type marker_with_file() :: {File :: file:filename(), erl_lint:error_info()}.
-type parse_transform_return() :: parse_transform_return([ast()]).
-type parse_transform_return(Forms) :: Forms | {warning, Forms, exceptions_grouped_by_file()} | {error, exceptions_grouped_by_file(), exceptions_grouped_by_file()}.
parse_transform
behaviour.
-type phase() :: enter | leaf | exit.
ast()
.
-type transformer() :: transformer(State :: term()).
-type transformer(State) :: fun((phase(), ast(), State) -> transformer_return(State)).
transform/3
.
-type transformer_return(State) :: node_or_nodes() | {node_or_nodes(), State} | continue | {continue, node_or_nodes()} | {continue, node_or_nodes(), State} | return | {return, node_or_nodes()} | {return, node_or_nodes(), State} | delete | {delete, State} | {error, Reason :: term()} | {error, Reason :: term(), State} | {warning, Reason :: term()} | {warning, Reason :: term(), node_or_nodes()} | {warning, Reason :: term(), node_or_nodes(), State} | {exceptions, [{error | warning, Reason :: term()}]} | {exceptions, [{error | warning, Reason :: term()}], node_or_nodes()} | {exceptions, [{error | warning, Reason :: term()}], node_or_nodes(), State}.
Represents the return value from the transformer()
callback.
The first element is the action()
, the second the new or modified node or nodes, and the third the new state.
Returning an atom is a shorthand for reusing the current node and state. Returning a two-tuple is a shorthand for reusing the current state.
You may also return{error, Reason} | {warning, Reason}
to generate a compile time warning. These will be propagated to erl_lint
. To generate multiple warnings, return {exceptions, ListOfErrorsAndWarnings}
.
-type warning_marker() :: {warning, marker_with_file()}.
erl_lint:error_info()
.
Functions
-spec return(parse_transform_return(Forms) | {parse_transform_return(Forms), State}) -> parse_transform_return(Forms) when State :: term(), Forms :: [ast()].
Returns the result from transform/3
, or just the final forms, to an erl_lint
compatible format.
Returns the given node or nodes in erl_parse
format.
parse_trans:revert_form/1
and slightly modified. The original also handles a bug in R16B03, but that is ancient history now.
-spec transform(Forms, transformer(State), State) -> {parse_transform_return(Forms), State} when Forms :: [ast()].
Transforms the given Forms
using the given Transformer
with the given State
.
This is done thorugh a depth-first traversal of the given Forms
.
- First
enter
ing each tree node (top-down) - Then, when you encounter a
leaf
node, you both "enter" and "exit" the node - Finally
exit
ing each tree node (bottom-up)
For example, if you just want to change a call to a specific function, you would just use the top-down enter
phase.
parse_transform(Forms, _Options) ->
transform(
Forms,
fun (enter, Form, _State) ->
case erl_syntax:type(Form) of
%% Application is the name of a function call
application ->
Operator erl_syntax:application_operator(Form),
case erl_syntax:type(Operator) =:= atom andalso
erl_syntax:atom_value(Operator) =:= foo of
true ->
%% Change `foo(...)' to `my_module:foo(...)'
%% using `merl' or `merlin_quote_transform'
{return, ?Q("my_module:foo(123)")};
false ->
continue
end;
_ ->
continue
end;
(_, _, _) ->
continue
end,
state
).
Sometimes it is easier, or necessary, to use a bottom-up approach. You can use exit
for that. Sometimes you need both, and here is where merlin
really shines, as you can just pattern match of enter
and exit
in the same function.
The latter shows up when you want to do some analysis on the way down, and then use that information on the way up.
For a real world example seemerlin_quote_transform
. That one is also pretty handy for writing transformers.See also: transformer/3.