Packages

Bring Elixir-style __ENV__ macro into Erlang via parse transform

Current section

Files

Jump to
macro_env src __ENV__.erl
Raw

src/__ENV__.erl

-module('__ENV__').
-moduledoc """
Bring Elixir-style __ENV__ macro and macro calls into Erlang.
## Usage
Apply the parse transform to your module:
-compile({parse_transform, '__ENV__'}).
Then you can use the macro everywhere in your code:
io:format("~p~n", [__ENV__])
## But WHY!??
Well, you can call Elixir macros with it directly from Erlang. Here is how:
In Elixir macros look just like ordinary functions on the surface. You can call
them by the same `Module.macro_name(args)` syntax as you call functions. But
macros are not functions. They are compile time AST transformations. They
receive ASTs as arguments, and they return ASTs that should take the place of
the macro call. You can call `Logger.info("hello")` from Elixir, but
`'Elixir.Logger':info(<<"hello">>)` won't work, because this is actually an
Elixir macro. But you can hack this from Erlang, like this:
-define(APPLY_MACRO(Module, Macro, Args),
'Elixir.Code':eval_quoted(
Module,
list_to_atom(string:concat("MACRO-", atom_to_list(Macro))),
[__ENV__ | Args]
)
).
?APPLY_MACRO('Elixir.Logger', :info, [<<"hello">>])
""".
-export([parse_transform/2]).
parse_transform(Forms, _Opts) ->
File = get_file(Forms),
Module = get_module(Forms),
Imports = get_imports(Forms),
transform_forms(Forms, File, Module, Imports).
transform_forms([], _File, _Module, _Imports) ->
[];
transform_forms([{attribute, _, file, {NewFile, _}} = Form | Rest], _File, Module, Imports) ->
[Form | transform_forms(Rest, NewFile, Module, Imports)];
transform_forms([{function, Loc, Name, Arity, Clauses} | Rest], File, Module, Imports) ->
NewClauses = [transform_clause(C, File, Module, Imports, Name, Arity) || C <- Clauses],
[{function, Loc, Name, Arity, NewClauses} | transform_forms(Rest, File, Module, Imports)];
transform_forms([Form | Rest], File, Module, Imports) ->
[Form | transform_forms(Rest, File, Module, Imports)].
transform_clause({clause, Loc, Pattern, Guards, Body}, File, Module, Imports, FunName, FunArity) ->
NewPattern = [transform_pattern(P, File, Module, Imports, FunName, FunArity) || P <- Pattern],
NewGuards = transform_guards(Guards, File, Module, Imports, FunName, FunArity),
NewBody = [transform_expr(E, File, Module, Imports, FunName, FunArity) || E <- Body],
{clause, Loc, NewPattern, NewGuards, NewBody}.
transform_guards(Guards, File, Module, Imports, FunName, FunArity) ->
[[transform_expr(G, File, Module, Imports, FunName, FunArity) || G <- Gs] || Gs <- Guards].
transform_pattern({var, Loc, '__ENV__'}, File, Module, Imports, FunName, FunArity) ->
macro_env_pattern(Loc, File, Module, Imports, FunName, FunArity);
transform_pattern({tuple, Loc, Es}, File, Module, Imports, FunName, FunArity) ->
{tuple, Loc, [transform_pattern(E, File, Module, Imports, FunName, FunArity) || E <- Es]};
transform_pattern({cons, Loc, H, T}, File, Module, Imports, FunName, FunArity) ->
{cons, Loc, transform_pattern(H, File, Module, Imports, FunName, FunArity),
transform_pattern(T, File, Module, Imports, FunName, FunArity)};
transform_pattern({match, Loc, P1, P2}, File, Module, Imports, FunName, FunArity) ->
{match, Loc, transform_pattern(P1, File, Module, Imports, FunName, FunArity),
transform_pattern(P2, File, Module, Imports, FunName, FunArity)};
transform_pattern({map, Loc, Assocs}, File, Module, Imports, FunName, FunArity) ->
NewAssocs = [transform_pattern(A, File, Module, Imports, FunName, FunArity) || A <- Assocs],
{map, Loc, NewAssocs};
transform_pattern({map_field_exact, Loc, Key, Value}, File, Module, Imports, FunName, FunArity) ->
{map_field_exact, Loc, transform_pattern(Key, File, Module, Imports, FunName, FunArity),
transform_pattern(Value, File, Module, Imports, FunName, FunArity)};
transform_pattern({record, Loc, Name, Fields}, File, Module, Imports, FunName, FunArity) ->
NewFields = [transform_pattern(F, File, Module, Imports, FunName, FunArity) || F <- Fields],
{record, Loc, Name, NewFields};
transform_pattern({record_field, Loc, Name, Value}, File, Module, Imports, FunName, FunArity) ->
{record_field, Loc, Name, transform_pattern(Value, File, Module, Imports, FunName, FunArity)};
transform_pattern({op, Loc, Op, LHS, RHS}, File, Module, Imports, FunName, FunArity) ->
{op, Loc, Op, transform_pattern(LHS, File, Module, Imports, FunName, FunArity),
transform_pattern(RHS, File, Module, Imports, FunName, FunArity)};
transform_pattern({op, Loc, Op, Expr}, File, Module, Imports, FunName, FunArity) ->
{op, Loc, Op, transform_pattern(Expr, File, Module, Imports, FunName, FunArity)};
transform_pattern({bin, Loc, Elements}, File, Module, Imports, FunName, FunArity) ->
NewElements = [transform_pattern(E, File, Module, Imports, FunName, FunArity) || E <- Elements],
{bin, Loc, NewElements};
transform_pattern({bin_element, Loc, Pat, Size, TSL}, File, Module, Imports, FunName, FunArity) ->
NewPat = transform_pattern(Pat, File, Module, Imports, FunName, FunArity),
NewSize = case Size of
default -> default;
_ -> transform_pattern(Size, File, Module, Imports, FunName, FunArity)
end,
{bin_element, Loc, NewPat, NewSize, TSL};
transform_pattern(Pattern, _, _, _, _, _) ->
Pattern.
transform_expr({var, Loc, '__ENV__'}, File, Module, Imports, FunName, FunArity) ->
macro_env(Loc, File, Module, Imports, FunName, FunArity);
transform_expr({call, Loc, Fun, Args}, File, Module, Imports, FunName, FunArity) ->
NewArgs = [transform_expr(A, File, Module, Imports, FunName, FunArity) || A <- Args],
{call, Loc, transform_expr(Fun, File, Module, Imports, FunName, FunArity), NewArgs};
transform_expr({match, Loc, P, E}, File, Module, Imports, FunName, FunArity) ->
{match, Loc, transform_pattern(P, File, Module, Imports, FunName, FunArity),
transform_expr(E, File, Module, Imports, FunName, FunArity)};
transform_expr({tuple, Loc, Es}, File, Module, Imports, FunName, FunArity) ->
{tuple, Loc, [transform_expr(E, File, Module, Imports, FunName, FunArity) || E <- Es]};
transform_expr({cons, Loc, H, T}, File, Module, Imports, FunName, FunArity) ->
{cons, Loc, transform_expr(H, File, Module, Imports, FunName, FunArity),
transform_expr(T, File, Module, Imports, FunName, FunArity)};
transform_expr({'case', Loc, Expr, Clauses}, File, Module, Imports, FunName, FunArity) ->
NewExpr = transform_expr(Expr, File, Module, Imports, FunName, FunArity),
NewClauses = [transform_expr(C, File, Module, Imports, FunName, FunArity) || C <- Clauses],
{'case', Loc, NewExpr, NewClauses};
transform_expr({'if', Loc, Clauses}, File, Module, Imports, FunName, FunArity) ->
NewClauses = [transform_expr(C, File, Module, Imports, FunName, FunArity) || C <- Clauses],
{'if', Loc, NewClauses};
transform_expr({'receive', Loc, Clauses}, File, Module, Imports, FunName, FunArity) ->
NewClauses = [transform_expr(C, File, Module, Imports, FunName, FunArity) || C <- Clauses],
{'receive', Loc, NewClauses};
transform_expr({'receive', Loc, Clauses, After, AfterBody}, File, Module, Imports, FunName, FunArity) ->
NewClauses = [transform_expr(C, File, Module, Imports, FunName, FunArity) || C <- Clauses],
NewAfter = transform_expr(After, File, Module, Imports, FunName, FunArity),
NewAfterBody = [transform_expr(A, File, Module, Imports, FunName, FunArity) || A <- AfterBody],
{'receive', Loc, NewClauses, NewAfter, NewAfterBody};
transform_expr({'try', Loc, Body, CaseClauses, CatchClauses, After}, File, Module, Imports, FunName, FunArity) ->
NewBody = [transform_expr(B, File, Module, Imports, FunName, FunArity) || B <- Body],
NewCase = [transform_expr(C, File, Module, Imports, FunName, FunArity) || C <- CaseClauses],
NewCatch = [transform_expr(C, File, Module, Imports, FunName, FunArity) || C <- CatchClauses],
NewAfter = [transform_expr(A, File, Module, Imports, FunName, FunArity) || A <- After],
{'try', Loc, NewBody, NewCase, NewCatch, NewAfter};
transform_expr({'fun', Loc, {clauses, Clauses}}, File, Module, Imports, FunName, FunArity) ->
NewClauses = [transform_expr(C, File, Module, Imports, FunName, FunArity) || C <- Clauses],
{'fun', Loc, {clauses, NewClauses}};
transform_expr({block, Loc, Body}, File, Module, Imports, FunName, FunArity) ->
NewBody = [transform_expr(B, File, Module, Imports, FunName, FunArity) || B <- Body],
{block, Loc, NewBody};
transform_expr({lc, Loc, Expr, Qualifiers}, File, Module, Imports, FunName, FunArity) ->
NewExpr = transform_expr(Expr, File, Module, Imports, FunName, FunArity),
NewQuals = [transform_expr(Q, File, Module, Imports, FunName, FunArity) || Q <- Qualifiers],
{lc, Loc, NewExpr, NewQuals};
transform_expr({bc, Loc, Expr, Qualifiers}, File, Module, Imports, FunName, FunArity) ->
NewExpr = transform_expr(Expr, File, Module, Imports, FunName, FunArity),
NewQuals = [transform_expr(Q, File, Module, Imports, FunName, FunArity) || Q <- Qualifiers],
{bc, Loc, NewExpr, NewQuals};
transform_expr({map, Loc, Assocs}, File, Module, Imports, FunName, FunArity) ->
NewAssocs = [transform_expr(A, File, Module, Imports, FunName, FunArity) || A <- Assocs],
{map, Loc, NewAssocs};
transform_expr({map, Loc, Expr, Assocs}, File, Module, Imports, FunName, FunArity) ->
NewExpr = transform_expr(Expr, File, Module, Imports, FunName, FunArity),
NewAssocs = [transform_expr(A, File, Module, Imports, FunName, FunArity) || A <- Assocs],
{map, Loc, NewExpr, NewAssocs};
transform_expr({op, Loc, Op, LHS, RHS}, File, Module, Imports, FunName, FunArity) ->
{op, Loc, Op, transform_expr(LHS, File, Module, Imports, FunName, FunArity),
transform_expr(RHS, File, Module, Imports, FunName, FunArity)};
transform_expr({op, Loc, Op, Expr}, File, Module, Imports, FunName, FunArity) ->
{op, Loc, Op, transform_expr(Expr, File, Module, Imports, FunName, FunArity)};
transform_expr({record, Loc, Name, Fields}, File, Module, Imports, FunName, FunArity) ->
NewFields = [transform_expr(F, File, Module, Imports, FunName, FunArity) || F <- Fields],
{record, Loc, Name, NewFields};
transform_expr({record_field, Loc, Expr, Name, Field}, File, Module, Imports, FunName, FunArity) ->
{record_field, Loc, transform_expr(Expr, File, Module, Imports, FunName, FunArity), Name, Field};
transform_expr({clause, Loc, Pattern, Guards, Body}, File, Module, Imports, FunName, FunArity) ->
NewPattern = [transform_pattern(P, File, Module, Imports, FunName, FunArity) || P <- Pattern],
NewGuards = transform_guards(Guards, File, Module, Imports, FunName, FunArity),
NewBody = [transform_expr(E, File, Module, Imports, FunName, FunArity) || E <- Body],
{clause, Loc, NewPattern, NewGuards, NewBody};
transform_expr({map_field_assoc, Loc, Key, Value}, File, Module, Imports, FunName, FunArity) ->
{map_field_assoc, Loc, transform_expr(Key, File, Module, Imports, FunName, FunArity),
transform_expr(Value, File, Module, Imports, FunName, FunArity)};
transform_expr({map_field_exact, Loc, Key, Value}, File, Module, Imports, FunName, FunArity) ->
{map_field_exact, Loc, transform_expr(Key, File, Module, Imports, FunName, FunArity),
transform_expr(Value, File, Module, Imports, FunName, FunArity)};
transform_expr({generate, Loc, Pattern, Expr}, File, Module, Imports, FunName, FunArity) ->
{generate, Loc, transform_pattern(Pattern, File, Module, Imports, FunName, FunArity),
transform_expr(Expr, File, Module, Imports, FunName, FunArity)};
transform_expr({b_generate, Loc, Pattern, Expr}, File, Module, Imports, FunName, FunArity) ->
{b_generate, Loc, transform_pattern(Pattern, File, Module, Imports, FunName, FunArity),
transform_expr(Expr, File, Module, Imports, FunName, FunArity)};
transform_expr({record_field, Loc, Name, Value}, File, Module, Imports, FunName, FunArity) ->
{record_field, Loc, Name, transform_expr(Value, File, Module, Imports, FunName, FunArity)};
transform_expr({bin, Loc, Elements}, File, Module, Imports, FunName, FunArity) ->
NewElements = [transform_expr(E, File, Module, Imports, FunName, FunArity) || E <- Elements],
{bin, Loc, NewElements};
transform_expr({bin_element, Loc, Expr, Size, TSL}, File, Module, Imports, FunName, FunArity) ->
NewExpr = transform_expr(Expr, File, Module, Imports, FunName, FunArity),
NewSize = case Size of
default -> default;
_ -> transform_expr(Size, File, Module, Imports, FunName, FunArity)
end,
{bin_element, Loc, NewExpr, NewSize, TSL};
transform_expr({'fun', Loc, {function, M, F, A}}, File, Module, Imports, FunName, FunArity)
when is_tuple(M) ->
{'fun', Loc, {function, transform_expr(M, File, Module, Imports, FunName, FunArity),
transform_expr(F, File, Module, Imports, FunName, FunArity),
transform_expr(A, File, Module, Imports, FunName, FunArity)}};
transform_expr({'catch', Loc, Expr}, File, Module, Imports, FunName, FunArity) ->
{'catch', Loc, transform_expr(Expr, File, Module, Imports, FunName, FunArity)};
transform_expr({named_fun, Loc, Name, Clauses}, File, Module, Imports, FunName, FunArity) ->
NewClauses = [transform_expr(C, File, Module, Imports, FunName, FunArity) || C <- Clauses],
{named_fun, Loc, Name, NewClauses};
transform_expr({record, Loc, Expr, Name, Fields}, File, Module, Imports, FunName, FunArity) ->
NewExpr = transform_expr(Expr, File, Module, Imports, FunName, FunArity),
NewFields = [transform_expr(F, File, Module, Imports, FunName, FunArity) || F <- Fields],
{record, Loc, NewExpr, Name, NewFields};
transform_expr({remote, Loc, ModExpr, FunExpr}, File, Module, Imports, FunName, FunArity) ->
{remote, Loc, transform_expr(ModExpr, File, Module, Imports, FunName, FunArity),
transform_expr(FunExpr, File, Module, Imports, FunName, FunArity)};
transform_expr({'maybe', Loc, Body}, File, Module, Imports, FunName, FunArity) ->
NewBody = [transform_expr(E, File, Module, Imports, FunName, FunArity) || E <- Body],
{'maybe', Loc, NewBody};
transform_expr({'maybe', Loc, Body, {'else', ElseLoc, Clauses}}, File, Module, Imports, FunName, FunArity) ->
NewBody = [transform_expr(E, File, Module, Imports, FunName, FunArity) || E <- Body],
NewClauses = [transform_expr(C, File, Module, Imports, FunName, FunArity) || C <- Clauses],
{'maybe', Loc, NewBody, {'else', ElseLoc, NewClauses}};
transform_expr({maybe_match, Loc, P, E}, File, Module, Imports, FunName, FunArity) ->
{maybe_match, Loc, transform_pattern(P, File, Module, Imports, FunName, FunArity),
transform_expr(E, File, Module, Imports, FunName, FunArity)};
transform_expr({mc, Loc, Expr, Qualifiers}, File, Module, Imports, FunName, FunArity) ->
NewExpr = transform_expr(Expr, File, Module, Imports, FunName, FunArity),
NewQuals = [transform_expr(Q, File, Module, Imports, FunName, FunArity) || Q <- Qualifiers],
{mc, Loc, NewExpr, NewQuals};
transform_expr({m_generate, Loc, Pattern, Expr}, File, Module, Imports, FunName, FunArity) ->
{m_generate, Loc, transform_pattern(Pattern, File, Module, Imports, FunName, FunArity),
transform_expr(Expr, File, Module, Imports, FunName, FunArity)};
transform_expr({generate_strict, Loc, Pattern, Expr}, File, Module, Imports, FunName, FunArity) ->
{generate_strict, Loc, transform_pattern(Pattern, File, Module, Imports, FunName, FunArity),
transform_expr(Expr, File, Module, Imports, FunName, FunArity)};
transform_expr({b_generate_strict, Loc, Pattern, Expr}, File, Module, Imports, FunName, FunArity) ->
{b_generate_strict, Loc, transform_pattern(Pattern, File, Module, Imports, FunName, FunArity),
transform_expr(Expr, File, Module, Imports, FunName, FunArity)};
transform_expr({m_generate_strict, Loc, Pattern, Expr}, File, Module, Imports, FunName, FunArity) ->
{m_generate_strict, Loc, transform_pattern(Pattern, File, Module, Imports, FunName, FunArity),
transform_expr(Expr, File, Module, Imports, FunName, FunArity)};
transform_expr({zip, Loc, Qualifiers}, File, Module, Imports, FunName, FunArity) ->
NewQuals = [transform_expr(Q, File, Module, Imports, FunName, FunArity) || Q <- Qualifiers],
{zip, Loc, NewQuals};
transform_expr(Expr, _, _, _, _, _) ->
Expr.
macro_env(Loc, File, Module, Imports, FunName, FunArity) ->
macro_env(Loc, File, Module, Imports, FunName, FunArity, map_field_assoc).
macro_env_pattern(Loc, File, Module, Imports, FunName, FunArity) ->
macro_env(Loc, File, Module, Imports, FunName, FunArity, map_field_exact).
macro_env(Loc, File, Module, Imports, FunName, FunArity, FieldType) ->
Line = get_line(Loc),
{map, Loc, [
{FieldType, Loc, {atom, Loc, '__struct__'}, {atom, Loc, 'Elixir.Macro.Env'}},
{FieldType, Loc, {atom, Loc, file}, {bin, Loc, [{bin_element, Loc, {string, Loc, File}, default, default}]}},
{FieldType, Loc, {atom, Loc, line}, {integer, Loc, Line}},
{FieldType, Loc, {atom, Loc, module}, {atom, Loc, Module}},
{FieldType, Loc, {atom, Loc, function}, {tuple, Loc, [{atom, Loc, FunName}, {integer, Loc, FunArity}]}},
{FieldType, Loc, {atom, Loc, context}, {atom, Loc, nil}},
{FieldType, Loc, {atom, Loc, functions}, imports(Loc, Imports)},
{FieldType, Loc, {atom, Loc, aliases}, {nil, Loc}},
{FieldType, Loc, {atom, Loc, lexical_tracker}, {atom, Loc, nil}},
{FieldType, Loc, {atom, Loc, macro_aliases}, {nil, Loc}},
{FieldType, Loc, {atom, Loc, versioned_vars}, {map, Loc, []}},
{FieldType, Loc, {atom, Loc, tracers}, {nil, Loc}},
{FieldType, Loc, {atom, Loc, requires}, {nil, Loc}},
{FieldType, Loc, {atom, Loc, macros}, {nil, Loc}},
{FieldType, Loc, {atom, Loc, context_modules}, list(Loc, [{atom, Loc, Module}])}
]}.
get_file([{attribute, _, file, {File, _}} | _]) -> File;
get_file([_ | Rest]) -> get_file(Rest);
get_file([]) -> "nofile".
get_module([{attribute, _, module, Module} | _]) -> Module;
get_module([_ | Rest]) -> get_module(Rest);
get_module([]) -> undefined.
get_imports(Forms) -> [{Mod, Funs} || {attribute, _, import, {Mod, Funs}} <- Forms].
get_line({Line, _}) when is_integer(Line) -> Line;
get_line(Line) when is_integer(Line) -> Line.
imports(Loc, Imports) -> list(Loc, [import_tuple(Loc, Mod, Funs) || {Mod, Funs} <- Imports]).
import_tuple(Loc, Mod, Funs) ->
FunsAST = list(Loc, [{tuple, Loc, [{atom, Loc, F}, {integer, Loc, A}]} || {F, A} <- Funs]),
{tuple, Loc, [{atom, Loc, Mod}, FunsAST]}.
list(Loc, []) -> {nil, Loc};
list(Loc, [H | T]) -> {cons, Loc, H, list(Loc, T)}.