Jelajahi Sumber

Parsing variables -- Variable Scope

runningwater 2 tahun lalu
induk
melakukan
edb5e7a9b7

+ 19 - 13
README.md

@@ -68,21 +68,24 @@
                            | "(" expression ")" | IDENTIFIER ;
            ```
 
-        - Syntax tree - Statement
+          - Syntax tree - Statement
 
-            ```
-            program        → statement* EOF ;
+              ```
+              program        → statement* EOF ;
       
-            statement      → exprStmt
-                           | printStmt ;
+              statement      → exprStmt
+                             | printStmt 
+                             | block;
+            
+              block         → "{" declaration* "}"
       
-            exprStmt       → expression ";" ;
-            printStmt      → "print" expression ";" ;
-            ```
+              exprStmt       → expression ";" ;
+              printStmt      → "print" expression ";" ;
+              ```
 
-          A program is a list of statements followed by the special "end of file" token.
-          The mandatory end token ensures the parse consumes the entire input and don't
-          silently ignore erroneous unconsumed tokens at the end of a script.
+            A program is a list of statements followed by the special "end of file" token.
+            The mandatory end token ensures the parse consumes the entire input and don't
+            silently ignore erroneous unconsumed tokens at the end of a script.
         - Syntax tree - add Variable syntax
           ```
             program   → declaration* EOF ;
@@ -90,8 +93,11 @@
             declaration  → varDecl
                          | statement ;
           
