8.3
FossilOrigin-Name: 485f20b296cc9e9eb567580bff6d3ef8655a1200720388070503b3cdb7abde77
change ovkzkzzqlmrrvuymrkounwxtsswysypu
commit 8c35e41689cc5d040003f87c080fcde2ec4a8826
author alpha <alpha@kejadlen.dev>
date
parent putuxown
diff --git a/ruby/lib/lox/ast_printer.rb b/ruby/lib/lox/ast_printer.rb
index 3a23375..be30643 100644
--- a/ruby/lib/lox/ast_printer.rb
+++ b/ruby/lib/lox/ast_printer.rb
@@ -7,6 +7,9 @@ module Lox
     def visit_literal(expr)  = expr.value&.to_s || "nil"
     def visit_unary(expr)    = parenthesize(expr.op.lexeme, expr.right)
 
+    def visit_print(expr)    = parenthesize("print", expr.expr)
+    def visit_var(expr)      = "(var #{expr.name.lexeme} #{expr.initializer.value})"
+
     private
 
     def parenthesize(name, *exprs)
diff --git a/ruby/lib/lox/expr.rb b/ruby/lib/lox/expr.rb
index 762129b..0f81c8f 100644
--- a/ruby/lib/lox/expr.rb
+++ b/ruby/lib/lox/expr.rb
@@ -17,5 +17,9 @@ module Lox
     Unary = Struct.new(:op, :right) do
       include Visitable
     end
+
+    Variable = Struct.new(:name) do
+      include Visitable
+    end
   end
 end
diff --git a/ruby/lib/lox/parser.rb b/ruby/lib/lox/parser.rb
index 7cadd06..c263624 100644
--- a/ruby/lib/lox/parser.rb
+++ b/ruby/lib/lox/parser.rb
@@ -13,17 +13,33 @@ module Lox
     def parse!
       statements = []
       until eot?
-        statements << statement
+        statements << declaration
       end
       statements
     end
 
     private
 
+    def declaration
+      return var_declaration if match?(:VAR)
+
+      statement
+    rescue ParseError
+      synchronize!
+    end
+
+    def var_declaration
+      name = consume!(:IDENTIFIER, "Expect variable name.")
+      initializer = match?(:EQUAL) ? expression : nil
+      consume!(:SEMICOLON, "Expect ';' after variable declaration.")
+
+      Stmt::Var.new(name, initializer)
+    end
+
     def statement
       return print if match?(:PRINT)
 
-      expressionStmt
+      expression_stmt
     end
 
     def print
@@ -32,7 +48,7 @@ module Lox
       Stmt::Print.new(value)
     end
 
-    def expressionStmt
+    def expression_stmt
       value = expression
       consume!(:SEMICOLON, "Expect ';' after value.")
       Stmt::Expr.new(value)
@@ -101,6 +117,7 @@ module Lox
       return Expr::Literal.new(true) if match?(:TRUE)
       return Expr::Literal.new(nil) if match?(:NIL)
       return Expr::Literal.new(prev.literal) if match?(:NUMBER, :STRING)
+      return Expr::Variable.new(prev) if match?(:IDENTIFIER)
 
       if match?(:LEFT_PAREN)
         expr = expression
@@ -151,6 +168,6 @@ module Lox
         advance!
       end
     end
-  end
 
+  end
 end
diff --git a/ruby/lib/lox/stmt.rb b/ruby/lib/lox/stmt.rb
index acbebef..9f5d6c2 100644
--- a/ruby/lib/lox/stmt.rb
+++ b/ruby/lib/lox/stmt.rb
@@ -9,5 +9,9 @@ module Lox
     Print = Struct.new(:expr) do
       include Visitable
     end
+
+    Var = Struct.new(:name, :initializer) do
+      include Visitable
+    end
   end
 end
diff --git a/ruby/test/lox/test_parser.rb b/ruby/test/lox/test_parser.rb
index 646d071..cc9f1e9 100644
--- a/ruby/test/lox/test_parser.rb
+++ b/ruby/test/lox/test_parser.rb
@@ -45,22 +45,26 @@ class TestParser < Lox::Test
       parse("(42", :primary)
     end
     assert_equal "[line 1] Error at end: Expect ')' after expression.", e.message
+  end
 
-    e = assert_raises Lox::ParseError do
-      parse("foo foo", :primary)
-    end
-    assert_equal "[line 1] Error at 'foo': Expect expression.", e.message
+  def test_print
+    assert_parsed "(print 42.0)", :statement, "print 42.0;"
+  end
+
+  def test_var
+    assert_parsed "(var foo 42.0)", :declaration, "var foo = 42.0;"
   end
 
   private
 
+  def assert_parsed(expected, name, src)
+    expr = parse(src, name)
+    assert_equal expected, @ast_printer.print(expr)
+  end
+
   def parse(src, name)
     tokens = @scanner.scan(src)
     Lox::Parser.new(tokens).send(name)
   end
 
-  def assert_parsed(expected, name, src)
-    expr = parse(src, name)
-    assert_equal expected, @ast_printer.print(expr)
-  end
 end