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
statementrule to accomodateifStmt, which leads toexpression(). ThethenBranchcallsstatement()again. - Also, we avoid the dangling-else problem, wherein nesting-if conditions cause uncertainty about which
ifstatement anelseclause belongs to - inner or outer? - This is by checking for the existence of an
elsebranch which is assumed to belong to the innermostifstatement.
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
statementrule 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
-
forStmtis added to thestatementrule. 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
forloop as aforloop, and actually convert it into awhileloop - We first check for the initialiser by looking for
VARand 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
whileloop, 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)