In Grammars§

See primary documentation in context for Action objects

A successful grammar match gives you a parse tree of Match objects, and the deeper that match tree gets, and the more branches in the grammar are, the harder it becomes to navigate the match tree to get the information you are actually interested in.

To avoid the need for diving deep into a match tree, you can supply an actions object. After each successful parse of a named rule in your grammar, it tries to call a method of the same name as the grammar rule, giving it the newly created Match object as a positional argument. If no such method exists, it is skipped.

Here is a contrived example of a grammar and actions in action:

grammar TestGrammar {
    token TOP { \d+ }
}

class TestActions {
    method TOP($/) {
        make(2 + $/);
    }
}

my $match = TestGrammar.parse('40', actions => TestActions.new);
say $match;         # OUTPUT: «「40」␤»
say $match.made;    # OUTPUT: «42␤»

An instance of TestActions is passed as named argument actions to the parse call, and when token TOP has matched successfully, it automatically calls method TOP, passing the match object as an argument.

To make it clear that the argument is a match object, the example uses $/ as a parameter name to the action method, though that's just a handy convention, nothing intrinsic; $match would have worked too, though using $/ does give the advantage of providing $<capture> as a shortcut for $/<capture>; we use another argument, anyway, in the action for TOP.

A slightly more involved example follows:

grammar KeyValuePairs {
    token TOP {
        [<pair> \v+]*
    }

    token pair {
        <key=.identifier> '=' <value=.identifier>
    }

    token identifier {
        \w+
    }
}

class KeyValuePairsActions {
    method pair      ($/) {
        make $/<key>.made => $/<value>.made
    }
    method identifier($/) {
        # subroutine `make` is the same as calling .make on $/
        make ~$/
    }

    method TOP ($match) {
        # can use any variable name for parameter, not just $/
        $match.make: $match<pair>».made
    }
}


my $actions = KeyValuePairsActions;
my @res = KeyValuePairs.parse(q:to/EOI/, :$actions).made;
second=b
hits=42
raku=d
EOI

for @res -> $p {
    say "Key: $p.key()\tValue: $p.value()";
}

This produces the following output:

Key: second     Value: b
Key: hits       Value: 42
Key: raku       Value: d

Rule pair, which parsed a pair separated by an equals sign, aliases the two calls to token identifier to separate capture names so that they are available more easily and intuitively, as they will be in the corresponding Action. The corresponding action method constructs a Pair object, and uses the .made property of the sub match objects. So it (like the action method TOP too) exploits the fact that action methods for submatches are called before those of the calling/outer regex. So action methods are called in post-order.

The action method TOP simply collects all the objects that were .made by the multiple matches of the pair rule, and returns them in a list. Please note that, in this case, we need to use the method form of make, since the routine form can only be used if the argument to the action method is $/. Inversely, if the argument of the method is $/, we can use simply make, which is equivalent to $/.make.

Also note that KeyValuePairsActions was passed as a type object to method parse, which was possible because none of the action methods use attributes (which would only be available in an instance).

We can extend above example by using inheritance.

use KeyValuePairs;

unit grammar ConfigurationSets is KeyValuePairs;

token TOP {
    <configuration-element>+ %% \v
}

token configuration-element {
    <pair>+ %% \v
}

token comment {
    \s* '#' .+? $$
}

token pair {
    <key=.identifier> '=' <value=.identifier> <comment>?
}

We are sub-classing (actually, sub-grammaring) the previous example; we have overridden the definition of pair by adding a comment; the previous TOP rule has been demoted to configuration-element, and there's a new TOP which now considers sets of configuration elements separated by vertical space. We can also reuse actions by subclassing the action class:

use KeyValuePairs;

unit class ConfigurationSetsActions is KeyValuePairsActions;

method configuration-element($match) {
    $match.make: $match<pair>».made
}

method TOP ($match) {
    my @made-elements = gather for $match<configuration-element> {
        take $_.made
    };
    $match.make( @made-elements );

}

All existing actions are reused, although obviously new ones have to be written for the new elements in the grammar, including TOP. These can be used together from this script:

use ConfigurationSets;
use ConfigurationSetsActions;

my $actions = ConfigurationSetsActions;
my $sets = ConfigurationSets.parse(q:to/EOI/, :$actions).made;
second=b # Just a thing
hits=42
raku=d

third=c # New one
hits=33
EOI

for @$sets -> $set {
    say "Element→ $set";
}

Which will print

Element→ second b hits 42 raku d
Element→ third c hits 33

In other cases, action methods might want to keep state in attributes. Then of course you must pass an instance to method parse.

Note that token ws is special: when :sigspace is enabled (and it is when we are using rule), it replaces certain whitespace sequences. This is why the spaces around the equals sign in rule pair work just fine and why the whitespace before closing } does not gobble up the newlines looked for in token TOP.