Skip to content
slepher edited this page Sep 25, 2019 · 3 revisions

Usage

-include_lib("astranaut/include/macro.hrl").

macro.hrl add three attribute: use_macro, exec_macro debug_macro

use_macro

-use_macro({Macro/A, opts()}).
-use_macro({Module, Macro/A, opts()}).

exec_macro

  execute macro and add result to current ast.

-exec_macro({Macro, Arguments}).
-exec_macro({Module, Macro, Arguments}).

export_macro

  used in where macro defined, options in export_macro will be merged to options in use_macro.

-export_macro({[MacroA/A, MacroB/B], opts()}).

debug_macro

-debug_macro(true).

   module will be printed to console after astranaut_macro transform.

opts()

  #{debug => Debug, debug_ast => DebugAst, alias => Alias, 
    formatter => Formatter, attrs => Attrs, order => Order,
    as_attr => AsAttr, merge_function => MergeFunction, auto_export => AutoExport,
    group_args => GroupArgs}
  }

   opts() could also be proplists, same usage of map().

Debug

  print code generated when macro called compile time.

DebugAst

  print ast generated when macro called compile time.

Alias

   use Alias(Arguments) instead of Module:Macro(Arguments).

Formatter

   module include format_error/1 to format macro errors,
   if formatter is true, formatter is the module where macro defined,
   default is astranaut_traverse.

Attrs

   module attributes as extra args while calling macro.

-module(a).
-behaviour(gen_server).
-use_macro({macro/2, [{attrs, [module, line, behaviour]}]}).

hello() ->
  macro_a:macro(world).

macro(Ast, #{module => Module, line => Line, behaviour => Behaviours} = Attributes) ->
    {warning, Ast, {attributes, Module, Line, Behaviours}}.

Order

   macro expand order for nested macro , value is pre | post. default is post.
   pre is expand macro from outside to inside, post is expand macro from inside to outside.

AsAttr

   user defined attribute name replace of -exec_macro.

MergeFunction

   -exec_macro ast function merge to function with same name and arity if exists.

AutoExport

   -exec_macro ast function auto export, merge to current export if exists.

GroupArgs

   treat macro arguments as list

-use_macro({a, [group_args]}).

test() ->
    a(hello, world).

a(Asts) ->
  quote({unquote_splicing(Asts)}).

  define macro as normal erlang functions.
  macro expand order is the order of -use_macro in file.
  macro will be expand at compile time by parse_transformer astranaut_macro.
  macro does not know runtime value of arguments.
  arguments passed in macro is erlang ast.
  arguments passed in -exec_macro is term.
  -export will be moved to appropriate location in ast forms.
  macro return value is same meaning of traverse_fun_return().

-use_macro({macro_1/1, []}).
-use_macro({macro_2/1, []}).

-export([test/0]).

test() ->
  macro_1(hello()).

macro_1(Ast) ->
  quote(
      fun() -> unquote(Ast) end
  ).

-exec_macro({macro_2, [hello]}).

macro_2(Name) ->
  astranaut:function(
    Name,
    quote(
      fun() ->
          unquote_atom(Name)
      end)).

=>

-use_macro({macro_1/1, []}).
-export([test/0]).
-export([hello/0]).

test_macro_1() ->
  fun() -> hello() end.

macro_1(Ast) ->
  quote(
      fun() -> unquote(Ast) end
  ).

hello() ->
  hello.

macro_2(Name) ->
  astranaut:function(
    Name,
    quote(
      fun() ->
          unquote_atom(Name)
      end)).

hygienic macro

   each macro expansion has it's unique namespace.

   @{macro_module_name}@_{counter} is added to it's original name.

-module(macro_example).
macro_with_vars_1(Ast) ->
    quote(
      begin
          A = 10,
          B = unquote(Ast),
          A + B
      end
     ).
macro_with_vars_2(Ast) ->
    quote(
      begin
          A = 10,
          B = unquote(Ast),
          A + B
      end
     ).
test_macro_with_vars(N) ->
    A1 = macro_with_vars_1(N),
    A2 = macro_with_vars_2(A1),
    A3 = macro_with_vars_2(N),
    A4 = macro_with_vars_1(A1),
    A1 + A2.

=>

test_macro_with_vars(N) ->
A1 =
begin
  A@macro_example@_1 = 10,
  B@macro_example@_1 = N,
  A@macro_example@_1 + B@macro_example@_1
end,
A2 = 
begin
  A@macro_example@_3 = 10,
  B@macro_example@_3 = A1,
  A@macro_example@_3 + B@macro_example@_3
end,
A3 = 
begin
  A@macro_example@_4 = 10,
  B@macro_example@_4 = N,
  A@macro_example@_4 + B@macro_example@_4
end,
A4 =
begin
  A@macro_example@_2 = 10,
  B@macro_example@_2 = A1,
  A@macro_example@_2 + B@macro_example@_2
end,
A1 + A2 + A3 + A4.

parse_transform

   for old parse_transform module which is used widely, two function is provided.

*astranaut_macro:transform_macro(Module, Function, Arity, Opts, Forms).
*astranaut_macro:transform_macros([Macro...], Forms).
Macro = {Module, Function, Arity, Opts}.

   example:

-module(do).

-include_lib("astranaut/include/quote.hrl").

-export([parse_transform/2]).

parse_transform(Forms, _Options) ->
    astranaut_macro:transform_macro(do_macro, do, 1, [{alias, do}, formatter], Forms).
Clone this wiki locally