-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)}.