README.md


![Travis CI](https://travis-ci.org/slepher/astranaut.svg?branch=master)

# requirements

  erlang R19 or higher  
  
# traverse

### traverse functions:

```erlang
  astranaut_traverse:map(map_fun(), form(), Opts :: opts()) -> 
    traverse_return(node()) | parse_transform_return(node()).
    
  astranaut_traverse:reduce(reduce_fun(), state(), form(), Opts :: opts()) -> 
    traverse_return(state()).
    
  astranaut_traverse:map_with_state(map_state_fun(), state(), form(), Opts :: opts()) -> 
    traverse_return(node()) | parse_transform_return(node()).
    
  astranaut_traverse:mapfold(mapfold_fun(), state(), form(), Opts :: opts()) -> 
    traverse_return({form(), state()}).
```

*arguments*

```erlang
  form()    :: node() | [node()].
  node()    :: erlang ast node.
  state()   :: any().
```

*traverse_fun()*

```erlang
  map_fun()       :: (node(), Attr :: attr()) -> TraverseFunReturn :: traverse_fun_return(node()).
  reduce_fun()    :: (node(), state(), Attr :: attr()) -> TraverseFunReturn :: traverse_fun_return(state()).
  map_state_fun() :: (node(), state(), Attr :: attr()) -> TraverseFunReturn :: traverse_fun_return(node()).
  mapfold_fun()   :: (node(), state(), Attr :: attr()) -> TraverseFunReturn :: traverse_fun_return({node(), state()}).
```

*Attr*

```erlang
  attr() :: #{step => Step :: step(), node :: NodeType :: node_type(), attribute :: Attribute}.
```

*Step*

    which traverse step while traversing, very useful while traverse_style() in opts() is all.

```erlang
  step()  :: pre | post | leaf. 
```

*NodeType*

    ast node type.

```erlang
  node_type() :: form | attribute | pattern | expression | guard. 
```

*Attribute*

  if NodeType is attribute, Attribute is name of attribute, or Attribute does not exists.

*TraverseFunReturn*

```erlang
  traverse_fun_return(A) :: A | {error, error()} | continue | {continue, A} |
                            #{'__struct__' => astranaut_traverse_fun_return, 
                              node := Node :: node(), state := State :: state(), continue := Continue :: boolean(),
                              error := error(), warning := error(), errors := [error()], warnings => [error()]}.
```

*Node*

  node transformed to new node in traverse_walk_fun(), default is node() provided in traverse_walk_fun().

*State*

    state used in traverse_walk_fun(), default is state() provided in traverse_walk_fun().

*Continue*
  
  if Continue is true or traverse_fun_return(A) is continue | {continue, A}, and Step of attr() is pre  
  skip traverse children of currrent node and go to next node, nothing affected when Step of attr() is leaf or post.

*error()*

```erlang
  error()   :: #{'__struct__' => astranaut_traverse_error, 
                 line := Line :: integer(), module := Module :: module(), reason => Reason :: term()} | 
                {Line, Module, Reason} | {Line, Reason} | Reason.
```            
    
*Line*

  expected error line, default is line of node in traverse_walk_fun().

*Module*

  error formatter module which provide format_error/1, default is formatter option in opts().

*Opts*

```erlang
  opts()    :: {traverse => TraverseStyle :: traverse_style(), parse_transform => ParseTransform :: boolean(),
                node => FormType :: form_type(), formatter => Formatter, 
                children => Children, sequence_children => SequenceChildren}.
```


*Formatter*

  error formatter module which provide format_error/1, default is astranaut_traverse.

*ParseTransform*

  traverse_return(node()) will be transformed to parse_transform_return()  
  which could directed used as return in parse_transform/2, useful in map/3, map_with_state/3.

*NodeType*

  node_type(). if from() is incomplete erlang ast, this should be provided to help generate node_type() in attr().  
  if top node is function or attribute, default top node_type() in attr() is form.  
  else, default top node_type() in attr() is expression.

*TraverseStyle*

  pre | post | all | leaf.

*Children*

   true: Only traverse children of node, not traverse node its self.

*SequenceChildren*

   callback to defined your own traverse children method

```erlang
SequenceChildren = fun(DeepListOfChildrenM) -> MChildren end.
```

   traverse right expression first in match expression

```erlang
SequenceChildren = 
  fun([PatternMs, ExpressionMs]) -> 
    %% reverse the traverse order, traverse ExpressionMs first
    %% deep_r_sequence_m means reverse sequence_m the first level of deep list.
    astranaut_traverse_monad:deep_r_sequence_m([PatternMs, ExpressionMs]) 
  end.
```

   do something special to Clause Patterns

```erlang
SequenceChildren = 
  fun([PatternMs|GuardsAndExpressionMs]) -> 
    %% PatternMs is a list of monad, sequence_m it to get a monad of list.
    PatternsM = astranaut_traverse_monad:sequence_m(PatternMs),
    %% do something special to PatternsM monad.
    PatternsM1 = do_something_special(PatternsM),
    %% deep_sequence_m the new tree.
    astranaut_traverse_monad:deep_sequence_m([PatternsM1|GuardsAndExpressionMs]) 
  end.
```

   do something special to Each Clause Patterns

```erlang
SequenceChildren = 
  fun([PatternMs|GuardsAndExpressionMs]) -> 
    %% PatternMs is a list of monad, sequence_m it to get a monad of list.
    PatternMs1 = lists:map(fun(PatternM) -> do_something_special(PatternM) end, PatternMs),
    %% deep_sequence_m the new tree.
    astranaut_traverse_monad:deep_sequence_m([PatternMs1|GuardsAndExpressionMs]) 
  end.
```
  
*traverse_return(Return)*

```erlang
  traverse_return(Return) :: Return | {ok, Return, Errors :: traverse_return_error(), Warnings :: traverse_return_error()} | 
                             {error, Errors, Warnings}.
```

*parse_transform_return(Return)*

```erlang
  parse_transform_return(Return) :: Return | {warning, Return, Warnings :: prase_transform_error()} |
                                    {error, Errors :: parse_transform_error(), Warnings}.
```

*ReturnError*

```erlang
  traverse_return_error() :: [{Line :: line(), Module :: module(), Reason :: term()}].
  parse_transform_error() :: [{File, traverse_retrun_error()}].
```

*Structs*

```erlang  
  astranaut_traverse:traverse_fun_return(#{}) -> traverse_fun_return(). 
  astranaut_traverse:traverse_error(#{}) -> error(). 
```

*Advanced*

  powerful map_m function if you famillar with monad.

```erlang
  astranaut_traverse:map_m((A, attr()) => monad(A), map_m_opts()) -> monad(A). 
```

## Quote

### quick start
   
  with 
   
    -include_lib("astranaut/include/quote.hrl").

  you can use quote(Code) to represent ast of the code.
  
    quote(Code) | quote(Code, Options)

*Options*

```
  atom() => {atom() => true}
  proplists() => map(),
  Line => #{line => Line}
  #{line => Line, code_line => CodeLine, debug => Debug}.
```

*Line*

  
   Line could be any expression, the ast will be transformed.

```erlang  
    quote(
      fun(_) ->
        ok
      end, 10). 
    =>
    astranaut:replace_line_zero(quote(fun(_) -> ok end), 10).
    =>
    {'fun', 10, {clauses, [{clause, 10, [{var, 10, '_'}], [], [{atom, 10, ok}]}]}}.
```

*CodeLine*

   if CodeLine is true

```erlang
    10: quote(
    11:   fun(_) ->
    12:     ok
    13: end, code_line).
    =>  
    {'fun' 10, {clauses, [{clause, 11, [{var, 11, '_'}], [], [{atom, 12, ok}]}]}}.
```

*Debug*

   if Debug is true, ast generated by quote will be printed to console at compile time.

### unquote

```erlang
unquote(Ast)
unquote = Ast.
unquote_splicing(Asts)
unquote_splicing = Asts.
```

*why two forms*

   unquote(Var) is not a valid ast in function clause pattern.

```erlang
Var = {var, 0, A}
quote(fun(unquote = Var) -> unquote(Var) end).
```
    
### variable binding
  
*bind one ast*

  _@V, same as unquote(V)
  
```erlang
    V = {var, 10, 'Var'},
    quote({hello, World, unquote(V)}) =>
    {tuple, 1, [{atom, 1, hello}, {var, 1, 'World'}, V]} =>
    {tuple, 1, [{atom, 1, hello}, {var, 1, 'World'}, {var, 10, 'Var'}]}
```

*bind a list of ast*

  _L@Vs,same as unquote_splicing(Vs)

```erlang
    Vs = [{var, 2, 'Var'}, {atom, 2, atom}],
    quote({A, unquote_splicing(Vs), B}) => 
    {tuple, 1, [{var, 1, 'A'}, Vs ++ [{var, 1, 'B'}]]} =>
    {tuple, 1, [{var, 1, 'A'}, {var, 2, 'Var'}, {atom, 2, atom}, {var, 1, 'B'}]}
```

*bind a value*

```erlang
  Atom = hello,
  Integer = 10,
  Float = 1.3,
  String = "123",
  Variable = 'Var',

  _A@Atom => {atom, 0, Atom} => {atom, 0, hello}
  _I@Integer => {integer, 0, Integer} => {integer, 0, 10}
  _F@Float => {float, 0, Float} => {float, 0, 1.3}
  _S@String => {string, 0, String} => {string, 0, "123"}
  _V@Variable => {var, 0, Variable} => {var, 0, 'Var'}
```

*why binding*

  _X@V could be used in any part of quoted ast.  
  it's legal:
  
```erlang
    Class = 'Class0',
    Exception = 'Exception0',
    StackTrace = 'StackTrace0',
    quote(
      try
        throw(hello)
      catch
        _V@Class:_V@Exception:_V@StackTrace ->
          erlang:raise(_V@Class, _V@Exception, _V@StackTrace)
      end).
```      

  it's illegal

```erlang
    Class = {var, 0, 'Class0'},
    Exception = {var, 0, 'Exception0'},
    StackTrace = {var, 0, 'StackTrace0'},   

    quote(
      try
        A
      catch
        unquote(Class):unquote(Exception):unquote(StackTrace) ->
          erlang:raise(_@Class, _@Exception, _@StackTrace)
      end).
```

   in other hand, V in unquote_xxx(V) could be any expression, it's more powerful than _X@V
   
### unquote and variable binding in pattern

  quote macro could also be used in pattern match such as  
  for limit of erlang ast format in pattern, some special forms is used
   
   left side of match

```erlang
     quote(_A@Atom) = {atom, 1, A}
     
     =>
     
     {atom, _, Atom} = {atom, 1, A}
```

  function pattern

```erlang
     macro_clause(quote = {hello, _A@World = World2} = C) ->
       quote({hello2, _A@World, _@World2,_@C});
     
     => 
     
     macro_clause({tuple, _, [{atom, _, hello}, {atom, _, World} = World2]} = C) ->
       {tuple, 2, {atom, 2, hello2}, {atom, 2, World}, World2, C}
```

  case clause pattern:
   
```erlang
     case Ast of
       quote(_A@Atom) ->
         Atom;
       _ ->
         other
     end.
     
     =>
     
     case ast of
         {atom, _, Atom} ->
             Atom;
         _ ->
             other
     end.
```

## Macro

*Usage*

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

macro.hrl add three attribute: use_macro, exec_macro debug_macro

*use_macro*

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

*exec_macro*

  execute macro and add result to current ast.

```erlang
-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.

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

*debug_macro*

```erlang
-debug_macro(true).
```

   module will be printed to console after astranaut_macro transform.

*opts()*

```erlang
  #{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 

```erlang
-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().  

```erlang
-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)).
```

=>

```erlang
-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.

