[jlox] Classes
This commit is contained in:
@@ -33,6 +33,12 @@ public class AstPrinter {
|
|||||||
return reverse ? wrap(left, right, op) : wrap(op, left, right);
|
return reverse ? wrap(left, right, op) : wrap(op, left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitGetExpr(Expr.Get expr) {
|
||||||
|
var left = expr.object().accept(this);
|
||||||
|
return left + "." + expr.name().lexeme();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String visitCallExpr(Expr.Call expr) {
|
public String visitCallExpr(Expr.Call expr) {
|
||||||
var fun = expr.callee().accept(this);
|
var fun = expr.callee().accept(this);
|
||||||
@@ -64,6 +70,20 @@ public class AstPrinter {
|
|||||||
return reverse ? wrap(left, right, op) : wrap(op, left, right);
|
return reverse ? wrap(left, right, op) : wrap(op, left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitSetExpr(Expr.Set expr) {
|
||||||
|
|
||||||
|
|
||||||
|
var left = expr.object().accept(this);
|
||||||
|
var val = expr.object().accept(this);
|
||||||
|
return wrap("=", left + "." + expr.name().lexeme(), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitThisExpr(Expr.This expr) {
|
||||||
|
return "this";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String visitUnaryExpr(Expr.Unary expr) {
|
public String visitUnaryExpr(Expr.Unary expr) {
|
||||||
var op = expr.operator().lexeme();
|
var op = expr.operator().lexeme();
|
||||||
@@ -102,6 +122,12 @@ public class AstPrinter {
|
|||||||
expr.right().accept(this));
|
expr.right().accept(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitGetExpr(Expr.Get expr) {
|
||||||
|
var left = expr.object().accept(this);
|
||||||
|
return left + "." + expr.name().lexeme();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String visitCallExpr(Expr.Call expr) {
|
public String visitCallExpr(Expr.Call expr) {
|
||||||
var fun = expr.callee().accept(this);
|
var fun = expr.callee().accept(this);
|
||||||
@@ -129,6 +155,18 @@ public class AstPrinter {
|
|||||||
expr.right().accept(this));
|
expr.right().accept(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitSetExpr(Expr.Set expr) {
|
||||||
|
var left = expr.object().accept(this);
|
||||||
|
var val = expr.object().accept(this);
|
||||||
|
return left + "." + expr.name().lexeme() + " = " + val;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitThisExpr(Expr.This expr) {
|
||||||
|
return "this";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String visitUnaryExpr(Expr.Unary expr) {
|
public String visitUnaryExpr(Expr.Unary expr) {
|
||||||
return expr.operator().lexeme() + expr.right().accept(this);
|
return expr.operator().lexeme() + expr.right().accept(this);
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ package xyz.ctsk.lox;
|
|||||||
public enum FunctionType {
|
public enum FunctionType {
|
||||||
NONE,
|
NONE,
|
||||||
FUNCTION,
|
FUNCTION,
|
||||||
METHOD
|
INITIALIZER, METHOD
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,21 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visitClassStmt(Stmt.Class stmt) {
|
||||||
|
environment.define(stmt.name().lexeme(), null);
|
||||||
|
|
||||||
|
Map<String, LoxFunction> methods = new HashMap<>();
|
||||||
|
for (var method : stmt.methods()) {
|
||||||
|
var function = new LoxFunction(method, environment, method.name().lexeme().equals("init"));
|
||||||
|
methods.put(method.name().lexeme(), function);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoxClass clazz = new LoxClass(stmt.name().lexeme(), methods);
|
||||||
|
environment.assign(stmt.name(), clazz);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitExpressionStmt(Stmt.Expression stmt) {
|
public Void visitExpressionStmt(Stmt.Expression stmt) {
|
||||||
evaluate(stmt.expression());
|
evaluate(stmt.expression());
|
||||||
@@ -86,7 +101,7 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitFunctionStmt(Stmt.Function stmt) {
|
public Void visitFunctionStmt(Stmt.Function stmt) {
|
||||||
environment.define(stmt.name().lexeme(), new LoxFunction(stmt, environment));
|
environment.define(stmt.name().lexeme(), new LoxFunction(stmt, environment, false));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,6 +192,15 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitGetExpr(Expr.Get expr) {
|
||||||
|
var object = evaluate(expr.object());
|
||||||
|
if (object instanceof LoxInstance loxInstance) {
|
||||||
|
return loxInstance.get(expr.name());
|
||||||
|
}
|
||||||
|
throw new RuntimeError(expr.name(), "Only instances have properties.");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitCallExpr(Expr.Call expr) {
|
public Object visitCallExpr(Expr.Call expr) {
|
||||||
var callee = evaluate(expr.callee());
|
var callee = evaluate(expr.callee());
|
||||||
@@ -219,6 +243,23 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
|||||||
return evaluate(expr.right());
|
return evaluate(expr.right());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitSetExpr(Expr.Set expr) {
|
||||||
|
var object = evaluate(expr.object());
|
||||||
|
if (object instanceof LoxInstance instance) {
|
||||||
|
var value = evaluate(expr.value());
|
||||||
|
instance.set(expr.name(), value);
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeError(expr.name(), "Only instances have fields.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitThisExpr(Expr.This expr) {
|
||||||
|
return lookupVariable(expr.keyword(), expr);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitUnaryExpr(Expr.Unary expr) {
|
public Object visitUnaryExpr(Expr.Unary expr) {
|
||||||
var right = evaluate(expr.right());
|
var right = evaluate(expr.right());
|
||||||
@@ -235,7 +276,7 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
|||||||
return lookupVariable(expr.name(), expr);
|
return lookupVariable(expr.name(), expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object lookupVariable(Token name, Expr.Variable expr) {
|
private Object lookupVariable(Token name, Expr expr) {
|
||||||
var distance = locals.get(expr);
|
var distance = locals.get(expr);
|
||||||
if (distance != null) {
|
if (distance != null) {
|
||||||
return environment.getAt(distance, name.lexeme());
|
return environment.getAt(distance, name.lexeme());
|
||||||
|
|||||||
44
jlox/lox/src/main/java/xyz/ctsk/lox/LoxClass.java
Normal file
44
jlox/lox/src/main/java/xyz/ctsk/lox/LoxClass.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package xyz.ctsk.lox;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class LoxClass implements LoxCallable {
|
||||||
|
final String name;
|
||||||
|
private final Map<String, LoxFunction> methods;
|
||||||
|
|
||||||
|
LoxClass(String name, Map<String, LoxFunction> methods) {
|
||||||
|
this.name = name;
|
||||||
|
this.methods = methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int arity() {
|
||||||
|
LoxFunction initializer = findMethod("init");
|
||||||
|
if (initializer == null) return 0;
|
||||||
|
return initializer.arity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(Interpreter interpreter, List<Object> arguments) {
|
||||||
|
var instance = new LoxInstance(this);
|
||||||
|
LoxFunction initializer = findMethod("init");
|
||||||
|
if (initializer != null) {
|
||||||
|
initializer.bind(instance).call(interpreter, arguments);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoxFunction findMethod(String name) {
|
||||||
|
if (methods.containsKey(name)) {
|
||||||
|
return methods.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,9 +6,12 @@ public class LoxFunction implements LoxCallable {
|
|||||||
private final Stmt.Function declaration;
|
private final Stmt.Function declaration;
|
||||||
private final Environment closure;
|
private final Environment closure;
|
||||||
|
|
||||||
public LoxFunction(Stmt.Function declaration, Environment closure) {
|
private final boolean isInitializer;
|
||||||
|
|
||||||
|
public LoxFunction(Stmt.Function declaration, Environment closure, boolean isInitializer) {
|
||||||
this.declaration = declaration;
|
this.declaration = declaration;
|
||||||
this.closure = closure;
|
this.closure = closure;
|
||||||
|
this.isInitializer = isInitializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -27,11 +30,20 @@ public class LoxFunction implements LoxCallable {
|
|||||||
try {
|
try {
|
||||||
interpreter.executeBlock(declaration.body(), environment);
|
interpreter.executeBlock(declaration.body(), environment);
|
||||||
} catch (Return ret) {
|
} catch (Return ret) {
|
||||||
|
if (isInitializer) return closure.getAt(0, "this");
|
||||||
return ret.value;
|
return ret.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isInitializer) return closure.getAt(0, "this");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoxFunction bind(LoxInstance instance) {
|
||||||
|
Environment env = new Environment(closure);
|
||||||
|
env.define("this", instance);
|
||||||
|
return new LoxFunction(declaration, env, isInitializer);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "<fn " + declaration.name().lexeme() + " >";
|
return "<fn " + declaration.name().lexeme() + " >";
|
||||||
|
|||||||
33
jlox/lox/src/main/java/xyz/ctsk/lox/LoxInstance.java
Normal file
33
jlox/lox/src/main/java/xyz/ctsk/lox/LoxInstance.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package xyz.ctsk.lox;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class LoxInstance {
|
||||||
|
private LoxClass clazz;
|
||||||
|
private final Map<String, Object> fields = new HashMap<>();
|
||||||
|
|
||||||
|
LoxInstance(LoxClass clazz) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object get(Token name) {
|
||||||
|
if (fields.containsKey(name.lexeme())) {
|
||||||
|
return fields.get(name.lexeme());
|
||||||
|
}
|
||||||
|
|
||||||
|
LoxFunction method = clazz.findMethod(name.lexeme());
|
||||||
|
if (method != null) return method.bind(this);
|
||||||
|
|
||||||
|
throw new RuntimeError(name, "Undefined property '%s'.".formatted(name.lexeme()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(Token name, Object value) {
|
||||||
|
fields.put(name.lexeme(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "<" + clazz.name + " instance>";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,9 +10,11 @@ import static xyz.ctsk.lox.TokenType.*;
|
|||||||
* A recursive descent parser for the following grammar:
|
* A recursive descent parser for the following grammar:
|
||||||
* <p>
|
* <p>
|
||||||
* program → declaration* EOF ;
|
* program → declaration* EOF ;
|
||||||
* declaration → funDecl
|
* declaration → classDecl
|
||||||
|
* | funDecl
|
||||||
* | varDecl
|
* | varDecl
|
||||||
* | statement ;
|
* | statement ;
|
||||||
|
* classDecl → "class" IDENTIFIER "{" function* "}" ;
|
||||||
* funDecl → "fun" function ;
|
* funDecl → "fun" function ;
|
||||||
* function → IDENTIFIER "(" parameters? ")" block ;
|
* function → IDENTIFIER "(" parameters? ")" block ;
|
||||||
* parameters → IDENTIFIER ( "," IDENTIFIER )* ;
|
* parameters → IDENTIFIER ( "," IDENTIFIER )* ;
|
||||||
@@ -35,7 +37,7 @@ import static xyz.ctsk.lox.TokenType.*;
|
|||||||
* exprStmt → expression ";" ;
|
* exprStmt → expression ";" ;
|
||||||
* expression → equality ;
|
* expression → equality ;
|
||||||
* expression → assignment ;
|
* expression → assignment ;
|
||||||
* assignment → IDENTIFIER "=" assignment
|
* assignment → ( call "." )? IDENTIFIER "=" assignment
|
||||||
* | logic_or ;
|
* | logic_or ;
|
||||||
* logic_or → logic_and ( "or" logic_and )* ;
|
* logic_or → logic_and ( "or" logic_and )* ;
|
||||||
* logic_and → equality ( "and" equality )* ;
|
* logic_and → equality ( "and" equality )* ;
|
||||||
@@ -45,11 +47,12 @@ import static xyz.ctsk.lox.TokenType.*;
|
|||||||
* factor → unary ( ( "/" | "*" ) unary )* ;
|
* factor → unary ( ( "/" | "*" ) unary )* ;
|
||||||
* unary → ( "!" | "-" ) unary
|
* unary → ( "!" | "-" ) unary
|
||||||
* | call ;
|
* | call ;
|
||||||
* call → primary ( "(" arguments? ")" )* ;
|
* call → primary ( "(" arguments? ")" | "." IDENTIFIER )* ;
|
||||||
* arguments → expression ( "," expression )* ;
|
* arguments → expression ( "," expression )* ;
|
||||||
* primary → NUMBER | STRING | "true" | "false" | "nil"
|
* primary → NUMBER | STRING | "true" | "false" | "nil"
|
||||||
* | "(" expression ")"
|
* | "(" expression ")"
|
||||||
* | IDENTIFIER;
|
* | IDENTIFIER
|
||||||
|
* | THIS ;
|
||||||
*/
|
*/
|
||||||
public class Parser {
|
public class Parser {
|
||||||
private final List<Token> tokens;
|
private final List<Token> tokens;
|
||||||
@@ -72,6 +75,7 @@ public class Parser {
|
|||||||
|
|
||||||
private Stmt declaration() {
|
private Stmt declaration() {
|
||||||
try {
|
try {
|
||||||
|
if (match(CLASS)) return classDeclaration();
|
||||||
if (match(FUN)) return function(FunctionType.FUNCTION);
|
if (match(FUN)) return function(FunctionType.FUNCTION);
|
||||||
if (match(VAR)) return varDeclaration();
|
if (match(VAR)) return varDeclaration();
|
||||||
return match(VAR) ? varDeclaration() : statement();
|
return match(VAR) ? varDeclaration() : statement();
|
||||||
@@ -81,6 +85,21 @@ public class Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Stmt classDeclaration() {
|
||||||
|
var 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(FunctionType.METHOD));
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(LEFT_BRACE, "Expect '}' after class body.");
|
||||||
|
return new Stmt.Class(name, methods);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private Stmt.Function function(FunctionType kind) {
|
private Stmt.Function function(FunctionType kind) {
|
||||||
Token name = consume(IDENTIFIER, "Expect %s name.".formatted(kind.toString().toLowerCase()));
|
Token name = consume(IDENTIFIER, "Expect %s name.".formatted(kind.toString().toLowerCase()));
|
||||||
|
|
||||||
@@ -232,6 +251,8 @@ public class Parser {
|
|||||||
|
|
||||||
if (expr instanceof Expr.Variable varExpr) {
|
if (expr instanceof Expr.Variable varExpr) {
|
||||||
return new Expr.Assign(varExpr.name(), value);
|
return new Expr.Assign(varExpr.name(), value);
|
||||||
|
} else if (expr instanceof Expr.Get getExpr) {
|
||||||
|
return new Expr.Set(getExpr.object(), getExpr.name(), value);
|
||||||
} else {
|
} else {
|
||||||
//We report an error but don't throw it because we do not need our parser to panic and sync.
|
//We report an error but don't throw it because we do not need our parser to panic and sync.
|
||||||
//noinspection ThrowableNotThrown
|
//noinspection ThrowableNotThrown
|
||||||
@@ -330,7 +351,9 @@ public class Parser {
|
|||||||
while(true) {
|
while(true) {
|
||||||
if (match(LEFT_PAREN)) {
|
if (match(LEFT_PAREN)) {
|
||||||
expr = finishCall(expr);
|
expr = finishCall(expr);
|
||||||
} else {
|
} else if (match(DOT)) {
|
||||||
|
Token name = consume(IDENTIFIER, "Expect property name after '.'.");
|
||||||
|
expr = new Expr.Get(expr, name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,6 +388,8 @@ public class Parser {
|
|||||||
return new Expr.Literal(previous().literal());
|
return new Expr.Literal(previous().literal());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (match(THIS)) return new Expr.This(previous());
|
||||||
|
|
||||||
if (match(IDENTIFIER)) {
|
if (match(IDENTIFIER)) {
|
||||||
return new Expr.Variable(previous());
|
return new Expr.Variable(previous());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ public class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
|
|||||||
private final Stack<Map<String, Boolean>> scopes = new Stack<>();
|
private final Stack<Map<String, Boolean>> scopes = new Stack<>();
|
||||||
private FunctionType currentFuntion = FunctionType.NONE;
|
private FunctionType currentFuntion = FunctionType.NONE;
|
||||||
|
|
||||||
|
private enum ClassType { NONE, CLASS }
|
||||||
|
|
||||||
|
private ClassType currentClass = ClassType.NONE;
|
||||||
|
|
||||||
|
|
||||||
Resolver(Interpreter interpreter) {
|
Resolver(Interpreter interpreter) {
|
||||||
this.interpreter = interpreter;
|
this.interpreter = interpreter;
|
||||||
@@ -82,6 +86,31 @@ public class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visitClassStmt(Stmt.Class stmt) {
|
||||||
|
var enclosing = currentClass;
|
||||||
|
currentClass = ClassType.CLASS;
|
||||||
|
declare(stmt.name());
|
||||||
|
define(stmt.name());
|
||||||
|
beginScope();
|
||||||
|
scopes.peek().put("this", true);
|
||||||
|
|
||||||
|
for (var method : stmt.methods()) {
|
||||||
|
FunctionType declaration = FunctionType.METHOD;
|
||||||
|
|
||||||
|
if (method.name().lexeme().equals("init")) {
|
||||||
|
declaration = FunctionType.INITIALIZER;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveFunction(method, declaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
endScope();
|
||||||
|
|
||||||
|
currentClass = enclosing;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitExpressionStmt(Stmt.Expression stmt) {
|
public Void visitExpressionStmt(Stmt.Expression stmt) {
|
||||||
@@ -118,6 +147,9 @@ public class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stmt.value() != null) {
|
if (stmt.value() != null) {
|
||||||
|
if (currentFuntion == FunctionType.INITIALIZER) {
|
||||||
|
Lox.error(stmt.keyword(), "Can't return a value from an initializer.");
|
||||||
|
}
|
||||||
resolve(stmt.value());
|
resolve(stmt.value());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -155,6 +187,12 @@ public class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visitGetExpr(Expr.Get expr) {
|
||||||
|
resolve(expr.object());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitCallExpr(Expr.Call expr) {
|
public Void visitCallExpr(Expr.Call expr) {
|
||||||
resolve(expr.callee());
|
resolve(expr.callee());
|
||||||
@@ -180,6 +218,24 @@ public class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visitSetExpr(Expr.Set expr) {
|
||||||
|
resolve(expr.value());
|
||||||
|
resolve(expr.object());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visitThisExpr(Expr.This expr) {
|
||||||
|
if (currentClass == ClassType.NONE) {
|
||||||
|
Lox.error(expr.keyword(), "Can't use 'this' outside of a class.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveLocal(expr, expr.keyword());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitUnaryExpr(Expr.Unary expr) {
|
public Void visitUnaryExpr(Expr.Unary expr) {
|
||||||
resolve(expr.right());
|
resolve(expr.right());
|
||||||
|
|||||||
@@ -3,16 +3,20 @@
|
|||||||
rules = {
|
rules = {
|
||||||
@Rule(head = "Assign", body = {"Token name", "Expr value"}),
|
@Rule(head = "Assign", body = {"Token name", "Expr value"}),
|
||||||
@Rule(head = "Binary", body = {"Expr left", "Token operator", "Expr right"}),
|
@Rule(head = "Binary", body = {"Expr left", "Token operator", "Expr right"}),
|
||||||
|
@Rule(head = "Get", body = {"Expr object", "Token name"}),
|
||||||
@Rule(head = "Call", body = {"Expr callee", "Token paren", "List<Expr> arguments"}),
|
@Rule(head = "Call", body = {"Expr callee", "Token paren", "List<Expr> arguments"}),
|
||||||
@Rule(head = "Grouping", body = {"Expr expression"}),
|
@Rule(head = "Grouping", body = {"Expr expression"}),
|
||||||
@Rule(head = "Literal", body = {"Object value"}),
|
@Rule(head = "Literal", body = {"Object value"}),
|
||||||
@Rule(head = "Logical", body = {"Expr left", "Token operator", "Expr right"}),
|
@Rule(head = "Logical", body = {"Expr left", "Token operator", "Expr right"}),
|
||||||
|
@Rule(head = "Set", body = {"Expr object", "Token name", "Expr value"}),
|
||||||
|
@Rule(head = "This", body = {"Token keyword"}),
|
||||||
@Rule(head = "Unary", body = {"Token operator", "Expr right"}),
|
@Rule(head = "Unary", body = {"Token operator", "Expr right"}),
|
||||||
@Rule(head = "Variable", body = {"Token name"})
|
@Rule(head = "Variable", body = {"Token name"})
|
||||||
}),
|
}),
|
||||||
@Root(name = "Stmt",
|
@Root(name = "Stmt",
|
||||||
rules = {
|
rules = {
|
||||||
@Rule(head = "Block", body = {"List<Stmt> statements"}),
|
@Rule(head = "Block", body = {"List<Stmt> statements"}),
|
||||||
|
@Rule(head = "Class", body = {"Token name", "List<Stmt.Function> methods"}),
|
||||||
@Rule(head = "Expression", body = {"Expr expression"}),
|
@Rule(head = "Expression", body = {"Expr expression"}),
|
||||||
@Rule(head = "Function", body = {"Token name", "List<Token> params", "List<Stmt> body"}),
|
@Rule(head = "Function", body = {"Token name", "List<Token> params", "List<Stmt> body"}),
|
||||||
@Rule(head = "If", body = {"Expr condition", "Stmt thenBranch", "Stmt elseBranch"}),
|
@Rule(head = "If", body = {"Expr condition", "Stmt thenBranch", "Stmt elseBranch"}),
|
||||||
|
|||||||
Reference in New Issue
Block a user