Change Mom to SlotMachine

rather large commit, but essentially a simple rename
Rationale in docs and blogs
This commit is contained in:
2019-10-03 20:55:41 +03:00
parent fd8a3e9cc5
commit c43436f35a
170 changed files with 481 additions and 480 deletions

View File

@ -12,7 +12,7 @@ Possibly later other languages can compile to this level and use rx-file as code
Vool is a layer with concrete syntax tree, just like the ruby layer above.
Vool is just simplified, without fluff, see below.
The next layer down is the Mom, Minimal object Machine, which uses an instruction list.
The next layer down is the SlotMachine, Minimal object Machine, which uses an instruction list.
The nodes of the syntax tree are all the things one would expect from a language,
if statements and the like. There is no context yet, and actual objects,
@ -33,4 +33,4 @@ existence of until, which really means if not. Other examples, some more impactf
The compilation process ends up creating (parfait) objects to represent
things like classes, types and constants. This is done in this layer,
on the way down to MOM (ie not during init)
on the way down to SlotMachine (ie not during init)

View File

@ -30,13 +30,13 @@ module Vool
# When the right hand side is a CallStatement, it must be compiled, before the assign
# is executed
#
# Derived classes do not implement to_mom, only slot_position
def to_mom(compiler)
to = Mom::SlotDefinition.new(:message , self.slot_position(compiler))
from = @value.to_slot(compiler)
assign = Mom::SlotLoad.new(self,to,from)
# Derived classes do not implement to_slot, only slot_position
def to_slot(compiler)
to = SlotMachine::SlotDefinition.new(:message , self.slot_position(compiler))
from = @value.to_slot_definition(compiler)
assign = SlotMachine::SlotLoad.new(self,to,from)
return assign unless @value.is_a?(CallStatement)
@value.to_mom(compiler) << assign
@value.to_slot(compiler) << assign
end
end
end

View File

@ -9,8 +9,8 @@ module Vool
def initialize(value)
@value = value
end
def to_slot(_)
return Mom::SlotDefinition.new(Mom::IntegerConstant.new(@value) , [])
def to_slot_definition(_)
return SlotMachine::SlotDefinition.new(SlotMachine::IntegerConstant.new(@value) , [])
end
def ct_type
Parfait.object_space.get_type_by_class_name(:Integer)
@ -37,8 +37,8 @@ module Vool
def ct_type
Parfait.object_space.get_type_by_class_name(:True)
end
def to_slot(_)
return Mom::SlotDefinition.new(Parfait.object_space.true_object , [])
def to_slot_definition(_)
return SlotMachine::SlotDefinition.new(Parfait.object_space.true_object , [])
end
def to_s(depth = 0)
"true"
@ -49,8 +49,8 @@ module Vool
def ct_type
Parfait.object_space.get_type_by_class_name(:False)
end
def to_slot(_)
return Mom::SlotDefinition.new(Parfait.object_space.false_object , [])
def to_slot_definition(_)
return SlotMachine::SlotDefinition.new(Parfait.object_space.false_object , [])
end
def to_s(depth = 0)
"false"
@ -61,8 +61,8 @@ module Vool
def ct_type
Parfait.object_space.get_type_by_class_name(:Nil)
end
def to_slot(_)
return Mom::SlotDefinition.new(Parfait.object_space.nil_object , [])
def to_slot_definition(_)
return SlotMachine::SlotDefinition.new(Parfait.object_space.nil_object , [])
end
def to_s(depth = 0)
"nil"
@ -75,9 +75,9 @@ module Vool
def initialize(type = nil)
@my_type = type
end
def to_slot(compiler)
def to_slot_definition(compiler)
@my_type = compiler.receiver_type
Mom::SlotDefinition.new(:message , [:receiver])
SlotMachine::SlotDefinition.new(:message , [:receiver])
end
def ct_type
@my_type
@ -91,8 +91,8 @@ module Vool
def initialize(value)
@value = value
end
def to_slot(_)
return Mom::SlotDefinition.new(Mom::StringConstant.new(@value),[])
def to_slot_definition(_)
return SlotMachine::SlotDefinition.new(SlotMachine::StringConstant.new(@value),[])
end
def ct_type
Parfait.object_space.get_type_by_class_name(:Word)

