sym Proto regexes

Documentation for sym Proto regexes, assembled from the following types:

class Grammar

From Grammar

(Grammar) sym Proto regexes

If you have a lot of alternations, it may become difficult to produce readable code or subclass your grammar. In the Actions class below, the ternary in method TOP is less than ideal and it becomes even worse the more operations we add:

grammar Calculator {
    token TOP { [ <add> | <sub> ] }
    rule  add { <num> '+' <num> }
    rule  sub { <num> '-' <num> }
    token num { \d+ }
}
 
class Calculations {
    method TOP ($/{ make $<add> ?? $<add>.made !! $<sub>.made}
    method add ($/{ make [+$<num>}
    method sub ($/{ make [-] $<num>}
}
 
say Calculator.parse('2 + 3'actions => Calculations).made;
 
# OUTPUT: «5␤» 

To make things better, we can use proto regexes that look like :sym<...> adverbs on tokens:

grammar Calculator {
    token TOP { <calc-op> }
 
    proto rule calc-op          {*}
          rule calc-op:sym<add> { <num> '+' <num> }
          rule calc-op:sym<sub> { <num> '-' <num> }
 
    token num { \d+ }
}
 
class Calculations {
    method TOP              ($/{ make $<calc-op>.made}
    method calc-op:sym<add> ($/{ make [+$<num>}
    method calc-op:sym<sub> ($/{ make [-] $<num>}
}
 
say Calculator.parse('2 + 3'actions => Calculations).made;
 
# OUTPUT: «5␤» 

In the grammar, the alternation has now been replaced with <calc-op>, which is essentially the name of a group of values we'll create. We do so by defining a rule prototype with proto rule calc-op. Each of our previous alternations have been replaced by a new rule calc-op definition and the name of the alternation is attached with :sym<> adverb.

In the actions class, we now got rid of the ternary operator and simply take the .made value from the $<calc-op> match object. And the actions for individual alternations now follow the same naming pattern as in the grammar: method calc-op:sym<add> and method calc-op:sym<sub>.

The real beauty of this method can be seen when you subclass that grammar and actions class. Let's say we want to add a multiplication feature to the calculator:

grammar BetterCalculator is Calculator {
    rule calc-op:sym<mult> { <num> '*' <num> }
}
 
class BetterCalculations is Calculations {
    method calc-op:sym<mult> ($/{ make [*$<num> }
}
 
say BetterCalculator.parse('2 * 3'actions => BetterCalculations).made;
 
# OUTPUT: «6␤» 

All we had to add are additional rule and action to the calc-op group and the thing works—all thanks to proto regexes.