Raku Books / Creating a Compiler in Raku / Control Flow
Implementing else
A logical next step is to implement the else clause. Let
us expand the test program:
my flag = 0;
if flag say "Depends" else say "Otherwise";
say "Always";The else keyword can only appear if there was an
if before. In our current grammar, adding the
else block after the statement alternatives is tricky. It
is better to re-organise the grammar and make the statement
rule only contain statement alternatives, as it was before.
Let us allow the if-else construct on the
top level:
rule TOP {
[
| <one-line-comment>
| <statement=conditional-statement> ';'
| <statement> ';'
]*
}
rule conditional-statement {
'if' <value> <statement>
[
'else' <statement>
]?
}
rule statement {
| <statement=variable-declaration>
| <statement=assignment>
| <statement=function-call>
}This grammar will not allow an else clause without the
if prefix, and we could easily refer to the
statement rule twice inside
conditional-statement.
Notice that in the TOP rule, the new alternative is
given an alias, statement, which let us keep the same loop
in the corresponding action:
method TOP($/) {
. . .
for $<statement> -> $statement {
$top.statements.push($statement.made);
}
. . .
}OK, we can now have one or two statements in the conditional
construct. The AST::Condition node gets a second
attribute:
class AST::Condition is ASTNode {
has ASTNode $.value;
has ASTNode $.statement;
has ASTNode $.antistatement;
}The new attribute is filled with either AST::Null (if
there is no else clause) or with a statement node:
method conditional-statement($/) {
$/.make(AST::Condition.new(
value => $/<value>.made,
statement => $/<statement>[0].made,
antistatement => $/<statement>[1] ??
$/<statement>[1].made !! AST::Null,
));
}The statement action can now be reverted to a single
method with the trivial body:
method statement($/) {
$/.make($<statement>.made);
}Finally, evaluator must choose which statement to execute:
multi method eval-node(AST::Condition $node) {
if $node.value.value {
self.eval-node($node.statement);
}
else {
self.eval-node($node.antistatement);
}
}We also have to implement the behaviour of the AST::Null
node:
multi method eval-node(AST::Null) {
}The else keyword is ready and functioning, and a test
t/else.t must be added to the repository:
if 1 say "A" else say "B"; ## A
if 0 say "A" else say "B"; ## B
my x;
if 1 x = 10 else x = 20;
say x; ## 10
if 0 x = 30 else x = 40;
say x; ## 40