Fix forgotten block compiler

Especially on the way down to risc
This commit is contained in:
Torsten Rüger 2019-08-13 19:32:17 +03:00
parent 8036b23593
commit 155c042009
16 changed files with 250 additions and 183 deletions

View File

@ -4,7 +4,7 @@ module Mom
# #
class BlockCompiler < CallableCompiler class BlockCompiler < CallableCompiler
attr_reader :block , :risc_instructions , :constants attr_reader :block , :mom_instructions
alias :block :callable alias :block :callable
def initialize( block , method) def initialize( block , method)
@ -16,6 +16,13 @@ module Mom
"#{@method.self_type.name}.init" "#{@method.self_type.name}.init"
end end
def to_risc(in_method)
risc_compiler = Risc::BlockCompiler.new(@callable , in_method , mom_instructions)
instructions_to_risc(risc_compiler)
#recursive blocks not done
risc_compiler
end
# resolve the type of the slot, by inferring from it's name, using the type # resolve the type of the slot, by inferring from it's name, using the type
# scope related slots are resolved by the compiler by method/block # scope related slots are resolved by the compiler by method/block
# #

View File

@ -84,5 +84,21 @@ module Mom
@callable.self_type @callable.self_type
end end
private
# convert al instruction to risc
# method is called by Method/BlockCompiler from to_risc
def instructions_to_risc(risc_compiler)
instruction = mom_instructions.next
while( instruction )
raise "whats this a #{instruction}" unless instruction.is_a?(Mom::Instruction)
#puts "adding mom #{instruction.to_s}:#{instruction.next.to_s}"
instruction.to_risc( risc_compiler )
risc_compiler.reset_regs
#puts "adding risc #{risc.to_s}:#{risc.next.to_s}"
instruction = instruction.next
end
end
end end
end end

View File

@ -4,16 +4,12 @@ module Mom
# and to instantiate the methods correctly. # and to instantiate the methods correctly.
class MethodCompiler < CallableCompiler class MethodCompiler < CallableCompiler
alias :callable :method
def initialize( method ) def initialize( method )
super(method) super(method)
end end
#include block_compilers constants
def constants
block_compilers.inject(@constants.dup){|all, compiler| all += compiler.constants}
end
def source_name def source_name
"#{@callable.self_type.name}.#{@callable.name}" "#{@callable.self_type.name}.#{@callable.name}"
end end
@ -27,19 +23,16 @@ module Mom
@callable @callable
end end
# drop down to risc # drop down to risc by converting this compilers instructions to risc.
# and the doing the same for any block_compilers
def to_risc def to_risc
risc_comp = Risc::MethodCompiler.new(@callable , mom_instructions) risc_compiler = Risc::MethodCompiler.new(@callable , mom_instructions)
instruction = mom_instructions.next instructions_to_risc(risc_compiler)
while( instruction ) block_compilers.each do |m_comp|
raise "whats this a #{instruction}" unless instruction.is_a?(Mom::Instruction) puts "BLOCK #{m_comp}"
#puts "adding mom #{instruction.to_s}:#{instruction.next.to_s}" risc_compiler.block_compilers << m_comp.to_risc(@callable)
instruction.to_risc( risc_comp )
risc_comp.reset_regs
#puts "adding risc #{risc.to_s}:#{risc.next.to_s}"
instruction = instruction.next
end end
risc_comp risc_compiler
end end
# helper method for builtin mainly # helper method for builtin mainly

View File

@ -26,11 +26,6 @@ module Mom
@method_compilers + boot_compilers @method_compilers + boot_compilers
end end
# collects constants from all compilers into one array
def constants
compilers.inject([]){|sum ,comp| sum + comp.constants }
end
# Append another MomCompilers method_compilers to this one. # Append another MomCompilers method_compilers to this one.
def append(mom_compiler) def append(mom_compiler)
@method_compilers += mom_compiler.method_compilers @method_compilers += mom_compiler.method_compilers

View File

