Quellcode durchsuchen

Parsing Expressions

runningwater vor 2 Jahren
Ursprung
Commit
5a937e13ca

+ 20 - 0
README.md

@@ -47,3 +47,23 @@
   
     ```
 
+
+| Name       | Operators | Associates |
+|------------|-----------|------------|
+| Equality	  | == !=     | Left       |
+| Comparison | > >= < <= | Left       |
+| Term       | - +	      | Left       |
+| Factor     | / *       | Left       |
+| Unary      | ! -       | Right      |
+
+```
+expression     → equality ;
+equality       → comparison ( ( "!=" | "==" ) comparison )* ;
+comparison     → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
+term           → factor ( ( "-" | "+" ) factor )* ;
+factor         → unary ( ( "/" | "*" ) unary )* ;
+unary          → ( "!" | "-" ) unary
+               | primary ;
+primary        → NUMBER | STRING | "true" | "false" | "nil"
+               | "(" expression ")" ;
+```

+ 20 - 5
src/main/java/com/craftinginterpreters/lox/Lox.java

@@ -58,7 +58,7 @@ public class Lox {
 
             // reset this flag in the interactive loop. If the user makes a mistake,
             // it shouldn’t kill their entire session.
-            hadError = true;
+            //hadError = true;
         }
     }
 
@@ -66,9 +66,16 @@ public class Lox {
         Scanner scanner = new Scanner(source);
         List<Token> tokens = scanner.scanTokens();
 
-        for (Token token : tokens) {
-            System.out.println(token);
-        }
+        // for (Token token : tokens) {
+        //    System.out.println(token);
+        // }
+        Parser parser = new Parser(tokens);
+        Expr expression = parser.parse();
+
+        // Stop if there was a syntax error.
+        if (hadError) return;
+
+        System.out.println(new AstPrinter().print(expression));
     }
 
     /**
@@ -82,7 +89,15 @@ public class Lox {
     }
 
     private static void report(int line, String where, String message) {
-        System.err.println("[line " + line + " ] Error" + where + ": " + message);
+        System.err.println("[line " + line + " ] Error " + where + ": " + message);
         hadError = true;
     }
+
+    static void error(Token token, String message) {
+        if (token.type == TokenType.EOF) {
+            report(token.line, " at end", message);
+        } else {
+            report(token.line, " at '" + token.lexeme + "'", message);
+        }
+    }
 }

+ 187 - 0
src/main/java/com/craftinginterpreters/lox/Parser.java

@@ -0,0 +1,187 @@
+/* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
+ * All right reserved.*/
+package com.craftinginterpreters.lox;
+
+import java.util.List;
+
+import static com.craftinginterpreters.lox.TokenType.*;
+
+/**
+ * @author simon
+ * @date 2023-06-05 14:56
+ * @desc <p>| Name       | Operators | Associates | </p>
+ * <p>|------------|-----------|------------| </p>
+ * | Equality	  | == !=     | Left       |
+ * | Comparison | > >= < <= | Left       |
+ * | Term       | - +	      | Left       |
+ * | Factor     | / *       | Left       |
+ * | Unary      | ! -       | Right      |
+ * </p>
+ * <p>
+ * <p>
+ * expression     → equality ; <p>
+ * equality       → comparison ( ( "!=" | "==" ) comparison )* ; <p>
+ * comparison     → term ( ( ">" | ">=" | "<" | "<=" ) term )* ; <p>
+ * term           → factor ( ( "-" | "+" ) factor )* ; <p>
+ * factor         → unary ( ( "/" | "*" ) unary )* ; <p>
+ * unary          → ( "!" | "-" ) unary | primary ; <p>
+ * primary        → NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" ; <p>
+ * </p>
+ */
+public class Parser {
+    private static class ParseError extends RuntimeException {
+    }
+
+    private final List<Token> tokens;
+    private int current = 0;
+
+    Parser(List<Token> tokens) {
+        this.tokens = tokens;
+    }
+
+    Expr parse() {
+        try {
+            return expression();
+        } catch (ParseError e) {
+            return null;
+        }
+    }
+
+    // expression  → equality ;
+    private Expr expression() {
+        return equality();
+    }
+
+    // equality  → comparison ( ( "!=" | "==" ) comparison )* ;
+    private Expr equality() {
+        Expr expr = comparison();
+
+        while (match(BANG_EQUAL, EQUAL_EQUAL)) {
+            Token operator = previous();
+            Expr right = comparison();
+            expr = new Expr.Binary(expr, operator, right);
+        }
+        return expr;
+    }
+
+    // comparison     → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
+    private Expr comparison() {
+        Expr expr = term();
+        while (match(GRATER, GRATER_EQUAL, LESS, LESS_EQUAL)) {
+            Token operator = previous();
+            Expr right = term();
+            expr = new Expr.Binary(expr, operator, right);
+        }
+        return expr;
+    }
+
+    // term   → factor ( ( "-" | "+" ) factor )* ;
+    private Expr term() {
+        Expr expr = factor();
+        while (match(MINUS, PLUS)) {
+            Token operator = previous();
+            Expr right = factor();
+            expr = new Expr.Binary(expr, operator, right);
+        }
+        return expr;
+    }
+
+    // factor  → unary ( ( "/" | "*" ) unary )* ;
+    private Expr factor() {
+        Expr expr = unary();
+        while (match(SLASH, STAR)) {
+            Token operator = previous();
+            Expr right = unary();
+            expr = new Expr.Binary(expr, operator, right);
+        }
+        return expr;
+    }
+
+    // unary  → ( "!" | "-" ) unary | primary
+    private Expr unary() {
+        if (match(BANG, MINUS)) {
+            Token operator = previous();
+            Expr right = unary();
+            return new Expr.Unary(operator, right);
+        }
+        return primary();
+    }
+
+    //    primary        → NUMBER | STRING | "true" | "false" | "nil"
+    //            | "(" expression ")" ;
+    private Expr primary() {
+        if (match(FALSE)) return new Expr.Literal(false);
+        if (match(TRUE)) return new Expr.Literal(true);
+        if (match(NIL)) return new Expr.Literal(null);
+
+        if (match(NUMBER, STRING)) {
+            return new Expr.Literal(previous().literal);
+        }
+
+        if (match(LEFT_PARAM)) {
+            Expr expr = expression();
+            consume(RIGHT_PARAM, "Expect ') after expression.");
+            return new Expr.Grouping(expr);
+        }
+
+        throw error(peek(), "Expect expression.");
+    }
+
+    private Token consume(TokenType type, String message) {
+        if (check(type)) return advance();
+        throw error(peek(), message);
+    }
+
+
+    private boolean match(TokenType... types) {
+        for (TokenType type : types) {
+            if (check(type)) {
+                advance();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private Token advance() {
+        if (!isAtEnd()) current++;
+        return previous();
+    }
+
+    private boolean check(TokenType type) {
+        if (isAtEnd()) return false;
+        return peek().type == type;
+    }
+
+    private boolean isAtEnd() {
+        return peek().type == EOF;
+    }
+
+    private Token peek() {
+        return tokens.get(current);
+    }
+
+    private Token previous() {
+        return tokens.get(current - 1);
+    }
+
+    private ParseError error(Token token, String message) {
+        Lox.error(token, message);
+        return new ParseError();
+    }
+
+    private void synchronize() {
+        advance();
+
+        while (!isAtEnd()) {
+            if (previous().type == SEMICOLON) return;
+
+            switch (peek().type) {
+                case CLASS, FUN, VAR, FOR, IF, PRINT, WHILE, RETURN -> {
+                    return;
+                }
+                default -> advance();
+            }
+        } // End while
+    }
+}