Bläddra i källkod

Parsing variables -- Environment

runningwater 2 år sedan
förälder
incheckning
424b1e0fae

+ 57 - 45
README.md

@@ -29,57 +29,69 @@
 - **Literals**. Numbers,strings,Booleans, and nil
 - **Unary expressions.** A prefix ! to perform a logical not, and - to negate a number.
 - **Binary expressions.** The infix arithmetic(+,-,*,/) and logic operators (==, !=, <, <=,>,>=)
-- **Parentheses.** A pair of ( and ) wrapped around and expression.
+    - **Parentheses.** A pair of ( and ) wrapped around and expression.
 
-    - Syntax tree - Expression
+        - Syntax tree - Expression
 
-      ```
-      expression     → literal
-      | unary
-      | binary
-      | grouping ;
-    
-      literal        → NUMBER | STRING | "true" | "false" | "nil" ;
-      grouping       → "(" expression ")" ;
-      unary          → ( "-" | "!" ) expression ;
-      binary         → expression operator expression ;
-      operator       → "==" | "!=" | "<" | "<=" | ">" | ">="
-      | "+"  | "-"  | "*" | "/" ;
-    
-      ```
+              ```
+              expression     → literal
+              | unary
+              | binary
+              | grouping ;
 
-| Name       | Operators | Associates |
-|------------|-----------|------------|
-| Equality	  | == !=     | Left       |
-| Comparison | > >= < <= | Left       |
-| Term       | - +	      | Left       |
-| Factor     | / *       | Left       |
-| Unary      | ! -       | Right      |
+              literal        → NUMBER | STRING | "true" | "false" | "nil" ;
+              grouping       → "(" expression ")" ;
+              unary          → ( "-" | "!" ) expression ;
+              binary         → expression operator expression ;
+              operator       → "==" | "!=" | "<" | "<=" | ">" | ">="
+              | "+"  | "-"  | "*" | "/" ;
 
-```
-expression     → equality ;
-equality       → comparison ( ( "!=" | "==" ) comparison )* ;
-comparison     → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
-term           → factor ( ( "-" | "+" ) factor )* ;
-factor         → unary ( ( "/" | "*" ) unary )* ;
-unary          → ( "!" | "-" ) unary
-               | primary ;
-primary        → NUMBER | STRING | "true" | "false" | "nil"
-               | "(" expression ")" ;
-```
+              ```
 
-- Syntax tree - Statement
+          | Name       | Operators | Associates |
+                    |------------|-----------|------------|
+          | Equality	  | == !=     | Left       |
+          | Comparison | > >= < <= | Left       |
+          | Term       | - +	      | Left       |
+          | Factor     | / *       | Left       |
+          | Unary      | ! -       | Right      |
 
-```
-program        → statement* EOF ;
+            ```
+            expression     → equality ;
+            equality       → comparison ( ( "!=" | "==" ) comparison )* ;
+            comparison     → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
+            term           → factor ( ( "-" | "+" ) factor )* ;
+            factor         → unary ( ( "/" | "*" ) unary )* ;
+            unary          → ( "!" | "-" ) unary
+                           | primary ;
+            primary        → NUMBER | STRING | "true" | "false" | "nil"
+                           | "(" expression ")" | IDENTIFIER ;
+           ```
 
-statement      → exprStmt
-               | printStmt ;
+        - Syntax tree - Statement
 
-exprStmt       → expression ";" ;
-printStmt      → "print" expression ";" ;
-```
+            ```
+            program        → statement* EOF ;
+      
+            statement      → exprStmt
+                           | printStmt ;
+      
+            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 ;
+          
+            declaration  → varDecl
+                         | statement ;
+          
+            statement  → exprStmt
+                       | printStmt ;
+            
+            varDecl  → "var" IDENTIFIER ( "=" expression )? ";" ;
+          ```

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

@@ -31,6 +31,11 @@ public class AstPrinter implements Expr.Visitor<String>, Stmt.Visitor<String> {
   }
 
   @Override
+  public String visitVarStmt(Stmt.Var stmt) {
+    return parenthesize("var", stmt.initializer == null ? new Expr.Literal(null): stmt.initializer);
+  }
+
+  @Override
   public String visitBinaryExpr(Expr.Binary expr) {
     return parenthesize(expr.operator.lexeme, expr.left, expr.right);
   }
