Add function calling syntax

This commit is contained in:
Tom Willemse 2021-01-20 22:52:47 -08:00
parent 60b940f1f5
commit 922a8f6863
6 changed files with 100 additions and 26 deletions

View file

@ -10,14 +10,24 @@ class AstPrinter implements Expr.Visitor<String>, Stmt.Visitor<String> {
return expr.accept(this); return expr.accept(this);
} }
@Override
public String visitCallExpr(Expr.Call expr) {
StringBuilder builder = new StringBuilder();
builder.append(print(expr.callee));
builder.append(parenthesizeExpr("", expr.arguments));
return builder.toString();
}
@Override @Override
public String visitBinaryExpr(Expr.Binary expr) { public String visitBinaryExpr(Expr.Binary expr) {
return parenthesize(expr.operator.lexeme, expr.left, expr.right); return parenthesizeExpr(expr.operator.lexeme, expr.left, expr.right);
} }
@Override @Override
public String visitGroupingExpr(Expr.Grouping expr) { public String visitGroupingExpr(Expr.Grouping expr) {
return parenthesize("group", expr.expression); return parenthesizeExpr("group", expr.expression);
} }
@Override @Override
@ -29,12 +39,12 @@ class AstPrinter implements Expr.Visitor<String>, Stmt.Visitor<String> {
@Override @Override
public String visitLogicalExpr(Expr.Logical expr) { public String visitLogicalExpr(Expr.Logical expr) {
return parenthesize(expr.operator.type.toString(), expr.left, expr.right); return parenthesizeExpr(expr.operator.type.toString(), expr.left, expr.right);
} }
@Override @Override
public String visitUnaryExpr(Expr.Unary expr) { public String visitUnaryExpr(Expr.Unary expr) {
return parenthesize(expr.operator.lexeme, expr.right); return parenthesizeExpr(expr.operator.lexeme, expr.right);
} }
@Override @Override
@ -44,12 +54,12 @@ class AstPrinter implements Expr.Visitor<String>, Stmt.Visitor<String> {
@Override @Override
public String visitAssignExpr(Expr.Assign expression) { public String visitAssignExpr(Expr.Assign expression) {
return parenthesize(expression.name.lexeme + " = ", expression.value); return parenthesizeExpr(expression.name.lexeme + " = ", expression.value);
} }
@Override @Override
public String visitVarStmt(Stmt.Var statement) { public String visitVarStmt(Stmt.Var statement) {
return parenthesize("define " + statement.name.lexeme + " = ", statement.initializer); return parenthesizeExpr("define " + statement.name.lexeme + " = ", statement.initializer);
} }
@Override @Override
@ -57,8 +67,8 @@ class AstPrinter implements Expr.Visitor<String>, Stmt.Visitor<String> {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append('('); builder.append('(');
builder.append(parenthesize("while", statement.condition)); builder.append(parenthesizeExpr("while", statement.condition));
builder.append(parenthesize("do", statement.body)); builder.append(parenthesizeStmt("do", statement.body));
builder.append(')'); builder.append(')');
return builder.toString(); return builder.toString();
@ -66,7 +76,7 @@ class AstPrinter implements Expr.Visitor<String>, Stmt.Visitor<String> {
@Override @Override
public String visitPrintStmt(Stmt.Print statement) { public String visitPrintStmt(Stmt.Print statement) {
return parenthesize("print", statement.expression); return parenthesizeExpr("print", statement.expression);
} }
@Override @Override
@ -79,21 +89,25 @@ class AstPrinter implements Expr.Visitor<String>, Stmt.Visitor<String> {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("("); builder.append("(");
builder.append(parenthesize("if", stmt.condition)); builder.append(parenthesizeExpr("if", stmt.condition));
builder.append(parenthesize("then", stmt.thenBranch)); builder.append(parenthesizeStmt("then", stmt.thenBranch));
if (stmt.elseBranch != null) { if (stmt.elseBranch != null) {
builder.append(parenthesize("else", stmt.elseBranch)); builder.append(parenthesizeStmt("else", stmt.elseBranch));
} }
return builder.toString(); return builder.toString();
} }
@Override @Override
public String visitBlockStmt(Stmt.Block block) { public String visitBlockStmt(Stmt.Block block) {
return parenthesize("", block.statements); return parenthesizeStmt("", block.statements);
} }
private String parenthesize(String name, Expr... exprs) { private String parenthesizeExpr(String name, Expr... exprs) {
return parenthesizeExpr(name, exprs);
}
private String parenthesizeExpr(String name, List<Expr> exprs) {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("(").append(name); builder.append("(").append(name);
@ -106,7 +120,7 @@ class AstPrinter implements Expr.Visitor<String>, Stmt.Visitor<String> {
return builder.toString(); return builder.toString();
} }
private String parenthesize(String name, List<Stmt> statements) { private String parenthesizeStmt(String name, List<Stmt> statements) {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("(").append(name); builder.append("(").append(name);
@ -119,8 +133,8 @@ class AstPrinter implements Expr.Visitor<String>, Stmt.Visitor<String> {
return builder.toString(); return builder.toString();
} }
private String parenthesize(String name, Stmt... statements) { private String parenthesizeStmt(String name, Stmt... statements) {
return parenthesize(name, statements); return parenthesizeStmt(name, statements);
} }
public static void main(String[] args) { public static void main(String[] args) {

View file

@ -20,4 +20,5 @@ add_jar(Lox
Interpreter.java Interpreter.java
RuntimeError.java RuntimeError.java
Environment.java Environment.java
LoxCallable.java
ENTRY_POINT com/craftinginterpreters/lox/Lox) ENTRY_POINT com/craftinginterpreters/lox/Lox)

View file

@ -1,5 +1,6 @@
package com.craftinginterpreters.lox; package com.craftinginterpreters.lox;
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> {
@ -222,6 +223,27 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
return null; 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 got " + arguments.size() + ".");
}
return function.call(this, arguments);
}
public void interpret(List<Stmt> statements) { public void interpret(List<Stmt> statements) {
try { try {
for (Stmt statement : statements) { for (Stmt statement : statements) {

View file

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

View file

@ -84,13 +84,11 @@ class Parser {
Stmt body = statement(); Stmt body = statement();
if (increment != null) { if (increment != null) {
body = new Stmt.Block( body = new Stmt.Block(Arrays.asList(body, new Stmt.Expression(increment)));
Arrays.asList(
body,
new Stmt.Expression(increment)));
} }
if (condition == null) condition = new Expr.Literal(true); if (condition == null)
condition = new Expr.Literal(true);
body = new Stmt.While(condition, body); body = new Stmt.While(condition, body);
if (initializer != null) { if (initializer != null) {
@ -255,7 +253,37 @@ class Parser {
return new Expr.Unary(operator, right); return new Expr.Unary(operator, right);
} }
return primary(); return call();
}
private Expr finishCall(Expr callee) {
List<Expr> arguments = new ArrayList<>();
if (!check(RIGHT_PAREN)) {
do {
if (arguments.size() >= 255) {
error(peek(), "Can't have more than 255 arguments.");
}
arguments.add(expression());
} while (match(COMMA));
}
Token paren = consume(RIGHT_PAREN, "Expect ')' after arguments.");
return new Expr.Call(callee, paren, arguments);
}
private Expr call() {
Expr expr = primary();
while (true) {
if (match(LEFT_PAREN)) {
expr = finishCall(expr);
} else {
break;
}
}
return expr;
} }
private Expr primary() { private Expr primary() {

View file

@ -16,9 +16,9 @@ public class GenerateAst {
defineAst(outputDir, "Expr", defineAst(outputDir, "Expr",
Arrays.asList("Assign : Token name, Expr value", "Binary : Expr left, Token operator, Expr right", Arrays.asList("Assign : Token name, Expr value", "Binary : Expr left, Token operator, Expr right",
"Grouping : Expr expression", "Literal : Object value", "Call : Expr callee, Token paren, List<Expr> arguments", "Grouping : Expr expression",
"Logical : Expr left, Token operator, Expr right", "Unary : Token operator, Expr right", "Literal : Object value", "Logical : Expr left, 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",
"If : Expr condition, Stmt thenBranch, Stmt elseBranch", "Print : Expr expression", "If : Expr condition, Stmt thenBranch, Stmt elseBranch", "Print : Expr expression",