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/17 15:35] – Split evaluation tutorial into its own page 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 two chapters in //Crafting Interpreters//: | This tutorial page covers the next two chapters in //Crafting Interpreters//: | ||
Line 37: | Line 37: | ||
ternary | ternary | ||
variadic | variadic | ||
- | operator | + | operator |
</ | </ | ||
There are a few interesting differences. | There are a few interesting differences. | ||
Line 50: | 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 397: | 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 445: | 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 492: | 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 511: | 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 529: | 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 555: | 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 615: | 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 667: | 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 716: | 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:// | + | If you got out of sync, you can find a snapshot of the expected state of the code in [[https:// |
- | Next tutorial: [[https:// | + | Next tutorial: [[creating:evaluation|Evaluating Constant TLA⁺ Expressions]]! |
====== Challenges ====== | ====== Challenges ====== | ||
Line 723: | Line 695: | ||
Here are some optional challenges to flesh out your TLA⁺ interpreter, | Here are some optional challenges to flesh out your TLA⁺ interpreter, | ||
- | [[https:// | + | [[creating: |