Pārlūkot izejas kodu

goole-java-format

runningwater 2 gadi atpakaļ
vecāks
revīzija
f2eba4b617

+ 6 - 0
.idea/google-java-format.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GoogleJavaFormatSettings">
+    <option name="enabled" value="true" />
+  </component>
+</project>

+ 25 - 25
README.md

@@ -1,21 +1,22 @@
-
 ## Lox
+
 ```c
  // Your first Lox program!
  print "Hello, world!";
 ```
+
 - Dynamic typing
 - Automatic memory management
 - Data Types
-  - Booleans
-  - Numbers
-  - Strings
-  - Nil
+    - Booleans
+    - Numbers
+    - Strings
+    - Nil
 - Expressions
-  - Arithmetic
-  - Comparison and equality
-  - Logical operators
-  - Precedence and grouping
+    - Arithmetic
+    - Comparison and equality
+    - Logical operators
+    - Precedence and grouping
 - Statements
 - Variables
 - Control Flow
@@ -30,23 +31,22 @@
 - **Binary expressions.** The infix arithmetic(+,-,*,/) and logic operators (==, !=, <, <=,>,>=)
 - **Parentheses.** A pair of ( and ) wrapped around and expression.
 
-  - Syntax tree
-  
-    ```
-    expression     → literal
-    | unary
-    | binary
-    | grouping ;
-  
-    literal        → NUMBER | STRING | "true" | "false" | "nil" ;
-    grouping       → "(" expression ")" ;
-    unary          → ( "-" | "!" ) expression ;
-    binary         → expression operator expression ;
-    operator       → "==" | "!=" | "<" | "<=" | ">" | ">="
-    | "+"  | "-"  | "*" | "/" ;
-  
-    ```
+    - Syntax tree
 
+      ```
+      expression     → literal
+      | unary
+      | binary
+      | grouping ;
+    
+      literal        → NUMBER | STRING | "true" | "false" | "nil" ;
+      grouping       → "(" expression ")" ;
+      unary          → ( "-" | "!" ) expression ;
+      binary         → expression operator expression ;
+      operator       → "==" | "!=" | "<" | "<=" | ">" | ">="
+      | "+"  | "-"  | "*" | "/" ;
+    
+      ```
 
 | Name       | Operators | Associates |
 |------------|-----------|------------|

+ 54 - 52
src/main/java/com/craftinginterpreters/lox/Expr.java

