Skip to content

Commit

Permalink
Implement resolving and binding
Browse files Browse the repository at this point in the history
  • Loading branch information
nop committed Sep 9, 2023
1 parent 87c5486 commit b4c2204
Show file tree
Hide file tree
Showing 12 changed files with 533 additions and 74 deletions.
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ idea {
}
}

jar.manifest.attributes('Main-Class': 'com.craftinginterpreters.lox.Lox')

application {
mainClass = 'com.craftinginterpreters.lox.Lox'
}
Expand Down Expand Up @@ -87,6 +89,7 @@ tasks.register('genExpr', Exec) {
"Expr",
"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",
Expand All @@ -100,8 +103,10 @@ tasks.register('genStmt', Exec) {
"Stmt",
"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"
}
Expand Down
8 changes: 8 additions & 0 deletions examples/fib.lox
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fun fib(n) {
if (n <= 1) return n;
return fib(n - 2) + fib(n - 1);
}

for (var i = 0; i < 20; i = i + 1) {
print fib(i);
}
6 changes: 6 additions & 0 deletions src/main/java/com/craftinginterpreters/lox/AstPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ public String visitBinaryExpr(final Expr.Binary expr) {
return parenthesize(expr.operator.lexeme, expr.left, expr.right);
}

@Override
public String visitCallExpr(Expr.Call expr) {
// return parenthesize(expr.callee, expr.arguments.toArray());
return "";
}

@Override
public String visitGroupingExpr(final Expr.Grouping expr) {
return parenthesize("group", expr.expression);
Expand Down
75 changes: 46 additions & 29 deletions src/main/java/com/craftinginterpreters/lox/Environment.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,59 @@
import java.util.Map;

class Environment {
final Environment enclosing;
private final Map<String, Object> values = new HashMap<>();
final Environment enclosing;
private final Map<String, Object> values = new HashMap<>();

Environment() {
enclosing = null;
}
Environment() {
enclosing = null;
}

Environment(Environment enclosing) {
this.enclosing = enclosing;
}
Environment(Environment enclosing) {
this.enclosing = enclosing;
}

Object get(Token name) {
if (values.containsKey(name.lexeme)) {
return values.get(name.lexeme);
}
Object get(Token name) {
if (values.containsKey(name.lexeme)) {
return values.get(name.lexeme);
}

if (enclosing != null) return enclosing.get(name);
if (enclosing != null) return enclosing.get(name);

throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
}
throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
}

void assign(Token name, Object value) {
if (values.containsKey(name.lexeme)) {
values.put(name.lexeme, value);
return;
}
void assign(Token name, Object value) {
if (values.containsKey(name.lexeme)) {
values.put(name.lexeme, value);
return;
}

if (enclosing != null) {
enclosing.assign(name, value);
return;
}
if (enclosing != null) {
enclosing.assign(name, value);
return;
}

throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
}
throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
}

void define(String name, Object value) {
values.put(name, value);
}
void define(String name, Object value) {
values.put(name, value);
}

Environment ancestor(int distance) {
Environment environment = this;
for (int i = 0; i < distance; i++) {
environment = environment.enclosing;
}

return environment;
}

Object getAt(int distance, String name) {
return ancestor(distance).values.get(name);
}

void assignAt(int distance, Token name, Object value) {
ancestor(distance).values.put(name.lexeme, value);
}
}
91 changes: 86 additions & 5 deletions src/main/java/com/craftinginterpreters/lox/Interpreter.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
package com.craftinginterpreters.lox;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Interpreter
implements Expr.Visitor<Object>, Stmt.Visitor<Void>
{
private Environment environment = new Environment();
implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
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>";
}
});
}

void interpret(List<Stmt> statements) {
try {
Expand Down Expand Up @@ -63,6 +87,28 @@ public Object visitBinaryExpr(Expr.Binary expr) {
return 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 function)) {
throw new RuntimeError(expr.paren, "Can only call functions and classes.");
}

if (arguments.size() != function.arity()) {
throw new RuntimeError(expr.paren, "Expected " +
function.arity() + "arguments but got " +
arguments.size() + ".");
}

return function.call(this, arguments);
}

private void checkNumberOperands(Token operator, Object left, Object right) {
if (left instanceof Double && right instanceof Double) return;
throw new RuntimeError(operator, "Operands must be numbers.");
Expand Down Expand Up @@ -135,7 +181,16 @@ public Object visitUnaryExpr(Expr.Unary expr) {

@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);
}
}

private boolean isTruthy(Object object) {
Expand All @@ -152,6 +207,10 @@ private void execute(Stmt stmt) {
stmt.accept(this);
}

void resolve(Expr expr, int depth) {
locals.put(expr, depth);
}

void executeBlock(List<Stmt> statements, Environment environment) {
Environment previous = this.environment;
try {
Expand All @@ -177,6 +236,13 @@ public Void visitExpressionStmt(Stmt.Expression stmt) {
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))) {
Expand All @@ -194,6 +260,14 @@ public Void visitPrintStmt(Stmt.Print stmt) {
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;
Expand All @@ -215,7 +289,14 @@ public Void visitWhileStmt(Stmt.While stmt) {
@Override
public Object visitAssignExpr(Expr.Assign expr) {
Object value = evaluate(expr.value);
environment.assign(expr.name, value);

Integer distance = locals.get(expr);
if (distance != null) {
environment.assignAt(distance, expr.name, value);
} else {
globals.assign(expr.name, value);
}

return value;
}
}
6 changes: 6 additions & 0 deletions src/main/java/com/craftinginterpreters/lox/Lox.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ private static void run(String source) {
// Stop if there was a syntax error.
if (hadError) return;

Resolver resolver = new Resolver(interpreter);
resolver.resolve(statements);

// Stop if there was a resolution error.
if (hadError) return;

interpreter.interpret(statements);
}

Expand Down
9 changes: 9 additions & 0 deletions src/main/java/com/craftinginterpreters/lox/LoxCallable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.craftinginterpreters.lox;

import java.util.List;

interface LoxCallable {
int arity();

Object call(Interpreter interpreter, List<Object> arguments);
}
38 changes: 38 additions & 0 deletions src/main/java/com/craftinginterpreters/lox/LoxFunction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.craftinginterpreters.lox;

import java.util.List;

public class LoxFunction implements LoxCallable {
private final Stmt.Function declaration;
private final Environment closure;

LoxFunction(Stmt.Function declaration, Environment closure) {
this.closure = closure;
this.declaration = declaration;
}

@Override
public int arity() {
return declaration.params.size();
}

@Override
public Object call(Interpreter interpreter, List<Object> arguments) {
Environment environment = new Environment(closure);
for (int i = 0; i < declaration.params.size(); i++) {
environment.define(declaration.params.get(i).lexeme, arguments.get(i));
}

try {
interpreter.executeBlock(declaration.body, environment);
} catch (Return returnValue) {
return returnValue.value;
}
return null;
}

@Override
public String toString() {
return "<fn " + declaration.name.lexeme + ">";
}
}
Loading

0 comments on commit b4c2204

Please sign in to comment.