[jlox] Add function calls

This commit is contained in:
ctsk
2022-09-10 19:42:05 +02:00
parent a516090b4f
commit 8a1f0f1217
5 changed files with 86 additions and 3 deletions

View File

@@ -1,5 +1,7 @@
package xyz.ctsk.lox; package xyz.ctsk.lox;
import java.util.stream.Collectors;
public class AstPrinter { public class AstPrinter {
public static String polish(Expr expr, boolean parentheses) { public static String polish(Expr expr, boolean parentheses) {
return expr.accept(new Polish(parentheses, false)); return expr.accept(new Polish(parentheses, false));
@@ -31,6 +33,16 @@ 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 visitCallExpr(Expr.Call expr) {
var fun = expr.callee().accept(this);
var args = expr.arguments().stream()
.map(c -> c.accept(this))
.collect(Collectors.joining(" "));
return reverse ? wrap(fun, args, "call") : wrap("call", fun, args);
}
@Override @Override
public String visitGroupingExpr(Expr.Grouping expr) { public String visitGroupingExpr(Expr.Grouping expr) {
var inner = expr.expression().accept(this); var inner = expr.expression().accept(this);
@@ -90,6 +102,15 @@ public class AstPrinter {
expr.right().accept(this)); expr.right().accept(this));
} }
@Override
public String visitCallExpr(Expr.Call expr) {
var fun = expr.callee().accept(this);
var args = expr.arguments().stream()
.map(c -> c.accept(this))
.collect(Collectors.joining(", "));
return fun + "(" + args + ")";
}
@Override @Override
public String visitGroupingExpr(Expr.Grouping expr) { public String visitGroupingExpr(Expr.Grouping expr) {
return '(' + expr.expression().accept(this) + ')'; return '(' + expr.expression().accept(this) + ')';

View File

@@ -129,6 +129,25 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
}; };
} }
@Override
public Object visitCallExpr(Expr.Call expr) {
var callee = evaluate(expr.callee());
var arguments = expr.arguments().stream()
.map(this::evaluate)
.toList();
if (callee instanceof LoxCallable function) {
if (arguments.size() != function.arity()) {
var msg = "Expected %d arguments but got %d.".formatted(function.arity(), arguments.size());
throw new RuntimeError(expr.paren(), msg);
}
} else {
throw new RuntimeError(expr.paren(), "Can only call functions and classes.");
}
return null;
}
@Override @Override
public Object visitGroupingExpr(Expr.Grouping expr) { public Object visitGroupingExpr(Expr.Grouping expr) {
return evaluate(expr.expression()); return evaluate(expr.expression());

View File

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

View File

@@ -39,7 +39,9 @@ import static xyz.ctsk.lox.TokenType.*;
* term → factor ( ( "-" | "+" ) factor )* ; * term → factor ( ( "-" | "+" ) factor )* ;
* factor → unary ( ( "/" | "*" ) unary )* ; * factor → unary ( ( "/" | "*" ) unary )* ;
* unary → ( "!" | "-" ) unary * unary → ( "!" | "-" ) unary
* | primary ; * | call ;
* call → primary ( "(" arguments? ")" )* ;
* arguments → expression ( "," expression )* ;
* primary → NUMBER | STRING | "true" | "false" | "nil" * primary → NUMBER | STRING | "true" | "false" | "nil"
* | "(" expression ")" * | "(" expression ")"
* | IDENTIFIER; * | IDENTIFIER;
@@ -276,7 +278,39 @@ public class Parser {
return new Expr.Unary(operator, right); return new Expr.Unary(operator, right);
} }
return primary(); return call();
}
private Expr call() {
var 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 {
if (arguments.size() >= 255) {
// DO NOT PANIC
//noinspection ThrowableNotThrown
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 primary() { private Expr primary() {

View File

@@ -3,6 +3,7 @@
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 = "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"}),