implement AstPrinter
FossilOrigin-Name: ed84fb6a6150359d3756c8636af19f487cb4da6fc2f21a6147b335751416a06f
change xplsqsmuvuwrqoqtmnmmmtksqkowzuzz
commit 9fff14bbc3f6f3ba640ba7141ab8379fb0217cfa
author alpha <alpha@kejadlen.dev>
date
parent mzrwvqso
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