Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
creating:expressions [2025/04/14 17:57] – Finished first draft of expression interpretation tutorial ahelwer | creating:expressions [2025/05/13 15:58] (current) – Fixed lookahead in set literal parsing ahelwer | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ======= | + | ======= |
- | This tutorial page covers the next three chapters in //Crafting Interpreters//: | + | This tutorial page covers the next two chapters in //Crafting Interpreters//: |
- [[https:// | - [[https:// | ||
- [[https:// | - [[https:// | ||
- | - [[https:// | ||
Same as the book, we //could// build a parser for our entire minimal TLA⁺ language subset before moving on to interpreting it, but that would be boring! | Same as the book, we //could// build a parser for our entire minimal TLA⁺ language subset before moving on to interpreting it, but that would be boring! | ||
Line 38: | Line 37: | ||
ternary | ternary | ||
variadic | variadic | ||
- | operator | + | operator |
</ | </ | ||
There are a few interesting differences. | There are a few interesting differences. | ||
Line 51: | Line 50: | ||
The only difference between '' | The only difference between '' | ||
This is the perspective of a language implementer. | This is the perspective of a language implementer. | ||
- | Later on we will extend | + | |
+ | Note that we //could// also include | ||
===== Section 5.2: Implementing Syntax Trees ===== | ===== Section 5.2: Implementing Syntax Trees ===== | ||
Line 398: | Line 398: | ||
Now we have to define a table of operators with their details. | Now we have to define a table of operators with their details. | ||
- | For this, create | + | First use the handy Java 17 [[https:// |
+ | Put it at the top of the '' | ||
- | <code java> | + | <code java [highlight_lines_extra=" |
- | package tla; | + | class Parser { |
+ | private static enum Fix { PREFIX, INFIX, POSTFIX } | ||
+ | private static record Operator(Fix fix, TokenType token, | ||
+ | boolean assoc, int lowPrec, int highPrec) {} | ||
- | enum Fix { | + | private |
- | PREFIX, INFIX, POSTFIX | + | |
- | } | + | |
- | + | ||
- | class Operator { | + | |
- | | + | |
- | final TokenType token; | + | |
- | final boolean assoc; | + | |
- | final int lowPrec; | + | |
- | final int highPrec; | + | |
- | + | ||
- | public Operator(Fix fix, TokenType token, boolean assoc, | + | |
- | int lowPrec, int highPrec) { | + | |
- | this.fix = fix; | + | |
- | this.token = token; | + | |
- | this.assoc = assoc; | + | |
- | this.lowPrec = lowPrec; | + | |
- | this.highPrec = highPrec; | + | |
- | } | + | |
- | } | + | |
</ | </ | ||
- | |||
- | For convenience, | ||
- | |||
- | <code java [highlight_lines_extra=" | ||
- | package tla; | ||
- | |||
- | import java.util.List; | ||
- | import java.util.ArrayList; | ||
- | |||
- | import static tla.TokenType.*; | ||
- | import static tla.Fix.*; | ||
- | |||
- | class Parser { | ||
- | </ | ||
- | |||
You can find operator attributes on page 271 of // | You can find operator attributes on page 271 of // | ||
Line 446: | Line 416: | ||
<code java> | <code java> | ||
private static final Operator[] operators = new Operator[] { | private static final Operator[] operators = new Operator[] { | ||
- | new Operator(PREFIX, | + | new Operator(Fix.PREFIX, |
- | new Operator(PREFIX, | + | new Operator(Fix.PREFIX, |
- | new Operator(PREFIX, | + | new Operator(Fix.PREFIX, |
- | new Operator(INFIX, | + | new Operator(Fix.INFIX, |
- | new Operator(INFIX, | + | new Operator(Fix.INFIX, |
- | new Operator(INFIX, | + | new Operator(Fix.INFIX, |
- | new Operator(INFIX, | + | new Operator(Fix.INFIX, |
- | new Operator(INFIX, | + | new Operator(Fix.INFIX, |
- | new Operator(INFIX, | + | new Operator(Fix.INFIX, |
- | new Operator(INFIX, | + | new Operator(Fix.POSTFIX, PRIME, |
- | new Operator(INFIX, | + | |
- | new Operator(POSTFIX, | + | |
}; | }; | ||
</ | </ | ||
Line 493: | Line 461: | ||
Expr expr = operatorExpression(prec + 1); | Expr expr = operatorExpression(prec + 1); | ||
- | while ((op = matchOp(INFIX, | + | while ((op = matchOp(Fix.INFIX, prec)) != null) { |
Token operator = previous(); | Token operator = previous(); | ||
Expr right = operatorExpression(op.highPrec + 1); | Expr right = operatorExpression(op.highPrec + 1); | ||
Line 512: | Line 480: | ||
We need to modify the loop to return immediately if the infix operator is not associative: | We need to modify the loop to return immediately if the infix operator is not associative: | ||
<code java [highlight_lines_extra=" | <code java [highlight_lines_extra=" | ||
- | while ((op = matchOp(INFIX, | + | while ((op = matchOp(Fix.INFIX, prec)) != null) { |
Token operator = previous(); | Token operator = previous(); | ||
Expr right = operatorExpression(op.highPrec + 1); | Expr right = operatorExpression(op.highPrec + 1); | ||
Line 530: | Line 498: | ||
Operator op; | Operator op; | ||
- | if ((op = matchOp(PREFIX, | + | if ((op = matchOp(Fix.PREFIX, prec)) != null) { |
Token opToken = previous(); | Token opToken = previous(); | ||
Expr expr = operatorExpression( | Expr expr = operatorExpression( | ||
Line 556: | Line 524: | ||
} | } | ||
- | while ((op = matchOp(POSTFIX, | + | while ((op = matchOp(Fix.POSTFIX, prec)) != null) { |
Token opToken = previous(); | Token opToken = previous(); | ||
expr = new Expr.Unary(opToken, | expr = new Expr.Unary(opToken, | ||
Line 616: | Line 584: | ||
if (match(LEFT_BRACE)) { | if (match(LEFT_BRACE)) { | ||
Token operator = previous(); | Token operator = previous(); | ||
- | List< | + | List< |
- | if (RIGHT_BRACE | + | if (!check(RIGHT_BRACE)) { |
do { | do { | ||
elements.add(expression()); | elements.add(expression()); | ||
Line 668: | Line 636: | ||
Add the '' | Add the '' | ||
- | <code java [highlight_lines_extra=" | + | <code java [highlight_lines_extra=" |
class Parser { | class Parser { | ||
+ | private static enum Fix { PREFIX, INFIX, POSTFIX } | ||
+ | private static record Operator(Fix fix, TokenType token, | ||
+ | boolean assoc, int lowPrec, int highPrec) {} | ||
private static class ParseError extends RuntimeException {} | private static class ParseError extends RuntimeException {} | ||
Line 717: | Line 688: | ||
({ (+ 1 2) (IF true 3 4) ({)) | ({ (+ 1 2) (IF true 3 4) ({)) | ||
</ | </ | ||
- | + | If you got out of sync, you can find a snapshot of the expected state of the code in [[https:// | |
- | ====== Chapter 7: Evaluating Expressions ====== | + | Next tutorial: [[creating:evaluation|Evaluating Constant |
- | + | ||
- | Now that we can parse expressions, | + | |
- | Here we follow the material in [[https:// | + | |
- | It's an exciting chapter, so let's jump in. | + | |
- | + | ||
- | ===== Section 7.1: Representing Values ===== | + | |
- | + | ||
- | In [[https:// | + | |
- | Our mapping for TLA⁺ is fairly similar to Lox, although we use '' | + | |
- | TLA⁺ thankfully lacks '' | + | |
- | + | ||
- | ^ TLA⁺ type ^ Java representation | + | |
- | | Any TLA⁺ value | '' | + | |
- | | Boolean | + | |
- | | number | + | |
- | | set | '' | + | |
- | + | ||
- | Java does a huge amount of work for us with the capabilities of the '' | + | |
- | + | ||
- | ===== Section 7.2: Representing Values ===== | + | |
- | + | ||
- | [[https:// | + | |
- | Create a new file '' | + | |
- | + | ||
- | <code java [highlight_lines_extra=" | + | |
- | package tla; | + | |
- | + | ||
- | import java.util.Set; | + | |
- | import java.util.HashSet; | + | |
- | + | ||
- | class Interpreter implements Expr.Visitor< | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | If your IDE supports it, get it to automatically add all necessary stub methods to implement the '' | + | |
- | + | ||
- | Let's start with defining the value of literals. | + | |
- | Our code is completely unchanged from the book; add this '' | + | |
- | + | ||
- | <code java> | + | |
- | @Override | + | |
- | public Object visitLiteralExpr(Expr.Literal expr) { | + | |
- | return expr.value; | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | Our code for evaluating grouping (parentheses) is similarly unchanged from the book: | + | |
- | + | ||
- | <code java> | + | |
- | @Override | + | |
- | public Object visitGroupingExpr(Expr.Grouping expr) { | + | |
- | return evaluate(expr.expression); | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | This uses the '' | + | |
- | + | ||
- | <code java> | + | |
- | private Object evaluate(Expr expr) { | + | |
- | return expr.accept(this); | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | ==== Evaluating Unary Operators ==== | + | |
- | + | ||
- | Our first real difference is in the '' | + | |
- | Recall that since our '' | + | |
- | Here's how we define the negative prefix operator, casting the parameter to an '' | + | |
- | + | ||
- | <code java [highlight_lines_extra=" | + | |
- | @Override | + | |
- | public Object visitUnaryExpr(Expr.Unary expr) { | + | |
- | Object operand = evaluate(expr.expr); | + | |
- | + | ||
- | switch (expr.operator.type) { | + | |
- | case MINUS: | + | |
- | return -(int)operand; | + | |
- | } | + | |
- | + | ||
- | // Unreachable. | + | |
- | return null; | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | Note that the negative prefix operator is not actually a built-in operator of TLA⁺. | + | |
- | It is defined in the '' | + | |
- | However, because our minimal TLA⁺ language subset lacks module importing, for convenience we just define some common arithmetic operators as built-in. | + | |
- | You can find more information about the built-in TLA⁺ operators in chapter 16 of // | + | |
- | + | ||
- | Our logical-not prefix operator is denoted by the '' | + | |
- | We also have no use for the '' | + | |
- | Add this code to the '' | + | |
- | + | ||
- | <code java [highlight_lines_extra=" | + | |
- | switch (expr.operator.type) { | + | |
- | case NOT: | + | |
- | return !(boolean)operand; | + | |
- | case MINUS: | + | |
- | </ | + | |
- | + | ||
- | We still have two more unary operators to define: '' | + | |
- | Both are trivial in this domain but will become much more complicated later on. | + | |
- | For constant expressions, | + | |
- | Add the highlighted code to the '' | + | |
- | + | ||
- | <code java [highlight_lines_extra=" | + | |
- | switch (expr.operator.type) { | + | |
- | case ENABLED: | + | |
- | return (boolean)operand; | + | |
- | case NOT: | + | |
- | </ | + | |
- | + | ||
- | Priming a constant expression has no effect on the value of that expression, so just return the operand' | + | |
- | + | ||
- | <code java [highlight_lines_extra=" | + | |
- | switch (expr.operator.type) { | + | |
- | case PRIME: | + | |
- | return operand; | + | |
- | case ENABLED: | + | |
- | </ | + | |
- | + | ||
- | ==== Evaluating Binary Operators ==== | + | |
- | + | ||
- | Unlike Lox, addition in TLA⁺ is only defined between two numbers (at least in its standard '' | + | |
- | Here's how we define our basic binary arithmetic & comparison operators; add a '' | + | |
- | + | ||
- | <code java [highlight_lines_extra=" | + | |
- | @Override | + | |
- | public Object visitBinaryExpr(Expr.Binary expr) { | + | |
- | Object left = evaluate(expr.left); | + | |
- | Object right = evaluate(expr.right); | + | |
- | + | ||
- | switch (expr.operator.type) { | + | |
- | case MINUS: | + | |
- | return (int)left - (int)right; | + | |
- | case PLUS: | + | |
- | return (int)left + (int)right; | + | |
- | case LESS_THAN: | + | |
- | return (int)left < (int)right; | + | |
- | case EQUAL: | + | |
- | return left.equals(right); | + | |
- | } | + | |
- | + | ||
- | // Unreachable. | + | |
- | return null; | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | Note that since we don't have to worry about '' | + | |
- | However, this actually gives us different equality semantics from TLC. | + | |
- | In TLC, evaluating things like '' | + | |
- | In the interest of simplicity we are fine with keeping these looser semantics. | + | |
- | See the challenges at the end of this tutorial page if you're interested in more closely matching the semantics of TLC. | + | |
- | + | ||
- | Let's take our first foray into sets by defining the set membership operator in '' | + | |
- | + | ||
- | <code java [highlight_lines_extra=" | + | |
- | switch (expr.operator.type) { | + | |
- | case IN: | + | |
- | return ((Set<?> | + | |
- | case MINUS: | + | |
- | </ | + | |
- | + | ||
- | The question mark is some unusual Java syntax you probably haven' | + | |
- | It has to do with // | + | |
- | Think of it as casting the right operand to //some kind// of set, so we can access the '' | + | |
- | It is tempting to cast it to '' | + | |
- | + | ||
- | Similar to equality, we get looser semantics here than with TLC. | + | |
- | TLC will emit a runtime error when evaluating the expression '' | + | |
- | Again this is acceptable. | + | |
- | + | ||
- | Now for something more complicated. | + | |
- | Here's the set construction helper operator '' | + | |
- | + | ||
- | <code java [highlight_lines_extra=" | + | |
- | switch (expr.operator.type) { | + | |
- | case DOT_DOT: | + | |
- | Set< | + | |
- | int lower = (int)left; | + | |
- | int higher = (int)right; | + | |
- | if (lower <= higher) { | + | |
- | for (int i = lower; i <= higher; i++) { | + | |
- | set.add(i); | + | |
- | } | + | |
- | } | + | |
- | return set; | + | |
- | case IN: | + | |
- | </ | + | |
- | + | ||
- | So expressions like '' | + | |
- | We use '' | + | |
- | It's incredible how much we get for free here; Java sets even implement value-based de-duplication and equality comparison for us for arbitrarily-nested sets! | + | |
- | + | ||
- | The only binary operators remaining are logical '' | + | |
- | We actually want '' | + | |
- | Add the highlighted lines near the top of the '' | + | |
- | + | ||
- | <code java [highlight_lines_extra=" | + | |
- | public Object visitBinaryExpr(Expr.Binary expr) { | + | |
- | Object left = evaluate(expr.left); | + | |
- | switch (expr.operator.type) { | + | |
- | case AND: | + | |
- | if (!(boolean)left) return false; | + | |
- | Object right = evaluate(expr.right); | + | |
- | return (boolean)right; | + | |
- | default: | + | |
- | break; | + | |
- | } | + | |
- | + | ||
- | </ | + | |
- | + | ||
- | In an odd contrast, our '' | + | |
- | We add it in with the rest of the operators in '' | + | |
- | + | ||
- | <code java [highlight_lines_extra=" | + | |
- | Object right = evaluate(expr.right); | + | |
- | switch (expr.operator.type) { | + | |
- | case OR: | + | |
- | return (boolean)left || (boolean)right; | + | |
- | case DOT_DOT: | + | |
- | </ | + | |
- | + | ||
- | Why are these different? | + | |
- | It has to do with how they are used in TLA⁺ formulas. | + | |
- | Actions are usually defined as a conjunction of expressions serving as guards, where later expressions could result in a runtime error if evaluated in some cases. | + | |
- | For example, consider a variable '' | + | |
- | People often write expressions like '' | + | |
- | It would be a runtime error to evaluate '' | + | |
- | It is thus useful to use conjunction as a guard stopping expression interpretation if the first operand is not true. | + | |
- | In contrast, disjunction is used in TLA⁺ to express nondeterminism and so both branches of the disjunct need to be evaluated to see whether their expressions satisfy alternative next states. | + | |
- | You can read [[https:// | + | |
- | + | ||
- | ==== Evaluating Other Operators ==== | + | |
- | + | ||
- | Our '' | + | |
- | Add this '' | + | |
- | + | ||
- | <code java> | + | |
- | @Override | + | |
- | public Object visitTernaryExpr(Ternary expr) { | + | |
- | switch (expr.operator.type) { | + | |
- | case IF: | + | |
- | Object conditional = evaluate(expr.first); | + | |
- | return (boolean)conditional ? | + | |
- | evaluate(expr.second) : evaluate(expr.third); | + | |
- | default: | + | |
- | // Unreachable. | + | |
- | return null; | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | '' | + | |
- | Note our definition is also short-circuiting. | + | |
- | + | ||
- | Finally, here's how we define the set construction operator; add this '' | + | |
- | + | ||
- | <code java> | + | |
- | @Override | + | |
- | public Object visitVariadicExpr(Expr.Variadic expr) { | + | |
- | switch (expr.operator.type) { | + | |
- | case LEFT_BRACE: | + | |
- | Set< | + | |
- | for (Expr parameter : expr.parameters) { | + | |
- | set.add(evaluate(parameter)); | + | |
- | } | + | |
- | return set; | + | |
- | default: | + | |
- | // Unreachable. | + | |
- | return null; | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | ===== 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 ====== | ||
Here are some optional challenges to flesh out your TLA⁺ interpreter, | Here are some optional challenges to flesh out your TLA⁺ interpreter, | ||
- | You should save a copy of your code before attempting these. | ||
- | - TLC evaluates cross-type equality comparison as a runtime error. For example, '' | ||
- | - TLC requires set elements to all be of the same type. Trying to construct the set '' | ||
- | [[https:// | + | [[creating: |