From 899ecea236288b5ebc7795c204fdad59b3276002 Mon Sep 17 00:00:00 2001 From: Tom Willemse Date: Wed, 18 Nov 2020 23:30:46 -0800 Subject: [PATCH] Chapter 8: Add variable declarations --- .../craftinginterpreters/lox/AstPrinter.java | 22 +++++++++++++- .../craftinginterpreters/lox/CMakeLists.txt | 3 +- .../craftinginterpreters/lox/Environment.java | 20 +++++++++++++ .../craftinginterpreters/lox/Interpreter.java | 18 +++++++++++ src/com/craftinginterpreters/lox/Parser.java | 30 ++++++++++++++++++- .../tool/GenerateAst.java | 8 +++-- 6 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 src/com/craftinginterpreters/lox/Environment.java diff --git a/src/com/craftinginterpreters/lox/AstPrinter.java b/src/com/craftinginterpreters/lox/AstPrinter.java index cb494f0..82294c6 100644 --- a/src/com/craftinginterpreters/lox/AstPrinter.java +++ b/src/com/craftinginterpreters/lox/AstPrinter.java @@ -3,7 +3,7 @@ package com.craftinginterpreters.lox; /** * AstPrinter */ -class AstPrinter implements Expr.Visitor { +class AstPrinter implements Expr.Visitor, Stmt.Visitor { String print(Expr expr) { return expr.accept(this); } @@ -30,6 +30,26 @@ class AstPrinter implements Expr.Visitor { return parenthesize(expr.operator.lexeme, expr.right); } + @Override + public String visitVariableExpr(Expr.Variable expr) { + return expr.name.lexeme; + } + + @Override + public String visitVarStmt(Stmt.Var statement) { + return parenthesize("define " + statement.name.lexeme + " = ", statement.initializer); + } + + @Override + public String visitPrintStmt(Stmt.Print statement) { + return parenthesize("print", statement.expression); + } + + @Override + public String visitExpressionStmt(Stmt.Expression expression) { + return expression.expression.accept(this); + } + private String parenthesize(String name, Expr... exprs) { StringBuilder builder = new StringBuilder(); diff --git a/src/com/craftinginterpreters/lox/CMakeLists.txt b/src/com/craftinginterpreters/lox/CMakeLists.txt index e635245..447b2cf 100644 --- a/src/com/craftinginterpreters/lox/CMakeLists.txt +++ b/src/com/craftinginterpreters/lox/CMakeLists.txt @@ -7,7 +7,7 @@ set(GENERATED_JAVA_FILENAMES add_custom_command(OUTPUT ${GENERATED_JAVA_FILENAMES} COMMAND java -jar ${GENERATE_AST_JAR} ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS GenerateAst) + DEPENDS GenerateAst ${GENERATE_AST_JAR}) add_jar(Lox Lox.java @@ -19,4 +19,5 @@ add_jar(Lox Parser.java Interpreter.java RuntimeError.java + Environment.java ENTRY_POINT com/craftinginterpreters/lox/Lox) diff --git a/src/com/craftinginterpreters/lox/Environment.java b/src/com/craftinginterpreters/lox/Environment.java new file mode 100644 index 0000000..634659b --- /dev/null +++ b/src/com/craftinginterpreters/lox/Environment.java @@ -0,0 +1,20 @@ +package com.craftinginterpreters.lox; + +import java.util.HashMap; +import java.util.Map; + +class Environment { + private final Map values = new HashMap<>(); + + Object get(Token name) { + if (values.containsKey(name.lexeme)) { + return values.get(name.lexeme); + } + + throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'."); + } + + void define(String name, Object value) { + values.put(name, value); + } +} diff --git a/src/com/craftinginterpreters/lox/Interpreter.java b/src/com/craftinginterpreters/lox/Interpreter.java index b8ba090..f4e7f99 100644 --- a/src/com/craftinginterpreters/lox/Interpreter.java +++ b/src/com/craftinginterpreters/lox/Interpreter.java @@ -3,6 +3,8 @@ package com.craftinginterpreters.lox; import java.util.List; class Interpreter implements Expr.Visitor, Stmt.Visitor { + private Environment environment = new Environment(); + @Override public Object visitLiteralExpr(Expr.Literal expr) { return expr.value; @@ -24,6 +26,11 @@ class Interpreter implements Expr.Visitor, Stmt.Visitor { return null; } + @Override + public Object visitVariableExpr(Expr.Variable expr) { + return environment.get(expr.name); + } + private void checkNumberOperand(Token operator, Object operand) { if (operand instanceof Double) return; @@ -95,6 +102,17 @@ class Interpreter implements Expr.Visitor, Stmt.Visitor { return null; } + @Override + public Void visitVarStmt(Stmt.Var stmt) { + Object value = null; + if (stmt.initializer != null) { + value = evaluate(stmt.initializer); + } + + environment.define(stmt.name.lexeme, value); + return null; + } + @Override public Object visitBinaryExpr(Expr.Binary expr) { Object left = evaluate(expr.left); diff --git a/src/com/craftinginterpreters/lox/Parser.java b/src/com/craftinginterpreters/lox/Parser.java index c68316f..9a9c0e6 100644 --- a/src/com/craftinginterpreters/lox/Parser.java +++ b/src/com/craftinginterpreters/lox/Parser.java @@ -20,7 +20,7 @@ class Parser { List statements = new ArrayList<>(); while (!isAtEnd()) { - statements.add(statement()); + statements.add(declaration()); } return statements; @@ -30,6 +30,18 @@ class Parser { return equality(); } + private Stmt declaration() { + try { + if (match(VAR)) + return varDeclaration(); + + return statement(); + } catch (ParseError error) { + synchronize(); + return null; + } + } + private Stmt statement() { if (match(PRINT)) return printStatement(); @@ -43,6 +55,18 @@ class Parser { return new Stmt.Print(value); } + private Stmt varDeclaration() { + Token name = consume(IDENTIFIER, "Expect variable name."); + + Expr initializer = null; + if (match(EQUAL)) { + initializer = expression(); + } + + consume(SEMICOLON, "Expect ';' after variable declaration."); + return new Stmt.Var(name, initializer); + } + private Stmt expressionStatement() { Expr expr = expression(); consume(SEMICOLON, "Expect ';' after expression."); @@ -119,6 +143,10 @@ class Parser { return new Expr.Literal(previous().literal); } + if (match(IDENTIFIER)) { + return new Expr.Variable(previous()); + } + if (match(LEFT_PAREN)) { Expr expr = expression(); consume(RIGHT_PAREN, "Expect ')' after expression."); diff --git a/src/com/craftinginterpreters/tool/GenerateAst.java b/src/com/craftinginterpreters/tool/GenerateAst.java index 86d6942..d2cb5cf 100644 --- a/src/com/craftinginterpreters/tool/GenerateAst.java +++ b/src/com/craftinginterpreters/tool/GenerateAst.java @@ -14,9 +14,11 @@ public class GenerateAst { String outputDir = args[0]; - defineAst(outputDir, "Expr", Arrays.asList("Binary : Expr left, Token operator, Expr right", - "Grouping : Expr expression", "Literal : Object value", "Unary : Token operator, Expr right")); - defineAst(outputDir, "Stmt", Arrays.asList("Expression : Expr expression", "Print : Expr expression")); + defineAst(outputDir, "Expr", + Arrays.asList("Binary : Expr left, Token operator, Expr right", "Grouping : Expr expression", + "Literal : Object value", "Unary : Token operator, Expr right", "Variable : Token name")); + defineAst(outputDir, "Stmt", Arrays.asList("Expression : Expr expression", "Print : Expr expression", + "Var : Token name, Expr initializer")); } private static void defineAst(String outputDir, String baseName, List types) throws IOException {