Raku Books / Creating a Compiler in Raku / Control Flow
Implementing a loop
Let us implement a loop in our Lingua language. At the moment, let us
make it simple and allow only the loop keyword, where the
counter variable is decremented by the compiler.
my n = 5;
loop n say n;This program prints the values from 5 to 1 and stops.
The syntax of the loop clause is similar to the
if condition we saw in this chapter, and it can be embedded
on the top level of the grammar too:
rule TOP {
[
| <one-line-comment>
| <statement=conditional-statement> ';'
| <statement=loopped-statement> ';'
| <statement> ';'
]*
}
rule loopped-statement {
'loop' <variable-name> <statement>
}Next, we need a new type of AST node:
class AST::Loop is ASTNode {
has ASTNode $.variable;
has ASTNode $.statement;
}The syntax is chosen in such a way that you have to provide the loop
with already existing variable. In this case, we do not need to
introduce a new loop variable (such as $_ in Perl). So, in
the action method, reference to the variable using a
AST::Variable node:
method loopped-statement($/) {
$/.make(AST::Loop.new(
variable => AST::Variable.new(
variable-name => ~$/<variable-name>
),
statement => $/<statement>.made,
));
}Decreasing the counter is done in the evaluator:
multi method eval-node(AST::Loop $node) {
while %!var{$node.variable.variable-name} {
self.eval-node($node.statement);
%!var{$node.variable.variable-name}--;
}
}It may be safer to stop the loop when the counter is equal zero or it is negative:
while %!var{$node.variable.variable-name} > 0 {
. . .This allows us to avoid infinite loops when the initial value is not integer, for example:
my n = 5.5;
loop n say n;The test case this time needs more expected output lines than usual:
my n = 3;
loop n say n;
## 3
## 2
## 1