Index

This file was automatically generated from http://svn.pugscode.org/pugs/docs/Perl6/API/macros.pod on Wed Jun 6 22:16:47 2007 GMT, revision 16639.

Macros in Perl have access to the raw, just-parsed representation of the program through their parameters. This representation is called the AST. The AST is documented in full elsewhere, but this document is written from the perspective of the macro itself, and demonstrates the major components of the AST that must be in place for typical macros to function.


API documentation: The AST API from the Macro Perspective

Macros in Perl have access to the raw, just-parsed representation of the program through their parameters. This representation is called the AST. The AST is documented in full elsewhere, but this document is written from the perspective of the macro itself, and demonstrates the major components of the AST that must be in place for typical macros to function.

Macro definition

A macro definition looks like this:

 macro fizzle(...) { ... }

Within the parameter list, any parameters can be defined, but they will always be of type AST, some sub-type of AST or plain scalars.

An invocant may be declared. If so, it will be the $/ object that contains the macro invocation itself, and thus contains all state relevant to the macro.

A macro can have an operator type:

 macro quote:<q> (...) { ... }

If it does, the operator type will constrain the parameters (if any) that are available to the macro, and a mis-match between the operator type and the parameter count will generate a compile-time error.

Macro types

Macros can be defined for each of the following operator types:

list
List operators are typical function-like macros that take arguments as a list of named or positional parameters. Each parameter is an Expression node.
term
Term macros are like list macros, but never take any arguments.
quote

[ Note: notice camel-casing of LiteralString. Is that what was intended? -ajs ]

Quote macros take a single LiteralString node. Backslash escaping is performed only for the balancing quote terminator. Should some other sort of parsing be required, use the "is parsed" trait.

prefix
Prefix macros take only a single Expression parameter.
infix
Infix macros take two Expression parameters.
postfix
Postfix macros can never be "is parsed" because their only parameter (an Expression node) is found before the macro name in the program text.
circumfix
Circumfix macros take a single Expression as a parameter.
postcircumfix
Postcircumfix macros take two parameters. The first a Value. The second is an Expression that comes inside of the circumfix delimeters.
regex_metachar
regex_backslash
regex_mod_internal
regex_assertion
regex_mod_external
TBD
trait_verb
trait_auxiliary
[ Ok, here's a scary kind of question... can a macro be multi, dispatched at compile-time based on AST sub-types? If so, that answers the question of how "class Foo is Bar" is distinguished from "my Foo $x is Bar" which is similar, but certainly has very different types of parameters (the LHS is an AST node that is either a class name or a variable declaration). -ajs ]
scope_declarator
Scope_delcatator macros take a Pad node which contains the information about the variable being declared.
statement_control

Statement_controls are like if or while, and take an Expression and a Block.

[ Question: How is elsif/else handled? Are they named as part of the macro somehow, or must any elsif block ever be called "elsif"? -ajs ]

statement_modifier
Statement_modifier macros take two parameters, a Statement and a expression.
infix_prefix_meta_operator
infix_postfix_meta_operator
prefix_circumfix_meta_operator

All of these macro types take three paramters: a Expression for the LHS, an InfixOperator node for the operator that it is modifying, and an Expression for the RHS.

[ Question: infix_postfix_meta_operator is designed for defining '=' which will take an LValue for its first parameter, but not all such operators will be for assignment, potentially. Again, this brings up the question of multi dispatch on AST node types. Is that what's intended, or should macro operator types be richer? -ajs ]

postfix_prefix_meta_operator
Postfix_prefix_meta_operator macros take an LValue and a PostfixOperator as parameters.
prefix_postfix_meta_operator
Prefix_postfix_meta_operator macros take a PrefixOperator and an LValue as parameters.
infix_circumfix_meta_operator

Infix_circumfix_meta_operator macros take an InfixOperator and an Expression as parameters.

[ Question: What about sub, is that a statement_control? What about use? Is that just a list op that does its thing at run-time? -ajs ]

Accessing AST Internals

ASTs can be treated much the same as any rule state object. They can be indexed like a hash to extract their match terms (in this case, subrule names that match AST nodes). However, they also carry state information about the file being parsed.

[ Question: how is that state information extracted? Is there a method that can be called to get line number for example? -ajs ]

Expressions are the most often-seen element of a macro's parameter list. Expressions

An Expression is either a Literal, a LValue (Variable/Apply/Call), or one of the special forms (Binding/Assignment). It can be tested like so:

 macro debug(*@exprs) {
   for @exprs -> $expr {
     if exists $expr<Literal> {
       ...
     } elsif exists $expr<LValue> {
       ...
     } elsif exists $expr<Binding> or exists $expr<Assignment> {
       ...
     } else {
       die "Unknown Expression type '$expr'\n";
     }
   }
 }

But in many cases, such a test is not required, as Expressions are so universally useful:

 use AST::Tools :all;
 macro debug(*@exprs) {
   q:code{ say {{{ astlist(@exprs) }}} };
 }

[ Question: we need some tools for constructing AST nodes from other AST nodes. One of the most obvious to me is the above, but my name for it ("astlist") is just a suggestion. It's probably exported by something like AST::Tools -ajs ]

[ Question: Another way to do that would be to have a generic AST initializer so that this worked: q:code{ say {{{ AST.new('List',@exprs) }}} }; which might make more sense, as you could do arbitrarily complex things with the combination of high-level tools and initializers like: macro curry($subroutine, *@args) { my $body = call_as_block($subroutine,\(=@args)); return AST.new('Closure', :$body); } -ajs ]

... More to come once we work out the questions above ...