Rename Vool to Sol
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
This commit is contained in:
36
lib/sol/README.md
Normal file
36
lib/sol/README.md
Normal file
@ -0,0 +1,36 @@
|
||||
# SOL
|
||||
|
||||
Simple Object Language
|
||||
--------------------------------
|
||||
|
||||
in other words, ruby without the fluff.
|
||||
|
||||
Possibly later other languages can compile to this level and use rx-file as code definition.
|
||||
|
||||
## Syntax tree
|
||||
|
||||
Sol is a layer with concrete syntax tree, just like the ruby layer above.
|
||||
Sol is just simplified, without fluff, see below.
|
||||
|
||||
The next layer down is the SlotMachine, 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,
|
||||
representing classes and methods, will be created on the way down.
|
||||
|
||||
## Fluff
|
||||
|
||||
Ruby has lots of duplication to help programmers to write less. An obvious example is the
|
||||
existence of until, which really means if not. Other examples, some more impactful are:
|
||||
|
||||
- No implicit blocks, those get passed as normal arguments (the last)
|
||||
- No splats
|
||||
- no case
|
||||
- no elseif (no unless, no ternary operator)
|
||||
- no global variables.
|
||||
|
||||
## Parfait objects
|
||||
|
||||
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 SlotMachine (ie not during init)
|
42
lib/sol/assignment.rb
Normal file
42
lib/sol/assignment.rb
Normal file
@ -0,0 +1,42 @@
|
||||
module Sol
|
||||
|
||||
# Base class for assignments (local/ivar), works just as you'd expect
|
||||
# Only "quirk" maybe, that arguments are like locals
|
||||
#
|
||||
# Only actual functionality here is the compile_assign_call which compiles
|
||||
# the call, should the assigned value be a call.
|
||||
class Assignment < Statement
|
||||
attr_reader :name , :value
|
||||
def initialize(name , value )
|
||||
raise "Name nil #{self}" unless name
|
||||
raise "Value nil #{self}" unless value
|
||||
raise "Value cant be Assignment #{value}" if value.is_a?(Assignment)
|
||||
raise "Value cant be Statements #{value}" if value.is_a?(Statements)
|
||||
@name , @value = name , value
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
block.call(self)
|
||||
@value.each(&block)
|
||||
end
|
||||
|
||||
def to_s(depth = 0)
|
||||
at_depth(depth , "#{@name} = #{@value}")
|
||||
end
|
||||
|
||||
# The assign instruction (a slot_load) is produced by delegating the slot to derived
|
||||
# class
|
||||
#
|
||||
# When the right hand side is a CallStatement, it must be compiled, before the assign
|
||||
# is executed
|
||||
#
|
||||
# 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_slot(compiler) << assign
|
||||
end
|
||||
end
|
||||
end
|
112
lib/sol/basic_values.rb
Normal file
112
lib/sol/basic_values.rb
Normal file
@ -0,0 +1,112 @@
|
||||
module Sol
|
||||
#Marker class for different constants
|
||||
class Constant < Expression
|
||||
end
|
||||
|
||||
# An integer at the sol level
|
||||
class IntegerConstant < Constant
|
||||
attr_reader :value
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
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)
|
||||
end
|
||||
def to_s(depth = 0)
|
||||
value.to_s
|
||||
end
|
||||
end
|
||||
# An float at the sol level
|
||||
class FloatConstant < Constant
|
||||
attr_reader :value
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
def ct_type
|
||||
true
|
||||
end
|
||||
def to_s(depth = 0)
|
||||
value.to_s
|
||||
end
|
||||
end
|
||||
# True at the sol level
|
||||
class TrueConstant < Constant
|
||||
def ct_type
|
||||
Parfait.object_space.get_type_by_class_name(:True)
|
||||
end
|
||||
def to_slot_definition(_)
|
||||
return SlotMachine::SlotDefinition.new(Parfait.object_space.true_object , [])
|
||||
end
|
||||
def to_s(depth = 0)
|
||||
"true"
|
||||
end
|
||||
end
|
||||
# False at the sol level
|
||||
class FalseConstant < Constant
|
||||
def ct_type
|
||||
Parfait.object_space.get_type_by_class_name(:False)
|
||||
end
|
||||
def to_slot_definition(_)
|
||||
return SlotMachine::SlotDefinition.new(Parfait.object_space.false_object , [])
|
||||
end
|
||||
def to_s(depth = 0)
|
||||
"false"
|
||||
end
|
||||
end
|
||||
# Nil at the sol level
|
||||
class NilConstant < Constant
|
||||
def ct_type
|
||||
Parfait.object_space.get_type_by_class_name(:Nil)
|
||||
end
|
||||
def to_slot_definition(_)
|
||||
return SlotMachine::SlotDefinition.new(Parfait.object_space.nil_object , [])
|
||||
end
|
||||
def to_s(depth = 0)
|
||||
"nil"
|
||||
end
|
||||
end
|
||||
|
||||
# Self at the sol level
|
||||
class SelfExpression < Expression
|
||||
attr_reader :my_type
|
||||
def initialize(type = nil)
|
||||
@my_type = type
|
||||
end
|
||||
def to_slot_definition(compiler)
|
||||
@my_type = compiler.receiver_type
|
||||
SlotMachine::SlotDefinition.new(:message , [:receiver])
|
||||
end
|
||||
def ct_type
|
||||
@my_type
|
||||
end
|
||||
def to_s(depth = 0)
|
||||
"self"
|
||||
end
|
||||
end
|
||||
class StringConstant < Constant
|
||||
attr_reader :value
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
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)
|
||||
end
|
||||
def to_s(depth = 0)
|
||||
"'#{@value}'"
|
||||
end
|
||||
end
|
||||
class SymbolConstant < StringConstant
|
||||
def ct_type
|
||||
Parfait.object_space.get_type_by_class_name(:Word)
|
||||
end
|
||||
def to_s(depth = 0)
|
||||
":#{@value}"
|
||||
end
|
||||
end
|
||||
end
|
54
lib/sol/builtin.rb
Normal file
54
lib/sol/builtin.rb
Normal file
@ -0,0 +1,54 @@
|
||||
module Sol
|
||||
module Builtin
|
||||
def self.boot_methods(options)
|
||||
return if options[:boot_methods] == false
|
||||
load_builtin( "Integer.plus" )
|
||||
end
|
||||
|
||||
def self.load_builtin(loads)
|
||||
return "class Space;def main(arg);return 0; end; end" if(loads == "Space.main")
|
||||
raise "no preload #{loads}" unless builtin[loads]
|
||||
clazz , meth = loads.split(".")
|
||||
"class #{clazz} #{derive(clazz)}; #{builtin[loads]};end;"
|
||||
end
|
||||
|
||||
def self.derive(clazz) #must get derived classes rigth, so no mismatch
|
||||
case clazz
|
||||
when "Integer"
|
||||
"< Data4"
|
||||
when "Word"
|
||||
" < Data8"
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
def self.builtin
|
||||
{
|
||||
"Object.get" => "def get_internal_word(at); X.get_internal_word;end",
|
||||
"Object.missing" => "def method_missing(at); X.method_missing(:r1);end",
|
||||
"Object.exit" => "def exit; X.exit;end",
|
||||
"Integer.div4" => "def div4; X.div4;end",
|
||||
"Integer.div10" => "def div10; X.div10;end",
|
||||
"Integer.gt" => "def >; X.comparison(:>);end",
|
||||
"Integer.lt" => "def <; X.comparison(:<);end",
|
||||
"Integer.ge" => "def >=; X.comparison(:>=);end",
|
||||
"Integer.le" => "def <=; X.comparison(:<=);end",
|
||||
"Integer.plus" => "def +; X.int_operator(:+);end",
|
||||
"Integer.minus" => "def -; X.int_operator(:-);end",
|
||||
"Integer.mul" => "def *; X.int_operator(:*);end",
|
||||
"Integer.and" => "def &; X.int_operator(:&);end",
|
||||
"Integer.or" => "def |; X.int_operator(:|);end",
|
||||
"Integer.ls" => "def <<; X.int_operator(:<<);end",
|
||||
"Integer.rs" => "def >>; X.int_operator(:>>);end",
|
||||
"Word.put" => "def putstring(at); X.putstring;end",
|
||||
"Word.set" => "def set_internal_byte(at, val); X.set_internal_byte;end",
|
||||
"Word.get" => "def get_internal_byte(at); X.get_internal_byte;end",
|
||||
}
|
||||
end
|
||||
def self.builtin_code
|
||||
keys = builtin.keys
|
||||
keys.pop
|
||||
keys.collect { |loads| load_builtin(loads)}.join
|
||||
end
|
||||
end
|
||||
end
|
31
lib/sol/call_statement.rb
Normal file
31
lib/sol/call_statement.rb
Normal file
@ -0,0 +1,31 @@
|
||||
module Sol
|
||||
|
||||
class CallStatement < Statement
|
||||
attr_reader :name , :receiver , :arguments
|
||||
|
||||
def initialize(name , receiver , arguments )
|
||||
@name , @receiver , @arguments = name , receiver , arguments
|
||||
@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_definition(_)
|
||||
SlotMachine::SlotDefinition.new(:message ,[ :return_value])
|
||||
end
|
||||
|
||||
def to_s(depth = 0)
|
||||
sen = "#{receiver}.#{name}(#{@arguments.collect{|a| a.to_s}.join(', ')})"
|
||||
at_depth(depth , sen)
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
block.call(self)
|
||||
block.call(@receiver)
|
||||
@arguments.each do |arg|
|
||||
block.call(arg)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
92
lib/sol/class_expression.rb
Normal file
92
lib/sol/class_expression.rb
Normal file
@ -0,0 +1,92 @@
|
||||
module Sol
|
||||
# This represents a class at the sol level. Sol is a syntax tree,
|
||||
# so here the only child (or children) is a body.
|
||||
# Body may either be a MethodStatement, or Statements (either empty or
|
||||
# containing MethodStatement)
|
||||
#
|
||||
# We store the class name and the parfait class
|
||||
#
|
||||
# The Parfait class gets created by to_parfait, ie only after that is the clazz
|
||||
# attribute set.
|
||||
#
|
||||
class ClassExpression < Expression
|
||||
attr_reader :name, :super_class_name , :body
|
||||
attr_reader :clazz
|
||||
|
||||
def initialize( name , supe , body)
|
||||
@name = name
|
||||
@super_class_name = supe || :Object
|
||||
raise "what body #{body}" unless body.is_a?(Statements)
|
||||
@body = body
|
||||
end
|
||||
|
||||
# This creates the Parfait class.
|
||||
# Creating the class involves creating the instance_type (or an initial version)
|
||||
# which means knowing all used names. So we go through the code looking for
|
||||
# InstanceVariables or InstanceVariable Assignments, to do that.
|
||||
def to_parfait
|
||||
@clazz = Parfait.object_space.get_class_by_name(@name )
|
||||
if(@clazz)
|
||||
if( @super_class_name != clazz.super_class_name)
|
||||
raise "Superclass mismatch for #{@name} , was #{clazz.super_class_name}, now: #{super_class_name}"
|
||||
end
|
||||
else
|
||||
@clazz = Parfait.object_space.create_class(@name , @super_class_name )
|
||||
end
|
||||
create_types
|
||||
@body.statements.each {|meth| meth.to_parfait(@clazz)}
|
||||
@clazz
|
||||
end
|
||||
|
||||
# We transforms every method (class and object)
|
||||
# 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 SlotCollection is returned,
|
||||
# which is just a list of SlotMachine::MethodCompilers
|
||||
# The compilers help to transform the code further, into Risc next
|
||||
def to_slot( _ )
|
||||
method_compilers = body.statements.collect do |node|
|
||||
case node
|
||||
when MethodExpression
|
||||
node.to_slot(@clazz)
|
||||
when ClassMethodExpression
|
||||
node.to_slot(@clazz.single_class)
|
||||
else
|
||||
raise "Only methods for now #{node.class}:#{node}"
|
||||
end
|
||||
end
|
||||
SlotMachine::SlotCollection.new(method_compilers)
|
||||
end
|
||||
|
||||
# goes through the code looking for instance variables and their assignments.
|
||||
# Adding each to the respective type, ie class or singleton_class, depending
|
||||
# on if they are instance or class instance variables.
|
||||
#
|
||||
# Class variables are deemed a design mistake, ie not implemented (yet)
|
||||
def create_types
|
||||
self.body.statements.each do |node|
|
||||
case node
|
||||
when MethodExpression
|
||||
target = @clazz
|
||||
when ClassMethodExpression
|
||||
target = @clazz.single_class
|
||||
else
|
||||
raise "Only methods for now #{node.class}:#{node}"
|
||||
end
|
||||
node.each do |exp|
|
||||
case exp
|
||||
when InstanceVariable, IvarAssignment
|
||||
target.add_instance_variable( exp.name , :Object )
|
||||
when ClassVariable #, ClassVarAssignment
|
||||
raise "Class variables not implemented #{node.name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
def to_s(depth = 0)
|
||||
derive = super_class_name ? "< #{super_class_name}" : ""
|
||||
at_depth(depth , "class #{name} #{derive}\n#{@body.to_s(depth + 1)}\nend")
|
||||
end
|
||||
end
|
||||
end
|
61
lib/sol/class_method_expression.rb
Normal file
61
lib/sol/class_method_expression.rb
Normal file
@ -0,0 +1,61 @@
|
||||
module Sol
|
||||
class ClassMethodExpression < Expression
|
||||
attr_reader :name, :args , :body
|
||||
|
||||
def initialize( name , args , body )
|
||||
@name , @args , @body = name , args , body
|
||||
raise "no bod" unless @body
|
||||
end
|
||||
|
||||
# create the parfait SolMethod to hold the code for this method
|
||||
#
|
||||
# Must pass in the actual Parfait class (default nil is just to conform to api)
|
||||
def to_parfait( clazz = nil )
|
||||
raise "No class given to class method #{name}" unless clazz
|
||||
sol_m = clazz.single_class.create_instance_method_for(name , make_arg_type , make_frame , body )
|
||||
sol_m.create_callable_method_for(clazz.single_class.instance_type)
|
||||
sol_m
|
||||
end
|
||||
|
||||
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 )
|
||||
raise( "no class method in #{@name} in #{clazz}") unless method
|
||||
#puts "CLass method Class:#{clazz}:#{name}"
|
||||
compiler = method.compiler_for(clazz.instance_type)
|
||||
each {|node| raise "Blocks not implemented" if node.is_a?(LambdaExpression)}
|
||||
compiler
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
block.call(self)
|
||||
@body.each(&block)
|
||||
end
|
||||
|
||||
def make_arg_type( )
|
||||
type_hash = {}
|
||||
@args.each {|arg| type_hash[arg] = :Object }
|
||||
Parfait::Type.for_hash( type_hash )
|
||||
end
|
||||
|
||||
def to_s(depth = 0)
|
||||
arg_str = @args.collect{|a| a.to_s}.join(', ')
|
||||
at_depth(depth , "def self.#{name}(#{arg_str})\n#{@body.to_s(1)}\nend")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def make_frame
|
||||
nodes = []
|
||||
@body.each { |node| nodes << node }
|
||||
type_hash = {}
|
||||
nodes.each do |node|
|
||||
next unless node.is_a?(LocalVariable) or node.is_a?(LocalAssignment)
|
||||
type_hash[node.name] = :Object
|
||||
end
|
||||
Parfait::Type.for_hash( type_hash )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
61
lib/sol/if_statement.rb
Normal file
61
lib/sol/if_statement.rb
Normal file
@ -0,0 +1,61 @@
|
||||
|
||||
module Sol
|
||||
class IfStatement < Statement
|
||||
|
||||
attr_reader :condition , :if_true , :if_false
|
||||
|
||||
def initialize( cond , if_true , if_false = nil)
|
||||
@condition = cond
|
||||
@if_true = if_true
|
||||
@if_false = if_false
|
||||
end
|
||||
|
||||
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_slot(compiler)
|
||||
head << check_slot(compiler , false_label)
|
||||
else
|
||||
head = check_slot(compiler , false_label)
|
||||
end
|
||||
head << true_label
|
||||
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_slot(compiler) if @if_false
|
||||
head << merge_label if @if_false
|
||||
head
|
||||
end
|
||||
|
||||
# create the slot lazily, so to_slot gets called first
|
||||
def check_slot(compiler , false_label)
|
||||
SlotMachine::TruthCheck.new(@condition.to_slot_definition(compiler) , false_label)
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
block.call(condition)
|
||||
@if_true.each(&block) if @if_true
|
||||
@if_false.each(&block) if @if_false
|
||||
end
|
||||
|
||||
def has_false?
|
||||
@if_false != nil
|
||||
end
|
||||
|
||||
def has_true?
|
||||
@if_true != nil
|
||||
end
|
||||
|
||||
def to_s(depth = 0)
|
||||
parts = "if (#{@condition.to_s(0)})\n"
|
||||
parts += " #{@if_true}\n" if @if_true
|
||||
parts += "else\n" if(@if_false)
|
||||
parts += " #{@if_false}\n" if(@if_false)
|
||||
parts += "end\n"
|
||||
at_depth(depth , parts )
|
||||
end
|
||||
end
|
||||
end
|
19
lib/sol/ivar_assignment.rb
Normal file
19
lib/sol/ivar_assignment.rb
Normal file
@ -0,0 +1,19 @@
|
||||
module Sol
|
||||
|
||||
class IvarAssignment < Assignment
|
||||
|
||||
def to_s(depth = 0)
|
||||
at_depth(depth,"@#{super(0)}")
|
||||
end
|
||||
|
||||
# We return the position where the local is stored. This is an array, giving the
|
||||
# position relative to :message- A SlotLoad is constructed from this.
|
||||
#
|
||||
# As we know it is a instance variable, it is stored in the :receiver , and has
|
||||
# the name @name
|
||||
def slot_position( compiler )
|
||||
[ :receiver , @name]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
64
lib/sol/lambda_expression.rb
Normal file
64
lib/sol/lambda_expression.rb
Normal file
@ -0,0 +1,64 @@
|
||||
module Sol
|
||||
class LambdaExpression < Expression
|
||||
attr_reader :args , :body , :clazz
|
||||
|
||||
def initialize( args , body , clazz = nil)
|
||||
@args , @body = args , body
|
||||
raise "no bod" unless @body
|
||||
@clazz = clazz
|
||||
end
|
||||
|
||||
# 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_slot, which is in
|
||||
# fact never called)
|
||||
def to_slot_definition(compiler)
|
||||
compile(compiler) unless @parfait_block
|
||||
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 = 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
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
block.call(self)
|
||||
@body.each(&block)
|
||||
end
|
||||
|
||||
def to_s(depth=0)
|
||||
"Block #{args} #{body}"
|
||||
end
|
||||
# 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.create_block( make_arg_type , make_frame(compiler))
|
||||
end
|
||||
|
||||
private
|
||||
def make_arg_type( )
|
||||
type_hash = {}
|
||||
@args.each {|arg| type_hash[arg] = :Object }
|
||||
Parfait::Type.for_hash( type_hash )
|
||||
end
|
||||
def make_frame(compiler)
|
||||
type_hash = {}
|
||||
@body.each do |node|
|
||||
next unless node.is_a?(LocalVariable) or node.is_a?(LocalAssignment)
|
||||
next if compiler.in_scope?(node.name)
|
||||
type_hash[node.name] = :Object
|
||||
end
|
||||
Parfait::Type.for_hash( type_hash )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
20
lib/sol/local_assignment.rb
Normal file
20
lib/sol/local_assignment.rb
Normal file
@ -0,0 +1,20 @@
|
||||
module Sol
|
||||
|
||||
# Local assignment really only differs in where the variable is actually stored,
|
||||
# slot_position defines that
|
||||
class LocalAssignment < Assignment
|
||||
|
||||
# We return the position where the local is stored. This is an array, giving the
|
||||
# position relative to :message- A SlotLoad is constructed from this.
|
||||
#
|
||||
# Only snag is that we do not know this position, as only the compiler knows
|
||||
# if the variable name is a local or an arg. So we delegate to the compiler.
|
||||
def slot_position( compiler )
|
||||
slot = compiler.slot_type_for(@name)
|
||||
#puts "SLOT= #{slot}"
|
||||
slot
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
27
lib/sol/macro_expression.rb
Normal file
27
lib/sol/macro_expression.rb
Normal file
@ -0,0 +1,27 @@
|
||||
module Sol
|
||||
|
||||
class MacroExpression < CallStatement
|
||||
|
||||
def initialize(name , arguments )
|
||||
super(name , SelfExpression.new , arguments)
|
||||
end
|
||||
|
||||
def to_slot(compiler)
|
||||
parts = name.to_s.split("_")
|
||||
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_definition(_)
|
||||
SlotMachine::SlotDefinition.new(:message ,[ :return_value])
|
||||
end
|
||||
|
||||
def to_s(depth = 0)
|
||||
sen = "X.#{name}(#{@arguments.collect{|a| a.to_s}.join(', ')})"
|
||||
at_depth(depth , sen)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
74
lib/sol/method_expression.rb
Normal file
74
lib/sol/method_expression.rb
Normal file
@ -0,0 +1,74 @@
|
||||
module Sol
|
||||
class MethodExpression < Expression
|
||||
attr_reader :name, :args , :body
|
||||
|
||||
def initialize( name , args , body )
|
||||
@name , @args , @body = name , args , body
|
||||
raise "no bod" unless @body
|
||||
raise "Not Sol #{@body}" unless @body.is_a?(Statement)
|
||||
end
|
||||
|
||||
# create the parfait SolMethod to hold the code for this method
|
||||
#
|
||||
# Must pass in the actual Parfait class (default nil is just to conform to api)
|
||||
def to_parfait( clazz = nil )
|
||||
raise "No class given to method #{name}" unless clazz
|
||||
if( method = clazz.get_instance_method(name))
|
||||
#FIXME , should check arg_type, and if the same, clear method and ok
|
||||
raise "Redefining #{clazz.name}.#{name} not supported #{method}"
|
||||
end
|
||||
sol_m = clazz.create_instance_method_for(name , make_arg_type , make_frame , body )
|
||||
sol_m.create_callable_method_for(clazz.instance_type)
|
||||
sol_m
|
||||
end
|
||||
|
||||
# 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
|
||||
compiler = method.compiler_for(clazz.instance_type)
|
||||
compiler
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
block.call(self)
|
||||
@body.each(&block)
|
||||
end
|
||||
|
||||
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] = :Block if has_yield?
|
||||
Parfait::Type.for_hash( type_hash )
|
||||
end
|
||||
|
||||
def to_s(depth = 0)
|
||||
arg_str = @args.collect{|a| a.to_s}.join(', ')
|
||||
at_depth(depth , "def #{name}(#{arg_str})\n#{@body.to_s(1)}\nend")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def make_frame
|
||||
nodes = []
|
||||
@body.each { |node| nodes << node }
|
||||
nodes.dup.each do |node|
|
||||
next unless node.is_a?(LambdaExpression)
|
||||
node.each {|block_scope| nodes.delete(block_scope)}
|
||||
end
|
||||
type_hash = {}
|
||||
nodes.each do |node|
|
||||
next unless node.is_a?(LocalVariable) or node.is_a?(LocalAssignment)
|
||||
type_hash[node.name] = :Object
|
||||
end
|
||||
Parfait::Type.for_hash( type_hash )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
38
lib/sol/return_statement.rb
Normal file
38
lib/sol/return_statement.rb
Normal file
@ -0,0 +1,38 @@
|
||||
module Sol
|
||||
class ReturnStatement < Statement
|
||||
|
||||
attr_reader :return_value
|
||||
|
||||
def initialize(value)
|
||||
@return_value = value
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
block.call(self)
|
||||
@return_value.each(&block)
|
||||
end
|
||||
|
||||
# Since the return is normalized to only allow simple values it is simple.
|
||||
# To return form a method in slot_machine 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_slot( compiler )
|
||||
if @return_value.is_a?(CallStatement)
|
||||
ret = @return_value.to_slot(compiler)
|
||||
ret << slot_load(compiler)
|
||||
else
|
||||
ret = slot_load(compiler)
|
||||
end
|
||||
ret << SlotMachine::ReturnJump.new(self , compiler.return_label )
|
||||
end
|
||||
|
||||
def to_s(depth = 0)
|
||||
at_depth(depth , "return #{@return_value.to_s}")
|
||||
end
|
||||
|
||||
def slot_load(compiler)
|
||||
SlotMachine::SlotLoad.new( self , [:message , :return_value] ,
|
||||
@return_value.to_slot_definition(compiler) )
|
||||
end
|
||||
end
|
||||
end
|
124
lib/sol/send_statement.rb
Normal file
124
lib/sol/send_statement.rb
Normal file
@ -0,0 +1,124 @@
|
||||
module Sol
|
||||
# Sending in a dynamic language is off course not as simple as just calling.
|
||||
# The function that needs to be called depends after all on the receiver,
|
||||
# and no guarantees can be made on what that is.
|
||||
#
|
||||
# It helps to know that usually (>99%) the class of the receiver does not change.
|
||||
# Our stategy then is to cache the functions and only dynamically determine it in
|
||||
# case of a miss (the 1%, and first invocation)
|
||||
#
|
||||
# As cache key we must use the type of the object (which is the first word of _every_ object)
|
||||
# as that is constant, and function implementations depend on the type (not class)
|
||||
class SendStatement < CallStatement
|
||||
|
||||
def block
|
||||
return nil if arguments.empty?
|
||||
bl = arguments.last
|
||||
bl.is_a?(LambdaExpression) ? bl : nil
|
||||
end
|
||||
|
||||
def add_block( block )
|
||||
@arguments << block
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
super
|
||||
self.block.each(&block) if self.block
|
||||
end
|
||||
|
||||
# lazy init this, to keep the dependency (which goes to parfait and booting) at bay
|
||||
def dynamic_call
|
||||
@dynamic ||= SlotMachine::DynamicCall.new()
|
||||
end
|
||||
|
||||
# A Send breaks down to 2 steps:
|
||||
# - 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
|
||||
#
|
||||
# A slight complication occurs for methods defined in superclasses. Since we are
|
||||
# type, not class, based, these are not part of our type.
|
||||
# So we check, and if find, add the source (sol_method) to the class and start
|
||||
# compiling the sol for the receiver_type
|
||||
#
|
||||
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)
|
||||
#puts "Known #{@receiver.ct_type}: method #{method}"
|
||||
method = create_method_from_source(compiler) unless( method )
|
||||
return simple_call(compiler, method) if method
|
||||
end
|
||||
cached_call(compiler)
|
||||
end
|
||||
|
||||
# If a method is found in the class (not the type)
|
||||
# we add it to the class that the receiver type represents, and create a compiler
|
||||
# to compile the sol for the specific type (the receiver)
|
||||
def create_method_from_source(compiler)
|
||||
sol_method = @receiver.ct_type.object_class.resolve_method!(@name)
|
||||
return nil unless sol_method
|
||||
#puts "#{sol_method.name} , adding to #{@receiver.ct_type.object_class.name}"
|
||||
@receiver.ct_type.object_class.add_instance_method(sol_method)
|
||||
sol_method.create_callable_method_for(@receiver.ct_type)
|
||||
new_compiler = sol_method.compiler_for(@receiver.ct_type)
|
||||
compiler.add_method_compiler(new_compiler)
|
||||
new_compiler.callable
|
||||
end
|
||||
|
||||
def message_setup(compiler,called_method)
|
||||
setup = SlotMachine::MessageSetup.new( called_method )
|
||||
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 )
|
||||
end
|
||||
|
||||
def simple_call(compiler, called_method)
|
||||
message_setup(compiler,called_method) << SlotMachine::SimpleCall.new(called_method)
|
||||
end
|
||||
|
||||
# this breaks cleanly into two parts:
|
||||
# - check the cached type and if neccessary update
|
||||
# - call the cached method
|
||||
def cached_call(compiler)
|
||||
cache_check(compiler) << call_cached_method(compiler)
|
||||
end
|
||||
|
||||
# check that current type is the cached type
|
||||
# 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 = SlotMachine::Label.new(self,"cache_ok_#{self.object_id}")
|
||||
check = build_condition(ok, compiler) # if cached_type != current_type
|
||||
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
|
||||
|
||||
# to call the method (that we know now to be in the cache), we move the method
|
||||
# to reg1, do the setup (very similar to static) and call
|
||||
def call_cached_method(compiler)
|
||||
message_setup(compiler,dynamic_call.cache_entry) << dynamic_call
|
||||
end
|
||||
|
||||
def to_s(depth = 0)
|
||||
sen = "#{receiver}.#{name}(#{@arguments.collect{|a| a.to_s}.join(', ')})"
|
||||
at_depth(depth , sen)
|
||||
end
|
||||
|
||||
private
|
||||
def receiver_type_definition(compiler)
|
||||
defi = @receiver.to_slot_definition(compiler)
|
||||
defi.slots << :type
|
||||
defi
|
||||
end
|
||||
def build_condition(ok_label, compiler)
|
||||
cached_type = SlotMachine::SlotDefinition.new(dynamic_call.cache_entry , [:cached_type])
|
||||
current_type = receiver_type_definition(compiler)
|
||||
SlotMachine::NotSameCheck.new(cached_type , current_type, ok_label)
|
||||
end
|
||||
end
|
||||
end
|
101
lib/sol/statement.rb
Normal file
101
lib/sol/statement.rb
Normal file
@ -0,0 +1,101 @@
|
||||
#
|
||||
# SOL -- Simple Object Language
|
||||
#
|
||||
# SOL is the abstraction of ruby: ruby minus the fluff
|
||||
# fluff is generally what makes ruby nice to use, like 3 ways to achieve the same thing
|
||||
# if/unless/ternary , reverse ifs (ie statement if condition), reverse whiles,
|
||||
# implicit blocks, splats and multiple assigns etc
|
||||
#
|
||||
# Sol has expression and statements, revealing that age old dichotomy of code and
|
||||
# data. Statements represent code whereas Expressions resolve to data.
|
||||
# (in ruby there are no pure statements, everthing resolves to data)
|
||||
#
|
||||
# Sol 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_slot , which will return a slot version of the statement. This may be code
|
||||
# or a compiler (for methods), or compiler collection (for classes)
|
||||
#
|
||||
module Sol
|
||||
|
||||
# Base class for all statements in the tree. Derived classes correspond to known language
|
||||
# constructs
|
||||
#
|
||||
# Basically Statements represent code, generally speaking code "does things".
|
||||
# But Sol distinguishes Expressions (see below), that represent data, and as such
|
||||
# don't do things themselves, rather passively participate in being pushed around
|
||||
class Statement
|
||||
|
||||
# Create any neccessary parfait object and add them to the parfait object_space
|
||||
# return the object for testing
|
||||
#
|
||||
# Default implementation (ie this one) riases to show errors
|
||||
# argument is general and depends on caller
|
||||
def to_parfait(arg)
|
||||
raise "Called when it shouldn't #{self.class}"
|
||||
end
|
||||
|
||||
# create slot_machine version of the statement, this is often code, that is added
|
||||
# to the compiler, but for methods it is a compiler and for classes a collection of those.
|
||||
#
|
||||
# The argument given most often is a compiler
|
||||
# The default implementation (this) is to raise an error
|
||||
def to_slot( _ )
|
||||
raise "Not implemented for #{self}"
|
||||
end
|
||||
|
||||
def at_depth(depth , lines)
|
||||
prefix = " " * 2 * depth
|
||||
strings = lines.split("\n")
|
||||
strings.collect{|str| prefix + str}.join("\n")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# An Expression is a Statement that represents data. ie variables constants
|
||||
# (see basic_values) , but alos classes, methods and lambdas
|
||||
class Expression < Statement
|
||||
|
||||
def each(&block)
|
||||
block.call(self)
|
||||
end
|
||||
|
||||
def ct_type
|
||||
nil
|
||||
end
|
||||
|
||||
def normalize
|
||||
raise "should not be normalized #{self}"
|
||||
end
|
||||
|
||||
# for loading into a slot, return the "slot_definition" that can be passed to
|
||||
# SlotLoad.
|
||||
def to_slot(compiler)
|
||||
raise "not iplemented in #{self}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
require_relative "assignment"
|
||||
require_relative "basic_values"
|
||||
require_relative "call_statement"
|
||||
require_relative "class_expression"
|
||||
require_relative "if_statement"
|
||||
require_relative "ivar_assignment"
|
||||
require_relative "lambda_expression"
|
||||
require_relative "local_assignment"
|
||||
require_relative "macro_expression"
|
||||
require_relative "method_expression"
|
||||
require_relative "class_method_expression"
|
||||
require_relative "return_statement"
|
||||
require_relative "statements"
|
||||
require_relative "send_statement"
|
||||
require_relative "super_statement"
|
||||
require_relative "variables"
|
||||
require_relative "while_statement"
|
||||
require_relative "yield_statement"
|
93
lib/sol/statements.rb
Normal file
93
lib/sol/statements.rb
Normal file
@ -0,0 +1,93 @@
|
||||
module Sol
|
||||
class Statements < Statement
|
||||
attr_reader :statements
|
||||
def initialize(statements)
|
||||
case statements
|
||||
when nil
|
||||
@statements = []
|
||||
when Array
|
||||
@statements = statements
|
||||
when Statement
|
||||
@statements = statements.statements
|
||||
when Statement
|
||||
@statements = [statements]
|
||||
else
|
||||
raise "Invalid class, must be Statement or Array, not #{statements.class}"
|
||||
end
|
||||
end
|
||||
|
||||
def empty?
|
||||
@statements.empty?
|
||||
end
|
||||
def single?
|
||||
@statements.length == 1
|
||||
end
|
||||
def first
|
||||
@statements.first
|
||||
end
|
||||
def last
|
||||
@statements.last
|
||||
end
|
||||
def length
|
||||
@statements.length
|
||||
end
|
||||
def [](i)
|
||||
@statements[i]
|
||||
end
|
||||
def <<(o)
|
||||
if(o.is_a?(Statements))
|
||||
o.statements.each do |s|
|
||||
raise "not a statement #{s}" unless s.is_a?(Statement)
|
||||
@statements << s
|
||||
end
|
||||
else
|
||||
raise "not a statement #{o}" unless o.is_a?(Statement)
|
||||
@statements << o
|
||||
end
|
||||
self
|
||||
end
|
||||
def prepend(o)
|
||||
raise "not a statement #{o}" unless o.is_a?(Statement)
|
||||
@statements = [o] + @statements
|
||||
end
|
||||
def shift
|
||||
@statements.shift
|
||||
end
|
||||
def pop
|
||||
@statements.pop
|
||||
end
|
||||
|
||||
# apply for all statements , return collection (for testing)
|
||||
def to_parfait
|
||||
@statements.collect{|s| s.to_parfait}
|
||||
end
|
||||
# to_slot all the statements. Append subsequent ones to the first, and return the
|
||||
# first.
|
||||
#
|
||||
# For ClassStatements this creates and returns a SlotMachineCompiler
|
||||
#
|
||||
def to_slot( compiler )
|
||||
raise "Empty list ? #{statements.length}" if empty?
|
||||
stats = @statements.dup
|
||||
first = stats.shift.to_slot(compiler)
|
||||
while( nekst = stats.shift )
|
||||
next_slot = nekst.to_slot(compiler)
|
||||
first.append next_slot
|
||||
end
|
||||
first
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
block.call(self)
|
||||
@statements.each{|a| a.each(&block)}
|
||||
end
|
||||
|
||||
def to_s(depth = 0)
|
||||
at_depth(depth , @statements.collect{|st| st.to_s(depth)}.join("\n"))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class ScopeStatement < Statements
|
||||
end
|
||||
end
|
5
lib/sol/super_statement.rb
Normal file
5
lib/sol/super_statement.rb
Normal file
@ -0,0 +1,5 @@
|
||||
module Sol
|
||||
class SuperStatement < SendStatement
|
||||
|
||||
end
|
||||
end
|
67
lib/sol/variables.rb
Normal file
67
lib/sol/variables.rb
Normal file
@ -0,0 +1,67 @@
|
||||
module Sol
|
||||
module Named
|
||||
attr_reader :name
|
||||
def initialize name
|
||||
@name = name
|
||||
end
|
||||
def each(&block)
|
||||
end
|
||||
end
|
||||
|
||||
class LocalVariable < Expression
|
||||
include Named
|
||||
def to_slot_definition(compiler)
|
||||
slot_def = compiler.slot_type_for(@name)
|
||||
SlotMachine::SlotDefinition.new(:message , slot_def)
|
||||
end
|
||||
def to_s(depth = 0)
|
||||
name.to_s
|
||||
end
|
||||
def each(&block)
|
||||
block.call(self)
|
||||
end
|
||||
end
|
||||
|
||||
class InstanceVariable < Expression
|
||||
include Named
|
||||
def to_slot_definition(_)
|
||||
SlotMachine::SlotDefinition.new(:message , [ :receiver , @name] )
|
||||
end
|
||||
# used to collect type information
|
||||
def add_ivar( array )
|
||||
array << @name
|
||||
end
|
||||
def to_s(depth = 0)
|
||||
at_depth(depth , "@#{name}")
|
||||
end
|
||||
def each(&block)
|
||||
block.call(self)
|
||||
end
|
||||
end
|
||||
|
||||
class ClassVariable < Expression
|
||||
include Named
|
||||
def each(&block)
|
||||
block.call(self)
|
||||
end
|
||||
end
|
||||
|
||||
class ModuleName < Expression
|
||||
include Named
|
||||
def ct_type
|
||||
get_named_class.single_class.instance_type
|
||||
end
|
||||
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)
|
||||
end
|
||||
def each(&block)
|
||||
block.call(self)
|
||||
end
|
||||
def to_s
|
||||
@name.to_s
|
||||
end
|
||||
end
|
||||
end
|
38
lib/sol/while_statement.rb
Normal file
38
lib/sol/while_statement.rb
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
module Sol
|
||||
class WhileStatement < Statement
|
||||
attr_reader :condition , :body , :hoisted
|
||||
|
||||
def initialize( condition , body , hoisted = nil)
|
||||
@hoisted = hoisted
|
||||
@condition = condition
|
||||
@body = body
|
||||
end
|
||||
|
||||
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_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
|
||||
|
||||
def each(&block)
|
||||
block.call(self)
|
||||
@condition.each(&block)
|
||||
@hoisted.each(&block) if @hoisted
|
||||
@body.each(&block)
|
||||
end
|
||||
|
||||
def to_s(depth = 0)
|
||||
lines =[ "while (#{@condition})" , @body.to_s(1) , "end"]
|
||||
lines.unshift( @hoisted.to_s) if @hoisted
|
||||
at_depth(depth , *lines )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
63
lib/sol/yield_statement.rb
Normal file
63
lib/sol/yield_statement.rb
Normal file
@ -0,0 +1,63 @@
|
||||
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
|
Reference in New Issue
Block a user