@ -4,16 +4,15 @@ module Risc
# #
class BlockCompiler < CallableCompiler class BlockCompiler < CallableCompiler
attr_reader :block , :risc_instructions , :constants attr_reader :block , :risc_instructions , :constants , :in_method
alias :block :callable
def initialize( block , method) def initialize( block , in_method , mom_label)
@method = method @in_method = in_method
super(block) super(block , mom_label)
end end
def source_name def source_name
"#{@method.self_type.name}.init" "#{@in_method.self_type.name}.init"
end end
# resolve the type of the slot, by inferring from it's name, using the type # resolve the type of the slot, by inferring from it's name, using the type
@ -24,9 +23,9 @@ module Risc
def slot_type( slot , type) def slot_type( slot , type)
new_type = super new_type = super
if slot == :caller if slot == :caller
extra_info = { type_frame: @method.frame_type , extra_info = { type_frame: @in_method.frame_type ,
type_arguments: @method.arguments_type , type_arguments: @in_method.arguments_type ,
type_self: @method.self_type} type_self: @in_method.self_type}
end end
return new_type , extra_info return new_type , extra_info
end end
@ -38,9 +37,9 @@ module Risc
slot_def = [:arguments] slot_def = [:arguments]
elsif @callable.frame_type.variable_index(name) elsif @callable.frame_type.variable_index(name)
slot_def = [:frame] slot_def = [:frame]
elsif @method.arguments_type.variable_index(name) elsif @in_method.arguments_type.variable_index(name)
slot_def = [:caller , :caller ,:arguments ] slot_def = [:caller , :caller ,:arguments ]
elsif @method.frame_type.variable_index(name) elsif @in_method.frame_type.variable_index(name)
slot_def = [:caller ,:caller , :frame ] slot_def = [:caller ,:caller , :frame ]
elsif elsif
raise "no variable #{name} , need to resolve at runtime" raise "no variable #{name} , need to resolve at runtime"

View File

@ -30,7 +30,6 @@ module Risc
end end
end end
# add a constant (which get created during compilation and need to be linked) # add a constant (which get created during compilation and need to be linked)
def add_constant(const) def add_constant(const)
raise "Must be Parfait #{const}" unless const.is_a?(Parfait::Object) raise "Must be Parfait #{const}" unless const.is_a?(Parfait::Object)

View File

@ -51,9 +51,9 @@ module RubyX
# #
# After creating vool, we call to_target # After creating vool, we call to_target
# Return a Linker # Return a Linker
def ruby_to_target(ruby) def ruby_to_target(ruby , platform)
ruby_to_vool(ruby) ruby_to_vool(ruby)
to_target() to_target( platform )
end end
# ruby_to_risc creates Risc instructions # ruby_to_risc creates Risc instructions

View File

@ -3,6 +3,8 @@ module Vool
class Assignment < Statement class Assignment < Statement
attr_reader :name , :value attr_reader :name , :value
def initialize(name , value ) def initialize(name , value )
raise "Name nil #{self}" unless name
raise "Value nil #{self}" unless value
@name , @value = name , value @name , @value = name , value
end end

View File

@ -0,0 +1,56 @@
require_relative "../helper"
module Risc
class TestBlockAssign < MiniTest::Test
include Statements
def setup
super
@input = as_block("return 5")
@expect = [LoadConstant, SlotToReg, RegToSlot, LoadConstant, SlotToReg, #4
RegToSlot, LoadConstant, LoadConstant, SlotToReg, SlotToReg, #9
RegToSlot, RegToSlot, RegToSlot, RegToSlot, SlotToReg, #14
SlotToReg, RegToSlot, SlotToReg, SlotToReg, SlotToReg, #19
SlotToReg, RegToSlot, LoadConstant, SlotToReg, RegToSlot, #24
SlotToReg, FunctionCall, Label]
end
def test_send_instructions
assert_nil msg = check_nil(produce_block) , msg
end
def test_load_5
produced = produce_body
assert_load( produced , Parfait::Integer)
assert_equal 5 , produced.constant.value
end
def test_load_block
produced = produce_body.next(3)
assert_load( produced , Parfait::Block)
assert_equal :main_block , produced.constant.name
end
def test_load_method_to_call
produced = produce_body.next(6)
assert_load( produced , Parfait::CallableMethod)
assert_equal :main , produced.constant.name
end
def test_load_next_message
produced = produce_body.next(7)
assert_load( produced , Parfait::Factory)
assert_equal "Message_Type" , produced.constant.for_type.name
end
def test_load_return
produced = produce_body.next(22)
assert_load( produced , Label)
assert produced.constant.name.start_with?("continue_")
end
def test_function_call
produced = produce_body.next(26)
assert_equal FunctionCall , produced.class
assert_equal :main , produced.method.name
end
def test_check_continue
produced = produce_body.next(27)
assert_equal Label , produced.class
assert produced.name.start_with?("continue_")
end
end
end

View File

