Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision | |||
creating:expressions [2025/04/14 17:08] – Wrote evaluation code ahelwer | creating:expressions [2025/04/14 17:57] (current) – Finished first draft of expression interpretation tutorial ahelwer | ||
---|---|---|---|
Line 849: | Line 849: | ||
public Object visitBinaryExpr(Expr.Binary expr) { | public Object visitBinaryExpr(Expr.Binary expr) { | ||
Object left = evaluate(expr.left); | Object left = evaluate(expr.left); | ||
- | Object right = evaluate(expr.right); | + | Object right = evaluate(expr.right); |
switch (expr.operator.type) { | switch (expr.operator.type) { | ||
Line 995: | Line 995: | ||
===== Section 7.3: Runtime Errors ===== | ===== Section 7.3: Runtime Errors ===== | ||
+ | |||
+ | In [[https:// | ||
+ | |||
+ | Here's our variant of the '' | ||
+ | |||
+ | <code java [highlight_lines_extra=" | ||
+ | private void checkNumberOperand(Token operator, Object operand) { | ||
+ | if (operand instanceof Integer) return; | ||
+ | throw new RuntimeError(operator, | ||
+ | } | ||
+ | |||
+ | private void checkNumberOperands(Token operator, | ||
+ | | ||
+ | if (left instanceof Integer && right instanceof Integer) return; | ||
+ | |||
+ | throw new RuntimeError(operator, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Create a new '' | ||
+ | Contents are identical to that give in the book except for the package name: | ||
+ | |||
+ | <code java [highlight_lines_extra=" | ||
+ | package tla; | ||
+ | |||
+ | class RuntimeError extends RuntimeException { | ||
+ | final Token token; | ||
+ | |||
+ | RuntimeError(Token token, String message) { | ||
+ | super(message); | ||
+ | this.token = token; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | We also need some helpers for boolean operator parameters since TLA⁺ is more strict about that; add these to the '' | ||
+ | |||
+ | <code java> | ||
+ | private void checkBooleanOperand(Token operator, Object operand) { | ||
+ | if (operand instanceof Boolean) return; | ||
+ | throw new RuntimeError(operator, | ||
+ | } | ||
+ | |||
+ | private void checkBooleanOperands(Token operator, Object left, Object right) { | ||
+ | if (left instanceof Boolean && right instanceof Boolean) return; | ||
+ | throw new RuntimeError(operator, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Finally, we need a helper for checking whether an operand is a set: | ||
+ | |||
+ | <code java> | ||
+ | private void checkSetOperand(Token operator, Object operand) { | ||
+ | if (operand instanceof Set<?> | ||
+ | throw new RuntimeError(operator, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Now add these checks to our interpreter code. | ||
+ | Here's negative: | ||
+ | <code java [highlight_lines_extra=" | ||
+ | case MINUS: | ||
+ | checkNumberOperand(expr.operator, | ||
+ | return -(int)operand; | ||
+ | </ | ||
+ | Logical not: | ||
+ | <code java [highlight_lines_extra=" | ||
+ | case NOT: | ||
+ | checkBooleanOperand(expr.operator, | ||
+ | return !(boolean)operand; | ||
+ | </ | ||
+ | Enabled: | ||
+ | <code java [highlight_lines_extra=" | ||
+ | case ENABLED: | ||
+ | checkBooleanOperand(expr.operator, | ||
+ | return (boolean)operand; | ||
+ | </ | ||
+ | Subtraction: | ||
+ | <code java [highlight_lines_extra=" | ||
+ | case MINUS: | ||
+ | checkNumberOperands(expr.operator, | ||
+ | return (int)left - (int)right; | ||
+ | </ | ||
+ | Addition: | ||
+ | <code java [highlight_lines_extra=" | ||
+ | case PLUS: | ||
+ | checkNumberOperands(expr.operator, | ||
+ | return (int)left + (int)right; | ||
+ | </ | ||
+ | Less than: | ||
+ | <code java [highlight_lines_extra=" | ||
+ | case LESS_THAN: | ||
+ | checkNumberOperands(expr.operator, | ||
+ | return (int)left < (int)right; | ||
+ | </ | ||
+ | The '' | ||
+ | <code java [highlight_lines_extra=" | ||
+ | case DOT_DOT: | ||
+ | checkNumberOperands(expr.operator, | ||
+ | Set< | ||
+ | </ | ||
+ | Set membership: | ||
+ | <code java [highlight_lines_extra=" | ||
+ | case IN: | ||
+ | checkSetOperand(expr.operator, | ||
+ | return ((Set<?> | ||
+ | </ | ||
+ | Disjunction: | ||
+ | <code java [highlight_lines_extra=" | ||
+ | case OR: | ||
+ | checkBooleanOperands(expr.operator, | ||
+ | return (boolean)left || (boolean)right; | ||
+ | </ | ||
+ | Conjunction: | ||
+ | <code java [highlight_lines_extra=" | ||
+ | case AND: | ||
+ | checkBooleanOperand(expr.operator, | ||
+ | if (!(boolean)left) return false; | ||
+ | Object right = evaluate(expr.right); | ||
+ | checkBooleanOperand(expr.operator, | ||
+ | return (boolean)right; | ||
+ | </ | ||
+ | Finally, '' | ||
+ | <code java [highlight_lines_extra=" | ||
+ | case IF: | ||
+ | Object conditional = evaluate(expr.first); | ||
+ | checkBooleanOperand(expr.operator, | ||
+ | return (boolean)conditional ? | ||
+ | evaluate(expr.second) : evaluate(expr.third); | ||
+ | </ | ||
+ | |||
+ | ===== Section 7.4: Hooking Up the Interpreter ===== | ||
+ | |||
+ | We're very close! | ||
+ | In [[https:// | ||
+ | First, add this public '' | ||
+ | <code java [highlight_lines_extra=" | ||
+ | void interpret(Expr expression) { | ||
+ | try { | ||
+ | Object value = evaluate(expression); | ||
+ | System.out.println(stringify(value)); | ||
+ | } catch (RuntimeError error) { | ||
+ | TlaPlus.runtimeError(error); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | In contrast to the book, our '' | ||
+ | |||
+ | <code java> | ||
+ | private String stringify(Object object) { | ||
+ | return object.toString(); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Add a '' | ||
+ | |||
+ | <code java> | ||
+ | static void runtimeError(RuntimeError error) { | ||
+ | System.err.println(error.getMessage() + | ||
+ | " | ||
+ | hadRuntimeError = true; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Then add a static '' | ||
+ | |||
+ | <code java [highlight_lines_extra=" | ||
+ | static boolean hadError = false; | ||
+ | static boolean hadRuntimeError = false; | ||
+ | |||
+ | public static void main(String[] args) throws IOException { | ||
+ | </ | ||
+ | |||
+ | And exit from the '' | ||
+ | |||
+ | <code java [highlight_lines_extra=" | ||
+ | run(new String(bytes, | ||
+ | |||
+ | // Indicate an error in the exit code. | ||
+ | if (hadError) System.exit(65); | ||
+ | if (hadRuntimeError) System.exit(70); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Finally, create a static '' | ||
+ | |||
+ | <code java [highlight_lines_extra=" | ||
+ | public class TlaPlus { | ||
+ | private static final Interpreter interpreter = new Interpreter(); | ||
+ | static boolean hadError = false; | ||
+ | </ | ||
+ | |||
+ | Then finish it off by actually interpreting the expression given by the user; add this in the '' | ||
+ | |||
+ | <code java [highlight_lines_extra=" | ||
+ | // Stop if there was a syntax error. | ||
+ | if (hadError) return; | ||
+ | |||
+ | interpreter.interpret(expression); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Voila! | ||
+ | Your program will now interpret any constant TLA⁺ expression you provide it! | ||
+ | Try it out: | ||
+ | < | ||
+ | > {0 .. 2, IF TRUE THEN 1 ELSE 2, {}} | ||
+ | [[], 1, [0, 1, 2]] | ||
+ | </ | ||
+ | |||
+ | If you got out of sync, you can find a snapshot of the expected state of the code in [[https:// | ||
+ | Next tutorial: [[https:// | ||
====== Challenges ====== | ====== Challenges ====== |