-            statement  → exprStmt
-                       | printStmt ;
+             statement      → exprStmt
+                             | printStmt 
+                             | block;
+            
+             block         → "{" declaration* "}"
             
             varDecl  → "var" IDENTIFIER ( "=" expression )? ";" ;
           ```

+ 11 - 0
src/main/java/com/craftinginterpreters/lox/AstPrinter.java

@@ -21,6 +21,17 @@ public class AstPrinter implements Expr.Visitor<String>, Stmt.Visitor<String> {
   }
 
   @Override
+  public String visitBlockStmt(Stmt.Block stmt) {
+    StringBuilder builder = new StringBuilder();
+    builder.append("    {").append("\n");
+    for (Stmt statement : stmt.statements) {
+      builder.append("      ").append(statement.accept(this)).append("\n");
+    }
+    builder.append("    }").append("\n");
+    return builder.toString();
+  }
+
+  @Override
   public String visitExpressionStmt(Stmt.Expression stmt) {
     return stmt.expression.accept(this);
   }

+ 16 - 0
src/main/java/com/craftinginterpreters/lox/Environment.java

@@ -12,6 +12,15 @@ import java.util.Map;
  */
 public class Environment {
   private final Map<String, Object> values = new HashMap<>(16);
+  final Environment enclosing;
+
+  public Environment() {
+    enclosing = null;
+  }
+
+  public Environment(Environment enclosing) {
+    this.enclosing = enclosing;
+  }
 
   void define(String name, Object value) {
     values.put(name, value);
@@ -22,6 +31,8 @@ public class Environment {
       return values.get(name.lexeme);
     }
 
+    if (enclosing != null) return enclosing.get(name);
+
     throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
   }
 
@@ -31,6 +42,11 @@ public class Environment {
       return;
     }
 
+    if (enclosing != null) {
+      enclosing.assign(name, value);
+      return;
+    }
+
     throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
   }
 }

+ 24 - 5
src/main/java/com/craftinginterpreters/lox/Interpreter.java

@@ -10,7 +10,7 @@ import java.util.List;
  * @desc
  */
 public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
-  private final Environment environment = new Environment();
+  private Environment environment = new Environment();
   /**
    * public API
    *
@@ -26,10 +26,6 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
     }
   }
 
-  private void execute(Stmt stmt) {
-    stmt.accept(this);
-  }
-
   @Override
   public Object visitAssignExpr(Expr.Assign expr) {
     Object value = evaluate(expr.value);
@@ -117,6 +113,12 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
   }
 
   @Override
+  public Void visitBlockStmt(Stmt.Block stmt) {
+    executeBlock(stmt.statements, new Environment(environment));
+    return null;
+  }
+
+  @Override
   public Void visitExpressionStmt(Stmt.Expression stmt) {
     Object value = evaluate(stmt.expression);
     System.out.println("  " + stringify(value));
@@ -141,6 +143,23 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
     return null;
   }
 
+  private void execute(Stmt stmt) {
+    stmt.accept(this);
+  }
+
+  private void executeBlock(List<Stmt> statements, Environment environment) {
+    Environment previous = this.environment;
+    try {
+      this.environment = environment;
+
+      for (Stmt statement : statements) {
+        execute(statement);
+      }
+    } finally {
+      this.environment = previous;
+    }
+  }
+
   private Object evaluate(Expr expr) {
     return expr.accept(this);
   }

+ 14 - 1
src/main/java/com/craftinginterpreters/lox/Parser.java

@@ -53,12 +53,25 @@ public class Parser {
     }
   }
 
-  // statement  → exprStmt | printStmt ;
+  // statement  → exprStmt | printStmt | block;
   private Stmt statement() {
     if (match(PRINT)) return printStatement();
+    if (match(LEFT_BRACE)) return new Stmt.Block(block());
     return expressionStatement();
   }
 
+  // block          → "{" declaration* "}" ;
+  private List<Stmt> block() {
+    List<Stmt> statements = new ArrayList<>();
+
+    while (!check(RIGHT_BRACE) && !isAtEnd()) {
+      statements.add(declaration());
+    }
+
+    consume(RIGHT_BRACE, "Expect '}' after block.");
+    return statements;
+  }
+
   // exprStmt  → expression ";" ;
   private Stmt expressionStatement() {
     Expr expr = expression();

+ 28 - 11
src/main/java/com/craftinginterpreters/lox/Stmt.java

@@ -1,14 +1,15 @@
 package com.craftinginterpreters.lox;
 
+import java.util.List;
+
 /**
  * @author GenerateAst
- * @date 2023-06-09 16:00
+ * @date 2023-06-13 10:59
  */
 abstract class Stmt {
-    abstract <R> R accept(Visitor<R> visitor);
-
-
     interface Visitor<R> {
+        R visitBlockStmt(Block stmt);
+
         R visitExpressionStmt(Expression stmt);
 
         R visitPrintStmt(Print stmt);
@@ -17,9 +18,21 @@ abstract class Stmt {
 
     }
 
-    static class Expression extends Stmt {
-        final Expr expression;
 
+    static class Block extends Stmt {
+        Block(List<Stmt> statements) {
+            this.statements = statements;
+        }
+
+        @Override
+        <R> R accept(Visitor<R> visitor) {
+            return visitor.visitBlockStmt(this);
+        }
+
+        final List<Stmt> statements;
+    }
+
+    static class Expression extends Stmt {
         Expression(Expr expression) {
             this.expression = expression;
         }
@@ -28,11 +41,11 @@ abstract class Stmt {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitExpressionStmt(this);
         }
-    }
 
-    static class Print extends Stmt {
         final Expr expression;
+    }
 
+    static class Print extends Stmt {
         Print(Expr expression) {
             this.expression = expression;
         }
@@ -41,12 +54,11 @@ abstract class Stmt {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitPrintStmt(this);
         }
+
+        final Expr expression;
     }
 
     static class Var extends Stmt {
-        final Token name;
-        final  Expr initializer;
-
         Var(Token name, Expr initializer) {
             this.name = name;
             this.initializer = initializer;
@@ -56,5 +68,10 @@ abstract class Stmt {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitVarStmt(this);
         }
+
+        final Token name;
+        final  Expr initializer;
     }
+
+    abstract <R> R accept(Visitor<R> visitor);
 }

+ 11 - 10
src/main/java/com/craftinginterpreters/tool/GenerateAst.java

@@ -42,15 +42,16 @@ public class GenerateAst {
             System.exit(64);
         }
         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, "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(
+                "Block       : List<Stmt> statements",
                 "Expression  : Expr expression",
                 "Print       : Expr expression",
                 "Var         : Token name, Expr initializer"
@@ -64,8 +65,8 @@ public class GenerateAst {
         SimpleDateFormat matter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
         writer.println("package com.craftinginterpreters.lox;");
         writer.println();
-        //writer.println("import java.util.List;");
-        //writer.println();
+        writer.println("import java.util.List;");
+        writer.println();
         writer.println("/**");
         writer.println(" * @author GenerateAst");
         writer.println(" * @date " + matter.format(new Date()));

+ 16 - 10
src/test/java/com/craftinginterpreters/lox/AstPrinterTest.java

@@ -1,19 +1,25 @@
 package com.craftinginterpreters.lox;
 
+import java.util.ArrayList;
+import java.util.List;
 import org.junit.Test;
 
 /* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
  * All right reserved.*/
 public class AstPrinterTest {
 
-    @Test
-    public void print() {
-        Expr expression = new Expr.Binary(
-                new Expr.Unary(new Token(TokenType.MINUS, "-", null, 1), new Expr.Literal(123)),
-                new Token(TokenType.STAR, "*", null, 1),
-                new Expr.Grouping(new Expr.Literal(45.67))
-        );
+  @Test
+  public void print() {
+    Expr expression =
+        new Expr.Binary(
+            new Expr.Unary(new Token(TokenType.MINUS, "-", null, 1), new Expr.Literal(123)),
+            new Token(TokenType.STAR, "*", null, 1),
+            new Expr.Grouping(new Expr.Literal(45.67)));
 
-        System.out.println(new AstPrinter().print(expression));
-    }
-}
+    List<Stmt> stmts = new ArrayList<>();
+    Stmt.Expression stmtExpr = new Stmt.Expression(expression);
+    stmts.add(stmtExpr);
+
+    System.out.println(new AstPrinter().print(stmts));
+  }
+}

+ 18 - 0
src/test/java/com/craftinginterpreters/lox/LoxTest.java

@@ -0,0 +1,18 @@
+package com.craftinginterpreters.lox;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import org.junit.Test;
+
+/* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
+ * All right reserved.*/ public class LoxTest {
+
+  @Test
+  public void main() throws IOException {
+    String[] args = new String[1];
+    args[0] =
+        "/Users/lixiaoming/Documents/haish/jlox/src/test/java/com/craftinginterpreters/lox/scope.lox"; // path
+    Lox.main(args);
+  }
+}

+ 19 - 0
src/test/java/com/craftinginterpreters/lox/scope.lox

@@ -0,0 +1,19 @@
+var a = "global a";
+var b = "global b";
+var c = "global c";
+{
+  var a = "outer a";
+  var b = "outer b";
+  {
+    var a = "inner a";
+    print a;
+    print b;
+    print c;
+  }
+  print a;
+  print b;
+  print c;
+}
+print a;
+print b;
+print c;