```erlang
-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
     ).
```

```erlang
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.
```

=>

```erlang
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.

```erlang
*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).
```

### Rebinding 

```erlang
-include_lib("erlando/include/rebinding.hrl").

-rebinding_all(Opts).
-rebinding_fun(FAs).
-rebinding_fun({FAs, Opts}).

FAs = FA | [FA...].
FA = F | F/A.
Opts = Opt | [Opt...] | #{OptKey => OptValue}.
Opt = OptKey | {OptKey, OptValue}.
#{OptKey => OptValue} = #{debug => true | false}.
```

*Rebinding Attributes*

   -rebinding_all -rebinding_fun defines rebinding scope.  
   -rebinding_all meaning rebinding scope is all function.  
   -rebinding_fun meaning rebinding scope is in functions mentioned.  
   rebinding options is avaliable in scope mentioned.  
   rebinding option debug means print code after rebinding rules applied.  
   if neither -rebinding_fun nor -rebinding_all is used, rebinding scope is all function and rebinding options is [].

*Rebinding Rules* 

   pattern variables will be renamed while already used include:  
     function pattern variables  
     match pattern variables  
     list comprehension pattern variables  
     bitstring comprehension pattern variables   
   pattern variables with same name in same pattern scope will be renamed to same name.  
   other variable will be renamed follow last renamed vaiable last avaliable scope used.  
   +{pattern variable} means pinned variable like Elixir ^{pattern variable}, also works like other variable.

*Examples* 

```erlang
hello(A, A, B) ->
    {A, A, B} = {A + 1, A + 1, B + 1},
    {A, A, B}.
