[jlox] Add function calls
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package xyz.ctsk.lox;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AstPrinter {
|
||||
public static String polish(Expr expr, boolean parentheses) {
|
||||
return expr.accept(new Polish(parentheses, false));
|
||||
@@ -31,6 +33,16 @@ public class AstPrinter {
|
||||
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
|
||||
public String visitGroupingExpr(Expr.Grouping expr) {
|
||||
var inner = expr.expression().accept(this);
|
||||
@@ -90,6 +102,15 @@ public class AstPrinter {
|
||||
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
|
||||
public String visitGroupingExpr(Expr.Grouping expr) {
|
||||
return '(' + expr.expression().accept(this) + ')';
|
||||
|
||||
@@ -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
|
||||
public Object visitGroupingExpr(Expr.Grouping expr) {
|
||||
return evaluate(expr.expression());
|
||||
|
||||
8
jlox/lox/src/main/java/xyz/ctsk/lox/LoxCallable.java
Normal file
8
jlox/lox/src/main/java/xyz/ctsk/lox/LoxCallable.java
Normal 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);
|
||||
}
|
||||
@@ -39,7 +39,9 @@ import static xyz.ctsk.lox.TokenType.*;
|
||||
* term → factor ( ( "-" | "+" ) factor )* ;
|
||||
* factor → unary ( ( "/" | "*" ) unary )* ;
|
||||
* unary → ( "!" | "-" ) unary
|
||||
* | primary ;
|
||||
* | call ;
|
||||
* call → primary ( "(" arguments? ")" )* ;
|
||||
* arguments → expression ( "," expression )* ;
|
||||
* primary → NUMBER | STRING | "true" | "false" | "nil"
|
||||
* | "(" expression ")"
|
||||
* | IDENTIFIER;
|
||||
@@ -126,7 +128,7 @@ public class Parser {
|
||||
if (initializer != null) {
|
||||
body = new Stmt.Block(List.of(initializer, body));
|
||||
}
|
||||
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
@@ -276,7 +278,39 @@ public class Parser {
|
||||
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() {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
rules = {
|
||||
@Rule(head = "Assign", body = {"Token name", "Expr value"}),
|
||||
@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 = "Literal", body = {"Object value"}),
|
||||
@Rule(head = "Logical", body = {"Expr left", "Token operator", "Expr right"}),
|
||||
|
||||
Reference in New Issue
Block a user