After statements, we will now be adding support for logical operators and control flow statements - conditional (if-else) and looping (while, for).
What I built: Commit 089ad4d
What I understood:
1) If Statements
- We extend the
statement
rule to accomodateifStmt
, which leads toexpression()
. ThethenBranch
callsstatement()
again. - Also, we avoid the dangling-else problem, wherein nesting-if conditions cause uncertainty about which
if
statement anelse
clause belongs to - inner or outer? - This is by checking for the existence of an
else
branch which is assumed to belong to the innermostif
statement.
2) Logical Operators
- We modify the grammar to have
assignment()
callor()
(which callsand()
) instead ofequality()
.equality()
is called byand()
- We use short-circuiting while using logical-operators, which makes decisions basis the state of the left-operand.
- Ex: In
OR
, if the left operand is truthy (evaluates to true), the interpreter skips the right operand, and returns the left operand itself. - But if the left-operand is
false
, it has to check the right operand and compute the result based on it.
var greet = greeting or "Hello";
//defaults to Hello if greeting is null/false.
- In
AND
, if the left-operand isfalse
, the entire expression results infalse
, and thus the second operand needn't be evaluated.
//LOGIC
public Object visitLogicalExpr(Expr.Logical expr) {
Object left = evaluate(expr.left);
if (expr.operator.type == TokenType.OR) {
if (isTruthy(left)) return left;
} else {
if (!isTruthy(left)) return left;
}
return evaluate(expr.right);
}
3) While Loops
- The
statement
rule is further extended to callwhileStmt
, which basically executes the body of statements if the given condition evaluates to ‘true’
//LOGIC
public Void visitWhileStmt(Stmt.While stmt) {
while (isTruthy(evaluate(stmt.condition))) {
execute(stmt.body);
}
return null;
}
4) For Loops
-
forStmt
is added to thestatement
rule. A for condition is supposed to have three parts - initialiser, the condition, and the action (increment/decrement). Variables are created to hold these three parts. - Interestingly, we don’t evaluate a
for
loop as afor
loop, and actually convert it into awhile
loop - We first check for the initialiser by looking for
VAR
and callvarDeclaration()
if it’s present. - Then, the interpreter looks for a
;
, after which it callsexpression()
. Finally, we callexpression()
again on encountering an increment/decrement. - The statements are wrapped inside a block, as a list.
- We also append the increment condition to the body of statements.
- When then wrap this body into a
while
loop, that is executed after the initialiser statement runs exactly once.
for (var i = 0; i < 10; i = i + 1) print i;
is converted to
var i = 0; // Initialiser runs once
while (i < 10) {
// the parser here groups the original body of statements and the
increment into a single unit for the while loop’s body
{
print i;
i = i + 1;
}
}
What's next:
Functions! This takes the interpreter ahead significantly by introducing concepts like lexical scope, closures, call stack management, and a return mechanism.
Musings:
Coffee Cold is a song I deeply love. For some reason, it feels like both a dig at and a celebration of life’s strangeness. Like it’s trying to tell me that at the end of the day, there are things way beyond my control that shape my world. I can either choose to get upset due to them or smile and walk on, towards my next adventure. “Life is neither a Tempest, nor a Midsummer Night’s Dream. It’s more like a Comedy of Errors and you take it As You Like It.” As the Tao Te Ching says, it's alllll about the flow. 😉
Top comments (0)