Add function calls

This commit is contained in:
Tom Willemse 2021-01-31 17:11:47 -08:00
parent 922a8f6863
commit 4a71c219b5
5 changed files with 84 additions and 4 deletions

View file

@ -15,10 +15,10 @@ add_jar(Lox
Token.java Token.java
Scanner.java Scanner.java
${GENERATED_JAVA_FILENAMES} ${GENERATED_JAVA_FILENAMES}
AstPrinter.java
Parser.java Parser.java
Interpreter.java Interpreter.java
RuntimeError.java RuntimeError.java
Environment.java Environment.java
LoxCallable.java LoxCallable.java
LoxFunction.java
ENTRY_POINT com/craftinginterpreters/lox/Lox) ENTRY_POINT com/craftinginterpreters/lox/Lox)

View file

@ -4,7 +4,23 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> { class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
private Environment environment = new Environment(); final Environment globals = new Environment();
private Environment environment = globals;
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>"; }
});
}
@Override @Override
public Object visitLiteralExpr(Expr.Literal expr) { public Object visitLiteralExpr(Expr.Literal expr) {
@ -105,7 +121,7 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
stmt.accept(this); stmt.accept(this);
} }
private void executeBlock(List<Stmt> statements, Environment environment) { public void executeBlock(List<Stmt> statements, Environment environment) {
Environment previous = this.environment; Environment previous = this.environment;
try { try {
@ -131,6 +147,13 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
return null; return null;
} }
@Override
public Void visitFunctionStmt(Stmt.Function stmt) {
LoxFunction function = new LoxFunction(stmt);
environment.define(stmt.name.lexeme, function);
return null;
}
@Override @Override
public Void visitIfStmt(Stmt.If stmt) { public Void visitIfStmt(Stmt.If stmt) {
if (isTruthy(evaluate(stmt.condition))) { if (isTruthy(evaluate(stmt.condition))) {
@ -238,7 +261,8 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
LoxCallable function = (LoxCallable) callee; LoxCallable function = (LoxCallable) callee;
if (arguments.size() != function.arity()) { if (arguments.size() != function.arity()) {
throw new RuntimeError(expr.paren, "Expected " + function.arity() + " arguments but got " + arguments.size() + "."); throw new RuntimeError(expr.paren,
"Expected " + function.arity() + " arguments but got " + arguments.size() + ".");
} }
return function.call(this, arguments); return function.call(this, arguments);

View file

@ -0,0 +1,33 @@
package com.craftinginterpreters.lox;
import java.util.List;
class LoxFunction implements LoxCallable {
private final Stmt.Function declaration;
LoxFunction(Stmt.Function declaration) {
this.declaration = declaration;
}
@Override
public int arity() {
return declaration.params.size();
}
@Override
public Object call(Interpreter interpreter, List<Object> arguments) {
Environment environment = new Environment(interpreter.globals);
for (int i = 0; i < declaration.params.size(); i++) {
environment.define(declaration.params.get(i).lexeme, arguments.get(i));
}
interpreter.executeBlock(declaration.body, environment);
return null;
}
@Override
public String toString() {
return "<fn " + declaration.name.lexeme + ">";
}
}

View file

@ -33,6 +33,8 @@ class Parser {
private Stmt declaration() { private Stmt declaration() {
try { try {
if (match(FUN))
return function("function");
if (match(VAR)) if (match(VAR))
return varDeclaration(); return varDeclaration();
@ -145,6 +147,26 @@ class Parser {
return new Stmt.Expression(expr); return new Stmt.Expression(expr);
} }
private Stmt.Function function(String kind) {
Token name = consume(IDENTIFIER, "Expect " + kind + " name.");
consume(LEFT_PAREN, "Expect '(' after " + kind + " name.");
List<Token> parameters = new ArrayList();
if (!check(RIGHT_PAREN)) {
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_PAREN, "Expect ')' after parameters.");
consume(LEFT_BRACE, "Expect '{' before " + kind + " body.");
List<Stmt> body = block();
return new Stmt.Function(name, parameters, body);
}
private List<Stmt> block() { private List<Stmt> block() {
List<Stmt> statements = new ArrayList<>(); List<Stmt> statements = new ArrayList<>();

View file

@ -21,6 +21,7 @@ public class GenerateAst {
"Unary : Token operator, Expr right", "Variable : Token name")); "Unary : Token operator, Expr right", "Variable : Token name"));
defineAst(outputDir, "Stmt", defineAst(outputDir, "Stmt",
Arrays.asList("Block : List<Stmt> statements", "Expression : Expr expression", 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", "If : Expr condition, Stmt thenBranch, Stmt elseBranch", "Print : Expr expression",
"Var : Token name, Expr initializer", "While : Expr condition, Stmt body")); "Var : Token name, Expr initializer", "While : Expr condition, Stmt body"));
} }