@ -0,0 +1,32 @@
require_relative "../helper"
module Risc
class TestBlockSetup < MiniTest::Test
include Statements
def setup
super
@input = as_block("return 5")
@mom = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(as_test_main)
end
def main_risc
@mom.to_risc.method_compilers.find{|c| c.callable.name == :main }
end
def test_mom
assert_equal Mom::MomCollection , @mom.class
end
def test_mom_block_comp
assert_equal 1 , @mom.method_compilers.first.block_compilers.length
end
def test_risc
assert_equal Risc::RiscCollection , @mom.to_risc.class
end
def test_risc_comp
assert_equal :main , main_risc.callable.name
end
def test_risc_block_comp
assert_equal 1 , main_risc.block_compilers.length
end
end
end

View File

@ -0,0 +1,57 @@
require_relative "../helper"
module Risc
class TestBlockSetup < MiniTest::Test
include Statements
def setup
super
@input = as_block("return 5")
@expect = [LoadConstant, SlotToReg, RegToSlot, LoadConstant, SlotToReg, #4
RegToSlot, LoadConstant, LoadConstant, SlotToReg, SlotToReg, #9
RegToSlot, RegToSlot, RegToSlot, RegToSlot, SlotToReg, #14
SlotToReg, RegToSlot, SlotToReg, SlotToReg, SlotToReg, #19
SlotToReg, RegToSlot, LoadConstant, SlotToReg, RegToSlot, #24
SlotToReg, FunctionCall, Label]
end
def test_send_instructions
assert_nil msg = check_nil , msg
end
def test_load_5
produced = produce_body
assert_load( produced , Parfait::Integer)
assert_equal 5 , produced.constant.value
end
def test_load_block
produced = produce_body.next(3)
assert_load( produced , Parfait::Block)
assert_equal :main_block , produced.constant.name
end
def test_load_method_to_call
produced = produce_body.next(6)
assert_load( produced , Parfait::CallableMethod)
assert_equal :main , produced.constant.name
end
def test_load_next_message
produced = produce_body.next(7)
assert_load( produced , Parfait::Factory)
assert_equal "Message_Type" , produced.constant.for_type.name
end
def test_load_return
produced = produce_body.next(22)
assert_load( produced , Label)
assert produced.constant.name.start_with?("continue_")
end
def test_function_call
produced = produce_body.next(26)
assert_equal FunctionCall , produced.class
assert_equal :main , produced.method.name
end
def test_check_continue
produced = produce_body.next(27)
assert_equal Label , produced.class
assert produced.name.start_with?("continue_")
end
end
end

View File

@ -24,17 +24,32 @@ module Risc
preamble.each{ produced = produced.next } preamble.each{ produced = produced.next }
produced produced
end end
def as_block( block_input , method_input = "main_local = 5")
"#{method_input} ; self.main{|val| #{block_input}}"
end
def as_test_main def as_test_main
"class Test; def main(arg);#{@input};end;end" "class Test; def main(arg);#{@input};end;end"
end end
def produce_instructions def to_target
assert @expect , "No output given" assert @expect , "No output given"
linker = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_risc(as_test_main).translate(:interpreter) RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_target(as_test_main,:interpreter)
compiler = linker.assemblers.find{|c| c.callable.name == :main and c.callable.self_type.object_class.name == :Test}
compiler.instructions
end end
def check_nil def find_main
produced = produce_instructions assert @expect , "No output given"
linker = to_target
linker.assemblers.find{|c| c.callable.name == :main and c.callable.self_type.object_class.name == :Test}
end
def produce_instructions
find_main.instructions
end
def produce_block
linker = to_target
linker.assemblers.each {|c| puts c.callable.name}
linker.block_compilers.first.instructions
end
def check_nil( instructions = nil )
produced = instructions || produce_instructions
compare_instructions( produced , @expect) compare_instructions( produced , @expect)
end end
def check_return def check_return

View File

