8.3
FossilOrigin-Name: 485f20b296cc9e9eb567580bff6d3ef8655a1200720388070503b3cdb7abde77
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