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/11 20:24] – Completed chapter 6 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 59: | Line 59: | ||
| <code java [highlight_lines_extra=" | <code java [highlight_lines_extra=" | ||
| - | package | + | package tool; |
| import java.io.IOException; | import java.io.IOException; | ||
| Line 94: | Line 94: | ||
| PrintWriter writer = new PrintWriter(path, | PrintWriter writer = new PrintWriter(path, | ||
| - | writer.println(" | + | writer.println(" |
| writer.println(); | writer.println(); | ||
| writer.println(" | writer.println(" | ||
| Line 207: | Line 207: | ||
| <code java [highlight_lines_extra=" | <code java [highlight_lines_extra=" | ||
| - | package | + | package tla; |
| class AstPrinter implements Expr.Visitor< | class AstPrinter implements Expr.Visitor< | ||
| Line 277: | Line 277: | ||
| In [[https:// | In [[https:// | ||
| - | ===== Chapter | + | ===== Section |
| First, as in [[https:// | First, as in [[https:// | ||
| Line 299: | Line 299: | ||
| Worry not, it can still be made terse and understandable! | Worry not, it can still be made terse and understandable! | ||
| - | ===== Chapter | + | ===== Section |
| [[https:// | [[https:// | ||
| Line 310: | Line 310: | ||
| <code java [highlight_lines_extra=" | <code java [highlight_lines_extra=" | ||
| - | package | + | package tla; |
| import java.util.List; | import java.util.List; | ||
| import java.util.ArrayList; | import java.util.ArrayList; | ||
| - | import static | + | import static tla.TokenType.*; |
| class Parser { | class Parser { | ||
| 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 com.craftinginterpreters.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 com.craftinginterpreters.tla; | ||
| - | |||
| - | import java.util.List; | ||
| - | import java.util.ArrayList; | ||
| - | |||
| - | import static com.craftinginterpreters.tla.TokenType.*; | ||
| - | import static com.craftinginterpreters.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 491: | Line 459: | ||
| Operator op; | Operator op; | ||
| - | + | ||
| 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); | ||
| expr = new Expr.Binary(expr, | expr = new Expr.Binary(expr, | ||
| } | } | ||
| - | + | ||
| return expr; | return expr; | ||
| } | } | ||
| 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 681: | Line 652: | ||
| private void synchronize() { | private void synchronize() { | ||
| advance(); | advance(); | ||
| - | | + | |
| while(!isAtEnd()) { | while(!isAtEnd()) { | ||
| if (previous().type == EQUAL_EQUAL) return; | if (previous().type == EQUAL_EQUAL) return; | ||
| - | | + | |
| advance(); | advance(); | ||
| } | } | ||
| 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:// | ||
| + | Next tutorial: [[creating: | ||
| + | |||
| + | ====== Challenges ====== | ||
| + | |||
| + | Here are some optional challenges to flesh out your TLA⁺ interpreter, | ||
| + | |||
| + | [[creating: | ||