View File

@ -10,8 +10,8 @@ module Vool
# 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 to_slot(_)
Mom::SlotDefinition.new(:message ,[ :return_value])
def to_slot_definition(_)
SlotMachine::SlotDefinition.new(:message ,[ :return_value])
end
def to_s(depth = 0)

View File

@ -42,21 +42,21 @@ module Vool
# Other statements are not yet allowed (baring in mind that attribute
# accessors are transformed to methods in the ruby layer )
#
# As there is no class equivalnet in code, a MomCollection is returned,
# which is just a list of Mom::MethodCompilers
# As there is no class equivalnet in code, a SlotCollection is returned,
# which is just a list of SlotMachine::MethodCompilers
# The compilers help to transform the code further, into Risc next
def to_mom( _ )
def to_slot( _ )
method_compilers = body.statements.collect do |node|
case node
when MethodExpression
node.to_mom(@clazz)
node.to_slot(@clazz)
when ClassMethodExpression
node.to_mom(@clazz.single_class)
node.to_slot(@clazz.single_class)
else
raise "Only methods for now #{node.class}:#{node}"
end
end
Mom::MomCollection.new(method_compilers)
SlotMachine::SlotCollection.new(method_compilers)
end
# goes through the code looking for instance variables and their assignments.

View File

@ -17,7 +17,7 @@ module Vool
vool_m
end
def to_mom(clazz)
def to_slot(clazz)
raise "not singleton #{clazz.class}" unless clazz.class == Parfait::SingletonClass
raise( "no class in #{self}") unless clazz
method = clazz.get_instance_method(name )

View File

@ -10,29 +10,29 @@ module Vool
@if_false = if_false
end
def to_mom( compiler )
true_label = Mom::Label.new( self , "true_label_#{object_id.to_s(16)}")
false_label = Mom::Label.new( self , "false_label_#{object_id.to_s(16)}")
merge_label = Mom::Label.new( self , "merge_label_#{object_id.to_s(16)}")
def to_slot( compiler )
true_label = SlotMachine::Label.new( self , "true_label_#{object_id.to_s(16)}")
false_label = SlotMachine::Label.new( self , "false_label_#{object_id.to_s(16)}")
merge_label = SlotMachine::Label.new( self , "merge_label_#{object_id.to_s(16)}")
if @condition.is_a?(CallStatement)
head = @condition.to_mom(compiler)
head = @condition.to_slot(compiler)
head << check_slot(compiler , false_label)
else
head = check_slot(compiler , false_label)
end
head << true_label
head << if_true.to_mom(compiler) if @if_true
head << Mom::Jump.new(merge_label) if @if_false
head << if_true.to_slot(compiler) if @if_true
head << SlotMachine::Jump.new(merge_label) if @if_false
head << false_label
head << if_false.to_mom(compiler) if @if_false
head << if_false.to_slot(compiler) if @if_false
head << merge_label if @if_false
head
end
# create the slot lazily, so to_mom gets called first
# create the slot lazily, so to_slot gets called first
def check_slot(compiler , false_label)
Mom::TruthCheck.new(@condition.to_slot(compiler) , false_label)
SlotMachine::TruthCheck.new(@condition.to_slot_definition(compiler) , false_label)
end
def each(&block)

View File