@@ -5,71 +5,73 @@ package com.craftinginterpreters.lox;
  * @date 2023-06-05 13:42
  */
 abstract class Expr {
-    interface Visitor<R> {
-        R visitBinaryExpr(Binary expr);
+  abstract <R> R accept(Visitor<R> visitor);
 
-        R visitGroupingExpr(Grouping expr);
+  interface Visitor<R> {
+    R visitBinaryExpr(Binary expr);
 
-        R visitLiteralExpr(Literal expr);
+    R visitGroupingExpr(Grouping expr);
 
-        R visitUnaryExpr(Unary expr);
+    R visitLiteralExpr(Literal expr);
 
+    R visitUnaryExpr(Unary expr);
+  }
+
+  static class Binary extends Expr {
+    final Expr left;
+    final Token operator;
+    final Expr right;
+
+    Binary(Expr left, Token operator, Expr right) {
+      this.left = left;
+      this.operator = operator;
+      this.right = right;
     }
 
-    static class Binary extends Expr {
-        Binary(Expr left, Token operator, Expr right) {
-            this.left = left;
-            this.operator = operator;
-            this.right = right;
-        }
-
-        @Override
-        <R> R accept(Visitor<R> visitor) {
-            return visitor.visitBinaryExpr(this);
-        }
-
-        final Expr left;
-        final  Token operator;
-        final  Expr right;
+    @Override
+    <R> R accept(Visitor<R> visitor) {
+      return visitor.visitBinaryExpr(this);
     }
-    static class Grouping extends Expr {
-        Grouping(Expr expression) {
-            this.expression = expression;
-        }
+  }
 
-        @Override
-        <R> R accept(Visitor<R> visitor) {
-            return visitor.visitGroupingExpr(this);
-        }
+  static class Grouping extends Expr {
+    final Expr expression;
 
-        final Expr expression;
+    Grouping(Expr expression) {
+      this.expression = expression;
     }
-    static class Literal extends Expr {
-        Literal(Object value) {
-            this.value = value;
-        }
 
-        @Override
-        <R> R accept(Visitor<R> visitor) {
-            return visitor.visitLiteralExpr(this);
-        }
+    @Override
+    <R> R accept(Visitor<R> visitor) {
+      return visitor.visitGroupingExpr(this);
+    }
+  }
 
-        final Object value;
+  static class Literal extends Expr {
+    final Object value;
+
+    Literal(Object value) {
+      this.value = value;
     }
-    static class Unary extends Expr {
-        Unary(Token operator, Expr right) {
-            this.operator = operator;
-            this.right = right;
-        }
-
-        @Override
-        <R> R accept(Visitor<R> visitor) {
-            return visitor.visitUnaryExpr(this);
-        }
-
-        final Token operator;
-        final  Expr right;
+
+    @Override
+    <R> R accept(Visitor<R> visitor) {
+      return visitor.visitLiteralExpr(this);
     }
+  }
 
-    abstract <R> R accept(Visitor<R> visitor);
+  static class Unary extends Expr {
+    final Token operator;
+    final Expr right;
+
+    Unary(Token operator, Expr right) {
+      this.operator = operator;
+      this.right = right;
+    }
+
+    @Override
+    <R> R accept(Visitor<R> visitor) {
+      return visitor.visitUnaryExpr(this);
+    }
+  }
 }

+ 109 - 0
src/main/java/com/craftinginterpreters/lox/Interpreter.java

@@ -0,0 +1,109 @@
+/* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
+ * All right reserved.*/
+package com.craftinginterpreters.lox;
+
+/**
+ * @author simon
+ * @date 2023-06-07 12:20
+ * @desc
+ */
+public class Interpreter implements Expr.Visitor<Object> {
+  @Override
+  public Object visitBinaryExpr(Expr.Binary expr) {
+    Object left = evaluate(expr.left);
+    Object right = evaluate(expr.right);
+
+    return switch (expr.operator.type) {
+      case GRATER -> {
+        checkNumberOperand(expr.operator, left, right);
+        yield (double) left > (double) right;
+      }
+      case GRATER_EQUAL -> {
+        checkNumberOperand(expr.operator, left, right);
+        yield (double) left >= (double) right;
+      }
+      case LESS -> {
+        checkNumberOperand(expr.operator, left, right);
+        yield (double) left < (double) right;
+      }
+      case LESS_EQUAL -> {
+        checkNumberOperand(expr.operator, left, right);
+        yield (double) left <= (double) right;
+      }
+      case BANG_EQUAL -> !isEqual(left, right);
+      case EQUAL_EQUAL -> isEqual(left, right);
+      case MINUS -> {
+        checkNumberOperand(expr.operator, left, right);
+        yield (double) left - (double) right;
+      }
+      case PLUS -> {
+        if (left instanceof Double && right instanceof Double) {
+          yield (double) left + (double) right;
+        }
+        if (left instanceof String && right instanceof String) {
+          yield left + (String) right;
+        }
+        yield null;
+      }
+      case SLASH -> {
+        checkNumberOperand(expr.operator, left, right);
+        yield (double) left / (double) right;
+      }
+      case STAR -> {
+        checkNumberOperand(expr.operator, left, right);
+        yield (double) left * (double) right;
+      }
+      default -> null;
+    };
+  }
+
+  @Override
+  public Object visitGroupingExpr(Expr.Grouping expr) {
+    return evaluate(expr.expression);
+  }
+
+  @Override
+  public Object visitLiteralExpr(Expr.Literal expr) {
+    return expr.value;
+  }
+
+  @Override
+  public Object visitUnaryExpr(Expr.Unary expr) {
+    Object right = evaluate(expr.right);
+
+    return switch (expr.operator.type) {
+      case MINUS -> {
+        checkNumberOperand(expr.operator, right);
+        yield -(double) right;
+      }
+      case BANG -> !isTruthy(right);
+      default -> null;
+    };
+  }
+
+  private Object evaluate(Expr expr) {
+    return expr.accept(this);
+  }
+
+  private boolean isTruthy(Object object) {
+    if (object == null) return false;
+    if (object instanceof Boolean) return (boolean) object;
+    return true;
+  }
+
+  private boolean isEqual(Object a, Object b) {
+    if (a == null && b == null) return true;
+    if (a == null) return false;
+    return a.equals(b);
+  }
+
+  private void checkNumberOperand(Token operator, Object operand) {
+    if (operand instanceof Double) return;
+    throw new RuntimeError(operator, "Operand must be a number.");
+  }
+
+  private void checkNumberOperand(Token operator, Object left, Object right) {
+    if (left instanceof Double && right instanceof Double) return;
+    throw new RuntimeError(operator, "Operands must be numbers.");
+  }
+}

+ 66 - 68
src/main/java/com/craftinginterpreters/lox/Lox.java

@@ -16,88 +16,86 @@ import java.util.List;
  * @desc
  */
 public class Lox {
-    private static boolean hadError = false;
+  private static boolean hadError = false;
 
-    public static void main(String[] args) throws IOException {
-        if (args.length > 1) {
-            System.out.println("Usage: jlox [script]");
-            System.exit(64);
-        } else if (args.length == 1) {
-            runFile(args[0]);
-        } else {
-            runPrompt();
-        }
+  public static void main(String[] args) throws IOException {
+    if (args.length > 1) {
+      System.out.println("Usage: jlox [script]");
+      System.exit(64);
+    } else if (args.length == 1) {
+      runFile(args[0]);
+    } else {
+      runPrompt();
     }
+  }
 
-    /**
-     * start jlox from the command line and give it a path to a file,
-     * it reads the file and executes it
-     *
-     * @param path 文件路径
-     */
-    private static void runFile(String path) throws IOException {
-        byte[] bytes = Files.readAllBytes(Path.of(path));
-        run(new String(bytes, Charset.defaultCharset()));
+  /**
+   * start jlox from the command line and give it a path to a file, it reads the file and executes
+   * it
+   *
+   * @param path 文件路径
+   */
+  private static void runFile(String path) throws IOException {
+    byte[] bytes = Files.readAllBytes(Path.of(path));
+    run(new String(bytes, Charset.defaultCharset()));
 
-        // Indicate an error in the exit code.
-        if (hadError) System.exit(65);
-    }
+    // Indicate an error in the exit code.
+    if (hadError) System.exit(65);
+  }
 
-    /**
-     * 提示符里面输入
-     */
-    private static void runPrompt() throws IOException {
-        InputStreamReader input = new InputStreamReader(System.in);
-        BufferedReader reader = new BufferedReader(input);
+  /** 提示符里面输入 */
+  private static void runPrompt() throws IOException {
+    InputStreamReader input = new InputStreamReader(System.in);
+    BufferedReader reader = new BufferedReader(input);
 
-        for (; ; ) {
-            System.out.print("> ");
-            String line = reader.readLine();
-            if (line == null) break;
-            run(line);
+    for (; ; ) {
+      System.out.print("> ");
+      String line = reader.readLine();
+      if (line == null) break;
+      run(line);
 
-            // reset this flag in the interactive loop. If the user makes a mistake,
-            // it shouldn’t kill their entire session.
-            //hadError = true;
-        }
+      // reset this flag in the interactive loop. If the user makes a mistake,
+      // it shouldn’t kill their entire session.
+      // hadError = true;
     }
+  }
 
-    private static void run(String source) {
-        Scanner scanner = new Scanner(source);
-        List<Token> tokens = scanner.scanTokens();
+  private static void run(String source) {
+    Scanner scanner = new Scanner(source);
+    List<Token> tokens = scanner.scanTokens();
 
-        // for (Token token : tokens) {
-        //    System.out.println(token);
-        // }
-        Parser parser = new Parser(tokens);
-        Expr expression = parser.parse();
+    // 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;
+    // Stop if there was a syntax error.
+    if (hadError) return;
 
-        System.out.println(new AstPrinter().print(expression));
-    }
+    System.out.println(new AstPrinter().print(expression));
+  }
 
-    /**
-     * 错误处理
-     *
-     * @param line    行号
-     * @param message 消息
-     */
-    static void error(int line, String message) {
-        report(line, "", message);
-    }
+  /**
+   * 错误处理
+   *
+   * @param line 行号
+   * @param message 消息
+   */
+  static void error(int line, String message) {
+    report(line, "", message);
+  }
 
-    private static void report(int line, String where, String message) {
-        System.err.println("[line " + line + " ] Error " + where + ": " + message);
-        hadError = true;
-    }
+  private static void report(int line, String where, String 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);
-        }
+  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);
     }
+  }
 }

+ 156 - 163
src/main/java/com/craftinginterpreters/lox/Parser.java

@@ -2,186 +2,179 @@
  * All right reserved.*/
 package com.craftinginterpreters.lox;
 
-import java.util.List;
-
 import static com.craftinginterpreters.lox.TokenType.*;
 
+import java.util.List;
+
 /**
  * @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>
+ * @desc
+ *     <p>| Name | Operators | Associates |
+ *     <p>|------------|-----------|------------| | Equality | == != | Left | | Comparison | > >= <
+ *     <= | Left | | Term | - + | Left | | Factor | / * | Left | | Unary | ! - | Right |
+ *     <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>
  */
 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;
+  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;
+  }
 
-    // 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;
-    }
+  private Token advance() {
+    if (!isAtEnd()) current++;
+    return previous();
+  }
 
-    // unary  → ( "!" | "-" ) unary | primary
-    private Expr unary() {
-        if (match(BANG, MINUS)) {
-            Token operator = previous();
-            Expr right = unary();
-            return new Expr.Unary(operator, right);
-        }
-        return primary();
-    }
+  private boolean check(TokenType type) {
+    if (isAtEnd()) return false;
+    return peek().type == type;
+  }
 
-    //    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);
+  private boolean isAtEnd() {
+    return peek().type == EOF;
+  }
 
-        if (match(NUMBER, STRING)) {
-            return new Expr.Literal(previous().literal);
-        }
+  private Token peek() {
+    return tokens.get(current);
+  }
 
-        if (match(LEFT_PARAM)) {
-            Expr expr = expression();
-            consume(RIGHT_PARAM, "Expect ') after expression.");
-            return new Expr.Grouping(expr);
-        }
+  private Token previous() {
+    return tokens.get(current - 1);
+  }
 
-        throw error(peek(), "Expect expression.");
-    }
+  private ParseError error(Token token, String message) {
+    Lox.error(token, message);
+    return new ParseError();
+  }
 
