Patterns
The package lang:bs:macro
contains a feature called patterns. Patterns are a convenient way to
create syntax trees in Basic Storm without manually instantiating classes for each node in the
tree. A pattern saves the parse tree generated by the parser and uses that (by calling transform
)
to generate the desired syntax tree automatically. For example, we can generate a syntax tree that
represents creating an array as follows:
Expr x = pattern(block) { Array<Int> array; array.push(1); array.push(2); }; print(x.toS());
The parameter block
(expected to be a subclass of lang.bs.Block
) to the pattern
syntax
dictates in which context identifiers inside the pattern shall be resolved. When creating syntax
trees for custom syntaxes, the parent rule will provide an appropriate block to use for context.
Patterns also allow inserting expressions generated by the surrounding code using the syntax $name
or ${expression}
. The first syntax only allows a single identifier, while the second syntax allow
arbitrary Basic Storm expressions to be evaluated and inserted at that location in the syntax
tree. They can be used as follows:
Expr e = Constant(SrcPos(), 20); Expr f = pattern(block) { 10 * $e + ${Constant(SrcPos(), 10)}; }; print(f.toS());
Which produces the expression:
{ 10 * 20 + 10; }
It is also possible to insert a variable number of expressions into a parameter list, an array
initializer or a block using @name
or @expression
. In this case, the expression or identifier
should evaluate to an array of Expr
objects, which are inserted in sequence at the specified
location. For example:
Expr[] e = Expr:[Constant(SrcPos(), 1), Constant(SrcPos(), 2)]; Expr f = pattern(block) { foo(@e, 8); }; print(f.toS());
Which produces the expression:
{ foo(1, 2, 8); }
It is not necessary to create a block. It is also possible to create a pattern containing a single statement:
Expr e = Constant(SrcPos(), 20); Expr f = pattern(block) 10 + $e;
As previously mentioned, patterns are useful when implementing syntax extensions. For example, we could implement a syntax for absolute values as follows:
BNF-file:
SAtom => absExpr(block, expr) : "|", SExpr(block) expr, "|";
BS-file:
use lang:bs; use lang:bs:macro; Expr absExpr(Block block, Expr expr) { pattern(block) { Int tmp = $expr; if (tmp < 0) { 0 - tmp; } else { tmp; } } }
From this example, we can also see that the patterns are hygienic, which means that if evaluating
the expression |tmp|
works as expected, even if tmp
is used as a variable name inside the
pattern.