From 922a8f68637f24fc447a8d687b75a676d3b310bb Mon Sep 17 00:00:00 2001 From: Tom Willemse Date: Wed, 20 Jan 2021 22:52:47 -0800 Subject: Add function calling syntax --- src/com/craftinginterpreters/lox/AstPrinter.java | 48 ++++++++++++++-------- src/com/craftinginterpreters/lox/CMakeLists.txt | 1 + src/com/craftinginterpreters/lox/Interpreter.java | 22 ++++++++++ src/com/craftinginterpreters/lox/LoxCallable.java | 9 ++++ src/com/craftinginterpreters/lox/Parser.java | 40 +++++++++++++++--- src/com/craftinginterpreters/tool/GenerateAst.java | 6 +-- 6 files changed, 100 insertions(+), 26 deletions(-) create mode 100644 src/com/craftinginterpreters/lox/LoxCallable.java diff --git a/src/com/craftinginterpreters/lox/AstPrinter.java b/src/com/craftinginterpreters/lox/AstPrinter.java index 65f1916..4c120d3 100644 --- a/src/com/craftinginterpreters/lox/AstPrinter.java +++ b/src/com/craftinginterpreters/lox/AstPrinter.java @@ -10,14 +10,24 @@ class AstPrinter implements Expr.Visitor, Stmt.Visitor { 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 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 public String visitGroupingExpr(Expr.Grouping expr) { - return parenthesize("group", expr.expression); + return parenthesizeExpr("group", expr.expression); } @Override @@ -29,12 +39,12 @@ class AstPrinter implements Expr.Visitor, Stmt.Visitor { @Override 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 public String visitUnaryExpr(Expr.Unary expr) { - return parenthesize(expr.operator.lexeme, expr.right); + return parenthesizeExpr(expr.operator.lexeme, expr.right); } @Override @@ -44,12 +54,12 @@ class AstPrinter implements Expr.Visitor, Stmt.Visitor { @Override public String visitAssignExpr(Expr.Assign expression) { - return parenthesize(expression.name.lexeme + " = ", expression.value); + return parenthesizeExpr(expression.name.lexeme + " = ", expression.value); } @Override public String visitVarStmt(Stmt.Var statement) { - return parenthesize("define " + statement.name.lexeme + " = ", statement.initializer); + return parenthesizeExpr("define " + statement.name.lexeme + " = ", statement.initializer); } @Override @@ -57,8 +67,8 @@ class AstPrinter implements Expr.Visitor, Stmt.Visitor { StringBuilder builder = new StringBuilder(); builder.append('('); - builder.append(parenthesize("while", statement.condition)); - builder.append(parenthesize("do", statement.body)); + builder.append(parenthesizeExpr("while", statement.condition)); + builder.append(parenthesizeStmt("do", statement.body)); builder.append(')'); return builder.toString(); @@ -66,7 +76,7 @@ class AstPrinter implements Expr.Visitor, Stmt.Visitor { @Override public String visitPrintStmt(Stmt.Print statement) { - return parenthesize("print", statement.expression); + return parenthesizeExpr("print", statement.expression); } @Override @@ -79,21 +89,25 @@ class AstPrinter implements Expr.Visitor, Stmt.Visitor { StringBuilder builder = new StringBuilder(); builder.append("("); - builder.append(parenthesize("if", stmt.condition)); - builder.append(parenthesize("then", stmt.thenBranch)); + builder.append(parenthesizeExpr("if", stmt.condition)); + builder.append(parenthesizeStmt("then", stmt.thenBranch)); if (stmt.elseBranch != null) { - builder.append(parenthesize("else", stmt.elseBranch)); + builder.append(parenthesizeStmt("else", stmt.elseBranch)); } return builder.toString(); } @Override public String visitBlockStmt(Stmt.Block block) { - return parenthesize("", block.statements); + return parenthesizeStmt("", block.statements); + } + + private String parenthesizeExpr(String name, Expr... exprs) { + return parenthesizeExpr(name, exprs); } - private String parenthesize(String name, Expr... exprs) { + private String parenthesizeExpr(String name, List exprs) { StringBuilder builder = new StringBuilder(); builder.append("(").append(name); @@ -106,7 +120,7 @@ class AstPrinter implements Expr.Visitor, Stmt.Visitor { return builder.toString(); } - private String parenthesize(String name, List statements) { + private String parenthesizeStmt(String name, List statements) { StringBuilder builder = new StringBuilder(); builder.append("(").append(name); @@ -119,8 +133,8 @@ class AstPrinter implements Expr.Visitor, Stmt.Visitor { return builder.toString(); } - private String parenthesize(String name, Stmt... statements) { - return parenthesize(name, statements); + private String parenthesizeStmt(String name, Stmt... statements) { + return parenthesizeStmt(name, statements); } public static void main(String[] args) { diff --git a/src/com/craftinginterpreters/lox/CMakeLists.txt b/src/com/craftinginterpreters/lox/CMakeLists.txt index 447b2cf..b437b7a 100644 --- a/src/com/craftinginterpreters/lox/CMakeLists.txt +++ b/src/com/craftinginterpreters/lox/CMakeLists.txt @@ -20,4 +20,5 @@ add_jar(Lox Interpreter.java RuntimeError.java Environment.java + LoxCallable.java ENTRY_POINT com/craftinginterpreters/lox/Lox) diff --git a/src/com/craftinginterpreters/lox/Interpreter.java b/src/com/craftinginterpreters/lox/Interpreter.java index 9d0cfb3..f752e77 100644 --- a/src/com/craftinginterpreters/lox/Interpreter.java +++ b/src/com/craftinginterpreters/lox/Interpreter.java @@ -1,5 +1,6 @@ package com.craftinginterpreters.lox; +import java.util.ArrayList; import java.util.List; class Interpreter implements Expr.Visitor, Stmt.Visitor { @@ -222,6 +223,27 @@ class Interpreter implements Expr.Visitor, Stmt.Visitor { return null; } + @Override + public Object visitCallExpr(Expr.Call expr) { + Object callee = evaluate(expr.callee); + + List 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 statements) { try { for (Stmt statement : statements) { diff --git a/src/com/craftinginterpreters/lox/LoxCallable.java b/src/com/craftinginterpreters/lox/LoxCallable.java new file mode 100644 index 0000000..4204c20 --- /dev/null +++ b/src/com/craftinginterpreters/lox/LoxCallable.java @@ -0,0 +1,9 @@ +package com.craftinginterpreters.lox; + +import java.util.List; + +interface LoxCallable { + int arity(); + + Object call(Interpreter interpreter, List arguments); +} diff --git a/src/com/craftinginterpreters/lox/Parser.java b/src/com/craftinginterpreters/lox/Parser.java index 7d7b27a..df3ce3f 100644 --- a/src/com/craftinginterpreters/lox/Parser.java +++ b/src/com/craftinginterpreters/lox/Parser.java @@ -84,13 +84,11 @@ class Parser { Stmt body = statement(); if (increment != null) { - body = new Stmt.Block( - Arrays.asList( - body, - new Stmt.Expression(increment))); + body = new Stmt.Block(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); if (initializer != null) { @@ -255,7 +253,37 @@ class Parser { return new Expr.Unary(operator, right); } - return primary(); + return call(); + } + + private Expr finishCall(Expr callee) { + List 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() { diff --git a/src/com/craftinginterpreters/tool/GenerateAst.java b/src/com/craftinginterpreters/tool/GenerateAst.java index 8845c6e..8c063bf 100644 --- a/src/com/craftinginterpreters/tool/GenerateAst.java +++ b/src/com/craftinginterpreters/tool/GenerateAst.java @@ -16,9 +16,9 @@ public class GenerateAst { defineAst(outputDir, "Expr", Arrays.asList("Assign : Token name, Expr value", "Binary : Expr left, Token operator, Expr right", - "Grouping : Expr expression", "Literal : Object value", - "Logical : Expr left, Token operator, Expr right", "Unary : Token operator, Expr right", - "Variable : Token name")); + "Call : Expr callee, Token paren, List arguments", "Grouping : Expr expression", + "Literal : Object value", "Logical : Expr left, Token operator, Expr right", + "Unary : Token operator, Expr right", "Variable : Token name")); defineAst(outputDir, "Stmt", Arrays.asList("Block : List statements", "Expression : Expr expression", "If : Expr condition, Stmt thenBranch, Stmt elseBranch", "Print : Expr expression", -- cgit v1.2.3-54-g00ecf