[jlox] Add function calls
This commit is contained in:
@@ -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) + ')';
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
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 )* ;
|
* 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() {
|
||||||
|
|||||||
@@ -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"}),
|
||||||
|
|||||||
Reference in New Issue
Block a user