From 18994d2b4b8382b87b07bb2cfc2972dc87a3e462 Mon Sep 17 00:00:00 2001 From: Torsten Ruger Date: Thu, 28 Jun 2018 20:15:24 +0300 Subject: [PATCH] start on yield statement --- lib/vool/ruby_compiler.rb | 7 +- lib/vool/statement.rb | 1 + lib/vool/statements/method_statement.rb | 8 +- lib/vool/statements/send_statement.rb | 12 ++- lib/vool/statements/yield_statement.rb | 84 +++++++++++++++++++ .../ruby_compiler/test_yield_statement.rb | 27 ++++++ test/vool/to_mom/test_yield_statement.rb | 0 7 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 lib/vool/statements/yield_statement.rb create mode 100644 test/vool/ruby_compiler/test_yield_statement.rb create mode 100644 test/vool/to_mom/test_yield_statement.rb diff --git a/lib/vool/ruby_compiler.rb b/lib/vool/ruby_compiler.rb index 2302e570..7e698575 100644 --- a/lib/vool/ruby_compiler.rb +++ b/lib/vool/ruby_compiler.rb @@ -41,10 +41,15 @@ module Vool sendd = process(block_node.children[0]) args = process(block_node.children[1]) body = process(block_node.children[2]) - sendd.block = BlockStatement.new(args , body) + sendd.add_block BlockStatement.new(args , body) sendd end + def on_yield(node) + args = process_all(node.children) + YieldStatement.new(args) + end + def on_args(args) args.children.collect{|a| process(a)} end diff --git a/lib/vool/statement.rb b/lib/vool/statement.rb index c51760f9..e182092c 100644 --- a/lib/vool/statement.rb +++ b/lib/vool/statement.rb @@ -77,3 +77,4 @@ require_relative "statements/statements" require_relative "statements/send_statement" require_relative "statements/variables" require_relative "statements/while_statement" +require_relative "statements/yield_statement" diff --git a/lib/vool/statements/method_statement.rb b/lib/vool/statements/method_statement.rb index 6f94cb8c..b5dd2cb3 100644 --- a/lib/vool/statements/method_statement.rb +++ b/lib/vool/statements/method_statement.rb @@ -23,14 +23,20 @@ module Vool MethodStatement.new( @name , @args , @body.normalize) end - private + def has_yield? + each{|statement| return true if statement.is_a?(YieldStatement)} + return false + end def make_arg_type( ) type_hash = {} @args.each {|arg| type_hash[arg] = :Object } + type_hash[:implicit_block] = :Object if has_yield? Parfait::NamedList.type_for( type_hash ) end + private + def make_frame type_hash = {} @body.each do |node| diff --git a/lib/vool/statements/send_statement.rb b/lib/vool/statements/send_statement.rb index c917ffd4..53c2a955 100644 --- a/lib/vool/statements/send_statement.rb +++ b/lib/vool/statements/send_statement.rb @@ -17,8 +17,14 @@ module Vool @arguments ||= [] end - def block=( block ) - @block = block + def block + return nil if arguments.empty? + bl = arguments.last + bl.is_a?(BlockStatement) ? bl : nil + end + + def add_block( block ) + @arguments << block end def normalize @@ -54,6 +60,7 @@ module Vool @arguments.each do |arg| block.call(arg) end + self.block.each(block) if self.block end # lazy init this, to keep the dependency (which goes to parfait and booting) at bay @@ -65,6 +72,7 @@ module Vool # - Setting up the next message, with receiver, arguments, and (importantly) return address # - a CachedCall , or a SimpleCall, depending on wether the receiver type can be determined def to_mom( in_method ) + @parfait_block = self.block.to_mom(in_method) if self.block @receiver = SelfExpression.new(in_method.for_type) if @receiver.is_a?(SelfExpression) if(@receiver.ct_type) simple_call(in_method) diff --git a/lib/vool/statements/yield_statement.rb b/lib/vool/statements/yield_statement.rb new file mode 100644 index 00000000..2ef145dc --- /dev/null +++ b/lib/vool/statements/yield_statement.rb @@ -0,0 +1,84 @@ +module Vool + + class YieldStatement < Statement + attr_reader :arguments + + def initialize(arguments ) + @arguments = arguments + @arguments ||= [] + end + + def normalize + statements = Statements.new([]) + arguments = [] + @arguments.each_with_index do |arg , index | + normalize_arg(arg , arguments , statements) + end + if statements.empty? + return YieldStatement.new(@name, @receiver , @arguments) + else + statements << YieldStatement.new(@name, @receiver , arguments) + return statements + end + end + + def normalize_arg(arg , arguments , statements) + if arg.respond_to?(:slot_definition) and !arg.is_a?(YieldStatement) + arguments << arg + return + end + assign = LocalAssignment.new( "tmp_#{arg.object_id}".to_sym, arg) + statements << assign + arguments << LocalVariable.new(assign.name) + end + + def to_s + "#{receiver}.#{name}(#{arguments.join(',')})" + end + def each(&block) + block.call(self) + block.call(@receiver) + @arguments.each do |arg| + block.call(arg) + end + end + + # A Send breaks down to 2 steps: + # - Setting up the next message, with receiver, arguments, and (importantly) return address + # - a SimpleCall, + def to_mom( in_method ) + @parfait_block = @block.to_mom(in_method) if @block + @receiver = SelfExpression.new(in_method.for_type) if @receiver.is_a?(SelfExpression) + if(@receiver.ct_type) + simple_call(in_method) + else + raise "ERROR" + end + end + + # When used as right hand side, this tells what data to move to get the result into + # a varaible. It is (off course) the return value of the message + def slot_definition(in_method) + Mom::SlotDefinition.new(:message ,[ :return_value]) + end + + def message_setup(in_method,called_method) + setup = Mom::MessageSetup.new( called_method ) + mom_receive = @receiver.slot_definition(in_method) + arg_target = [:message , :next_message , :arguments] + args = [] + @arguments.each_with_index do |arg , index| # +1 because of type + args << Mom::SlotLoad.new( arg_target + [index + 1] , arg.slot_definition(in_method)) + end + setup << Mom::ArgumentTransfer.new( mom_receive , args ) + end + + def simple_call(in_method) + type = @receiver.ct_type + called_method = type.resolve_method(@name) + raise "No method #{@name} for #{type}" unless called_method + message_setup(in_method,called_method) << Mom::SimpleCall.new(called_method) + end + + end +end diff --git a/test/vool/ruby_compiler/test_yield_statement.rb b/test/vool/ruby_compiler/test_yield_statement.rb new file mode 100644 index 00000000..60409504 --- /dev/null +++ b/test/vool/ruby_compiler/test_yield_statement.rb @@ -0,0 +1,27 @@ +require_relative "helper" + +module Vool + class TestYieldStatement < MiniTest::Test + + def setup() + input = "def plus_one; yield(0) ; end " + @lst = RubyCompiler.compile( input ) + end + def test_method + assert_equal MethodStatement , @lst.class + end + def test_block + assert_equal YieldStatement , @lst.body.class + end + def test_block_args + assert_equal IntegerConstant , @lst.body.arguments.first.class + end + def test_method_yield? + assert_equal true , @lst.has_yield? + end + def test_method_args + Risc.machine.boot + assert_equal 2 , @lst.make_arg_type.get_length + end + end +end diff --git a/test/vool/to_mom/test_yield_statement.rb b/test/vool/to_mom/test_yield_statement.rb new file mode 100644 index 00000000..e69de29b