# Still stolen from August 2011 # The visitor pattern is very useful # when manipulating expression trees or # ASTs in a non-FP OO language. class Int attr_reader :i def initialize i @i = i end def accept(visitor, arg=nil) visitor.visitInt(self, arg) end end class Neg attr_reader :e def initialize e @e = e end def accept(visitor, arg=nil) visitor.visitNeg(self, arg) end end class Add attr_reader :e1, :e2 def initialize(e1,e2) @e1 = e1 @e2 = e2 end def accept(visitor, arg=nil) visitor.visitAdd(self, arg) end end SAMPLE = Neg.new(Add.new(Add.new(Add.new(Int.new(3), Neg.new(Int.new 9)), Int.new(-42)), Add.new(Int.new(73), Neg.new(Int.new(14))))) # Pretty print the expression. class Stringer def visitInt(int, arg) int.i.to_s end def visitNeg(neg, arg) "-(" + neg.e.accept(self) + ")" end def visitAdd(add, arg) "(" + add.e1.accept(self) + " + " + add.e2.accept(self) + ")" end end # SAMPLE.accept(Stringer.new) # Evaluate the expression. class Evaluator def visitInt(int, arg) int end def visitNeg(neg, arg) Int.new(- neg.e.accept(self).i) end def visitAdd(add, arg) Int.new(add.e1.accept(self).i + add.e2.accept(self).i) end end # SAMPLE.accept(Evaluator.new).accept(Stringer.new) # Make sure only positive constants are used. # All negative numbers in the resulting expression # are expressed as Negs. class Positivator def visitInt(int, arg) if int.i < 0 Neg.new(Int.new(-int.i)) else int end end def visitNeg(neg, arg) Neg.new(neg.e.accept(self)) end def visitAdd(add, arg) Add.new(add.e1.accept(self), add.e2.accept(self)) end end # SAMPLE.accept(Positivator.new).accept(Stringer.new) # SAMPLE.accept(Positivator.new).accept(Evaluator.new).accept(Stringer.new) # Only allow Negs of Ints. No Adds under Negs. class NegLeafer def visitInt(int, flip) if flip Neg.new(int) else int end end def visitNeg(neg, flip) neg.e.accept(self, !flip) end def visitAdd(add, flip) Add.new(add.e1.accept(self, flip), add.e2.accept(self, flip)) end end # SAMPLE.accept(Positivator.new).accept(NegLeafer.new).accept(Stringer.new) # SAMPLE.accept(Positivator.new).accept(NegLeafer.new).accept(Evaluator.new).accept(Stringer.new)