瀏覽代碼

class define and class instance define

runningwater 2 年之前
父節點
當前提交
27ec93a44e

+ 94 - 91
README.md

@@ -28,99 +28,102 @@
 
 - **Literals**. Numbers,strings,Booleans, and nil
 - **Unary expressions.** A prefix ! to perform a logical not, and - to negate a number.
-  - **Binary expressions.** The infix arithmetic(+,-,*,/) and logic operators (==, !=, <, <=,>,>=)
-      - **Parentheses.** A pair of ( and ) wrapped around and expression.
-
-          - Syntax tree - Expression
+    - **Binary expressions.** The infix arithmetic(+,-,*,/) and logic operators (==, !=, <, <=,>,>=)
+        - **Parentheses.** A pair of ( and ) wrapped around and expression.
+
+            - Syntax tree - Expression
+
+                  ```
+                  expression     → literal
+                  | unary
+                  | binary
+                  | grouping ;
+
+                  literal        → NUMBER | STRING | "true" | "false" | "nil" ;
+                  grouping       → "(" expression ")" ;
+                  unary          → ( "-" | "!" ) expression ;
+                  binary         → expression operator expression ;
+                  operator       → "==" | "!=" | "<" | "<=" | ">" | ">="
+                  | "+"  | "-"  | "*" | "/" ;
+
+                  ```
+
+              | Name       | Operators | Associates |
+                                                  |------------|-----------|------------|
+              | Equality	  | == !=     | Left       |
+              | Comparison | > >= < <= | Left       |
+              | Term       | - +	      | Left       |
+              | Factor     | / *       | Left       |
+              | Unary      | ! -       | Right      |
 
                 ```
-                expression     → literal
-                | unary
-                | binary
-                | grouping ;
-
-                literal        → NUMBER | STRING | "true" | "false" | "nil" ;
-                grouping       → "(" expression ")" ;
-                unary          → ( "-" | "!" ) expression ;
-                binary         → expression operator expression ;
-                operator       → "==" | "!=" | "<" | "<=" | ">" | ">="
-                | "+"  | "-"  | "*" | "/" ;
-
-                ```
-
-            | 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 | call ;
-              call           → primary( "(" arguments? ")" )* ;
-              arguments       → expression ( "," expression)* ;
-              primary        → NUMBER | STRING | "true" | "false" | "nil"
-                             | "(" expression ")" | IDENTIFIER ;
-             ```
-
-            - Syntax tree - Statement
-
-                ```
-                program        → statement* EOF ;
-      
-                statement      → exprStmt
-                               | printStmt 
-                               | block;
-            
-                block         → "{" declaration* "}"
-      
-                exprStmt       → expression ";" ;
-                printStmt      → "print" expression ";" ;
-                ```
-
-              A program is a list of statements followed by the special "end of file" token.
-              The mandatory end token ensures the parse consumes the entire input and don't
-              silently ignore erroneous unconsumed tokens at the end of a script.
-            - Syntax tree - add Variable syntax
-              ```
-                program   → declaration* EOF ;
+                expression     → equality ;
+                equality       → comparison ( ( "!=" | "==" ) comparison )* ;
+                comparison     → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
+                term           → factor ( ( "-" | "+" ) factor )* ;
+                factor         → unary ( ( "/" | "*" ) unary )* ;
+                unary          → ( "!" | "-" ) unary
+                               | primary | call ;
+                call           → primary( "(" arguments? ")" )* ;
+                arguments       → expression ( "," expression)* ;
+                primary        → NUMBER | STRING | "true" | "false" | "nil"
+                               | "(" expression ")" | IDENTIFIER ;
+               ```
+
+                - Syntax tree - Statement
+
+                    ```
+                    program        → statement* EOF ;
           