@ -1,85 +1,23 @@
require_relative "../helper" require_relative "helper"
module Mom module Mom
class TestBlockCompiler < MiniTest::Test class TestBlockCompiler < MiniTest::Test
include MomCompile include ScopeHelper
def setup def setup
Parfait.boot!(Parfait.default_test_options) code = as_test_main_block("return 5" , "a = 1")
@ins = compile_first_block( "local = 5") @risc = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_risc(code)
end end
def test_block_compiles def test_collection
assert_equal Mom::SlotLoad , @ins.class , @ins assert_equal Risc::RiscCollection, @risc.class
end end
def test_slot_is_set def test_main_compiler
assert @ins.left assert_equal :main , @risc.method_compilers.first.callable.name
end end
def test_slot_starts_at_message def test_main_block_compiler
assert_equal :message , @ins.left.known_object assert_equal :main , @risc.method_compilers.first.block_compilers.first.in_method.name
end assert_equal :main_block , @risc.method_compilers.first.block_compilers.first.callable.name
def test_slots_left
assert_equal [:frame , :local] , @ins.left.slots
end
def test_slot_assigns_something
assert @ins.right
end
def test_slot_assigns_int
assert_equal Mom::IntegerConstant , @ins.right.known_object.class
end end
end end
class TestAssignMomInstanceToLocal < MiniTest::Test
include MomCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_block( "local = @a" , "@a = 5") #second arg in method scope
end
def test_class_compiles
assert_equal Mom::SlotLoad , @ins.class , @ins
end
def test_slots_left
assert_equal [:frame, :local] , @ins.left.slots
end
def test_slots_right
assert_equal [:receiver, :a] , @ins.right.slots
end
end
class TestAssignToArg < MiniTest::Test
include MomCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_block( "arg = 5")
end
def test_class_compiles
assert_equal Mom::SlotLoad , @ins.class , @ins
end
def test_slot_is_set
assert @ins.left
end
def test_slots_left
assert_equal [:caller,:caller, :arguments, :arg] , @ins.left.slots
end
end
class TestAssignMomToInstance < MiniTest::Test
include MomCompile
def setup
Parfait.boot!(Parfait.default_test_options)
end
def test_assigns_const
@ins = compile_first_block( "@a = 5")
assert_equal Mom::SlotLoad , @ins.class , @ins
assert_equal Mom::IntegerConstant , @ins.right.known_object.class , @ins
end
def test_assigns_move
@ins = compile_first_block( "@a = arg")
assert_equal Mom::SlotLoad , @ins.class , @ins
assert_equal Mom::SlotDefinition , @ins.right.class , @ins
end
end
end end

View File

@ -21,12 +21,6 @@ module Mom
def test_compilers_bare def test_compilers_bare
assert_equal 21 , MomCollection.new.compilers.length assert_equal 21 , MomCollection.new.compilers.length
end end
def test_returns_constants
assert_equal Array , @comp.constants.class
end
def test_has_constant_before
assert_equal [] , @comp.constants
end
def test_append_class def test_append_class
assert_equal MomCollection, (@comp.append @comp).class assert_equal MomCollection, (@comp.append @comp).class
end end

View File

@ -16,39 +16,31 @@ module ScopeHelper
def as_test_main( statements ) def as_test_main( statements )
in_Test("def main(arg) ; #{statements}; end") in_Test("def main(arg) ; #{statements}; end")
end end
def as_test_main_block( block_input = "return 5", method_input = "main_local = 5")
as_test_main("#{method_input} ; self.main{|val| #{block_input}}")
end
end end
module VoolCompile module VoolCompile
include ScopeHelper include ScopeHelper
include Mom include Mom
def compile_vool_method(input)
statements = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_vool(as_main(input)) def compile_first_method( input )
assert statements.is_a?(Vool::Statement) , statements.class inut = as_test_main( input )
statements
end
def compile_method(input)
collection = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(input) collection = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(input)
assert collection.is_a?(Mom::MomCollection) assert collection.is_a?(Mom::MomCollection)
compiler = collection.compilers.first compiler = collection.compilers.first
assert compiler.is_a?(Mom::MethodCompiler) assert compiler.is_a?(Mom::MethodCompiler)
assert_equal Mom::MethodCompiler , compiler.class
compiler compiler
end end
def compile_first_method( input )
ret = compile_method( as_test_main( input ))
assert_equal Mom::MethodCompiler , ret.class
ret
end
def compile_first_block( block_input , method_input = "main_local = 5") def compile_first_block( block_input , method_input = "main_local = 5")
source = "#{method_input} ; self.main{|val| #{block_input}}" source = "#{method_input} ; self.main{|val| #{block_input}}"
vool = Ruby::RubyCompiler.compile( as_test_main(source) ).to_vool risc_col = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_risc( as_test_main(source) )
mom_c = vool.to_mom(nil) compiler = risc_col.method_compilers.find{|c| c.get_method.name.to_s.start_with?("implicit") }
compiler = mom_c.method_compilers.find{|c| c.get_method.name == :main and c.get_method.self_type.object_class.name == :Test} assert_equal 1 , compiler.class
block = nil
vool.each {|b| block = b if b.is_a?(Vool::BlockStatement)}
assert block
block_c = compiler.block_compilers.first
assert block_c
block.body.to_mom(block_c)
end end
def check_array( should , is ) def check_array( should , is )
index = 0 index = 0
@ -79,34 +71,6 @@ end
module MomCompile module MomCompile
include ScopeHelper include ScopeHelper
def compile_method(input)
statements = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_vool(input)
assert statements.is_a?(Vool::ClassStatement)
ret = statements.to_mom(nil)
assert_equal Parfait::Class , statements.clazz.class , statements
@method = statements.clazz.get_method(:main)
assert_equal Parfait::VoolMethod , @method.class
ret
end
def compile_first_method( input )
ret = compile_method( as_test_main( input ))
assert_equal Mom::MomCompiler , ret.class
compiler = ret.method_compilers.find{|c| c.get_method.name == :main and c.get_method.self_type.object_class.name == :Test}
assert_equal Risc::MethodCompiler , compiler.class
@method.source.to_mom( compiler )
end
def compile_first_block( block_input , method_input = "main_local = 5")
source = "#{method_input} ; self.main{|val| #{block_input}}"
vool = Ruby::RubyCompiler.compile( as_test_main(source) ).to_vool
mom_c = vool.to_mom(nil)
compiler = mom_c.method_compilers.find{|c| c.get_method.name == :main and c.get_method.self_type.object_class.name == :Test}
block = nil
vool.each {|b| block = b if b.is_a?(Vool::BlockStatement)}
assert block
block_c = compiler.block_compilers.first
assert block_c
block.body.to_mom(block_c)
end
def compile_mom(input) def compile_mom(input)
RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(input) RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(input)
end end

