From ca9fd3ae3a1ef5ca1fbe33a83abb12e656a2556f Mon Sep 17 00:00:00 2001 From: Tom Willemse Date: Wed, 25 Nov 2020 23:10:58 -0800 Subject: Chapter 8: Add blocks and scopes --- src/com/craftinginterpreters/lox/AstPrinter.java | 20 ++++++++++++++++++++ src/com/craftinginterpreters/lox/Environment.java | 17 +++++++++++++++++ src/com/craftinginterpreters/lox/Interpreter.java | 20 ++++++++++++++++++++ src/com/craftinginterpreters/lox/Parser.java | 13 +++++++++++++ src/com/craftinginterpreters/tool/GenerateAst.java | 9 +++++---- 5 files changed, 75 insertions(+), 4 deletions(-) (limited to 'src/com/craftinginterpreters') diff --git a/src/com/craftinginterpreters/lox/AstPrinter.java b/src/com/craftinginterpreters/lox/AstPrinter.java index 58376cb..d5163df 100644 --- a/src/com/craftinginterpreters/lox/AstPrinter.java +++ b/src/com/craftinginterpreters/lox/AstPrinter.java @@ -1,5 +1,7 @@ package com.craftinginterpreters.lox; +import java.util.List; + /** * AstPrinter */ @@ -55,6 +57,11 @@ class AstPrinter implements Expr.Visitor, Stmt.Visitor { return expression.expression.accept(this); } + @Override + public String visitBlockStmt(Stmt.Block block) { + return parenthesize("", block.statements); + } + private String parenthesize(String name, Expr... exprs) { StringBuilder builder = new StringBuilder(); @@ -68,6 +75,19 @@ class AstPrinter implements Expr.Visitor, Stmt.Visitor { return builder.toString(); } + private String parenthesize(String name, List statements) { + StringBuilder builder = new StringBuilder(); + + builder.append("(").append(name); + for (Stmt statement : statements) { + builder.append(" "); + builder.append(statement.accept(this)); + } + builder.append(")"); + + return builder.toString(); + } + public static void main(String[] args) { Expr expression = new Expr.Binary( new Expr.Unary(new Token(TokenType.MINUS, "-", null, 1), new Expr.Literal(123)), diff --git a/src/com/craftinginterpreters/lox/Environment.java b/src/com/craftinginterpreters/lox/Environment.java index 8d8a374..0a95282 100644 --- a/src/com/craftinginterpreters/lox/Environment.java +++ b/src/com/craftinginterpreters/lox/Environment.java @@ -4,13 +4,25 @@ import java.util.HashMap; import java.util.Map; class Environment { + private final Environment enclosing; private final Map values = new HashMap<>(); + Environment() { + enclosing = null; + } + + Environment(Environment enclosing) { + this.enclosing = enclosing; + } + Object get(Token name) { if (values.containsKey(name.lexeme)) { return values.get(name.lexeme); } + if (enclosing != null) + return enclosing.get(name); + throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'."); } @@ -20,6 +32,11 @@ class Environment { return; } + if (enclosing != null) { + enclosing.assign(name, value); + return; + } + throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'."); } diff --git a/src/com/craftinginterpreters/lox/Interpreter.java b/src/com/craftinginterpreters/lox/Interpreter.java index 7378164..e03ca37 100644 --- a/src/com/craftinginterpreters/lox/Interpreter.java +++ b/src/com/craftinginterpreters/lox/Interpreter.java @@ -89,6 +89,26 @@ class Interpreter implements Expr.Visitor, Stmt.Visitor { stmt.accept(this); } + private void executeBlock(List statements, Environment environment) { + Environment previous = this.environment; + + try { + this.environment = environment; + + for (Stmt statement : statements) { + execute(statement); + } + } finally { + this.environment = previous; + } + } + + @Override + public Void visitBlockStmt(Stmt.Block stmt) { + executeBlock(stmt.statements, new Environment(environment)); + return null; + } + @Override public Void visitExpressionStmt(Stmt.Expression stmt) { evaluate(stmt.expression); diff --git a/src/com/craftinginterpreters/lox/Parser.java b/src/com/craftinginterpreters/lox/Parser.java index 9e76a54..f044feb 100644 --- a/src/com/craftinginterpreters/lox/Parser.java +++ b/src/com/craftinginterpreters/lox/Parser.java @@ -45,6 +45,8 @@ class Parser { private Stmt statement() { if (match(PRINT)) return printStatement(); + if (match(LEFT_BRACE)) + return new Stmt.Block(block()); return expressionStatement(); } @@ -73,6 +75,17 @@ class Parser { return new Stmt.Expression(expr); } + private List block() { + List statements = new ArrayList<>(); + + while (!check(RIGHT_BRACE) && !isAtEnd()) { + statements.add(declaration()); + } + + consume(RIGHT_BRACE, "Expect '}' after block."); + return statements; + } + private Expr assignment() { Expr expr = equality(); diff --git a/src/com/craftinginterpreters/tool/GenerateAst.java b/src/com/craftinginterpreters/tool/GenerateAst.java index f96e315..c9f1398 100644 --- a/src/com/craftinginterpreters/tool/GenerateAst.java +++ b/src/com/craftinginterpreters/tool/GenerateAst.java @@ -15,10 +15,11 @@ public class GenerateAst { String outputDir = args[0]; defineAst(outputDir, "Expr", - Arrays.asList("Assign : Token name, Expr value", "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")); + Arrays.asList("Assign : Token name, Expr value", "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("Block : List statements", "Expression : Expr expression", + "Print : Expr expression", "Var : Token name, Expr initializer")); } private static void defineAst(String outputDir, String baseName, List types) throws IOException { -- cgit v1.2.3-54-g00ecf