#!/usr/bin/ruby class Value < Struct.new(:value) def to_s value.to_s end def inspect "<#{self}>" end def reducible? false end end class Number < Value end class Boolean < Value end class Variable < Struct.new(:name) def to_s name.to_s end def inspect "<#{self}>" end def reduce(environment) environment[name] end def reducible? true end end class BiOp < Struct.new(:left, :right) def inspect "<#{self}>" end def reducible? true end end class Add < BiOp def to_s "#{left} + #{right}" end def reduce(environment) if left.reducible? Add.new(left.reduce(environment),right) elsif right.reducible? Add.new(left,right.reduce(environment)) else Number.new(left.value+right.value) end end end class Multiply < BiOp def to_s "#{left} * #{right}" end def reduce(environment) if left.reducible? Add.new(left.reduce(environment),right) elsif right.reducible? Add.new(left,right.reduce(environment)) else Number.new(left.value*right.value) end end end class LessThan < BiOp def to_s "#{left} < #{right}" end def reduce(environment) if left.reducible? LessThan.new(left.reduce(environment), right) elsif right.reducible? LessThan.new(left, right.reduce(environment)) else Boolean.new(left.value < right.value) end end end class DoNothing def to_s 'do-nothing' end def inspect "<#{self}>" end def ==(other_statement) other_statement.instance_of?(DoNothing) end def reducible? false end end class Assign < Struct.new(:name, :expression) def to_s "#{name} = #{expression}" end def inspect "<#{self}>" end def reducible? true end def reduce(environment) if expression.reducible? [Assign.new(name, expression.reduce(environment)), environment] else [DoNothing.new, environment.merge({name => expression })] end end end class If < Struct.new(:condition, :consequence, :alternative) def to_s "if (#{condition}) { #{consequence} } else { #{alternative} }" end def inspect "<#{self}>" end def reducible? true end def reduce(environment) if condition.reducible? [If.new(condition.reduce(environment), consequence, alternative), environment] else case condition when Boolean.new(true) [consequence, environment] when Boolean.new(false) [alternative, environment] end end end end class Sequence < Struct.new(:first, :second) def to_s "#{first}; #{second}" end def inspect "<#{self}>" end def reducible? true end def reduce(environment) case first when DoNothing.new [second,environment] else reduced_first, reduced_environment = first.reduce(environment) [Sequence.new(reduced_first, second), reduced_environment] end end end class While < Struct.new(:condition,:body) def to_s "while (#{condition} { #{body} })" end def inspect "<#{self}>" end def reducible? true end def reduce(environment) [If.new(condition,Sequence.new(body,self),DoNothing.new), environment] end end class Machine < Struct.new(:statement, :environment) def step self.statement, self.environment = statement.reduce(environment) end def run while statement.reducible? puts "#{statement}, #{environment}" step end puts "#{statement}, #{environment}" end end