From 8a1f0f12177dbe63443df950ad610d36aa7cc3ff Mon Sep 17 00:00:00 2001 From: ctsk <9384305+ctsk@users.noreply.github.com> Date: Sat, 10 Sep 2022 19:42:05 +0200 Subject: [PATCH] [jlox] Add function calls --- .../main/java/xyz/ctsk/lox/AstPrinter.java | 21 ++++++++++ .../main/java/xyz/ctsk/lox/Interpreter.java | 19 +++++++++ .../main/java/xyz/ctsk/lox/LoxCallable.java | 8 ++++ .../src/main/java/xyz/ctsk/lox/Parser.java | 40 +++++++++++++++++-- .../main/java/xyz/ctsk/lox/package-info.java | 1 + 5 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 jlox/lox/src/main/java/xyz/ctsk/lox/LoxCallable.java diff --git a/jlox/lox/src/main/java/xyz/ctsk/lox/AstPrinter.java b/jlox/lox/src/main/java/xyz/ctsk/lox/AstPrinter.java index 8bade14..69a8e32 100644 --- a/jlox/lox/src/main/java/xyz/ctsk/lox/AstPrinter.java +++ b/jlox/lox/src/main/java/xyz/ctsk/lox/AstPrinter.java @@ -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) + ')'; diff --git a/jlox/lox/src/main/java/xyz/ctsk/lox/Interpreter.java b/jlox/lox/src/main/java/xyz/ctsk/lox/Interpreter.java index 177acd8..97ded50 100644 --- a/jlox/lox/src/main/java/xyz/ctsk/lox/Interpreter.java +++ b/jlox/lox/src/main/java/xyz/ctsk/lox/Interpreter.java @@ -129,6 +129,25 @@ public class Interpreter implements Expr.Visitor, Stmt.Visitor { }; } + @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()); diff --git a/jlox/lox/src/main/java/xyz/ctsk/lox/LoxCallable.java b/jlox/lox/src/main/java/xyz/ctsk/lox/LoxCallable.java new file mode 100644 index 0000000..a2afa5f --- /dev/null +++ b/jlox/lox/src/main/java/xyz/ctsk/lox/LoxCallable.java @@ -0,0 +1,8 @@ +package xyz.ctsk.lox; + +import java.util.List; + +public interface LoxCallable { + int arity(); + Object call(Interpreter interpreter, List arguments); +} diff --git a/jlox/lox/src/main/java/xyz/ctsk/lox/Parser.java b/jlox/lox/src/main/java/xyz/ctsk/lox/Parser.java index 3e12ecc..ee85476 100644 --- a/jlox/lox/src/main/java/xyz/ctsk/lox/Parser.java +++ b/jlox/lox/src/main/java/xyz/ctsk/lox/Parser.java @@ -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 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() { diff --git a/jlox/lox/src/main/java/xyz/ctsk/lox/package-info.java b/jlox/lox/src/main/java/xyz/ctsk/lox/package-info.java index 7f75faf..1c6bc8c 100644 --- a/jlox/lox/src/main/java/xyz/ctsk/lox/package-info.java +++ b/jlox/lox/src/main/java/xyz/ctsk/lox/package-info.java @@ -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 arguments"}), @Rule(head = "Grouping", body = {"Expr expression"}), @Rule(head = "Literal", body = {"Object value"}), @Rule(head = "Logical", body = {"Expr left", "Token operator", "Expr right"}),