-    private Token consume(TokenType type, String message) {
-        if (check(type)) return advance();
-        throw error(peek(), message);
-    }
+  private void synchronize() {
+    advance();
 
+    while (!isAtEnd()) {
+      if (previous().type == SEMICOLON) return;
 
-    private boolean match(TokenType... types) {
-        for (TokenType type : types) {
-            if (check(type)) {
-                advance();
-                return true;
-            }
+      switch (peek().type) {
+        case CLASS, FUN, VAR, FOR, IF, PRINT, WHILE, RETURN -> {
+          return;
         }
-        return false;
-    }
-
-    private Token advance() {
-        if (!isAtEnd()) current++;
-        return previous();
-    }
-
-    private boolean check(TokenType type) {
-        if (isAtEnd()) return false;
-        return peek().type == type;
-    }
+        default -> advance();
+      }
+    } // End while
+  }
 
-    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
-    }
+  private static class ParseError extends RuntimeException {}
 }

+ 17 - 0
src/main/java/com/craftinginterpreters/lox/RuntimeError.java

@@ -0,0 +1,17 @@
+/* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
+ * All right reserved.*/
+package com.craftinginterpreters.lox;
+
+/**
+ * @author simon
+ * @date 2023-06-07 15:22
+ * @desc
+ */
+public class RuntimeError extends RuntimeException {
+    final Token token;
+
+    public RuntimeError(Token token, String message) {
+        super(message);
+        this.token = token;
+    }
+}