```

=> 

```erlang
hello(A, A, B) ->
  {A_1, A_1, B_1} = {A + 1, A + 1, B + 1},
  {A_1, A_1, B_1}.
```

```erlang
hello(A, B) ->
  A = 
    case A of
        B -> 
          B = A + B,
          A = A + B,
          B = A + B,
          B;
        A ->
          B = A + B
          B
    end,
  B = 
    case A of
        B -> 
          B = A + B,
          A = A + B,
          B = A + B,
          B;
        A ->
          B = A + B
          B
    end,
  {A, B}.
```

=>

```erlang
hello(A, B) ->
  A_2 = 
    case A of
        B -> 
          B_1 = A + B,
          A_1 = A + B_1,
          B_2 = A_1 + B_1,
          B_2;
        A ->
          B_1 = A + B
          B_1
    end,
    
  B_5 = 
    case A_2 of
        B -> 
          %% B_1 and B_2 is already used, next var name is B_3, last var name in scope is B.
          B_3 = A_2 + B,
          A_3 = A_2 + B_3,
          B_4 = A_3 + B_3,
          B_4,
        A_2 ->
          B_3 = A_2 + B
          B_3
    end,
  {A_2, B_5}.
```


```erlang
hello_f(A) ->
    A = A + 1,
    F = fun F (0) -> 0; F (A) -> A = F(A - 1), A end,
    A = F(A),
    A.
```

=> 

```erlang
hello_f(A) ->
    A_1 = A + 1,
    F = fun F(0) -> 0; F(A_2) -> A_3 = F(A_2 - 1), A_3 end,
    A_2 = F(A_1),
    F_1 = fun F_1(0) -> 0; F_1(A_3) -> A_4 = F_1(A_3 - 1), A_4 end,
    A_3 = F_1(A_2),
    A_3.
```