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

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.

    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

    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

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

why two forms

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

Var = {var, 0, A}
quote(fun(unquote = Var) -> unquote(Var) end).

variable binding

bind one ast

  _@V, same as unquote(V)

    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)

    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

  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:

    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

    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

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

  function pattern

     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:

     case Ast of
       quote(_A@Atom) ->
         Atom;
       _ ->
         other
     end.
     
     =>
     
     case ast of
         {atom, _, Atom} ->
             Atom;
         _ ->
             other
     end.
Clone this wiki locally