-                declaration  → funDecl
-                             | varDecl
-                             | statement ;
-
-                statement      → exprStmt
-                               | forStmt
-                               | ifStmt
-                               | printStmt
-                               | returnStmt
-                               | whileStmt
-                               | block ;
-                 funDecl       → "fun" function;
-                 function      →  IDENTIFIER "(" parameters? ")" block ;
-                 block         → "{" declaration* "}"
-                 ifStmt        → "if" "(" expression ")" statement ( "else" statement)? ;
-            
-                varDecl  → "var" IDENTIFIER ( "=" expression )? ";" ;
-                returnStmt → "return" expression? ";";
+                    statement      → exprStmt
+                                   | printStmt 
+                                   | block;
+                
+                    block         → "{" declaration* "}"
+          
+                    exprStmt       → expression ";" ;
+                    printStmt      → "print" expression ";" ;
+                    ```
+
+                  A program is a list of statements followed by the special "end of file" token.
+                  The mandatory end token ensures the parse consumes the entire input and don't
+                  silently ignore erroneous unconsumed tokens at the end of a script.
+                - Syntax tree - add Variable syntax
+                  ```
+                    program   → declaration* EOF ;
+              
+                    declaration  → classDecl 
+                                 | funDecl
+                                 | varDecl
+                                 | statement ;
+    
+                    statement      → exprStmt
+                                   | forStmt
+                                   | ifStmt
+                                   | printStmt
+                                   | returnStmt
+                                   | whileStmt
+                                   | block ;
+                     funDecl       → "fun" function;
+                     function      →  IDENTIFIER "(" parameters? ")" block ;
+                     block         → "{" declaration* "}"
+                     ifStmt        → "if" "(" expression ")" statement ( "else" statement)? ;
+                
+                    varDecl  → "var" IDENTIFIER ( "=" expression )? ";" ;
+                    returnStmt → "return" expression? ";";
+                    classDecl -> "class" IDENTIFIER "{" function* "}" ;
+                    function -> IDENTIFIER "(" parameters? ")" block;
+                    parameters -> IDENTIFIER ( "," IDENTIFIER )* ;
+                  ```
+            - Syntax tree -- Logical Operators
+              ```
+                expression     → assignment ;
+                assignment     → IDENTIFIER "=" assignment
+                | logic_or ;
+                logic_or       → logic_and ( "or" logic_and )* ;
+                logic_and      → equality ( "and" equality )* ;
               ```
-          - Syntax tree -- Logical Operators
-            ```
-              expression     → assignment ;
-              assignment     → IDENTIFIER "=" assignment
-              | logic_or ;
-              logic_or       → logic_and ( "or" logic_and )* ;
-              logic_and      → equality ( "and" equality )* ;
-            ```
-
 
-            whileStmt   → "while" "(" expression ")" statement ;
-            forStmt     → "for" " (" (varDecl | exprStmt | ";") 
-                          expression? ";" expression? ")" statement ;
+          whileStmt → "while" "(" expression ")" statement ;
+          forStmt → "for" " (" (varDecl | exprStmt | ";")
+          expression? ";" expression? ")" statement ;

+ 49 - 49
src/main/java/com/craftinginterpreters/lox/AstPrinter.java

@@ -8,63 +8,63 @@ package com.craftinginterpreters.lox;
  * @desc
  */
 public class AstPrinter implements Expr.Visitor<String> {
-  String print(Expr expr) {
-    return expr.accept(this);
-  }
+    String print(Expr expr) {
+        return expr.accept(this);
+    }
 
-  @Override
-  public String visitAssignExpr(Expr.Assign expr) {
-    return parenthesize("assign", expr.value);
-  }
+    @Override
+    public String visitAssignExpr(Expr.Assign expr) {
+        return parenthesize("assign", expr.value);
+    }
 
-  @Override
-  public String visitBinaryExpr(Expr.Binary expr) {
-    return parenthesize(expr.operator.lexeme, expr.left, expr.right);
-  }
+    @Override
+    public String visitBinaryExpr(Expr.Binary expr) {
+        return parenthesize(expr.operator.lexeme, expr.left, expr.right);
+    }
 
-  @Override
-  public String visitCallExpr(Expr.Call expr) {
-    return null;
-  }
+    @Override
+    public String visitCallExpr(Expr.Call expr) {
+        return null;
+    }
 
-  @Override
-  public String visitGroupingExpr(Expr.Grouping expr) {
-    return parenthesize("group", expr.expression);
-  }
+    @Override
+    public String visitGroupingExpr(Expr.Grouping expr) {
+        return parenthesize("group", expr.expression);
+    }
 
-  @Override
-  public String visitLiteralExpr(Expr.Literal expr) {
-    if (expr.value == null) return "nil";
-    return expr.value.toString();
-  }
+    @Override
+    public String visitLiteralExpr(Expr.Literal expr) {
+        if (expr.value == null) return "nil";
+        return expr.value.toString();
+    }
 
-  @Override
-  public String visitLogicalExpr(Expr.Logical expr) {
-    return parenthesize(null, expr.left)
-        + " "
-        + expr.operator.lexeme.toUpperCase()
-        + " "
-        + parenthesize(null, expr.right);
-  }
+    @Override
+    public String visitLogicalExpr(Expr.Logical expr) {
+        return parenthesize(null, expr.left)
+                + " "
+                + expr.operator.lexeme.toUpperCase()
+                + " "
+                + parenthesize(null, expr.right);
+    }
 
-  @Override
-  public String visitUnaryExpr(Expr.Unary expr) {
-    return parenthesize(expr.operator.lexeme, expr.right);
-  }
+    @Override
+    public String visitUnaryExpr(Expr.Unary expr) {
+        return parenthesize(expr.operator.lexeme, expr.right);
+    }
 
-  @Override
-  public String visitVariableExpr(Expr.Variable expr) {
-    return parenthesize(expr.name.lexeme);
-  }
+    @Override
+    public String visitVariableExpr(Expr.Variable expr) {
+        return parenthesize(expr.name.lexeme);
+    }
 
-  private String parenthesize(String name, Expr... exprs) {
-    StringBuilder builder = new StringBuilder();
-    builder.append("(").append(name == null ? "" : name);
-    for (Expr expr : exprs) {
-      builder.append(" ");
-      builder.append(expr.accept(this));
+    private String parenthesize(String name, Expr... exprs) {
+        StringBuilder builder = new StringBuilder();
+        builder.append("(").append(name == null ? "" : name);
+        for (Expr expr : exprs) {
+            builder.append(" ");
+            builder.append(expr.accept(this));
+        }
+        builder.append(")");
+        return builder.toString();
     }
-    builder.append(")");
-    return builder.toString();
-  }
 }

+ 24 - 27
src/main/java/com/craftinginterpreters/lox/Expr.java

@@ -7,6 +7,9 @@ import java.util.List;
  * @date 2023-06-18 16:05
  */
 abstract class Expr {
+    abstract <R> R accept(Visitor<R> visitor);
+
+
     interface Visitor<R> {
         R visitAssignExpr(Assign expr);
 
@@ -26,8 +29,10 @@ abstract class Expr {
 
     }
 
-
     static class Assign extends Expr {
+        final Token name;
+        final Expr value;
+
         Assign(Token name, Expr value) {
             this.name = name;
             this.value = value;
@@ -37,12 +42,12 @@ abstract class Expr {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitAssignExpr(this);
         }
-
-        final Token name;
-        final  Expr value;
     }
 
     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;
@@ -53,13 +58,12 @@ abstract class Expr {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitBinaryExpr(this);
         }
-
-        final Expr left;
-        final  Token operator;
-        final  Expr right;
     }
 
     static class Call extends Expr {
+        final Expr callee;
+        final Token paren;
+        final List<Expr> arguments;
         Call(Expr callee, Token paren, List<Expr> arguments) {
             this.callee = callee;
             this.paren = paren;
@@ -70,13 +74,11 @@ abstract class Expr {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitCallExpr(this);
         }
-
-        final Expr callee;
-        final  Token paren;
-        final  List<Expr> arguments;
     }
 
     static class Grouping extends Expr {
+        final Expr expression;
+
         Grouping(Expr expression) {
             this.expression = expression;
         }
@@ -85,11 +87,11 @@ abstract class Expr {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitGroupingExpr(this);
         }
-
-        final Expr expression;
     }
 
     static class Literal extends Expr {
+        final Object value;
+
         Literal(Object value) {
             this.value = value;
         }
@@ -98,11 +100,12 @@ abstract class Expr {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitLiteralExpr(this);
         }
-
-        final Object value;
     }
 
     static class Logical extends Expr {
+        final Expr left;
+        final Token operator;
+        final Expr right;
         Logical(Expr left, Token operator, Expr right) {
             this.left = left;
             this.operator = operator;
@@ -113,13 +116,12 @@ abstract class Expr {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitLogicalExpr(this);
         }
-
-        final Expr left;
-        final  Token operator;
-        final  Expr right;
     }
 
     static class Unary extends Expr {
+        final Token operator;
+        final Expr right;
+
         Unary(Token operator, Expr right) {
             this.operator = operator;
             this.right = right;
@@ -129,12 +131,11 @@ abstract class Expr {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitUnaryExpr(this);
         }
-
-        final Token operator;
-        final  Expr right;
     }
 
     static class Variable extends Expr {
+        final Token name;
+
         Variable(Token name) {
             this.name = name;
         }
@@ -143,9 +144,5 @@ abstract class Expr {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitVariableExpr(this);
         }
-
-        final Token name;
     }
-
-    abstract <R> R accept(Visitor<R> visitor);
 }

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

@@ -187,6 +187,14 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
     }
 
     @Override
+    public Void visitClassStmt(Stmt.Class stmt) {
+        environment.define(stmt.name.lexeme, null);
+        LoxClass klass = new LoxClass(stmt.name.lexeme);
+        environment.assign(stmt.name, klass);
+        return null;
+    }
+
+    @Override
     public Void visitExpressionStmt(Stmt.Expression stmt) {
         Object value = evaluate(stmt.expression);
         System.out.println("  " + stringify(value));

+ 92 - 90
src/main/java/com/craftinginterpreters/lox/Lox.java

@@ -16,98 +16,100 @@ import java.util.List;
  * @desc
  */
 public class Lox {
-  private static final Interpreter interpreter = new Interpreter();
-  private static boolean hadError = false;
-  private static boolean hadRuntimeError = 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();
+    private static final Interpreter interpreter = new Interpreter();
+    private static boolean hadError = false;
+    private static boolean hadRuntimeError = 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();
+        }
     }
-  }
-
-  /**
-   * 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);
-    if (hadRuntimeError) System.exit(70);
-  }
-
-  /** 提示符里面输入 */
-  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);
-
-      // reset this flag in the interactive loop. If the user makes a mistake,
-      // it shouldn’t kill their entire session.
-      // hadError = true;
+
+    /**
+     * 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);
+        if (hadRuntimeError) System.exit(70);
+    }
+
+    /**
+     * 提示符里面输入
+     */
+    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);
+
+            // 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();
-
-    // for (Token token : tokens) {
-    //    System.out.println(token);
-    // }
-    Parser parser = new Parser(tokens);
-    List<Stmt> statements = parser.parse();
-
-    Resolver resolver = new Resolver(interpreter);
-    resolver.resolve(statements);
-
-    // Stop if there was a syntax error.
-    if (hadError) return;
-
-    // System.out.println("  ...AST: " + new AstPrinter().print(statements));
-    interpreter.interpret(statements);
-  }
-
-  /**
-   * 错误处理
-   *
-   * @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;
-  }
-
-  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);
+
+    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);
+        List<Stmt> statements = parser.parse();
+
+        Resolver resolver = new Resolver(interpreter);
+        resolver.resolve(statements);
+
+        // Stop if there was a syntax error.
+        if (hadError) return;
+
+        // System.out.println("  ...AST: " + new AstPrinter().print(statements));
+        interpreter.interpret(statements);
     }
-  }
 
-  public static void runtimeError(RuntimeError error) {
-    System.err.println(error.getMessage() + "\n[line " + error.token.line + "]");
-    hadRuntimeError = true;
-  }
+    /**
+     * 错误处理
+     *
+     * @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;
+    }
+
+    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);
+        }
+    }
+
+    public static void runtimeError(RuntimeError error) {
+        System.err.println(error.getMessage() + "\n[line " + error.token.line + "]");
+        hadRuntimeError = true;
+    }
 }

+ 4 - 3
src/main/java/com/craftinginterpreters/lox/LoxCallable.java

@@ -9,7 +9,8 @@ import java.util.List;
  * @date 2023-06-18 17:33
  * @desc
  */
-interface LoxCallable{
-  int arity();
-  Object call(Interpreter interpreter, List<Object> arguments);
+interface LoxCallable {
+    int arity();
+
+    Object call(Interpreter interpreter, List<Object> arguments);
 }

+ 34 - 0
src/main/java/com/craftinginterpreters/lox/LoxClass.java

@@ -0,0 +1,34 @@
+/* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
+ * All right reserved.*/
+package com.craftinginterpreters.lox;
+
+import java.util.List;
+
+/**
+ * @author simon
+ * @date 2023-08-08 10:22
+ * @desc
+ */
+public class LoxClass implements LoxCallable {
+    final String name;
+
+    public LoxClass(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    @Override
+    public int arity() {
+        return 0;
+    }
+
+    @Override
+    public Object call(Interpreter interpreter, List<Object> arguments) {
+        LoxInstance instance = new LoxInstance(this);
+        return instance;
+    }
+}

+ 26 - 0
src/main/java/com/craftinginterpreters/lox/LoxInstance.java

@@ -0,0 +1,26 @@
+/* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
+ * All right reserved.*/
+package com.craftinginterpreters.lox;
+
+/**
+ * @author simon
+ * @date 2023-08-08 10:30
+ * @desc <p>
+ * <code>
+ * class Bagel {}
+ * var bagel = Bagel();
+ * print bagel; // Prints "Bagel instance".
+ * </code>
+ */
+public class LoxInstance {
+    private final LoxClass klass;
+
+    public LoxInstance(LoxClass klass) {
+        this.klass = klass;
+    }
+
+    @Override
+    public String toString() {
+        return klass.name + " instance";
+    }
+}

+ 19 - 3
src/main/java/com/craftinginterpreters/lox/Parser.java

@@ -2,12 +2,12 @@
  * All right reserved.*/
 package com.craftinginterpreters.lox;
 
+import static com.craftinginterpreters.lox.TokenType.*;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import static com.craftinginterpreters.lox.TokenType.*;
-
 /**
  * @author simon
  * @date 2023-06-05 14:56
@@ -42,9 +42,10 @@ public class Parser {
         return statements;
     }
 
-    // declaration  → funDecl | varDecl | statement ;
+    // declaration  → classDecl | funDecl | varDecl | statement ;
     private Stmt declaration() {
         try {
+            if (match(CLASS)) return classDeclaration();
             if (match(FUN)) return function("function");
             if (match(VAR)) return varDecl();
             return statement();
@@ -54,6 +55,7 @@ public class Parser {
         }
     }
 
+
     // statement  → exprStmt | forStmt | ifStmt | printStmt | returnStmt | whileStmt | block;
     private Stmt statement() {
         if (match(FOR)) return forStatement();
@@ -147,6 +149,20 @@ public class Parser {
         return new Stmt.Expression(expr);
     }
 
+    private Stmt classDeclaration() {
+        Token name = consume(IDENTIFIER, "Expect class name.");
+        consume(LEFT_BRACE, "Expect '{' before class body.");
+
+        List<Stmt.Function> methods = new ArrayList<>();
+        while (!check(RIGHT_BRACE) && !isAtEnd()) {
+            methods.add(function("method"));
+        }
+
+        consume(RIGHT_BRACE, "Expect '}' after class body.");
+
+        return new Stmt.Class(name, methods);
+    }
+
     private Stmt.Function function(String kind) {
         Token name = consume(IDENTIFIER, "Expect " + kind + " name.");
         consume(LEFT_PARAM, "Expect '(' after " + kind + " name.");

+ 7 - 0
src/main/java/com/craftinginterpreters/lox/Resolver.java

@@ -92,6 +92,13 @@ public class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
     }
 
     @Override
+    public Void visitClassStmt(Stmt.Class stmt) {
+        declare(stmt.name);
+        define(stmt.name);
+        return null;
+    }
+
+    @Override
     public Void visitExpressionStmt(Stmt.Expression stmt) {
         resolve(stmt.expression);
         return null;

+ 186 - 179
src/main/java/com/craftinginterpreters/lox/Scanner.java

@@ -15,188 +15,195 @@ import java.util.Map;
  * @desc
  */
 public class Scanner {
-  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);
-    keywords.put("for", FOR);
-  }
-
-  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);
+    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);
+        keywords.put("for", FOR);
+    }
+
+    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();
         }
-      }
 
-        // Ignore whitespace
-      case ' ', '\r', '\t' -> {}
-      case '\n' -> line++;
+        // 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);
+    }
 
-      case '"' -> string();
+    /**
+     * Number literals 1234 12.34
+     */
+    private void number() {
+        while (isDigit(peek())) advance();
 
-      default -> {
-        if (isDigit(c)) {
-          number();
-        } else if (isAlpha(c)) {
-          identifier();
-        } else {
-          Lox.error(line, "Unexpected character: " + c);
+        // 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();
         }
-      }
-    } // 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);
-  }
+        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);
+    }
 }

+ 41 - 26
src/main/java/com/craftinginterpreters/lox/Stmt.java

@@ -4,12 +4,17 @@ import java.util.List;
 
 /**
  * @author GenerateAst
- * @date 2023-07-26 10:50
+ * @date 2023-08-03 18:00
  */
 abstract class Stmt {
+    abstract <R> R accept(Visitor<R> visitor);
+
+
     interface Visitor<R> {
         R visitBlockStmt(Block stmt);
 
+        R visitClassStmt(Class stmt);
+
         R visitExpressionStmt(Expression stmt);
 
         R visitFunctionStmt(Function stmt);
@@ -26,8 +31,9 @@ abstract class Stmt {
 
     }
 
-
     static class Block extends Stmt {
+        final List<Stmt> statements;
+
         Block(List<Stmt> statements) {
             this.statements = statements;
         }
@@ -36,11 +42,26 @@ abstract class Stmt {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitBlockStmt(this);
         }
+    }
 
-        final List<Stmt> statements;
+    static class Class extends Stmt {
+        final Token name;
+        final List<Stmt.Function> methods;
+
+        Class(Token name, List<Stmt.Function> methods) {
+            this.name = name;
+            this.methods = methods;
+        }
+
+        @Override
+        <R> R accept(Visitor<R> visitor) {
+            return visitor.visitClassStmt(this);
+        }
     }
 
     static class Expression extends Stmt {
+        final Expr expression;
+
         Expression(Expr expression) {
             this.expression = expression;
         }
@@ -49,11 +70,12 @@ abstract class Stmt {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitExpressionStmt(this);
         }
-
-        final Expr expression;
     }
 
     static class Function extends Stmt {
+        final Token name;
+        final List<Token> params;
+        final List<Stmt> body;
         Function(Token name, List<Token> params, List<Stmt> body) {
             this.name = name;
             this.params = params;
@@ -64,13 +86,12 @@ abstract class Stmt {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitFunctionStmt(this);
         }
-
-        final Token name;
-        final  List<Token> params;
-        final  List<Stmt> body;
     }
 
     static class If extends Stmt {
+        final Expr condition;
+        final Stmt thenBranch;
+        final Stmt elseBranch;
         If(Expr condition, Stmt thenBranch, Stmt elseBranch) {
             this.condition = condition;
             this.thenBranch = thenBranch;
@@ -81,13 +102,11 @@ abstract class Stmt {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitIfStmt(this);
         }
-
-        final Expr condition;
-        final  Stmt thenBranch;
-        final  Stmt elseBranch;
     }
 
     static class Print extends Stmt {
+        final Expr expression;
+
         Print(Expr expression) {
             this.expression = expression;
         }
@@ -96,11 +115,12 @@ abstract class Stmt {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitPrintStmt(this);
         }
-
-        final Expr expression;
     }
 
     static class Return extends Stmt {
+        final Token keyword;
+        final Expr value;
+
         Return(Token keyword, Expr value) {
             this.keyword = keyword;
             this.value = value;
@@ -110,12 +130,12 @@ abstract class Stmt {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitReturnStmt(this);
         }
-
-        final Token keyword;
-        final  Expr value;
     }
 
     static class Var extends Stmt {
+        final Token name;
+        final Expr initializer;
+
         Var(Token name, Expr initializer) {
             this.name = name;
             this.initializer = initializer;
@@ -125,12 +145,12 @@ abstract class Stmt {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitVarStmt(this);
         }
-
-        final Token name;
-        final  Expr initializer;
     }
 
     static class While extends Stmt {
+        final Expr condition;
+        final Stmt body;
+
         While(Expr condition, Stmt body) {
             this.condition = condition;
             this.body = body;
@@ -140,10 +160,5 @@ abstract class Stmt {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitWhileStmt(this);
         }
-
-        final Expr condition;
-        final  Stmt body;
     }
-
-    abstract <R> R accept(Visitor<R> visitor);
 }

+ 11 - 10
src/main/java/com/craftinginterpreters/tool/GenerateAst.java

@@ -52,16 +52,17 @@ public class GenerateAst {
 //                "Unary    : Token operator, Expr right",
 //                "Variable : Token name"
 //        ));
-        defineAst(outputDir, "Stmt", Arrays.asList(
-                "Block       : List<Stmt> statements",
-                "Expression  : Expr expression",
-                "Function    : Token name, List<Token> params, List<Stmt> body",
-                "If          : Expr condition, Stmt thenBranch, Stmt elseBranch",
-                "Print       : Expr expression",
-                "Return      : Token keyword, Expr value",
-                "Var         : Token name, Expr initializer",
-                "While       : Expr condition, Stmt body"
-        ));
+//        defineAst(outputDir, "Stmt", Arrays.asList(
+//                "Block       : List<Stmt> statements",
+//                "Class       : Token name, List<Stmt.Function> methods",
+//                "Expression  : Expr expression",
+//                "Function    : Token name, List<Token> params, List<Stmt> body",
+//                "If          : Expr condition, Stmt thenBranch, Stmt elseBranch",
+//                "Print       : Expr expression",
+//                "Return      : Token keyword, Expr value",
+//                "Var         : Token name, Expr initializer",
+//                "While       : Expr condition, Stmt body"
+//        ));
     }
 
     private static void defineAst(String outputDir, String baseName, List<String> types)

+ 9 - 9
src/test/java/com/craftinginterpreters/lox/AstPrinterTest.java

@@ -6,14 +6,14 @@ import org.junit.Test;
  * All right reserved.*/
 public class AstPrinterTest {
 
-  @Test
-  public void print() {
-    Expr expression =
-        new Expr.Binary(
-            new Expr.Unary(new Token(TokenType.MINUS, "-", null, 1), new Expr.Literal(123)),
-            new Token(TokenType.STAR, "*", null, 1),
-            new Expr.Grouping(new Expr.Literal(45.67)));
+    @Test
+    public void print() {
+        Expr expression =
+                new Expr.Binary(
+                        new Expr.Unary(new Token(TokenType.MINUS, "-", null, 1), new Expr.Literal(123)),
+                        new Token(TokenType.STAR, "*", null, 1),
+                        new Expr.Grouping(new Expr.Literal(45.67)));
 
-    System.out.println(new AstPrinter().print(expression));
-  }
+        System.out.println(new AstPrinter().print(expression));
+    }
 }

+ 7 - 9
src/test/java/com/craftinginterpreters/lox/LoxTest.java

@@ -1,18 +1,16 @@
 package com.craftinginterpreters.lox;
 
-import static org.junit.Assert.*;
-
 import java.io.IOException;
 import org.junit.Test;
 
 /* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
  * All right reserved.*/ public class LoxTest {
 
-  @Test
-  public void main() throws IOException {
-    String[] args = new String[1];
-    args[0] =
-        "/Users/lixiaoming/Documents/haish/jlox/src/test/java/com/craftinginterpreters/lox/scope.lox"; // path
-    Lox.main(args);
-  }
+    @Test
+    public void main() throws IOException {
+        String[] args = new String[1];
+        args[0] =
+                "/Users/lixiaoming/Documents/haish/jlox/src/test/java/com/craftinginterpreters/lox/scope.lox"; // path
+        Lox.main(args);
+    }
 }