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