Finally, I created the Interpreter class that implements the visitor interface! From where all the function calls begin...
We can now evaluate various kinds of expressions - Literal, Binary, Unary, and Grouping (wherein we use accept()
to recursively evaluate the subexpression within the ()
).
I also created the RuntimeError class to handle errors in Lox.
What I built: Commits 6c14a8e & b49dfbc
What I understood:
1) The Interpreter class
- Like I'd previously mentioned, the visitor interface needs a separate class that implements it. This is how we "extend" behaviour.
- The class in question is
Interpret
, that begins with theinterpret()
method for evaluating and printing an expression, or reporting a runtime error. - Also, all the
visitSubclass()
methods we wrote in theExpr
class are overridden here to behave/act basis the type of expression parsed. - These
visitSubclass()
functions have not much to do - they merely extract the "values" from the "nodes" the Parser created and return them or the result of the expression they're in.
@Override
public Object visitBinaryExpr(Expr.Binary expr) {
Object left = evaluate(expr.left);
Object right = evaluate(expr.right);
switch (expr.operator.type) {
//comparison
case GREATER:
checkNumberOperands(expr.operator, left, right);
return (double)left > (double)right;
case GREATER_EQUAL:
checkNumberOperands(expr.operator, left, right);
return (double)left >= (double)right;
case LESS:
checkNumberOperands(expr.operator, left, right);
return (double)left < (double)right;
case LESS_EQUAL:
checkNumberOperands(expr.operator, left, right);
return (double)left <= (double)right;
//arithmetic
case MINUS:
checkNumberOperands(expr.operator, left, right);
return (double)left - (double)right;
case PLUS:
if (left instanceof Double && right instanceof Double) {
return (double)left + (double)right;
}
if (left instanceof String && right instanceof String) {
return (String)left + (String)right;
}
throw new RuntimeError(expr.operator, "Operands must be two numbers or two strings.");
case STAR:
checkNumberOperands(expr.operator, left, right);
return (double)left * (double)right;
case SLASH:
checkNumberOperands(expr.operator, left, right);
return (double)left / (double)right;
//equality
case BANG_EQUAL: return !isEqual(left, right);
case EQUAL_EQUAL: return isEqual(left, right);
}
//Unreachable
return null;
}
- We also use helper functions like
checkNumberOperands()
,checkNumberOperand()
,isTruthy()
,isEqual()
, andevaluate()
to simplify eachvisitSubclass()
function.
2) Runtime Errors
- Instead of having Lox's errors reported in Java, we needed to create a mechanism to display those errors in Lox itself.
- The underlying class we extend for this remains Java's
RuntimeException
though. - We create a
runtimeError()
method in Lox that uses theRuntimeError
class we created, to print the error message and line number.
What's next: Evaluating and working with statements (and not just expressions)!
Musings:
K-dramas are simply brilliant. I'm just using this platform to express my heartfelt gratitude for the gems that they are. And to the awesome people who make them. It's amusing how I'd scoff at their very mention just 3-4 years back. And I did so because I thought they were too unrealistic. Now I love them precisely because they're so unrealistic! I often look to them for inspiration on how not to take anything too seriously. When code or something else doesn't seem to work, I find it very helpful to watch yet another show revolving around a girl (a guy?) who suddenly finds herself in a Joseon-era royal kitchen. And lo! I return brimming with positivity, ideas, and the ability to understand just where in my code I erred. Sometimes, all we need to do is step back and take a chill-pill.
Top comments (0)