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.