diff --git a/ruby/lox.rb b/ruby/lox.rb index ebce4ab..b7dbda2 100755 --- a/ruby/lox.rb +++ b/ruby/lox.rb @@ -195,6 +195,39 @@ module Lox "#{type} #{lexeme} #{literal}" end end + + module Expr + Binary = Struct.new(:left, :op, :right) do + def accept(visitor) = visitor.visit_binary(self) + end + + Grouping = Struct.new(:expr) do + def accept(visitor) = visitor.visit_grouping(self) + end + + Literal = Struct.new(:value) do + def accept(visitor) = visitor.visit_literal(self) + end + + Unary = Struct.new(:op, :right) do + def accept(visitor) = visitor.visit_unary(self) + end + end + + class AstPrinter + def print(expr) = expr.accept(self) + + def visit_binary(expr) = parenthesize(expr.op.lexeme, expr.left, expr.right) + def visit_grouping(expr) = parenthesize("group", expr.expr) + def visit_literal(expr) = expr.value&.to_s || "nil" + def visit_unary(expr) = parenthesize(expr.op.lexeme, expr.right) + + private + + def parenthesize(name, *exprs) + "(#{name} #{exprs.map {|expr| expr.accept(self) }.join(" ")})" + end + end end if __FILE__ == $0 diff --git a/ruby/test_lox.rb b/ruby/test_lox.rb index 27f4ff3..e217fd1 100644 --- a/ruby/test_lox.rb +++ b/ruby/test_lox.rb @@ -137,3 +137,20 @@ class TestScanner < Minitest::Test end end end + +class TestAstPrinter < Minitest::Test + def test_ast_printer + expr = Expr::Binary.new( + Expr::Unary.new( + Token.new(:MINUS, ?-, nil, 1), + Expr::Literal.new(123), + ), + Token.new(:STAR, ?*, nil, 1), + Expr::Grouping.new( + Expr::Literal.new(45.67), + ), + ) + + assert_equal "(* (- 123) (group 45.67))", AstPrinter.new.print(expr) + end +end