diff --git a/lib/mom/instruction/basic_values.rb b/lib/mom/instruction/basic_values.rb index 8b6af583..efd62ba9 100644 --- a/lib/mom/instruction/basic_values.rb +++ b/lib/mom/instruction/basic_values.rb @@ -5,6 +5,16 @@ module Mom class Constant end + class BlockConstant < Constant + attr_reader :block + def initialize(bl) + @block = bl + end + def to_parfait(compiler) + @block + end + end + class IntegerConstant < Constant attr_reader :value def initialize(value) diff --git a/lib/parfait/callable_method.rb b/lib/parfait/callable_method.rb index 2de2674a..39664842 100644 --- a/lib/parfait/callable_method.rb +++ b/lib/parfait/callable_method.rb @@ -43,7 +43,7 @@ module Parfait def add_block(bl) block = self.blocks - bl.next = block if(block) + bl.set_next(block) if(block) @blocks = bl end diff --git a/lib/risc.rb b/lib/risc.rb index d92065c2..5fe2f6ed 100644 --- a/lib/risc.rb +++ b/lib/risc.rb @@ -27,6 +27,7 @@ require_relative "risc/parfait_adapter" require_relative "risc/parfait_boot" require_relative "risc/linker" require_relative "risc/method_compiler" +require_relative "risc/block_compiler" require_relative "risc/assembler" class Fixnum diff --git a/lib/risc/block_compiler.rb b/lib/risc/block_compiler.rb new file mode 100644 index 00000000..1dd75b8f --- /dev/null +++ b/lib/risc/block_compiler.rb @@ -0,0 +1,21 @@ +module Risc + + # A BlockCompiler is much like a Mehtodcompiler, exept for blocks + # + class BlockCompiler + + attr_reader :block , :risc_instructions , :constants + + def initialize( block , method) + @method = method + @regs = [] + @block = block + name = "#{method.self_type.name}.init" + @risc_instructions = Risc.label(name, name) + @risc_instructions << Risc.label( name, "unreachable") + @current = @risc_instructions + @constants = [] + end + + end +end diff --git a/lib/risc/method_compiler.rb b/lib/risc/method_compiler.rb index 87d83af4..de2e350c 100644 --- a/lib/risc/method_compiler.rb +++ b/lib/risc/method_compiler.rb @@ -19,18 +19,19 @@ module Risc @risc_instructions << Risc.label( name, "unreachable") @current = @risc_instructions @constants = [] + @block_compilers = [] end attr_reader :method , :risc_instructions , :constants # helper method for builtin mainly # the class_name is a symbol, which is resolved to the instance_type of that class # - # return compiler_self_type with the resolved type + # return compiler_for_type with the resolved type # def self.compiler_for_class( class_name , method_name , args , frame ) raise "create_method #{class_name}.#{class_name.class}" unless class_name.is_a? Symbol clazz = Parfait.object_space.get_class_by_name! class_name - compiler_self_type( clazz.instance_type , method_name , args , frame) + compiler_for_type( clazz.instance_type , method_name , args , frame) end # create a method for the given type ( Parfait type object) @@ -38,14 +39,18 @@ module Risc # args a hash that will be converted to a type # the created method is set as the current and the given type too # return the compiler - def self.compiler_self_type( type , method_name , args , frame) + def self.compiler_for_type( type , method_name , args , frame) raise "create_method #{type.inspect} is not a Type" unless type.is_a? Parfait::Type raise "Args must be Type #{args}" unless args.is_a?(Parfait::Type) raise "create_method #{method_name}.#{method_name.class}" unless method_name.is_a? Symbol method = type.create_method( method_name , args , frame) self.new(method) end - + + def add_block_compiler(compiler) + @block_compilers << compiler + end + # convert the given mom instruction to_risc and then add it (see add_code) # continue down the instruction chain unti depleted # (adding moves the insertion point so the whole mom chain is added as a risc chain) diff --git a/lib/vool/block_statement.rb b/lib/vool/block_statement.rb index 682c50ce..b1933f93 100644 --- a/lib/vool/block_statement.rb +++ b/lib/vool/block_statement.rb @@ -14,12 +14,19 @@ module Vool # This means we do the compiler here (rather than to_mom, which is in # fact never called) def slot_definition(compiler) - @parfait_block = to_parfait(compiler) - return Mom::SlotDefinition.new(Mom::IntegerConstant.new(1) , []) + return Mom::SlotDefinition.new(Mom::BlockConstant.new(parfait_block(compiler)) , []) end + # create a block, a compiler for it, comile the bock and add the compiler(code) + # to the method compiler for further processing def to_mom( compiler ) -# raise "should not be called " + parfait_block = self.parfait_block(compiler) + block_compiler = Risc::BlockCompiler.new( parfait_block , compiler.method ) + compiler.add_block_compiler(block_compiler) + puts "BODY #{body}" + head = body.to_mom( block_compiler ) + block_compiler.add_mom(head) + block_compiler end def each(&block) @@ -31,10 +38,14 @@ module Vool BlockStatement.new( @args , @body.normalize) end - private - def to_parfait(compiler) - compiler.method.create_block( make_arg_type , make_frame) + # create the parfait block (parfait representation of the block, a Callable similar + # to CallableMethod) + def parfait_block(compiler) + return @parfait_block if @parfait_block + @parfait_block = compiler.method.create_block( make_arg_type , make_frame) end + + private def make_arg_type( ) type_hash = {} @args.each {|arg| type_hash[arg] = :Object } diff --git a/lib/vool/method_statement.rb b/lib/vool/method_statement.rb index ac9acaf6..616f6ef8 100644 --- a/lib/vool/method_statement.rb +++ b/lib/vool/method_statement.rb @@ -11,7 +11,11 @@ module Vool def to_mom(clazz) @clazz = clazz || raise( "no class in #{self}") method = @clazz.add_method_for(name , make_arg_type , make_frame , body ) - method.compiler_for(clazz.instance_type) + compiler = method.compiler_for(clazz.instance_type) + each do |node| ## TODO: must account for nested blocks (someday) + node.to_mom(compiler) if node.is_a?(BlockStatement) + end + compiler end def each(&block) diff --git a/test/support/compiling.rb b/test/support/compiling.rb index 7d66aabd..727177cf 100644 --- a/test/support/compiling.rb +++ b/test/support/compiling.rb @@ -37,7 +37,10 @@ module MomCompile assert_equal Risc::MethodCompiler , compiler.class @method.source.to_mom( compiler ) end - + def compile_first_block( block_input ) + mom = compile_first_method( "main_local = 5 ; self.main{|val| #{block_input}}") + mom.next(4) # ignore local assign (1) and call (3) + end def compile_mom(input) Risc.boot! RubyX::RubyXCompiler.new(input).ruby_to_mom diff --git a/test/vool/blocks/helper.rb b/test/vool/blocks/helper.rb new file mode 100644 index 00000000..e69de29b diff --git a/test/vool/blocks/test_assign.rb b/test/vool/blocks/test_assign.rb new file mode 100644 index 00000000..2392de6f --- /dev/null +++ b/test/vool/blocks/test_assign.rb @@ -0,0 +1,91 @@ +require_relative "../helper" + +module VoolBlocks + class TestAssignMom < MiniTest::Test + include MomCompile + + def setup + Parfait.boot! + Risc::Builtin.boot_functions + @ins = compile_first_block( "local = 5") + end + + def test_class_compiles + assert_equal Mom::SlotLoad , @ins.class , @ins + end + def pest_slot_is_set + assert @ins.left + end + def pest_slot_starts_at_message + assert_equal :message , @ins.left.known_object + end + def pest_slot_gets_self + assert_equal :frame , @ins.left.slots[0] + end + def pest_slot_assigns_to_local + assert_equal :main_local , @ins.left.slots[-1] + end + def pest_slot_assigns_something + assert @ins.right + end + def pest_slot_assigns_int + assert_equal Mom::IntegerConstant , @ins.right.known_object.class + end + end + + #otherwise as above, but assigning instance, so should get a SlotLoad + class TestAssignMomInstanceToLocal < MiniTest::Test + include MomCompile + def setup + Parfait.boot! + @ins = compile_first_method( "@a = 5 ; local = @a") + end + def pest_class_compiles + assert_equal Mom::SlotLoad , @ins.next.class , @ins + end + end + + #compiling to an argument should result in different second parameter in the slot array + class TestAssignToArg < MiniTest::Test + include MomCompile + + def setup + Parfait.boot! + @ins = compile_first_method( "arg = 5") + end + + def pest_class_compiles + assert_equal Mom::SlotLoad , @ins.class , @ins + end + def pest_slot_is_set + assert @ins.left + end + def pest_slot_starts_at_message + assert_equal :message , @ins.left.known_object + end + def pest_slot_gets_self + assert_equal :arguments , @ins.left.slots[0] + end + def pest_slot_assigns_to_local + assert_equal :arg , @ins.left.slots[-1] + end + end + + class TestAssignMomToInstance < MiniTest::Test + include MomCompile + def setup + Parfait.boot! + end + def pest_assigns_const + @ins = compile_first_method( "@a = 5") + assert_equal Mom::SlotLoad , @ins.class , @ins + assert_equal Mom::IntegerConstant , @ins.right.known_object.class , @ins + end + def pest_assigns_move + @ins = compile_first_method( "@a = arg") + assert_equal Mom::SlotLoad , @ins.class , @ins + assert_equal Mom::SlotDefinition , @ins.right.class , @ins + end + end + +end