rubyx/lib/slot_machine/slot_compiler.rb
Torsten Rüger 8df2e4bf08 SlotLanguage reborn in the Machine
Just added the compiler, that can parse Slot directly into SlotMachine code (no language layer in between)
Still unclear wheather the Maker is a thing, but since it was in the Language layer i did not remove it (yet)
Otherwise just the compiler and all the tests, moved from the slot_language.
2020-02-19 02:19:14 +07:00

135 lines
3.6 KiB
Ruby

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