FossilOrigin-Name: 485f20b296cc9e9eb567580bff6d3ef8655a1200720388070503b3cdb7abde77
private
alpha 2 years ago
parent a4abf0b0b7
commit 4b1d6e58a5

@ -7,6 +7,9 @@ module Lox
def visit_literal(expr) = expr.value&.to_s || "nil" def visit_literal(expr) = expr.value&.to_s || "nil"
def visit_unary(expr) = parenthesize(expr.op.lexeme, expr.right) 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 private
def parenthesize(name, *exprs) def parenthesize(name, *exprs)

@ -17,5 +17,9 @@ module Lox
Unary = Struct.new(:op, :right) do Unary = Struct.new(:op, :right) do
include Visitable include Visitable
end end
Variable = Struct.new(:name) do
include Visitable
end
end end
end end

@ -13,17 +13,33 @@ module Lox
def parse! def parse!
statements = [] statements = []
until eot? until eot?
statements << statement statements << declaration
end end
statements statements
end end
private 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 def statement
return print if match?(:PRINT) return print if match?(:PRINT)
expressionStmt expression_stmt
end end
def print def print
@ -32,7 +48,7 @@ module Lox
Stmt::Print.new(value) Stmt::Print.new(value)
end end
def expressionStmt def expression_stmt
value = expression value = expression
consume!(:SEMICOLON, "Expect ';' after value.") consume!(:SEMICOLON, "Expect ';' after value.")
Stmt::Expr.new(value) Stmt::Expr.new(value)
@ -101,6 +117,7 @@ module Lox
return Expr::Literal.new(true) if match?(:TRUE) return Expr::Literal.new(true) if match?(:TRUE)
return Expr::Literal.new(nil) if match?(:NIL) return Expr::Literal.new(nil) if match?(:NIL)
return Expr::Literal.new(prev.literal) if match?(:NUMBER, :STRING) return Expr::Literal.new(prev.literal) if match?(:NUMBER, :STRING)
return Expr::Variable.new(prev) if match?(:IDENTIFIER)
if match?(:LEFT_PAREN) if match?(:LEFT_PAREN)
expr = expression expr = expression
@ -151,6 +168,6 @@ module Lox
advance! advance!
end end
end end
end
end end
end

@ -9,5 +9,9 @@ module Lox
Print = Struct.new(:expr) do Print = Struct.new(:expr) do
include Visitable include Visitable
end end
Var = Struct.new(:name, :initializer) do
include Visitable
end
end end
end end

@ -45,22 +45,26 @@ class TestParser < Lox::Test
parse("(42", :primary) parse("(42", :primary)
end end
assert_equal "[line 1] Error at end: Expect ')' after expression.", e.message assert_equal "[line 1] Error at end: Expect ')' after expression.", e.message
end
e = assert_raises Lox::ParseError do def test_print
parse("foo foo", :primary) assert_parsed "(print 42.0)", :statement, "print 42.0;"
end end
assert_equal "[line 1] Error at 'foo': Expect expression.", e.message
def test_var
assert_parsed "(var foo 42.0)", :declaration, "var foo = 42.0;"
end end
private private
def assert_parsed(expected, name, src)
expr = parse(src, name)
assert_equal expected, @ast_printer.print(expr)
end
def parse(src, name) def parse(src, name)
tokens = @scanner.scan(src) tokens = @scanner.scan(src)
Lox::Parser.new(tokens).send(name) Lox::Parser.new(tokens).send(name)
end end
def assert_parsed(expected, name, src)
expr = parse(src, name)
assert_equal expected, @ast_printer.print(expr)
end
end end

Loading…
Cancel
Save