View File

@ -6,25 +6,25 @@ module VoolBlocks
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_block( "local = 5") @ins = compile_first_block( "local = 5" )
end end
def test_block_compiles def test_block_compiles
assert_equal Mom::SlotLoad , @ins.class , @ins assert_equal Mom::SlotLoad , @ins.class , @ins
end end
def test_slot_is_set def pest_slot_is_set
assert @ins.left assert @ins.left
end end
def test_slot_starts_at_message def pest_slot_starts_at_message
assert_equal :message , @ins.left.known_object assert_equal :message , @ins.left.known_object
end end
def test_slots_left def pest_slots_left
assert_equal [:frame , :local] , @ins.left.slots assert_equal [:frame , :local] , @ins.left.slots
end end
def test_slot_assigns_something def pest_slot_assigns_something
assert @ins.right assert @ins.right
end end
def test_slot_assigns_int def pest_slot_assigns_int
assert_equal Mom::IntegerConstant , @ins.right.known_object.class assert_equal Mom::IntegerConstant , @ins.right.known_object.class
end end
end end
@ -35,13 +35,13 @@ module VoolBlocks
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_block( "local = @a" , "@a = 5") #second arg in method scope @ins = compile_first_block( "local = @a" , "@a = 5") #second arg in method scope
end end
def test_class_compiles def pest_class_compiles
assert_equal Mom::SlotLoad , @ins.class , @ins assert_equal Mom::SlotLoad , @ins.class , @ins
end end
def test_slots_left def pest_slots_left
assert_equal [:frame, :local] , @ins.left.slots assert_equal [:frame, :local] , @ins.left.slots
end end
def test_slots_right def pest_slots_right
assert_equal [:receiver, :a] , @ins.right.slots assert_equal [:receiver, :a] , @ins.right.slots
end end
end end
@ -54,13 +54,13 @@ module VoolBlocks
@ins = compile_first_block( "arg = 5") @ins = compile_first_block( "arg = 5")
end end
def test_class_compiles def pest_class_compiles
assert_equal Mom::SlotLoad , @ins.class , @ins assert_equal Mom::SlotLoad , @ins.class , @ins
end end
def test_slot_is_set def pest_slot_is_set
assert @ins.left assert @ins.left
end end
def test_slots_left def pest_slots_left
assert_equal [:caller,:caller, :arguments, :arg] , @ins.left.slots assert_equal [:caller,:caller, :arguments, :arg] , @ins.left.slots
end end
end end
@ -70,12 +70,12 @@ module VoolBlocks
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
end end
def test_assigns_const def pest_assigns_const
@ins = compile_first_block( "@a = 5") @ins = compile_first_block( "@a = 5")
assert_equal Mom::SlotLoad , @ins.class , @ins assert_equal Mom::SlotLoad , @ins.class , @ins
assert_equal Mom::IntegerConstant , @ins.right.known_object.class , @ins assert_equal Mom::IntegerConstant , @ins.right.known_object.class , @ins
end end
def test_assigns_move def pest_assigns_move
@ins = compile_first_block( "@a = arg") @ins = compile_first_block( "@a = arg")
assert_equal Mom::SlotLoad , @ins.class , @ins assert_equal Mom::SlotLoad , @ins.class , @ins
assert_equal Mom::SlotDefinition , @ins.right.class , @ins assert_equal Mom::SlotDefinition , @ins.right.class , @ins