+ 180 - 190
src/main/java/com/craftinginterpreters/lox/Scanner.java

@@ -2,210 +2,200 @@
  * All right reserved.*/
 package com.craftinginterpreters.lox;
 
+import static com.craftinginterpreters.lox.TokenType.*;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import static com.craftinginterpreters.lox.TokenType.*;
-
 /**
  * @author simon
  * @date 2023-06-04 16:30
  * @desc
  */
 public class Scanner {
-    private final String source;
-    private final List<Token> tokens = new ArrayList<>();
-    private int start = 0;  //  points to the first character in the lexeme being scanned
-    private int current = 0; // points at the character currently being considered.
-    private int line = 1;
-    private static final Map<String, TokenType> keywords;
-
-    static {
-        keywords = new HashMap<>();
-        keywords.put("and", AND);
-        keywords.put("class", CLASS);
-        keywords.put("else", ELSE);
-        keywords.put("false", FALSE);
-        keywords.put("fun", FUN);
-        keywords.put("if", IF);
-        keywords.put("nil", NIL);
-        keywords.put("or", OR);
-        keywords.put("print", PRINT);
-        keywords.put("return", RETURN);
-        keywords.put("super", SUPER);
-        keywords.put("this", THIS);
-        keywords.put("true", TRUE);
-        keywords.put("var", VAR);
-        keywords.put("while", WHILE);
-    }
-
-    public Scanner(String source) {
-        this.source = source;
-    }
-
-    public List<Token> scanTokens() {
-        while (!isAtEnd()) {
-            // We are at the beginning of the next lexeme.
-            start = current;
-            scanToken();
+  private static final Map<String, TokenType> keywords;
+
+  static {
+    keywords = new HashMap<>();
+    keywords.put("and", AND);
+    keywords.put("class", CLASS);
+    keywords.put("else", ELSE);
+    keywords.put("false", FALSE);
+    keywords.put("fun", FUN);
+    keywords.put("if", IF);
+    keywords.put("nil", NIL);
+    keywords.put("or", OR);
+    keywords.put("print", PRINT);
+    keywords.put("return", RETURN);
+    keywords.put("super", SUPER);
+    keywords.put("this", THIS);
+    keywords.put("true", TRUE);
+    keywords.put("var", VAR);
+    keywords.put("while", WHILE);
+  }
+
+  private final String source;
+  private final List<Token> tokens = new ArrayList<>();
+  private int start = 0; //  points to the first character in the lexeme being scanned
+  private int current = 0; // points at the character currently being considered.
+  private int line = 1;
+
+  public Scanner(String source) {
+    this.source = source;
+  }
+
+  public List<Token> scanTokens() {
+    while (!isAtEnd()) {
+      // We are at the beginning of the next lexeme.
+      start = current;
+      scanToken();
+    }
+
+    // add finish token to the end of list
+    tokens.add(new Token(EOF, "", null, line));
+    return tokens;
+  }
+
+  private void scanToken() {
+    char c = advance();
+    switch (c) {
+      case '(' -> addToken(LEFT_PARAM);
+      case ')' -> addToken(RIGHT_PARAM);
+      case '{' -> addToken(LEFT_BRACE);
+      case '}' -> addToken(RIGHT_BRACE);
+      case ',' -> addToken(COMMA);
+      case '.' -> addToken(DOT);
+      case '-' -> addToken(MINUS);
+      case '+' -> addToken(PLUS);
+      case ';' -> addToken(SEMICOLON);
+      case '*' -> addToken(STAR);
+        // 两个字符 != == >= <=
+      case '!' -> addToken(match('=') ? BANG_EQUAL : BANG);
+      case '=' -> addToken(match('=') ? EQUAL_EQUAL : EQUAL);
+      case '<' -> addToken(match('=') ? LESS_EQUAL : LESS);
+      case '>' -> addToken(match('=') ? GRATER_EQUAL : GRATER);
+      case '/' -> {
+        if (match('/')) {
+          // A comment goes until the end of the line.
+          while (peek() != '\n' && !isAtEnd()) advance();
+        } else {
+          addToken(SLASH);
         }
+      }
 
-        // add finish token to the end of list
-        tokens.add(new Token(EOF, "", null, line));
-        return tokens;
-    }
-
-    private void scanToken() {
-        char c = advance();
-        switch (c) {
-            case '(' -> addToken(LEFT_PARAM);
-            case ')' -> addToken(RIGHT_PARAM);
-            case '{' -> addToken(LEFT_BRACE);
-            case '}' -> addToken(RIGHT_BRACE);
-            case ',' -> addToken(COMMA);
-            case '.' -> addToken(DOT);
-            case '-' -> addToken(MINUS);
-            case '+' -> addToken(PLUS);
-            case ';' -> addToken(SEMICOLON);
-            case '*' -> addToken(STAR);
-            // 两个字符 != == >= <=
-            case '!' -> addToken(match('=') ? BANG_EQUAL : BANG);
-            case '=' -> addToken(match('=') ? EQUAL_EQUAL : EQUAL);
-            case '<' -> addToken(match('=') ? LESS_EQUAL : LESS);
-            case '>' -> addToken(match('=') ? GRATER_EQUAL : GRATER);
-            case '/' -> {
-                if (match('/')) {
-                    // A comment goes until the end of the line.
-                    while (peek() != '\n' && !isAtEnd()) advance();
-                } else {
-                    addToken(SLASH);
-                }
-            }
-
-            // Ignore whitespace
-            case ' ', '\r', '\t' -> {
-            }
-            case '\n' -> line++;
-
-            case '"' -> string();
-
-            default -> {
-                if (isDigit(c)) {
-                    number();
-                } else if (isAlpha(c)) {
-                    identifier();
-                } else {
-                    Lox.error(line, "Unexpected character: " + c);
-                }
-            }
-        } // end switch
-    }
-
-    /**
-     * keyword or user-defined identifier
-     */
-    private void identifier() {
-        while (isAlphaNumeric(peek())) advance();
-
-        String text = source.substring(start, current);
-        TokenType type = keywords.get(text);
-        if (type == null) type = IDENTIFIER;
-        addToken(type);
-    }
-
-    /**
-     * Number literals
-     * 1234
-     * 12.34
-     */
-    private void number() {
-        while (isDigit(peek())) advance();
+        // Ignore whitespace
+      case ' ', '\r', '\t' -> {}
+      case '\n' -> line++;
 
-        // Look for a fractional part.
-        if (peek() == '.' && isDigit(peekNext())) {
-            // consume .
-            advance();
+      case '"' -> string();
 
-            while (isDigit(peek())) advance();
+      default -> {
+        if (isDigit(c)) {
+          number();
+        } else if (isAlpha(c)) {
+          identifier();
+        } else {
+          Lox.error(line, "Unexpected character: " + c);
         }
-
-        addToken(NUMBER, Double.parseDouble(source.substring(start, current)));
-    }
-
-    /**
-     * String literals
-     */
-    private void string() {
-        while (peek() != '"' && !isAtEnd()) {
-            if (peek() == '\n') line++;
-            advance();
-        }
-        if (isAtEnd()) {
-            Lox.error(line, "Unterminated string.");
-            return;
-        }
-
-        // 读取最后的 “
-        advance();
-
-        // Trim the surrounding quotes.
-        String value = source.substring(start + 1, current - 1);
-        addToken(STRING, value);
-    }
-
-    private char peek() {
-        if (isAtEnd()) return '\0';
-        return source.charAt(current);
-    }
-
-    private char peekNext() {
-        if (current + 1 >= source.length()) return '\0';
-        return source.charAt(current + 1);
-    }
-
-    private void addToken(TokenType type) {
-        addToken(type, null);
-    }
-
-    private void addToken(TokenType type, Object literal) {
-        String text = source.substring(start, current);
-        tokens.add(new Token(type, text, literal, line));
-    }
-
-    /**
-     * consumes the next character in the source file and returns it
-     *
-     * @return character
-     */
-    private char advance() {
-        return source.charAt(current++);
-    }
-
-    private boolean isAtEnd() {
-        return current >= source.length();
-    }
-
-    private boolean match(char expected) {
-        if (isAtEnd()) return false;
-        if (source.charAt(current) != expected) return false;
-
-        current++;
-        return true;
-    }
-
-    private boolean isDigit(char c) {
-        return c >= '0' && c <= '9';
-    }
-
-    private boolean isAlpha(char c) {
-        return (c >= 'a' && c <= 'z') ||
-                (c >= 'A' && c <= 'Z') ||
-                c == '_';
-    }
-
-    private boolean isAlphaNumeric(char c) {
-        return isAlpha(c) || isDigit(c);
-    }
+      }
+    } // end switch
+  }
+
+  /** keyword or user-defined identifier */
+  private void identifier() {
+    while (isAlphaNumeric(peek())) advance();
+
+    String text = source.substring(start, current);
+    TokenType type = keywords.get(text);
+    if (type == null) type = IDENTIFIER;
+    addToken(type);
+  }
+
+  /** Number literals 1234 12.34 */
+  private void number() {
+    while (isDigit(peek())) advance();
+
+    // Look for a fractional part.
+    if (peek() == '.' && isDigit(peekNext())) {
+      // consume .
+      advance();
+
+      while (isDigit(peek())) advance();
+    }
+
+    addToken(NUMBER, Double.parseDouble(source.substring(start, current)));
+  }
+
+  /** String literals */
+  private void string() {
+    while (peek() != '"' && !isAtEnd()) {
+      if (peek() == '\n') line++;
+      advance();
+    }
+    if (isAtEnd()) {
+      Lox.error(line, "Unterminated string.");
+      return;
+    }
+
+    // 读取最后的 “
+    advance();
+
+    // Trim the surrounding quotes.
+    String value = source.substring(start + 1, current - 1);
+    addToken(STRING, value);
+  }
+
+  private char peek() {
+    if (isAtEnd()) return '\0';
+    return source.charAt(current);
+  }
+
+  private char peekNext() {
+    if (current + 1 >= source.length()) return '\0';
+    return source.charAt(current + 1);
+  }
+
+  private void addToken(TokenType type) {
+    addToken(type, null);
+  }
+
+  private void addToken(TokenType type, Object literal) {
+    String text = source.substring(start, current);
+    tokens.add(new Token(type, text, literal, line));
+  }
+
+  /**
+   * consumes the next character in the source file and returns it
+   *
+   * @return character
+   */
+  private char advance() {
+    return source.charAt(current++);
+  }
+
+  private boolean isAtEnd() {
+    return current >= source.length();
+  }
+
+  private boolean match(char expected) {
+    if (isAtEnd()) return false;
+    if (source.charAt(current) != expected) return false;
+
+    current++;
+    return true;
+  }
+
+  private boolean isDigit(char c) {
+    return c >= '0' && c <= '9';
+  }
+
+  private boolean isAlpha(char c) {
+    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
+  }
+
+  private boolean isAlphaNumeric(char c) {
+    return isAlpha(c) || isDigit(c);
+  }
 }

+ 0 - 1
src/main/java/com/craftinginterpreters/tool/GenerateAst.java

@@ -11,7 +11,6 @@ import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 
-
 /**
  * @author simon
  * @date 2023-06-05 10:53