@@ -51,6 +56,11 @@ public class AstPrinter implements Expr.Visitor<String>, Stmt.Visitor<String> {
     return parenthesize(expr.operator.lexeme, expr.right);
   }
 
+  @Override
+  public String visitVariableExpr(Expr.Variable expr) {
+    return parenthesize(expr.name.lexeme);
+  }
+
   private String parenthesize(String name, Expr... exprs) {
     StringBuilder builder = new StringBuilder();
     builder.append("(").append(name);

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

@@ -0,0 +1,27 @@
+/* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
+ * All right reserved.*/ package com.craftinginterpreters.lox;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author simon
+ * @date 2023-06-09 16:34
+ * @desc The bindings that associate variables to values need to be stored somewhere. Ever since the
+ *     Lisp folks invented parentheses, this data structure has been called an environment.
+ */
+public class Environment {
+  private final Map<String, Object> values = new HashMap<>(16);
+
+  void define(String name, Object value) {
+    values.put(name, value);
+  }
+
+  Object get(Token name) {
+    if (values.containsKey(name.lexeme)) {
+      return values.get(name.lexeme);
+    }
+
+    throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
+  }
+}

+ 29 - 14
src/main/java/com/craftinginterpreters/lox/Expr.java

@@ -2,11 +2,9 @@ package com.craftinginterpreters.lox;
 
 /**
  * @author GenerateAst
- * @date 2023-06-05 13:42
+ * @date 2023-06-09 16:00
  */
 abstract class Expr {
-  abstract <R> R accept(Visitor<R> visitor);
-
   interface Visitor<R> {
     R visitBinaryExpr(Binary expr);
 
@@ -15,13 +13,11 @@ abstract class Expr {
     R visitLiteralExpr(Literal expr);
 
     R visitUnaryExpr(Unary expr);
+
+    R visitVariableExpr(Variable expr);
   }
 
   static class Binary extends Expr {
-    final Expr left;
-    final Token operator;
-    final Expr right;
-
     Binary(Expr left, Token operator, Expr right) {
       this.left = left;
       this.operator = operator;
@@ -32,11 +28,13 @@ abstract class Expr {
     <R> R accept(Visitor<R> visitor) {
       return visitor.visitBinaryExpr(this);
     }
+
+    final Expr left;
+    final Token operator;
+    final Expr right;
   }
 
   static class Grouping extends Expr {
-    final Expr expression;
-
     Grouping(Expr expression) {
       this.expression = expression;
     }
@@ -45,11 +43,11 @@ abstract class Expr {
     <R> R accept(Visitor<R> visitor) {
       return visitor.visitGroupingExpr(this);
     }
+
+    final Expr expression;
   }
 
   static class Literal extends Expr {
-    final Object value;
-
     Literal(Object value) {
       this.value = value;
     }
@@ -58,12 +56,11 @@ abstract class Expr {
     <R> R accept(Visitor<R> visitor) {
       return visitor.visitLiteralExpr(this);
     }
+
+    final Object value;
   }
 
   static class Unary extends Expr {
-    final Token operator;
-    final Expr right;
-
     Unary(Token operator, Expr right) {
       this.operator = operator;
       this.right = right;
@@ -73,5 +70,23 @@ abstract class Expr {
     <R> R accept(Visitor<R> visitor) {
       return visitor.visitUnaryExpr(this);
     }
+
+    final Token operator;
+    final Expr right;
   }
+
+  static class Variable extends Expr {
+    Variable(Token name) {
+      this.name = name;
+    }
+
+    @Override
+    <R> R accept(Visitor<R> visitor) {
+      return visitor.visitVariableExpr(this);
+    }
+
+    final Token name;
+  }
+
+  abstract <R> R accept(Visitor<R> visitor);
 }

+ 17 - 0
src/main/java/com/craftinginterpreters/lox/Interpreter.java

@@ -10,6 +10,7 @@ import java.util.List;
  * @desc
  */
 public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
+  private final Environment environment = new Environment();
   /**
    * public API
    *
@@ -104,6 +105,11 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
   }
 
   @Override
+  public Object visitVariableExpr(Expr.Variable expr) {
+    return environment.get(expr.name);
+  }
+
+  @Override
   public Void visitExpressionStmt(Stmt.Expression stmt) {
     Object value = evaluate(stmt.expression);
     System.out.println("  " + stringify(value));
@@ -117,6 +123,17 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
     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;
+  }
+
   private Object evaluate(Expr expr) {
     return expr.accept(this);
   }

+ 31 - 2
src/main/java/com/craftinginterpreters/lox/Parser.java

@@ -32,14 +32,26 @@ public class Parser {
     this.tokens = tokens;
   }
 
+  // program   → declaration* EOF ;
   List<Stmt> parse() {
     List<Stmt> statements = new ArrayList<>();
     while (!isAtEnd()) {
-      statements.add(statement());
+      statements.add(declaration());
     }
     return statements;
   }
 
+  // declaration  → varDecl | statement ;
+  private Stmt declaration() {
+    try {
+      if (match(VAR)) return varDecl();
+      return statement();
+    } catch (ParseError error) {
+      synchronize();
+      return null;
+    }
+  }
+
   // statement  → exprStmt | printStmt ;
   private Stmt statement() {
     if (match(PRINT)) return printStatement();
@@ -60,6 +72,19 @@ public class Parser {
     return new Stmt.Print(value);
   }
 
+  // varDecl  → "var" IDENTIFIER ( "=" expression )? ";" ;
+  private Stmt varDecl() {
+    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);
+  }
+
   // expression  → equality ;
   private Expr expression() {
     return equality();
@@ -121,7 +146,7 @@ public class Parser {
   }
 
   //    primary        → NUMBER | STRING | "true" | "false" | "nil"
-  //            | "(" expression ")" ;
+  //            | "(" expression ")" | IDENTIFIER ;
   private Expr primary() {
     if (match(FALSE)) return new Expr.Literal(false);
     if (match(TRUE)) return new Expr.Literal(true);
@@ -137,6 +162,10 @@ public class Parser {
       return new Expr.Grouping(expr);
     }
 
+    if (match(IDENTIFIER)) {
+      return new Expr.Variable(previous());
+    }
+
     throw error(peek(), "Expect expression.");
   }
 

+ 25 - 6
src/main/java/com/craftinginterpreters/lox/Stmt.java

@@ -2,17 +2,24 @@ package com.craftinginterpreters.lox;
 
 /**
  * @author GenerateAst
- * @date 2023-06-09 11:18
+ * @date 2023-06-09 16:00
  */
 abstract class Stmt {
+    abstract <R> R accept(Visitor<R> visitor);
+
+
     interface Visitor<R> {
         R visitExpressionStmt(Expression stmt);
 
         R visitPrintStmt(Print stmt);
 
+        R visitVarStmt(Var stmt);
+
     }
 
     static class Expression extends Stmt {
+        final Expr expression;
+
         Expression(Expr expression) {
             this.expression = expression;
         }
@@ -21,10 +28,11 @@ abstract class Stmt {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitExpressionStmt(this);
         }
-
-        final Expr expression;
     }
+
     static class Print extends Stmt {
+        final Expr expression;
+
         Print(Expr expression) {
             this.expression = expression;
         }
@@ -33,9 +41,20 @@ abstract class Stmt {
         <R> R accept(Visitor<R> visitor) {
             return visitor.visitPrintStmt(this);
         }
-
-        final Expr expression;
     }
 
-    abstract <R> R accept(Visitor<R> visitor);
+    static class Var extends Stmt {
+        final Token name;
+        final  Expr initializer;
+
+        Var(Token name, Expr initializer) {
+            this.name = name;
+            this.initializer = initializer;
+        }
+
+        @Override
+        <R> R accept(Visitor<R> visitor) {
+            return visitor.visitVarStmt(this);
+        }
+    }
 }

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

@@ -42,15 +42,17 @@ public class GenerateAst {
             System.exit(64);
         }
         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, "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"
+                "Print       : Expr expression",
+                "Var         : Token name, Expr initializer"
         ));
     }
 
@@ -103,6 +105,7 @@ public class GenerateAst {
     private static void defineType(
             PrintWriter writer, String baseName,
             String className, String fieldList) {
+        writer.println();
         writer.println(space + "static class " + className + " extends " + baseName + " {");
 
         // Constructor.