@ -11,19 +11,19 @@ module Vool
# because of normalization (of send), slot_definition is called first,
# to assign the block to a variable.
#
# This means we do the compiler here (rather than to_mom, which is in
# This means we do the compiler here (rather than to_slot, which is in
# fact never called)
def to_slot(compiler)
def to_slot_definition(compiler)
compile(compiler) unless @parfait_block
return Mom::SlotDefinition.new(Mom::LambdaConstant.new(parfait_block(compiler)) , [])
return SlotMachine::SlotDefinition.new(SlotMachine::LambdaConstant.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 compile( compiler )
parfait_block = self.parfait_block(compiler)
block_compiler = Mom::BlockCompiler.new( parfait_block , compiler.get_method )
head = body.to_mom( block_compiler )
block_compiler = SlotMachine::BlockCompiler.new( parfait_block , compiler.get_method )
head = body.to_slot( block_compiler )
block_compiler.add_code(head)
compiler.add_method_compiler(block_compiler)
nil

View File

@ -6,16 +6,16 @@ module Vool
super(name , SelfExpression.new , arguments)
end
def to_mom(compiler)
def to_slot(compiler)
parts = name.to_s.split("_")
class_name = "Mom::#{parts.collect{|s| s.capitalize}.join}"
class_name = "SlotMachine::#{parts.collect{|s| s.capitalize}.join}"
eval(class_name).new( self , *arguments)
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 to_slot(_)
Mom::SlotDefinition.new(:message ,[ :return_value])
def to_slot_definition(_)
SlotMachine::SlotDefinition.new(:message ,[ :return_value])
end
def to_s(depth = 0)

View File

@ -22,8 +22,8 @@ module Vool
vool_m
end
# Creates the Mom::MethodCompiler that will do the next step
def to_mom(clazz)
# Creates the SlotMachine::MethodCompiler that will do the next step
def to_slot(clazz)
raise( "no class in #{self}") unless clazz
method = clazz.get_instance_method(@name)
raise( "no method in #{@name} in #{clazz.name}") unless method

View File

@ -16,14 +16,14 @@ module Vool
# To return form a method in mom instructions we only need to do two things:
# - store the given return value, this is a SlotMove
# - activate return sequence (reinstantiate old message and jump to return address)
def to_mom( compiler )
def to_slot( compiler )
if @return_value.is_a?(CallStatement)
ret = @return_value.to_mom(compiler)
ret = @return_value.to_slot(compiler)
ret << slot_load(compiler)
else
ret = slot_load(compiler)
end
ret << Mom::ReturnJump.new(self , compiler.return_label )
ret << SlotMachine::ReturnJump.new(self , compiler.return_label )
end
def to_s(depth = 0)
@ -31,8 +31,8 @@ module Vool
end
def slot_load(compiler)
Mom::SlotLoad.new( self , [:message , :return_value] ,
@return_value.to_slot(compiler) )
SlotMachine::SlotLoad.new( self , [:message , :return_value] ,
@return_value.to_slot_definition(compiler) )
end
end
end

View File

@ -28,7 +28,7 @@ module Vool
# lazy init this, to keep the dependency (which goes to parfait and booting) at bay
def dynamic_call
@dynamic ||= Mom::DynamicCall.new()
@dynamic ||= SlotMachine::DynamicCall.new()
end
# A Send breaks down to 2 steps:
@ -40,7 +40,7 @@ module Vool
# So we check, and if find, add the source (vool_method) to the class and start
# compiling the vool for the receiver_type
#
def to_mom( compiler )
def to_slot( compiler )
@receiver = SelfExpression.new(compiler.receiver_type) if @receiver.is_a?(SelfExpression)
if(@receiver.ct_type)
method = @receiver.ct_type.get_method(@name)
@ -66,18 +66,18 @@ module Vool
end
def message_setup(compiler,called_method)
setup = Mom::MessageSetup.new( called_method )
mom_receive = @receiver.to_slot(compiler)
setup = SlotMachine::MessageSetup.new( called_method )
mom_receive = @receiver.to_slot_definition(compiler)
arg_target = [:message , :next_message ]
args = []
@arguments.each_with_index do |arg , index| # +1 because of type
args << Mom::SlotLoad.new(self, arg_target + ["arg#{index+1}".to_sym] , arg.to_slot(compiler))
args << SlotMachine::SlotLoad.new(self, arg_target + ["arg#{index+1}".to_sym] , arg.to_slot_definition(compiler))
end
setup << Mom::ArgumentTransfer.new(self, mom_receive , args )
setup << SlotMachine::ArgumentTransfer.new(self, mom_receive , args )
end
def simple_call(compiler, called_method)
message_setup(compiler,called_method) << Mom::SimpleCall.new(called_method)
message_setup(compiler,called_method) << SlotMachine::SimpleCall.new(called_method)
end
# this breaks cleanly into two parts:
@ -91,10 +91,10 @@ module Vool
# if not, change and find method for the type (simple_call to resolve_method)
# conceptually easy in ruby, but we have to compile that "easy" ruby
def cache_check(compiler)
ok = Mom::Label.new(self,"cache_ok_#{self.object_id}")
ok = SlotMachine::Label.new(self,"cache_ok_#{self.object_id}")
check = build_condition(ok, compiler) # if cached_type != current_type
check << Mom::SlotLoad.new(self,[dynamic_call.cache_entry, :cached_type] , receiver_type_definition(compiler))
check << Mom::ResolveMethod.new(self, @name , dynamic_call.cache_entry )
check << SlotMachine::SlotLoad.new(self,[dynamic_call.cache_entry, :cached_type] , receiver_type_definition(compiler))
check << SlotMachine::ResolveMethod.new(self, @name , dynamic_call.cache_entry )
check << ok
end
@ -111,14 +111,14 @@ module Vool
private
def receiver_type_definition(compiler)
defi = @receiver.to_slot(compiler)
defi = @receiver.to_slot_definition(compiler)
defi.slots << :type
defi
end
def build_condition(ok_label, compiler)
cached_type = Mom::SlotDefinition.new(dynamic_call.cache_entry , [:cached_type])
cached_type = SlotMachine::SlotDefinition.new(dynamic_call.cache_entry , [:cached_type])
current_type = receiver_type_definition(compiler)
Mom::NotSameCheck.new(cached_type , current_type, ok_label)
SlotMachine::NotSameCheck.new(cached_type , current_type, ok_label)
end
end
end

View File

@ -11,12 +11,12 @@
# data. Statements represent code whereas Expressions resolve to data.
# (in ruby there are no pure statements, everthing resolves to data)
#
# Vool resolves to Mom in the next step down. But it also the place where we create
# Vool resolves to SlotMachine in the next step down. But it also the place where we create
# Parfait representations for the main oo players, ie classes and methods.
# The protocol is thus two stage:
# - first to_parfait with implicit side-effects of creating parfait objects that
# are added to the Parfait object_space
# - second to_mom , which will return a mom version of the statement. This may be code
# - second to_slot , which will return a mom version of the statement. This may be code
# or a compiler (for methods), or compiler collection (for classes)
#
module Vool
@ -43,7 +43,7 @@ module Vool
#
# The argument given most often is a compiler
# The default implementation (this) is to raise an error
def to_mom( _ )
def to_slot( _ )
raise "Not implemented for #{self}"
end

View File

@ -61,17 +61,17 @@ module Vool
def to_parfait
@statements.collect{|s| s.to_parfait}
end
# to_mom all the statements. Append subsequent ones to the first, and return the
# to_slot all the statements. Append subsequent ones to the first, and return the
# first.
#
# For ClassStatements this creates and returns a MomCompiler
# For ClassStatements this creates and returns a SlotMachineCompiler
#
def to_mom( compiler )
def to_slot( compiler )
raise "Empty list ? #{statements.length}" if empty?
stats = @statements.dup
first = stats.shift.to_mom(compiler)
first = stats.shift.to_slot(compiler)
while( nekst = stats.shift )
next_mom = nekst.to_mom(compiler)
next_mom = nekst.to_slot(compiler)
first.append next_mom
end
first

View File

@ -10,9 +10,9 @@ module Vool
class LocalVariable < Expression
include Named
def to_slot(compiler)
def to_slot_definition(compiler)
slot_def = compiler.slot_type_for(@name)
Mom::SlotDefinition.new(:message , slot_def)
SlotMachine::SlotDefinition.new(:message , slot_def)
end
def to_s(depth = 0)
name.to_s
@ -24,8 +24,8 @@ module Vool
class InstanceVariable < Expression
include Named
def to_slot(_)
Mom::SlotDefinition.new(:message , [ :receiver , @name] )
def to_slot_definition(_)
SlotMachine::SlotDefinition.new(:message , [ :receiver , @name] )
end
# used to collect type information
def add_ivar( array )
@ -51,8 +51,8 @@ module Vool
def ct_type
get_named_class.single_class.instance_type
end
def to_slot(_)
return Mom::SlotDefinition.new( get_named_class, [])
def to_slot_definition(_)
return SlotMachine::SlotDefinition.new( get_named_class, [])
end
def get_named_class
Parfait.object_space.get_class_by_name(self.name)

View File

@ -9,15 +9,15 @@ module Vool
@body = body
end
def to_mom( compiler )
merge_label = Mom::Label.new(self, "merge_label_#{object_id.to_s(16)}")
cond_label = Mom::Label.new(self, "cond_label_#{object_id.to_s(16)}")
def to_slot( compiler )
merge_label = SlotMachine::Label.new(self, "merge_label_#{object_id.to_s(16)}")
cond_label = SlotMachine::Label.new(self, "cond_label_#{object_id.to_s(16)}")
codes = cond_label
codes << @hoisted.to_mom(compiler) if @hoisted
codes << @condition.to_mom(compiler) if @condition.is_a?(SendStatement)
codes << Mom::TruthCheck.new(condition.to_slot(compiler) , merge_label)
codes << @body.to_mom(compiler)
codes << Mom::Jump.new(cond_label)
codes << @hoisted.to_slot(compiler) if @hoisted
codes << @condition.to_slot(compiler) if @condition.is_a?(SendStatement)
codes << SlotMachine::TruthCheck.new(condition.to_slot_definition(compiler) , merge_label)
codes << @body.to_slot(compiler)
codes << SlotMachine::Jump.new(cond_label)
codes << merge_label
end

View File

@ -5,7 +5,7 @@ module Vool
#
# On the ruby side, normalisation works pretty much the same too.
#
# On the way down to Mom, small differences become abvious, as the block that is
# 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.
@ -14,8 +14,8 @@ module Vool
# A Yield breaks down to 2 steps:
# - Setting up the next message, with receiver, arguments, and (importantly) return address
# - a SimpleCall,
def to_mom( compiler )
@parfait_block = @block.to_mom(compiler) if @block
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
@ -33,10 +33,10 @@ module Vool
# 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 = Mom::Label.new(self,"method_ok_#{self.object_id}")
compile_method = Mom::SlotDefinition.new( compiler.get_method , [])
runtime_method = Mom::SlotDefinition.new( :message , [ :method] )
check = Mom::NotSameCheck.new(compile_method , runtime_method, ok_label)
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 mom instructions for this
#builder = compiler.builder("yield")
#Risc::Macro.exit_sequence(builder)
@ -48,15 +48,15 @@ module Vool
# 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 = Mom::MessageSetup.new( arg_index )
mom_receive = @receiver.to_slot(compiler)
setup = SlotMachine::MessageSetup.new( arg_index )
mom_receive = @receiver.to_slot_definition(compiler)
arg_target = [:message , :next_message ]
args = []
@arguments.each_with_index do |arg , index| # +1 because of type
args << Mom::SlotLoad.new(self, arg_target + ["arg#{index+1}".to_sym] , arg.to_slot(compiler))
args << SlotMachine::SlotLoad.new(self, arg_target + ["arg#{index+1}".to_sym] , arg.to_slot_definition(compiler))
end
setup << Mom::ArgumentTransfer.new( self , mom_receive , args )
setup << Mom::BlockYield.new( self , arg_index )
setup << SlotMachine::ArgumentTransfer.new( self , mom_receive , args )
setup << SlotMachine::BlockYield.new( self , arg_index )
end
end