Torsten Rüger
d1f8733623
Simple is really the descriptive name for the layer Sure, it is "virtual" but that is not as important as the fact that it is simple (or simplified) Also objct (based really) is better, since orientated implies it is a little like that, but only orientated, not really it. Sol only has objects, nothing else Just cause i was renaming anyway
64 lines
2.9 KiB
Ruby
64 lines
2.9 KiB
Ruby
module Sol
|
|
|
|
# A Yield is a lot like a Send, which is why they share the base class CallStatement
|
|
# That means it has a receiver (self), arguments and an (implicitly assigned) name
|
|
#
|
|
# On the ruby side, normalisation works pretty much the same too.
|
|
#
|
|
# On the way down to SlotMachine, small differences become abvious, as the block that is
|
|
# yielded to is an argument. Whereas in a send it is either statically known
|
|
# or resolved and cached. Here it is dynamic, but sort of known dynamic.
|
|
# All we do before calling it is check that it is the right type.
|
|
class YieldStatement < CallStatement
|
|
|
|
# A Yield breaks down to 2 steps:
|
|
# - Setting up the next message, with receiver, arguments, and (importantly) return address
|
|
# - a SimpleCall,
|
|
def to_slot( compiler )
|
|
@parfait_block = @block.to_slot(compiler) if @block
|
|
@receiver = SelfExpression.new(compiler.receiver_type) if @receiver.is_a?(SelfExpression)
|
|
yield_call(compiler)
|
|
end
|
|
|
|
# this breaks into two parts:
|
|
# - check the calling method and break to a (not implemented) dynamic version
|
|
# - call the block, that is the last argument of the method
|
|
def yield_call(compiler)
|
|
method_check(compiler) << yield_arg_block(compiler)
|
|
end
|
|
|
|
# check that the calling method is the method that the block was created in.
|
|
# In that case variable resolution is reasy and we can prceed to yield
|
|
# Note: the else case is not implemented (ie passing lambdas around)
|
|
# this needs run-time variable resolution, which is just not done.
|
|
# we brace ourselves with the check, and exit (later raise) if . . .
|
|
def method_check(compiler)
|
|
ok_label = SlotMachine::Label.new(self,"method_ok_#{self.object_id}")
|
|
compile_method = SlotMachine::SlotDefinition.new( compiler.get_method , [])
|
|
runtime_method = SlotMachine::SlotDefinition.new( :message , [ :method] )
|
|
check = SlotMachine::NotSameCheck.new(compile_method , runtime_method, ok_label)
|
|
# TODO? Maybe create slot instructions for this
|
|
#builder = compiler.builder("yield")
|
|
#Risc::Macro.exit_sequence(builder)
|
|
#check << builder.built
|
|
check << ok_label
|
|
end
|
|
|
|
# to call the block (that we know now to be the last arg),
|
|
# we do a message setup, arg transfer and the a arg_yield (which is similar to dynamic_call)
|
|
def yield_arg_block(compiler)
|
|
arg_index = compiler.get_method.arguments_type.get_length - 1
|
|
setup = SlotMachine::MessageSetup.new( arg_index )
|
|
slot_receive = @receiver.to_slot_definition(compiler)
|
|
arg_target = [:message , :next_message ]
|
|
args = []
|
|
@arguments.each_with_index do |arg , index| # +1 because of type
|
|
args << SlotMachine::SlotLoad.new(self, arg_target + ["arg#{index+1}".to_sym] , arg.to_slot_definition(compiler))
|
|
end
|
|
setup << SlotMachine::ArgumentTransfer.new( self , slot_receive , args )
|
|
setup << SlotMachine::BlockYield.new( self , arg_index )
|
|
end
|
|
|
|
end
|
|
end
|