functions

This commit is contained in:
David 2021-09-26 12:54:32 -04:00
parent 10d51487ae
commit eca4492492
9 changed files with 251 additions and 2 deletions

View File

@ -28,6 +28,19 @@ class ASTPrinter implements Expr.Visitor<String> {
return parenthesize(expr.operator.lexeme, expr.right);
}
@Override
public String visitCallExpr(Expr.Call expr) {
StringBuilder builder = new StringBuilder();
builder.append(expr.callee.toString()).append("(");
for (Expr arg : expr.arguments) {
builder.append(arg.accept(this)).append(", ");
}
builder.append(")");
return builder.toString();
}
@Override
public String visitVariableExpr(Expr.Variable expr) {
return expr.name.toString();

View File

@ -6,6 +6,7 @@ abstract class Expr {
interface Visitor<R> {
R visitAssignExpr(Assign expr);
R visitBinaryExpr(Binary expr);
R visitCallExpr(Call expr);
R visitGroupingExpr(Grouping expr);
R visitLiteralExpr(Literal expr);
R visitLogicalExpr(Logical expr);
@ -45,6 +46,23 @@ abstract class Expr {
final Expr right;
}
static class Call extends Expr {
Call(Expr callee, Token paren, List<Expr> arguments) {
this.callee = callee;
this.paren = paren;
this.arguments = arguments;
}
@Override
<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 {
Grouping(Expr expression) {
this.expression = expression;

View File

@ -1,9 +1,32 @@
package lox;
import java.util.ArrayList;
import java.util.List;
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>";
}
});
}
void interpret(List<Stmt> statements) {
try {
@ -57,6 +80,27 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
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)) {
throw new RuntimeError(expr.paren, "Can only call functions and classes");
}
LoxCallable function = (LoxCallable) callee;
if (arguments.size() != function.arity()) {
throw new RuntimeError(
expr.paren,
"Expected " + function.arity() + " arguments but called with " + arguments.size() + ".");
}
return function.call(this, arguments);
}
@Override
public Object visitBinaryExpr(Expr.Binary expr) {
Object left = evaluate(expr.left);
@ -119,6 +163,13 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
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))) {
@ -136,6 +187,15 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
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;

9
src/lox/LoxCallable.java Normal file
View File

@ -0,0 +1,9 @@
package lox;
import java.util.List;
interface LoxCallable {
int arity();
Object call(Interpreter interpreter, List<Object> arguments);
}

37
src/lox/LoxFunction.java Normal file
View File

@ -0,0 +1,37 @@
package lox;
import java.util.List;
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 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 int arity() {
return declaration.params.size();
}
@Override
public String toString() {
return "<fn " + declaration.name.lexeme + ">";
}
}

View File

@ -26,6 +26,9 @@ class Parser {
private Stmt declaration() {
try {
if (match(FUN)) {
return function("function");
}
if (match(VAR)) {
return varDeclaration();
}
@ -57,6 +60,9 @@ class Parser {
if (match(PRINT)) {
return printStatement();
}
if (match(RETURN)) {
return returnStatement();
}
if (match(WHILE)) {
return whileStatement();
}
@ -72,6 +78,16 @@ class Parser {
return new Stmt.Print(value);
}
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);
}
private Stmt whileStatement() {
consume(LEFT_PAREN, "Expect '(' after 'while");
Expr condition = expression();
@ -87,6 +103,24 @@ class Parser {
return new Stmt.Expression(value);
}
private Stmt 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 Stmt ifStatement() {
consume(LEFT_PAREN, "Expect '(' after 'if'.");
Expr condition = expression();
@ -245,7 +279,38 @@ class Parser {
Expr right = unary();
return new Expr.Unary(operator, right);
}
return primary();
return call();
}
private Expr call() {
Expr expr = primary();
while (true) {
if (match(LEFT_PAREN)) {
expr = finishCall(expr);
} else {
break;
}
}
return expr;
}
private Expr finishCall(Expr callee) {
List<Expr> arguments = new ArrayList<>();
if (!check(RIGHT_PAREN)) {
do {
arguments.add(expression());
} while (match(COMMA));
}
Token paren = consume(RIGHT_PAREN, "Expected ')' after argument list");
if (arguments.size() >= 255) {
error(peek(), "Can't have more than 255 arguments");
}
return new Expr.Call(callee, paren, arguments);
}
private Expr primary() {

10
src/lox/Return.java Normal file
View File

@ -0,0 +1,10 @@
package lox;
class Return extends RuntimeException{
final Object value;
Return(Object value) {
super(null, null, false, false);
this.value = value;
}
}

View File

@ -6,9 +6,11 @@ abstract class Stmt {
interface Visitor<R> {
R visitBlockStmt(Block stmt);
R visitExpressionStmt(Expression stmt);
R visitFunctionStmt(Function stmt);
R visitIfStmt(If stmt);
R visitVarStmt(Var stmt);
R visitPrintStmt(Print stmt);
R visitReturnStmt(Return stmt);
R visitWhileStmt(While stmt);
}
@ -38,6 +40,23 @@ abstract class Stmt {
final Expr expression;
}
static class Function extends Stmt {
Function(Token name, List<Token> params, List<Stmt> body) {
this.name = name;
this.params = params;
this.body = body;
}
@Override
<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 {
If(Expr condition, Stmt thenBranch, Stmt elseBranch) {
this.condition = condition;
@ -83,6 +102,21 @@ abstract class Stmt {
final Expr expression;
}
static class Return extends Stmt {
Return(Token keyword, Expr value) {
this.keyword = keyword;
this.value = value;
}
@Override
<R> R accept(Visitor<R> visitor) {
return visitor.visitReturnStmt(this);
}
final Token keyword;
final Expr value;
}
static class While extends Stmt {
While(Expr condition, Stmt body) {
this.condition = condition;

View File

@ -20,6 +20,7 @@ public class GenerateAST {
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",
@ -32,9 +33,11 @@ public class GenerateAST {
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",
"Var : Token name, Expr initializer",
"Print : Expr expression",
"Return : Token keyword, Expr value",
"While : Expr condition, Stmt body"));
}