diff --git a/lib/mom/instruction.rb b/lib/mom/instruction.rb index 8608e1bb..5b5ed6ee 100644 --- a/lib/mom/instruction.rb +++ b/lib/mom/instruction.rb @@ -16,6 +16,9 @@ module Mom def initialize(name) @name = name end + def to_risc(compiler) + Risc::Label.new(self,name) + end end end diff --git a/lib/mom/message_setup.rb b/lib/mom/message_setup.rb index d779392b..a03d6423 100644 --- a/lib/mom/message_setup.rb +++ b/lib/mom/message_setup.rb @@ -16,6 +16,11 @@ module Mom def initialize(method) @method = method end + + def to_risc(compiler) + Risc::Label.new(self,method.name) + end + end diff --git a/lib/mom/not_same_check.rb b/lib/mom/not_same_check.rb index 43efb855..60081bb4 100644 --- a/lib/mom/not_same_check.rb +++ b/lib/mom/not_same_check.rb @@ -12,5 +12,9 @@ module Mom def initialize(left, right) @left , @right = left , right end + + def to_risc(compiler) + Risc::Label.new(self,"nosense") + end end end diff --git a/lib/mom/slot_load.rb b/lib/mom/slot_load.rb index 1bcdd9db..fa2d0423 100644 --- a/lib/mom/slot_load.rb +++ b/lib/mom/slot_load.rb @@ -33,6 +33,11 @@ module Mom raise "left not SlotDefinition, #{left}" unless left.is_a? SlotDefinition # raise "right not Mom, #{right.to_rxf}" unless right.class.name.include?("Mom") end + + def to_risc(compiler) + Risc::Label.new(self,"nosense") + end + end # A SlotConstant moves a constant into a known Slot. @@ -52,6 +57,9 @@ module Mom #SlotMove is a SlotLoad where the right side is a slot, just like the left. class SlotMove < SlotLoad + def to_risc(compiler) + + end end class SlotDefinition diff --git a/lib/mom/statement.rb b/lib/mom/statement.rb index 33d8a7af..9b7cc8d1 100644 --- a/lib/mom/statement.rb +++ b/lib/mom/statement.rb @@ -1,5 +1,6 @@ module Mom class Statement + include Common::List # flattening will change the structure from a tree to a linked list (and use # nekst to do so) def flatten(options = {}) @@ -18,6 +19,12 @@ module Mom flat end + def initialize(arr) + super(arr) + arr.each {|s| + raise "Not a Statement #{s}" unless s.is_a?( Statement) or s.is_a?(Instruction) + } + end end end diff --git a/lib/mom/truth_check.rb b/lib/mom/truth_check.rb index b4ce6d78..d167303a 100644 --- a/lib/mom/truth_check.rb +++ b/lib/mom/truth_check.rb @@ -18,5 +18,10 @@ module Mom def initialize(condition) @condition = condition end + + def to_risc(compiler) + Risc::Label.new(self,"nosense") + end + end end diff --git a/lib/mom/while_statement.rb b/lib/mom/while_statement.rb index 531f3a97..4c801cd9 100644 --- a/lib/mom/while_statement.rb +++ b/lib/mom/while_statement.rb @@ -11,13 +11,13 @@ module Mom end def flatten(options = {}) - cond_label = Label.new( "cond_label_#{object_id}") - head = cond_label - head.append hoisted.flatten merge_label = Label.new( "merge_label_#{object_id}") - head.append condition.flatten( true_label: cond_label , false_label: merge_label) - head.append merge_label - head + cond_label = Label.new( "cond_label_#{object_id}") + @nekst = cond_label + @nekst.append(hoisted.flatten) if hoisted + @nekst.append condition.flatten( true_label: cond_label , false_label: merge_label) + @nekst.append merge_label + @nekst end end diff --git a/lib/risc/instruction.rb b/lib/risc/instruction.rb index 7777d9df..26d7f8cd 100644 --- a/lib/risc/instruction.rb +++ b/lib/risc/instruction.rb @@ -18,7 +18,7 @@ module Risc @source = source @next = nekst return unless source - raise "Source must be string or ast node, not #{source.class}" unless source.is_a?(String) or source.is_a?(Vm::Code) + raise "Source must be string or ast node, not #{source.class}" unless source.is_a?(String) or source.is_a?(Mom::Instruction) end attr_reader :source diff --git a/lib/vool/statements/method_statement.rb b/lib/vool/statements/method_statement.rb index f18e8926..59416816 100644 --- a/lib/vool/statements/method_statement.rb +++ b/lib/vool/statements/method_statement.rb @@ -33,14 +33,10 @@ module Vool locals_type = make_locals method = Parfait::VoolMethod.new(name , args_type , locals_type , body ) @clazz.add_method( method ) - end - - def compile_methods(clazz , methods) - methods.each do |method| - code = Passes::MethodCompiler.new(method).get_code - typed_method = method.create_parfait_method(clazz.instance_type) - Risc::MethodCompiler.new( typed_method ).init_method.process( code ) - end + typed_method = method.create_parfait_method(clazz.instance_type) + compiler = Risc::MethodCompiler.new( typed_method ).init_method + head = @body.to_mom( method ).flatten + head.to_risc(compiler) end private diff --git a/test/mom/to_risc/helper.rb b/test/mom/to_risc/helper.rb new file mode 100644 index 00000000..958c6e9b --- /dev/null +++ b/test/mom/to_risc/helper.rb @@ -0,0 +1,81 @@ +require_relative '../helper' + +module Risc + module SpaceHack + # test hack to in place change object type + def add_space_field(name,type) + class_type = Parfait.object_space.get_class_by_name(:Space).instance_type + class_type.send(:private_add_instance_variable, name , type) + end + end + module ExpressionHelper + include SpaceHack + + def check + Risc.machine.boot unless Risc.machine.booted + compiler = Vm::MethodCompiler.new Parfait.object_space.get_main + code = Vm.ast_to_code @input + assert code.to_s , @input + produced = compiler.process( code ) + assert @output , "No output given" + assert_equal produced.class , @output , "Wrong class" + produced + end + + end + + module Statements + include AST::Sexp + include CleanCompile + include SpaceHack + + def setup + Risc.machine.boot # force boot to reset main + end + + def preamble + [Label, SlotToReg , LoadConstant, RegToSlot, LoadConstant,RegToSlot, LoadConstant, SlotToReg, SlotToReg ] + end + def postamble + [ Label, FunctionReturn] + end + def check_nil + assert @expect , "No output given" + + Vool::VoolCompiler.ruby_to_vool "class Space; def main(arg);#{@input};end;end" + + produced = Parfait.object_space.get_main.instructions + compare_instructions produced , @expect + end + def check_return + was = check_nil + raise was if was + Parfait.object_space.get_main.instructions + end + + def compare_instructions( instruction , expect ) + index = 0 + all = instruction.to_arr + full_expect = preamble + expect + postamble + full_expect = expect + begin + should = full_expect[index] + return "No instruction at #{index}" unless should + return "Expected at #{index+1}\n#{should(all)}" unless instruction.class == should + index += 1 + instruction = instruction.next + end while( instruction ) + nil + end + def should( all ) + #preamble.each {all.shift} + #postamble.each {all.pop} + str = all.to_s.gsub("Risc::","") + ret = "" + str.split(",").each_slice(6).each do |line| + ret += " " + line.join(",") + " ,\n" + end + ret + end + end +end diff --git a/test/mom/to_risc/test_assignment.rb b/test/mom/to_risc/test_assignment.rb new file mode 100644 index 00000000..ea28dad7 --- /dev/null +++ b/test/mom/to_risc/test_assignment.rb @@ -0,0 +1,112 @@ + +require_relative 'helper' + +module Risc + class TestAssignStatement < MiniTest::Test + include Statements + + def test_assign_op + Parfait.object_space.get_main.add_local(:r , :Integer) + + @input = "r = 10.mod4" + + @expect = [Label, LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn] + assert_nil msg = check_nil , msg + end + + def pest_assign_ivar_notpresent + @input =s(:statements, s(:i_assignment, s(:ivar, :r), s(:int, 5))) + @expect = [] + assert_raises{ check_nil } + end + + def pest_assign_ivar + add_space_field(:r , :Integer) + + @input =s(:statements, s(:i_assignment, s(:ivar, :r), s(:int, 5))) + + @expect = [Label, LoadConstant, SlotToReg, RegToSlot, LoadConstant, SlotToReg , + RegToSlot, Label, FunctionReturn] + assert_nil msg = check_nil , msg + end + + def pest_assign_local_assign + Parfait.object_space.get_main.add_local(:r , :Integer) + + @input = s(:statements, s(:l_assignment, s(:local, :r), s(:int, 5))) + + @expect = [Label, LoadConstant, SlotToReg, RegToSlot, LoadConstant, SlotToReg , + RegToSlot, Label, FunctionReturn] + assert_nil msg = check_nil , msg + end + + def pest_assign_call + Parfait.object_space.get_main.add_local(:r , :Object) + @input = s(:statements, s(:l_assignment, s(:local, :r), s(:call, :main, s(:arguments)))) + @expect = [Label, SlotToReg, SlotToReg, RegToSlot, LoadConstant, RegToSlot , + LoadConstant, SlotToReg, RegToSlot, LoadConstant, RegToSlot, RiscTransfer , + FunctionCall, Label, RiscTransfer, SlotToReg, SlotToReg, SlotToReg , + RegToSlot, LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn] + assert_nil msg = check_nil , msg + end + + def pest_named_list_get + Parfait.object_space.get_main.add_local(:r , :Integer) + @input = s(:statements, s(:l_assignment, s(:local, :r), s(:int, 5)), s(:return, s(:local, :r))) + @expect = [Label, LoadConstant, SlotToReg, RegToSlot, SlotToReg, SlotToReg , + RegToSlot, LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn] + was = check_return + get = was.next(5) + assert_equal SlotToReg , get.class + assert_equal 1 + 1, get.index , "Get to named_list index must be offset, not #{get.index}" + end + + def pest_assign_local_int + Parfait.object_space.get_main.add_local(:r , :Integer) + @input = s(:statements, s(:l_assignment, s(:local, :r), s(:int, 5)) ) + @expect = [Label, LoadConstant, SlotToReg, RegToSlot, LoadConstant, SlotToReg , + RegToSlot, Label, FunctionReturn] + was = check_return + set = was.next(3) + assert_equal RegToSlot , set.class + assert_equal 1 + 1, set.index , "Set to named_list index must be offset, not #{set.index}" + end + + def pest_misassign_local + Parfait.object_space.get_main.add_local(:r , :Integer) + @input = s(:statements, s(:l_assignment, s(:local, :r), s(:string, "5")) ) + @expect = [Label, LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn] + assert_raises {check } + end + + def pest_assign_arg + Parfait.object_space.get_main.add_argument(:blar , :Integer) + @input = s(:statements, s(:a_assignment, s(:arg, :blar), s(:int, 5))) + @expect = [Label, LoadConstant, SlotToReg, RegToSlot, LoadConstant, SlotToReg , + RegToSlot, Label, FunctionReturn] + was = check_return + set = was.next(3) + assert_equal RegToSlot , set.class + assert_equal 1 + 1, set.index , "Set to args index must be offset, not #{set.index}" + end + + def pest_misassign_arg + Parfait.object_space.get_main.add_argument(:blar , :Integer) + @input = s(:statements, s(:a_assignment, s(:arg, :blar), s(:string, "5"))) + @expect = [Label, LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn] + assert_raises {check } + end + + def pest_arg_get + # have to define bar externally, just because redefining main. Otherwise that would be automatic + Parfait.object_space.get_main.add_argument(:balr , :Integer) + @input = s(:statements, s(:return, s(:arg, :balr))) + @expect = [Label, SlotToReg, SlotToReg, RegToSlot, LoadConstant, SlotToReg , + RegToSlot, Label, FunctionReturn] + was = check_return + get = was.next(2) + assert_equal SlotToReg , get.class + assert_equal 1 + 1, get.index , "Get to args index must be offset, not #{get.index}" + end + end +end diff --git a/test/vool/compilers/test_class_compiler.rb b/test/vool/compilers/test_class_compiler.rb index 7ff2bbb2..a0ce7486 100644 --- a/test/vool/compilers/test_class_compiler.rb +++ b/test/vool/compilers/test_class_compiler.rb @@ -16,12 +16,12 @@ module Vool end def test_compile_class_one - itest = compile_in_test "def meth; @ivar; end" + itest = compile_in_test "def meth; @ivar = 5; end" assert itest.instance_type.names.include?(:ivar) , itest.instance_type.names.inspect end def test_compile_class_two - itest = compile_in_test "def meth; @ivar; end;def meth2(arg); @trivar = 5; end" + itest = compile_in_test "def meth; @ivar = 5; end;def meth2(arg); @trivar = 5; end" assert itest.instance_type.names.include?(:trivar) , itest.instance_type.names.inspect end @@ -33,7 +33,7 @@ module Vool end def test_class_body_is_scope - clazz = VoolCompiler.ruby_to_vool in_Test("def meth; @ivar ;end") + clazz = VoolCompiler.ruby_to_vool in_Test("def meth; @ivar = 5 ;end") assert_equal ScopeStatement , clazz.body.class end diff --git a/test/vool/compilers/test_method_compiler.rb b/test/vool/compilers/test_method_compiler.rb index d732bbd4..efed063b 100644 --- a/test/vool/compilers/test_method_compiler.rb +++ b/test/vool/compilers/test_method_compiler.rb @@ -9,7 +9,7 @@ module Vool end def create_method - VoolCompiler.ruby_to_vool in_Test("def meth; @ivar ;end") + VoolCompiler.ruby_to_vool in_Test("def meth; @ivar = 5;end") test = Parfait.object_space.get_class_by_name(:Test) test.get_method(:meth) end @@ -17,7 +17,7 @@ module Vool def test_method_has_source method = create_method assert_equal ScopeStatement , method.source.class - assert_equal InstanceVariable , method.source.statements.first.class + assert_equal IvarAssignment , method.source.statements.first.class end def test_method_has_no_locals @@ -36,24 +36,23 @@ module Vool assert_equal Parfait::VoolMethod , method.class end - def test_creates_method_statement_in_class - clazz = VoolCompiler.ruby_to_vool in_Test("def meth; @ivar ;end") + clazz = VoolCompiler.ruby_to_vool in_Test("def meth; @ivar = 5 ;end") assert_equal MethodStatement , clazz.body.statements.first.class end def test_parfait_class_creation - clazz = VoolCompiler.ruby_to_vool in_Test("def meth; @ivar ;end") + clazz = VoolCompiler.ruby_to_vool in_Test("def meth; @ivar = 5;end") assert_equal Parfait::Class , clazz.body.statements.first.clazz.class end def test_method_statement_has_class - clazz = VoolCompiler.ruby_to_vool in_Test("def meth; @ivar ;end") + clazz = VoolCompiler.ruby_to_vool in_Test("def meth; @ivar = 5;end") assert clazz.body.statements.first.clazz end def test_method_statement_has_class_in_main - clazz = VoolCompiler.ruby_to_vool as_main("def meth; @ivar ;end") + clazz = VoolCompiler.ruby_to_vool as_main("def meth; @ivar = 5;end") assert clazz.body.statements.first.clazz end