require "parser/current" require "ast" module SlotMachine class SlotCompiler < AST::Processor DEBUG = false # conditionals supported, currently only equal def self.checks [:==] end def self.compile(input) ast = Parser::CurrentRuby.parse( input ) self.new.process(ast) end attr_reader :labels def initialize @labels = {} end def not_implemented(node) raise "Not implemented #{node.type}" end # default to error, so non implemented stuff shows early def handler_missing(node) not_implemented(node) end def process_all(exp) arr = super(exp) head = arr.shift until( arr.empty?) head << arr.shift end head end def on_send(statement) kids = statement.children.dup receiver = kids.shift name = kids.shift return label(name) if(name.to_s.end_with?("_label")) return goto(name,kids) if(name == :goto) return check(name,receiver, kids) if SlotCompiler.checks.include?(name) return assign(receiver, name , kids) if(name.to_s.end_with?("=")) puts "Send: #{statement} " if DEBUG var = SlottedMessage.new( [name] ) if(receiver) puts "receiver at #{name} #{receiver}" if DEBUG prev = process(receiver) prev.set_next(var.slots) prev else var end end def on_lvar(lvar) puts "lvar #{lvar}" if DEBUG SlottedMessage.new( [lvar.children.first] ) end def on_lvasgn( expression) puts "i/lvasgn #{expression}" if DEBUG var = var_for(expression.children[0]) value = process(expression.children[1]) SlotLoad.new(expression.to_s,var , value) end alias :on_ivasgn :on_lvasgn def on_if(expression) puts "if #{expression}" if DEBUG condition = process(expression.children[0]) jump = process(expression.children[1]) condition.set_label( jump.label ) condition end def on_begin(exp) if( exp.children.length == 1) process(exp.first) else process_all(exp) end end def on_ivar(expression) puts "ivar #{expression}" if DEBUG var_for(expression.children.first) end private def var_for( name ) name = name.to_s if(name[0] == "@") var = name.to_s[1 .. -1].to_sym SlottedMessage.new([:receiver , var]) else SlottedMessage.new([:receiver , name]) end end def label(name) raise "no label #{name}" unless(name.to_s.end_with?("_label")) if @labels.has_key?(name) return @labels[name] else @labels[name] = SlotMachine::Label.new(name.to_s , name) end end def goto(name , args) # error handling would not hurt puts "goto #{name} , #{args}" if DEBUG label = process(args.first) Jump.new( label ) end def check(name , receiver , kids) raise "Familiy too large #{kids}" if kids.length > 1 puts "Kids " + kids.to_s if DEBUG right = process(kids.first) case name when :== return SameCheck.new(process(receiver) , right , Label.new("none", :dummy)) else raise "Only ==, not #{name}" unless name == :== end end def assign(receiver , name , kids) receiver = process(receiver) puts "Assign #{name} , #{receiver}" if DEBUG raise "Only one arg #{kids}" unless kids.length == 1 right = process kids.shift name = name.to_s[0...-1].to_sym receiver.set_next(Slot.new(name)) SlotLoad.new("#{receiver} = #{name}",receiver,right) end end end