Jelajahi Sumber

class get and set

runningwater 2 tahun lalu
induk
melakukan
e94d2ea359

+ 5 - 0
src/main/java/com/craftinginterpreters/lox/AstPrinter.java

@@ -28,6 +28,11 @@ public class AstPrinter implements Expr.Visitor<String> {
     }
 
     @Override
+    public String visitGetExpr(Expr.Get expr) {
+        return null;
+    }
+
+    @Override
     public String visitGroupingExpr(Expr.Grouping expr) {
         return parenthesize("group", expr.expression);
     }

+ 64 - 25
src/main/java/com/craftinginterpreters/lox/Expr.java

@@ -4,12 +4,9 @@ import java.util.List;
 
 /**
  * @author GenerateAst
- * @date 2023-06-18 16:05
+ * @date 2023-08-08 13:53
  */
 abstract class Expr {
-    abstract <R> R accept(Visitor<R> visitor);
-
-
     interface Visitor<R> {
         R visitAssignExpr(Assign expr);
 
@@ -17,22 +14,24 @@ abstract class Expr {
 
         R visitCallExpr(Call expr);
 
+        R visitGetExpr(Get expr);
+
         R visitGroupingExpr(Grouping expr);
 
         R visitLiteralExpr(Literal expr);
 
         R visitLogicalExpr(Logical expr);
 
+        R visitSetExpr(Set expr);
+
         R visitUnaryExpr(Unary expr);
 
         R visitVariableExpr(Variable expr);
 
     }
 
-    static class Assign extends Expr {
-        final Token name;
-        final Expr value;
 
+    static class Assign extends Expr {
         Assign(Token name, Expr value) {
             this.name = name;
             this.value = value;
@@ -42,12 +41,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;
@@ -58,12 +57,13 @@ 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;
@@ -74,11 +74,28 @@ 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;
+    static class Get extends Expr {
+        Get(Expr object, Token name) {
+            this.object = object;
+            this.name = name;
+        }
+
+        @Override
+        <R> R accept(Visitor<R> visitor) {
+            return visitor.visitGetExpr(this);
+        }
 
+        final Expr object;
+        final  Token name;
+    }
+
+    static class Grouping extends Expr {
         Grouping(Expr expression) {
             this.expression = expression;
         }
@@ -87,11 +104,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;
         }
@@ -100,12 +117,11 @@ 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;
@@ -116,12 +132,30 @@ 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;
+    static class Set extends Expr {
+        Set(Expr object, Token name, Expr value) {
+            this.object = object;
+            this.name = name;
+            this.value = value;
+        }
 
+        @Override
+        <R> R accept(Visitor<R> visitor) {
+            return visitor.visitSetExpr(this);
+        }
+
+        final Expr object;
+        final  Token name;
+        final  Expr value;
+    }
+
+    static class Unary extends Expr {
         Unary(Token operator, Expr right) {
             this.operator = operator;
             this.right = right;
@@ -131,11 +165,12 @@ 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;
         }
@@ -144,5 +179,9 @@ abstract class Expr {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitVariableExpr(this);
         }
+
+        final Token name;
     }
+
+    abstract <R> R accept(Visitor<R> visitor);
 }

+ 296 - 273
src/main/java/com/craftinginterpreters/lox/Interpreter.java

@@ -13,303 +13,326 @@ import java.util.Map;
  * @desc
  */
 class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
-    public final Environment globals = new Environment();
-    private final Map<Expr, Integer> locals = new HashMap<>();
-    private Environment environment = globals;
-
-    public Interpreter() {
-        globals.define("clock", new LoxCallable() {
-            @Override
-            public int arity() {
-                return 0;
-            }
-
-
-            @Override
-            public Object call(Interpreter interpreter, List<Object> arguments) {
-                return (double) System.currentTimeMillis() / 1000.0;
-            }
-
-            @Override
-            public String toString() {
-                return "<native fn>";
-            }
+  public final Environment globals = new Environment();
+  private final Map<Expr, Integer> locals = new HashMap<>();
+  private Environment environment = globals;
+
+  public Interpreter() {
+    globals.define(
+        "clock",
+        new LoxCallable() {
+          @Override
+          public int arity() {
+            return 0;
+          }
+
+          @Override
+          public Object call(Interpreter interpreter, List<Object> arguments) {
+            return (double) System.currentTimeMillis() / 1000.0;
+          }
+
+          @Override
+          public String toString() {
+            return "<native fn>";
+          }
         });
+  }
+
+  /**
+   * public API
+   *
+   * @param statements statements
+   */
+  void interpret(List<Stmt> statements) {
+    try {
+      for (Stmt statement : statements) {
+        execute(statement);
+      }
+    } catch (RuntimeError error) {
+      Lox.runtimeError(error);
     }
-
-    /**
-     * public API
-     *
-     * @param statements statements
-     */
-    void interpret(List<Stmt> statements) {
-        try {
-            for (Stmt statement : statements) {
-                execute(statement);
-            }
-        } catch (RuntimeError error) {
-            Lox.runtimeError(error);
+  }
+
+  @Override
+  public Object visitAssignExpr(Expr.Assign expr) {
+    Object value = evaluate(expr.value);
+    environment.assign(expr.name, value);
+    return value;
+  }
+
+  @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;
         }
-    }
-
-    @Override
-    public Object visitAssignExpr(Expr.Assign expr) {
-        Object value = evaluate(expr.value);
-        environment.assign(expr.name, value);
-        return value;
-    }
-
-    @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;
-                }
-                throw new RuntimeError(expr.operator, "Operands must be two numbers or two Strings.");
-            }
-            case SLASH -> {
-                checkNumberOperand(expr.operator, left, right);
-                if (((Double) right) == 0D) throw new RuntimeError(expr.operator, "Division by Zero.");
-                yield (double) left / (double) right;
-            }
-            case STAR -> {
-                checkNumberOperand(expr.operator, left, right);
-                yield (double) left * (double) right;
-            }
-            default -> null;
-        };
-    }
-
-    @Override
-    public Object visitCallExpr(Expr.Call expr) {
-        Object callee = evaluate(expr.callee);
-
-        List<Object> arguments = new ArrayList<>();
-        for (Expr argument : expr.arguments) {
-            arguments.add(evaluate(argument));
-        }
-
-        if (!(callee instanceof LoxCallable fun)) {
-            // "totally not a function"();
-            throw new RuntimeError(expr.paren, "Can only call functions and classes.");
+        if (left instanceof String && right instanceof String) {
+          yield left + (String) right;
         }
-        if (arguments.size() != fun.arity()) {
-            throw new RuntimeError(expr.paren, "Expected " + fun.arity() + " arguments bug got " +
-                    arguments.size() + ".");
-        }
-        return fun.call(this, arguments);
-    }
-
-    @Override
-    public Object visitGroupingExpr(Expr.Grouping expr) {
-        return evaluate(expr.expression);
+        throw new RuntimeError(expr.operator, "Operands must be two numbers or two Strings.");
+      }
+      case SLASH -> {
+        checkNumberOperand(expr.operator, left, right);
+        if (((Double) right) == 0D) throw new RuntimeError(expr.operator, "Division by Zero.");
+        yield (double) left / (double) right;
+      }
+      case STAR -> {
+        checkNumberOperand(expr.operator, left, right);
+        yield (double) left * (double) right;
+      }
+      default -> null;
+    };
+  }
+
+  @Override
+  public Object visitCallExpr(Expr.Call expr) {
+    Object callee = evaluate(expr.callee);
+
+    List<Object> arguments = new ArrayList<>();
+    for (Expr argument : expr.arguments) {
+      arguments.add(evaluate(argument));
     }
 
-    @Override
-    public Object visitLiteralExpr(Expr.Literal expr) {
-        return expr.value;
+    if (!(callee instanceof LoxCallable fun)) {
+      // "totally not a function"();
+      throw new RuntimeError(expr.paren, "Can only call functions and classes.");
     }
-
-    @Override
-    public Object visitLogicalExpr(Expr.Logical expr) {
-        Object left = evaluate(expr.left);
-
-        if (expr.operator.type == TokenType.OR) {
-            if (isTruthy(left)) return left;
-        } else {
-            if (!isTruthy(left)) return left;
-        }
-        return evaluate(expr.right);
+    if (arguments.size() != fun.arity()) {
+      throw new RuntimeError(
+          expr.paren, "Expected " + fun.arity() + " arguments bug got " + arguments.size() + ".");
     }
-
-    @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;
-        };
+    return fun.call(this, arguments);
+  }
+
+  @Override
+  public Object visitGetExpr(Expr.Get expr) {
+    Object object = evaluate(expr.object);
+    if (object instanceof LoxInstance) {
+      return ((LoxInstance) object).get(expr.name);
     }
 
-    @Override
-    public Object visitVariableExpr(Expr.Variable expr) {
-        // return environment.get(expr.name);
-        return lookUpVariable(expr.name, expr);
-    }
+    throw new RuntimeError(expr.name, "Only instances have properties.");
+  }
 
-    private Object lookUpVariable(Token name, Expr expr) {
-        Integer distance = locals.get(expr);
-        if (distance != null) {
-            return environment.getAt(distance, name.lexeme);
-        } else {
-            return globals.get(name);
-        }
-    }
+  @Override
+  public Object visitGroupingExpr(Expr.Grouping expr) {
+    return evaluate(expr.expression);
+  }
 
-    @Override
-    public Void visitBlockStmt(Stmt.Block stmt) {
-        executeBlock(stmt.statements, new Environment(environment));
-        return null;
-    }
+  @Override
+  public Object visitLiteralExpr(Expr.Literal expr) {
+    return expr.value;
+  }
 
-    @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 Object visitLogicalExpr(Expr.Logical expr) {
+    Object left = evaluate(expr.left);
 
-    @Override
-    public Void visitExpressionStmt(Stmt.Expression stmt) {
-        Object value = evaluate(stmt.expression);
-        System.out.println("  " + stringify(value));
-        return null;
+    if (expr.operator.type == TokenType.OR) {
+      if (isTruthy(left)) return left;
+    } else {
+      if (!isTruthy(left)) return left;
     }
-
-    @Override
-    public Void visitFunctionStmt(Stmt.Function stmt) {
-        LoxFunction function = new LoxFunction(stmt, environment);
-        environment.define(stmt.name.lexeme, function);
-        return null;
+    return evaluate(expr.right);
+  }
+
+  @Override
+  public Object visitSetExpr(Expr.Set expr) {
+    Object object = evaluate(expr.object);
+
+    if (!(object instanceof LoxInstance))
+      throw new RuntimeError(expr.name, "Only instances have fields.");
+
+    Object value = evaluate(expr.value);
+    ((LoxInstance) object).set(expr.name, value);
+    return 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;
+    };
+  }
+
+  @Override
+  public Object visitVariableExpr(Expr.Variable expr) {
+    // return environment.get(expr.name);
+    return lookUpVariable(expr.name, expr);
+  }
+
+  private Object lookUpVariable(Token name, Expr expr) {
+    Integer distance = locals.get(expr);
+    if (distance != null) {
+      return environment.getAt(distance, name.lexeme);
+    } else {
+      return globals.get(name);
     }
-
-    @Override
-    public Void visitIfStmt(Stmt.If stmt) {
-        if (isTruthy(evaluate(stmt.condition))) {
-            execute(stmt.thenBranch);
-        } else if (stmt.elseBranch != null) {
-            execute(stmt.elseBranch);
-        }
-        return null;
+  }
+
+  @Override
+  public Void visitBlockStmt(Stmt.Block stmt) {
+    executeBlock(stmt.statements, new Environment(environment));
+    return null;
+  }
+
+  @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));
+    return null;
+  }
+
+  @Override
+  public Void visitFunctionStmt(Stmt.Function stmt) {
+    LoxFunction function = new LoxFunction(stmt, environment);
+    environment.define(stmt.name.lexeme, function);
+    return null;
+  }
+
+  @Override
+  public Void visitIfStmt(Stmt.If stmt) {
+    if (isTruthy(evaluate(stmt.condition))) {
+      execute(stmt.thenBranch);
+    } else if (stmt.elseBranch != null) {
+      execute(stmt.elseBranch);
     }
-
-    @Override
-    public Void visitPrintStmt(Stmt.Print stmt) {
-        Object value = evaluate(stmt.expression);
-        System.out.println("  " + stringify(value));
-        return null;
+    return null;
+  }
+
+  @Override
+  public Void visitPrintStmt(Stmt.Print stmt) {
+    Object value = evaluate(stmt.expression);
+    System.out.println("  " + stringify(value));
+    return null;
+  }
+
+  @Override
+  public Void visitReturnStmt(Stmt.Return stmt) {
+    Object value = null;
+    if (stmt.value != null) value = evaluate(stmt.value);
+
+    throw new Return(value);
+  }
+
+  @Override
+  public Void visitVarStmt(Stmt.Var stmt) {
+    Object value = null;
+    if (stmt.initializer != null) {
+      value = evaluate(stmt.initializer);
     }
 
-    @Override
-    public Void visitReturnStmt(Stmt.Return stmt) {
-        Object value = null;
-        if (stmt.value != null) value = evaluate(stmt.value);
+    environment.define(stmt.name.lexeme, value);
+    return null;
+  }
 
-        throw new Return(value);
+  @Override
+  public Void visitWhileStmt(Stmt.While stmt) {
+    while (isTruthy(evaluate(stmt.condition))) {
+      execute(stmt.body);
     }
-
-    @Override
-    public Void visitVarStmt(Stmt.Var stmt) {
-        Object value = null;
-        if (stmt.initializer != null) {
-            value = evaluate(stmt.initializer);
-        }
-
-        environment.define(stmt.name.lexeme, value);
-        return null;
+    return null;
+  }
+
+  private void execute(Stmt stmt) {
+    stmt.accept(this);
+  }
+
+  void resolve(Expr expr, int depth) {
+    locals.put(expr, depth);
+  }
+
+  protected void executeBlock(List<Stmt> statements, Environment environment) {
+    Environment previous = this.environment;
+    try {
+      this.environment = environment;
+
+      for (Stmt statement : statements) {
+        execute(statement);
+      }
+    } finally {
+      this.environment = previous;
     }
-
-    @Override
-    public Void visitWhileStmt(Stmt.While stmt) {
-        while (isTruthy(evaluate(stmt.condition))) {
-            execute(stmt.body);
-        }
-        return 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 String stringify(Object object) {
+    if (object == null) return "nil";
+
+    if (object instanceof Double) {
+      String text = object.toString();
+      if (text.endsWith(".0")) {
+        text = text.substring(0, text.length() - 2);
+      }
+      return text;
     }
 
-    private void execute(Stmt stmt) {
-        stmt.accept(this);
-    }
+    return object.toString();
+  }
 
-    void resolve(Expr expr, int depth) {
-        locals.put(expr, depth);
-    }
-
-    protected void executeBlock(List<Stmt> statements, Environment environment) {
-        Environment previous = this.environment;
-        try {
-            this.environment = environment;
-
-            for (Stmt statement : statements) {
-                execute(statement);
-            }
-        } finally {
-            this.environment = previous;
-        }
-    }
-
-    private Object evaluate(Expr expr) {
-        return expr.accept(this);
-    }
+  private void checkNumberOperand(Token operator, Object operand) {
+    if (operand instanceof Double) return;
+    throw new RuntimeError(operator, "Operand must be a number.");
+  }
 
-    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 String stringify(Object object) {
-        if (object == null) return "nil";
-
-        if (object instanceof Double) {
-            String text = object.toString();
-            if (text.endsWith(".0")) {
-                text = text.substring(0, text.length() - 2);
-            }
-            return text;
-        }
-
-        return object.toString();
-    }
-
-    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.");
-    }
+  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.");
+  }
 }

+ 24 - 9
src/main/java/com/craftinginterpreters/lox/LoxInstance.java

@@ -2,25 +2,40 @@
  * All right reserved.*/
 package com.craftinginterpreters.lox;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * @author simon
  * @date 2023-08-08 10:30
- * @desc <p>
- * <code>
+ * @desc
+ *     <p><code>
  * class Bagel {}
  * var bagel = Bagel();
  * print bagel; // Prints "Bagel instance".
  * </code>
  */
 public class LoxInstance {
-    private final LoxClass klass;
+  private final LoxClass klass;
+  private final Map<String, Object> fields = new HashMap<>();
 
-    public LoxInstance(LoxClass klass) {
-        this.klass = klass;
-    }
+  public LoxInstance(LoxClass klass) {
+    this.klass = klass;
+  }
 
-    @Override
-    public String toString() {
-        return klass.name + " instance";
+  @Override
+  public String toString() {
+    return klass.name + " instance";
+  }
+
+  public Object get(Token name) {
+    if (fields.containsKey(name.lexeme)) {
+      return fields.get(name.lexeme);
     }
+    throw new RuntimeError(name, "undefined '" + name.lexeme + "'.");
+  }
+
+  public void set(Token name, Object value) {
+    fields.put(name.lexeme, value);
+  }
 }

+ 439 - 390
src/main/java/com/craftinginterpreters/lox/Parser.java

@@ -11,424 +11,473 @@ import java.util.List;
 /**
  * @author simon
  * @date 2023-06-05 14:56
- * @desc <p>| Name | Operators | Associates |
- * <p>|------------|-----------|------------| | Equality | == != | Left | | Comparison | > >= <
- * <= | Left | | Term | - + | Left | | Factor | / * | Left | | Unary | ! - | Right |
- * <p>
- * <p>expression → assignment;
- * <p>assignment → IDENTIFIER "=" assignment | 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>
+ * @desc
+ *     <p>| Name | Operators | Associates |
+ *     <p>|------------|-----------|------------| | Equality | == != | Left | | Comparison | > >= <
+ *     <= | Left | | Term | - + | Left | | Factor | / * | Left | | Unary | ! - | Right |
+ *     <p>
+ *     <p>expression → assignment;
+ *     <p>assignment → IDENTIFIER "=" assignment | 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 final List<Token> tokens;
-    private int current = 0;
-
-    Parser(List<Token> tokens) {
-        this.tokens = tokens;
-    }
-
-    // program   → declaration* EOF ;
-    List<Stmt> parse() {
-        List<Stmt> statements = new ArrayList<>();
-        while (!isAtEnd()) {
-            statements.add(declaration());
-        }
-        return statements;
-    }
-
-    // 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();
-        } catch (ParseError error) {
-            synchronize();
-            return null;
-        }
-    }
-
-
-    // statement  → exprStmt | forStmt | ifStmt | printStmt | returnStmt | whileStmt | block;
-    private Stmt statement() {
-        if (match(FOR)) return forStatement();
-        if (match(IF)) return ifStatement();
-        if (match(PRINT)) return printStatement();
-        if (match(RETURN)) return returnStatement();
-        if (match(WHILE)) return whileStatement();
-        if (match(LEFT_BRACE)) return new Stmt.Block(block());
-        return expressionStatement();
-    }
-
-    private Stmt forStatement() {
-        consume(LEFT_PARAM, "Expect '(' after 'for'.");
-
-        Stmt initializer;
-        if (match(SEMICOLON)) {
-            initializer = null;
-        } else if (match(VAR)) {
-            initializer = varDecl();
-        } else {
-            initializer = expressionStatement();
-        }
-
-        Expr condition = null;
-        if (!check(SEMICOLON)) {
-            condition = expression();
-        }
-        consume(SEMICOLON, "Expect ';' after loop condition.");
-
-        Expr increment = null;
-        if (!check(SEMICOLON)) {
-            increment = expression();
-        }
-        consume(RIGHT_PARAM, "Expect ')' after for clauses.");
-
-        Stmt body = statement();
-
-        // desugaring
-        if (increment != null) {
-            body = new Stmt.Block(Arrays.asList(body, new Stmt.Expression(increment)));
-        }
-        if (condition == null) condition = new Expr.Literal(true);
-
-        body = new Stmt.While(condition, body);
-
-        if (initializer != null) {
-            body = new Stmt.Block(Arrays.asList(initializer, body));
-        }
-        return body;
-    }
-
-    // whileStmt      → "while" "(" expression ")" statement ;
-    private Stmt whileStatement() {
-        consume(LEFT_PARAM, "Expect '(' after 'while'.");
-        Expr condition = expression();
-        consume(RIGHT_PARAM, "Expect ')' after 'while'.");
-        Stmt body = statement();
-
-        return new Stmt.While(condition, body);
-    }
-
-    //  ifStmt  → "if" "(" expression ")" statement ( "else" statement)? ;
-    private Stmt ifStatement() {
-        consume(LEFT_PARAM, "Expect '(' after if.");
-        Expr condition = expression();
-        consume(RIGHT_PARAM, "Expect ')' after if condition.");
-
-        Stmt thenBranch = statement();
-        Stmt elseBranch = null;
-        if (match(ELSE)) elseBranch = statement();
-
-        return new Stmt.If(condition, thenBranch, elseBranch);
-    }
-
-    // block          → "{" declaration* "}" ;
-    private List<Stmt> block() {
-        List<Stmt> statements = new ArrayList<>();
-
-        while (!check(RIGHT_BRACE) && !isAtEnd()) {
-            statements.add(declaration());
+  private final List<Token> tokens;
+  private int current = 0;
+
+  Parser(List<Token> tokens) {
+    this.tokens = tokens;
+  }
+
+  /**
+   * program → declaration* EOF ;
+   *
+   * @return List<Stmt>
+   */
+  List<Stmt> parse() {
+    List<Stmt> statements = new ArrayList<>();
+    while (!isAtEnd()) {
+      statements.add(declaration());
+    }
+    return statements;
+  }
+
+  /**
+   * declaration → classDecl | funDecl | varDecl | statement ;
+   *
+   * @return Stmt
+   */
+  private Stmt declaration() {
+    try {
+      if (match(CLASS)) return classDeclaration();
+      if (match(FUN)) return function("function");
+      if (match(VAR)) return varDecl();
+      return statement();
+    } catch (ParseError error) {
+      synchronize();
+      return null;
+    }
+  }
+
+  /**
+   * statement → exprStmt | forStmt | ifStmt | printStmt | returnStmt | whileStmt | block;
+   *
+   * @return Stmt
+   */
+  private Stmt statement() {
+    if (match(FOR)) return forStatement();
+    if (match(IF)) return ifStatement();
+    if (match(PRINT)) return printStatement();
+    if (match(RETURN)) return returnStatement();
+    if (match(WHILE)) return whileStatement();
+    if (match(LEFT_BRACE)) return new Stmt.Block(block());
+    return expressionStatement();
+  }
+
+  /**
+   * <code>for (var i = 0; i < 5; i++ ) {}</code>
+   *
+   * @return Stmt
+   */
+  private Stmt forStatement() {
+    consume(LEFT_PARAM, "Expect '(' after 'for'.");
+
+    Stmt initializer;
+    if (match(SEMICOLON)) {
+      initializer = null;
+    } else if (match(VAR)) {
+      initializer = varDecl();
+    } else {
+      initializer = expressionStatement();
+    }
+
+    Expr condition = null;
+    if (!check(SEMICOLON)) {
+      condition = expression();
+    }
+    consume(SEMICOLON, "Expect ';' after loop condition.");
+
+    Expr increment = null;
+    if (!check(SEMICOLON)) {
+      increment = expression();
+    }
+    consume(RIGHT_PARAM, "Expect ')' after for clauses.");
+
+    Stmt body = statement();
+
+    // desugaring
+    if (increment != null) {
+      body = new Stmt.Block(Arrays.asList(body, new Stmt.Expression(increment)));
+    }
+    if (condition == null) condition = new Expr.Literal(true);
+
+    body = new Stmt.While(condition, body);
+
+    if (initializer != null) {
+      body = new Stmt.Block(Arrays.asList(initializer, body));
+    }
+    return body;
+  }
+
+  /**
+   * whileStmt → "while" "(" expression ")" statement ;
+   *
+   * @return Stmt
+   */
+  private Stmt whileStatement() {
+    consume(LEFT_PARAM, "Expect '(' after 'while'.");
+    Expr condition = expression();
+    consume(RIGHT_PARAM, "Expect ')' after 'while'.");
+    Stmt body = statement();
+
+    return new Stmt.While(condition, body);
+  }
+
+  /**
+   * ifStmt → "if" "(" expression ")" statement ( "else" statement)? ;
+   *
+   * @return Stmt
+   */
+  private Stmt ifStatement() {
+    consume(LEFT_PARAM, "Expect '(' after if.");
+    Expr condition = expression();
+    consume(RIGHT_PARAM, "Expect ')' after if condition.");
+
+    Stmt thenBranch = statement();
+    Stmt elseBranch = null;
+    if (match(ELSE)) elseBranch = statement();
+
+    return new Stmt.If(condition, thenBranch, elseBranch);
+  }
+
+  /**
+   * block → "{" declaration* "}" ;
+   *
+   * @return List<Stmt>
+   */
+  private List<Stmt> block() {
+    List<Stmt> statements = new ArrayList<>();
+
+    while (!check(RIGHT_BRACE) && !isAtEnd()) {
+      statements.add(declaration());
+    }
+
+    consume(RIGHT_BRACE, "Expect '}' after block.");
+    return statements;
+  }
+
+  /**
+   * exprStmt → expression ";" ;
+   *
+   * @return Stmt
+   */
+  private Stmt expressionStatement() {
+    Expr expr = expression();
+    consume(SEMICOLON, "Expect ';' after expression.");
+    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.");
+    List<Token> parameters = new ArrayList<>();
+    if (!check(RIGHT_PARAM)) {
+      do {
+        if (parameters.size() >= 255) {
+          error(peek(), "Can't have more than 255 parameters.");
         }
-
-        consume(RIGHT_BRACE, "Expect '}' after block.");
-        return statements;
-    }
-
-    // exprStmt  → expression ";" ;
-    private Stmt expressionStatement() {
-        Expr expr = expression();
-        consume(SEMICOLON, "Expect ';' after expression.");
-        return new Stmt.Expression(expr);
+        parameters.add(consume(IDENTIFIER, "Expect parameter name."));
+      } while (match(COMMA));
     }
+    consume(RIGHT_PARAM, "Expect ')' after parameters.");
+
+    // body wrap
+    consume(LEFT_BRACE, "Expect '{' before " + kind + " body.");
+    List<Stmt> body = block();
+    return new Stmt.Function(name, parameters, body);
+  }
 
-    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"));
+  // printStmt → "print" expression ";" ;
+  private Stmt printStatement() {
+    Expr value = expression();
+    consume(SEMICOLON, "Expect ';' after value.");
+    return new Stmt.Print(value);
+  }
+
+  // returnStatement → "return" expression? ";" ;
+  private Stmt returnStatement() {
+    Token keyword = previous();
+    Expr value = null;
+    if (!check(SEMICOLON)) {
+      value = expression();
+    }
+
+    consume(SEMICOLON, "Expect ';' after return value.");
+    return new Stmt.Return(keyword, value);
+  }
+
+  // varDecl  → "var" IDENTIFIER ( "=" expression )? ";" ;
+  private Stmt varDecl() {
+    Token name = consume(IDENTIFIER, "Expect variable name.");
+
+    Expr initializer = null;
+    if (match(EQUAL)) {
+      initializer = expression();
+    }
+
+    consume(SEMICOLON, "Expect ';' after variable declaration.");
+    return new Stmt.Var(name, initializer);
+  }
+
+  // expression  → assignment ;
+  private Expr expression() {
+    return assignment();
+  }
+
+  /**
+   * assignment → (call ".")? IDENTIFIER "=" assignment | equality | logic_or
+   *
+   * @return Expr
+   */
+  private Expr assignment() {
+    Expr expr = or();
+    if (match(EQUAL)) {
+      Token equals = previous();
+      Expr value = assignment();
+
+      if (expr instanceof Expr.Variable) {
+        Token name = ((Expr.Variable) expr).name;
+        return new Expr.Assign(name, value);
+      }else if (expr instanceof Expr.Get get) {
+          return new Expr.Set(get.object, get.name, value);
+      }
+
+      error(equals, "Invalid assignment target.");
+    }
+    return expr;
+  }
+
+  // logic_or       → logic_and ( "or" logic_and )* ;
+  private Expr or() {
+    Expr expr = and();
+
+    while (match(OR)) {
+      Token operator = previous();
+      Expr right = and();
+      expr = new Expr.Logical(expr, operator, right);
+    }
+
+    return expr;
+  }
+
+  // logic_and      → equality ( "and" equality )* ;
+  private Expr and() {
+    Expr expr = equality();
+
+    while (match(AND)) {
+      Token operator = previous();
+      Expr right = equality();
+      expr = new Expr.Logical(expr, operator, right);
+    }
+
+    return expr;
+  }
+
+  // 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 | call
+   *
+   * @return Expr
+   */
+  private Expr unary() {
+    if (match(BANG, MINUS)) {
+      Token operator = previous();
+      Expr right = unary();
+      return new Expr.Unary(operator, right);
+    }
+    return call();
+  }
+
+  /**
+   * call → primary ( "(" arguments? ")" )* | "." IDENTIFIER;
+   *
+   * @return Expr
+   */
+  private Expr call() {
+    Expr expr = primary();
+
+    while (true) {
+      if (match(LEFT_PARAM)) {
+        expr = finishCall(expr);
+      } else if (match(DOT)) {
+        Token name = consume(IDENTIFIER, "Expect property name after '.'.");
+        expr = new Expr.Get(expr, name);
+      } else {
+        break;
+      }
+    }
+    return expr;
+  }
+
+  // arguments       → expression ( "," expression)* ;
+  private Expr finishCall(Expr callee) {
+    List<Expr> arguments = new ArrayList<>();
+    if (!check(RIGHT_PARAM)) {
+      do {
+        if (arguments.size() >= 255) {
+          error(peek(), "Can't have more than 255 arguments.");
         }
-
-        consume(RIGHT_BRACE, "Expect '}' after class body.");
-
-        return new Stmt.Class(name, methods);
+        arguments.add(expression());
+      } while (match(COMMA));
     }
+    Token paren = consume(RIGHT_PARAM, "Expect ')' after arguments");
 
-    private Stmt.Function function(String kind) {
-        Token name = consume(IDENTIFIER, "Expect " + kind + " name.");
-        consume(LEFT_PARAM, "Expect '(' after " + kind + " name.");
-        List<Token> parameters = new ArrayList<>();
-        if (!check(RIGHT_PARAM)) {
-            do {
-                if (parameters.size() >= 255) {
-                    error(peek(), "Can't have more than 255 parameters.");
-                }
-                parameters.add(consume(IDENTIFIER, "Expect parameter name."));
-            } while (match(COMMA));
-        }
-        consume(RIGHT_PARAM, "Expect ')' after parameters.");
+    return new Expr.Call(callee, paren, arguments);
+  }
 
-        // body wrap
-        consume(LEFT_BRACE, "Expect '{' before " + kind + " body.");
-        List<Stmt> body = block();
-        return new Stmt.Function(name, parameters, body);
-    }
+  //    primary        → NUMBER | STRING | "true" | "false" | "nil"
+  //            | "(" expression ")" | IDENTIFIER ;
+  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);
 
-    // printStmt → "print" expression ";" ;
-    private Stmt printStatement() {
-        Expr value = expression();
-        consume(SEMICOLON, "Expect ';' after value.");
-        return new Stmt.Print(value);
+    if (match(NUMBER, STRING)) {
+      return new Expr.Literal(previous().literal);
     }
 
-    // returnStatement → "return" expression? ";" ;
-    private Stmt returnStatement() {
-        Token keyword = previous();
-        Expr value = null;
-        if (!check(SEMICOLON)) {
-            value = expression();
-        }
-
-        consume(SEMICOLON, "Expect ';' after return value.");
-        return new Stmt.Return(keyword, value);
+    if (match(LEFT_PARAM)) {
+      Expr expr = expression();
+      consume(RIGHT_PARAM, "Expect ') after expression.");
+      return new Expr.Grouping(expr);
     }
 
-    // varDecl  → "var" IDENTIFIER ( "=" expression )? ";" ;
-    private Stmt varDecl() {
-        Token name = consume(IDENTIFIER, "Expect variable name.");
-
-        Expr initializer = null;
-        if (match(EQUAL)) {
-            initializer = expression();
-        }
-
-        consume(SEMICOLON, "Expect ';' after variable declaration.");
-        return new Stmt.Var(name, initializer);
+    if (match(IDENTIFIER)) {
+      return new Expr.Variable(previous());
     }
 
-    // expression  → assignment ;
-    private Expr expression() {
-        return assignment();
-    }
+    throw error(peek(), "Expect expression.");
+  }
 
-    // assignment → IDENTIFIER "=" assignment | equality | logic_or
-    private Expr assignment() {
-        Expr expr = or();
-        if (match(EQUAL)) {
-            Token equals = previous();
-            Expr value = assignment();
+  private Token consume(TokenType type, String message) {
+    if (check(type)) return advance();
+    throw error(peek(), message);
+  }
 
-            if (expr instanceof Expr.Variable) {
-                Token name = ((Expr.Variable) expr).name;
-                return new Expr.Assign(name, value);
-            }
-
-            error(equals, "Invalid assignment target.");
-        }
-        return expr;
-    }
-
-    // logic_or       → logic_and ( "or" logic_and )* ;
-    private Expr or() {
-        Expr expr = and();
-
-        while (match(OR)) {
-            Token operator = previous();
-            Expr right = and();
-            expr = new Expr.Logical(expr, operator, right);
-        }
-
-        return expr;
-    }
-
-    // logic_and      → equality ( "and" equality )* ;
-    private Expr and() {
-        Expr expr = equality();
-
-        while (match(AND)) {
-            Token operator = previous();
-            Expr right = equality();
-            expr = new Expr.Logical(expr, operator, right);
-        }
-
-        return expr;
-    }
-
-    // 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 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 | call
-    private Expr unary() {
-        if (match(BANG, MINUS)) {
-            Token operator = previous();
-            Expr right = unary();
-            return new Expr.Unary(operator, right);
-        }
-        return call();
-    }
+  private boolean check(TokenType type) {
+    if (isAtEnd()) return false;
+    return peek().type == type;
+  }
 
-    // call  → primary ( "(" arguments? ")" )* ;
-    private Expr call() {
-        Expr expr = primary();
+  private boolean isAtEnd() {
+    return peek().type == EOF;
+  }
 
-        while (true) {
-            if (match(LEFT_PARAM)) {
-                expr = finishCall(expr);
-            } else {
-                break;
-            }
-        }
-        return expr;
-    }
+  private Token peek() {
+    return tokens.get(current);
+  }
 
-    // arguments       → expression ( "," expression)* ;
-    private Expr finishCall(Expr callee) {
-        List<Expr> arguments = new ArrayList<>();
-        if (!check(RIGHT_PARAM)) {
-            do {
-                if (arguments.size() >= 255) {
-                    error(peek(), "Can't have more than 255 arguments.");
-                }
-                arguments.add(expression());
-            } while (match(COMMA));
-        }
-        Token paren = consume(RIGHT_PARAM, "Expect ')' after arguments");
+  private Token previous() {
+    return tokens.get(current - 1);
+  }
 
-        return new Expr.Call(callee, paren, arguments);
-    }
+  private ParseError error(Token token, String message) {
+    Lox.error(token, message);
+    return new ParseError();
+  }
 
-    //    primary        → NUMBER | STRING | "true" | "false" | "nil"
-    //            | "(" expression ")" | IDENTIFIER ;
-    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 void synchronize() {
+    advance();
 
-        if (match(NUMBER, STRING)) {
-            return new Expr.Literal(previous().literal);
-        }
+    while (!isAtEnd()) {
+      if (previous().type == SEMICOLON) return;
 
-        if (match(LEFT_PARAM)) {
-            Expr expr = expression();
-            consume(RIGHT_PARAM, "Expect ') after expression.");
-            return new Expr.Grouping(expr);
+      switch (peek().type) {
+        case CLASS, FUN, VAR, FOR, IF, PRINT, WHILE, RETURN -> {
+          return;
         }
+        default -> advance();
+      }
+    } // End while
+  }
 
-        if (match(IDENTIFIER)) {
-            return new Expr.Variable(previous());
-        }
-
-        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
-    }
-
-    private static class ParseError extends RuntimeException {
-    }
+  private static class ParseError extends RuntimeException {}
 }

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

@@ -47,6 +47,12 @@ public class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
     }
 
     @Override
+    public Void visitGetExpr(Expr.Get expr) {
+        resolve(expr.object);
+        return null;
+    }
+
+    @Override
     public Void visitGroupingExpr(Expr.Grouping expr) {
         resolve(expr.expression);
         return null;
@@ -68,6 +74,13 @@ public class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
     }
 
     @Override
+    public Void visitSetExpr(Expr.Set expr) {
+        resolve(expr.value);
+        resolve(expr.object);
+        return null;
+    }
+
+    @Override
     public Void visitUnaryExpr(Expr.Unary expr) {
         resolve(expr.right);
         return null;

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

@@ -42,16 +42,18 @@ public class GenerateAst {
             System.exit(64);
         }
         String outputDir = args[0];
-//        defineAst(outputDir, "Expr", Arrays.asList(
-//                "Assign   : Token name, Expr value",
-//                "Binary   : Expr left, Token operator, Expr right",
-//                "Call     : Expr callee, Token paren, List<Expr> arguments",
-//                "Grouping : Expr expression",
-//                "Literal  : Object value",
-//                "Logical  : Expr left, Token operator, Expr right",
-//                "Unary    : Token operator, Expr right",
-//                "Variable : Token name"
-//        ));
+        defineAst(outputDir, "Expr", Arrays.asList(
+                "Assign   : Token name, Expr value",
+                "Binary   : Expr left, Token operator, Expr right",
+                "Call     : Expr callee, Token paren, List<Expr> arguments",
+                "Get      : Expr object, Token name",
+                "Grouping : Expr expression",
+                "Literal  : Object value",
+                "Logical  : Expr left, Token operator, Expr right",
+                "Set      : Expr object, Token name, Expr value",
+                "Unary    : Token operator, Expr right",
+                "Variable : Token name"
+        ));
 //        defineAst(outputDir, "Stmt", Arrays.asList(
 //                "Block       : List<Stmt> statements",
 //                "Class       : Token name, List<Stmt.Function> methods",