rename for_type to self_type

and split a base class off TypedMethod
This commit is contained in:
Torsten Ruger 2018-07-06 20:01:17 +03:00
parent 3f80953385
commit acd5cd8f30
19 changed files with 103 additions and 75 deletions

View File

@ -27,7 +27,7 @@ module Elf
meth = asm.method meth = asm.method
asm.instructions.each do |label| asm.instructions.each do |label|
next unless label.is_a?(Risc::Label) next unless label.is_a?(Risc::Label)
add_symbol "#{meth.for_type.name}@#{meth.name}:Label=#{label.name}" , Risc::Position.get(label).at add_symbol "#{meth.self_type.name}@#{meth.name}:Label=#{label.name}" , Risc::Position.get(label).at
end end
meth.binary.each_block do |code| meth.binary.each_block do |code|
label = "BinaryCode@#{meth.name}" label = "BinaryCode@#{meth.name}"

57
lib/parfait/callable.rb Normal file
View File

@ -0,0 +1,57 @@
module Parfait
# Callable is an interface that Blocks and CallableMethods follow
#
# This mean they share the following state
# - self_type: The type of self, ie an object describing instance valriable names
# - arguments_type: A type object describing the arguments (name+types) to be passed
# - frame_type: A type object describing the local variables that the method has
# - binary: The binary (jumpable) code that instructions get assembled into
# - blocks: linked list of blocks inside this method/block
#
class Callable < Object
attr_reader :self_type , :arguments_type , :frame_type , :binary , :blocks
def initialize( self_type , arguments_type , frame_type)
super()
raise "No class #{self}" unless self_type
raise "For type, not class #{self_type}" unless self_type.is_a?(Type)
@self_type = self_type
init(arguments_type, frame_type)
end
def ==(other)
@self_type == other.self_type
end
# (re) init with given args and frame types
def init(arguments_type, frame_type)
raise "Wrong argument type, expect Type not #{arguments_type.class}" unless arguments_type.is_a? Type
raise "Wrong frame type, expect Type not #{frame_type.class}" unless frame_type.is_a? Type
@arguments_type = arguments_type
@frame_type = frame_type
@binary = BinaryCode.new(0)
end
# determine if method has a local variable or tmp (anonymous local) by given name
def has_local( name )
raise "has_local #{name}.#{name.class}" unless name.is_a? Symbol
frame_type.variable_index( name )
end
def add_local( name , type )
index = has_local( name )
return index if index
@frame_type = @frame_type.add_instance_variable(name,type)
end
def each_binary( &block )
bin = binary
while(bin) do
block.call( bin )
bin = bin.next
end
end
end
end

View File

@ -83,7 +83,7 @@ module Parfait
found = get_method( method_name ) found = get_method( method_name )
if found if found
#puts "redefining method #{method_name}" #TODO, this surely must get more complicated #puts "redefining method #{method_name}" #TODO, this surely must get more complicated
raise "attempt to redifine method for different type " unless self == found.for_type raise "attempt to redifine method for different type " unless self == found.self_type
found.init(arguments , frame) found.init(arguments , frame)
return found return found
else else
@ -94,7 +94,7 @@ module Parfait
def add_method( method ) def add_method( method )
raise "not a method #{method.class} #{method.inspect}" unless method.is_a? TypedMethod raise "not a method #{method.class} #{method.inspect}" unless method.is_a? TypedMethod
raise "syserr #{method.name.class}" unless method.name.is_a? Symbol raise "syserr #{method.name.class}" unless method.name.is_a? Symbol
if self.is_a?(Class) and (method.for_type != self) if self.is_a?(Class) and (method.self_type != self)
raise "Adding to wrong class, should be #{method.for_class}" raise "Adding to wrong class, should be #{method.for_class}"
end end
if get_method( method.name ) if get_method( method.name )

View File

@ -1,7 +1,8 @@
require_relative "callable"
# A TypedMethod is static object that primarily holds the executable code. # A TypedMethod is static object that primarily holds the executable code.
# It is called typed, because all arguments and variables it uses are "typed", # It is called typed, because all arguments and variables it uses are "typed",
# that is to say the names are known and form a type (not that the types of the # that is to say the names are known and form a type (not that the types of the
# variables are known). The objects type is known too, which means all instances # variables are known). The object's type is known too, which means all instances
# variable names are known (not their respective type). # variable names are known (not their respective type).
# It's relation to the method a ruby programmer knows (called VoolMethod) is many to one, # It's relation to the method a ruby programmer knows (called VoolMethod) is many to one,
@ -13,50 +14,24 @@
# - binary: The binary (jumpable) code that the instructions get assembled into # - binary: The binary (jumpable) code that the instructions get assembled into
# - arguments_type: A type object describing the arguments (name+types) to be passed # - arguments_type: A type object describing the arguments (name+types) to be passed
# - frame_type: A type object describing the local variables that the method has # - frame_type: A type object describing the local variables that the method has
# - for_type: The Type the Method is for # - self_type: The Type the Method is for
module Parfait module Parfait
class TypedMethod < Object class TypedMethod < Callable
attr_reader :name , :for_type attr_reader :name , :next_method
attr_reader :arguments_type , :frame_type , :binary , :next_method
def initialize( type , name , arguments_type , frame_type) def initialize( self_type , name , arguments_type , frame_type)
super()
raise "No class #{name}" unless type
raise "For type, not class #{type}" unless type.is_a?(Type)
@for_type = type
@name = name @name = name
init(arguments_type, frame_type) super(self_type , arguments_type , frame_type)
end end
def ==(other) def ==(other)
return false unless other.is_a?(TypedMethod) return false unless other.is_a?(TypedMethod)
return false if @name != other.name return false if @name != other.name
@for_type == other.for_type super
end
# (re) init with given args and frame types
def init(arguments_type, frame_type)
raise "Wrong argument type, expect Type not #{arguments_type.class}" unless arguments_type.is_a? Type
raise "Wrong frame type, expect Type not #{frame_type.class}" unless frame_type.is_a? Type
@arguments_type = arguments_type
@frame_type = frame_type
@binary = BinaryCode.new(0)
end
# determine if method has a local variable or tmp (anonymous local) by given name
def has_local( name )
raise "has_local #{name}.#{name.class}" unless name.is_a? Symbol
frame_type.variable_index( name )
end
def add_local( name , type )
index = has_local( name )
return index if index
@frame_type = @frame_type.add_instance_variable(name,type)
end end
def rxf_reference_name def rxf_reference_name
@ -64,16 +39,9 @@ module Parfait
end end
def inspect def inspect
"#{@for_type.object_class.name}:#{name}(#{arguments_type.inspect})" "#{@self_type.object_class.name}:#{name}(#{arguments_type.inspect})"
end end
def each_binary( &block )
bin = binary
while(bin) do
block.call( bin )
bin = bin.next
end
end
def each_method( &block ) def each_method( &block )
block.call( self ) block.call( self )
next_method.each_method( &block ) if next_method next_method.each_method( &block ) if next_method

View File

@ -28,8 +28,8 @@ module Parfait
type.create_method( @name , @args_type , @frame_type) type.create_method( @name , @args_type , @frame_type)
end end
def compiler_for(for_type) def compiler_for(self_type)
typed_method = create_typed_method(for_type) typed_method = create_typed_method(self_type)
compiler = Risc::MethodCompiler.new( typed_method ) compiler = Risc::MethodCompiler.new( typed_method )
head = source.to_mom( compiler ) head = source.to_mom( compiler )
compiler.add_mom(head) compiler.add_mom(head)

View File

@ -135,7 +135,7 @@ module Risc
def add_known(name) def add_known(name)
case name case name
when :receiver when :receiver
ret = compiler.use_reg compiler.method.for_type ret = compiler.use_reg compiler.method.self_type
add_slot_to_reg(" load self" , :message , :receiver , ret ) add_slot_to_reg(" load self" , :message , :receiver , ret )
return ret return ret
when :space when :space
@ -184,7 +184,7 @@ module Risc
when :arguments when :arguments
type = compiler.method.arguments_type type = compiler.method.arguments_type
when :receiver when :receiver
type = compiler.method.for_type type = compiler.method.self_type
when Parfait::Object when Parfait::Object
type = Parfait.object_space.get_class_by_name( object.class.name.split("::").last.to_sym).instance_type type = Parfait.object_space.get_class_by_name( object.class.name.split("::").last.to_sym).instance_type
when Symbol when Symbol

View File

@ -63,7 +63,7 @@ module Risc
message[:receiver] << space message[:receiver] << space
end end
exit_label = Risc.label(compiler.method , "#{compiler.method.for_type.object_class.name}.#{compiler.method.name}" ) exit_label = Risc.label(compiler.method , "#{compiler.method.self_type.object_class.name}.#{compiler.method.name}" )
ret_tmp = compiler.use_reg(:Label) ret_tmp = compiler.use_reg(:Label)
builder.build do builder.build do
add_load_constant("__init__ load return", exit_label , ret_tmp) add_load_constant("__init__ load return", exit_label , ret_tmp)

View File

@ -14,7 +14,7 @@ module Risc
def initialize( method ) def initialize( method )
@regs = [] @regs = []
@method = method @method = method
name = "#{method.for_type.name}.#{method.name}" name = "#{method.self_type.name}.#{method.name}"
@risc_instructions = Risc.label(name, name) @risc_instructions = Risc.label(name, name)
@risc_instructions << Risc.label( name, "unreachable") @risc_instructions << Risc.label( name, "unreachable")
@current = @risc_instructions @current = @risc_instructions
@ -25,12 +25,12 @@ module Risc
# helper method for builtin mainly # helper method for builtin mainly
# the class_name is a symbol, which is resolved to the instance_type of that class # the class_name is a symbol, which is resolved to the instance_type of that class
# #
# return compiler_for_type with the resolved type # return compiler_self_type with the resolved type
# #
def self.compiler_for_class( class_name , method_name , args , frame ) 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 raise "create_method #{class_name}.#{class_name.class}" unless class_name.is_a? Symbol
clazz = Parfait.object_space.get_class_by_name! class_name clazz = Parfait.object_space.get_class_by_name! class_name
compiler_for_type( clazz.instance_type , method_name , args , frame) compiler_self_type( clazz.instance_type , method_name , args , frame)
end end
# create a method for the given type ( Parfait type object) # create a method for the given type ( Parfait type object)
@ -38,7 +38,7 @@ module Risc
# args a hash that will be converted to a type # args a hash that will be converted to a type
# the created method is set as the current and the given type too # the created method is set as the current and the given type too
# return the compiler # return the compiler
def self.compiler_for_type( type , method_name , args , frame) def self.compiler_self_type( type , method_name , args , frame)
raise "create_method #{type.inspect} is not a Type" unless type.is_a? Parfait::Type 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 "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 raise "create_method #{method_name}.#{method_name.class}" unless method_name.is_a? Symbol

View File

@ -154,7 +154,7 @@ module Parfait
CacheEntry: {cached_type: :Type , cached_method: :TypedMethod } , CacheEntry: {cached_type: :Type , cached_method: :TypedMethod } ,
TypedMethod: {name: :Word, risc_instructions: :Object, TypedMethod: {name: :Word, risc_instructions: :Object,
cpu_instructions: :Object, binary: :BinaryCode, cpu_instructions: :Object, binary: :BinaryCode,
arguments_type: :Type , for_type: :Type, frame_type: :Type , arguments_type: :Type , self_type: :Type, frame_type: :Type ,
next_method: :TypedMethod} , next_method: :TypedMethod} ,
VoolMethod: { name: :Word , args_type: :Type , frame_type: :Type } , VoolMethod: { name: :Word , args_type: :Type , frame_type: :Type } ,
} }

View File

@ -70,7 +70,7 @@ module Vool
@my_type = type @my_type = type
end end
def slot_definition(compiler) def slot_definition(compiler)
@my_type = compiler.method.for_type @my_type = compiler.method.self_type
Mom::SlotDefinition.new(:message , [:receiver]) Mom::SlotDefinition.new(:message , [:receiver])
end end
def ct_type def ct_type

View File

@ -8,13 +8,21 @@ module Vool
@clazz = clazz @clazz = clazz
end end
def to_mom( compiler ) # because of normalization (of send), slot_definition is called first,
# raise "should not be called (call create_objects)" # to assign the block to a variable.
end #
# This means we do the compiler here (rather than to_mom, which is in
# fact never called)
def slot_definition(compiler) def slot_definition(compiler)
@parfait_block = to_parfait(compiler)
compiler.add_constant(@parfait_block)
return Mom::SlotDefinition.new(Mom::IntegerConstant.new(1) , []) return Mom::SlotDefinition.new(Mom::IntegerConstant.new(1) , [])
end end
def to_mom( compiler )
# raise "should not be called "
end
def each(&block) def each(&block)
block.call(self) block.call(self)
@body.each(&block) @body.each(&block)
@ -24,9 +32,10 @@ module Vool
BlockStatement.new( @args , @body.normalize) BlockStatement.new( @args , @body.normalize)
end end
def create_objects(clazz) private
end def to_parfait(compiler)
end
end end
end end

View File

@ -24,10 +24,9 @@ module Vool
def to_mom( _ ) def to_mom( _ )
create_class_object create_class_object
method_compilers = [] method_compilers = body.statements.collect do |node|
body.statements.each do |node|
raise "Only methods for now #{node}" unless node.is_a?(MethodStatement) raise "Only methods for now #{node}" unless node.is_a?(MethodStatement)
method_compilers << node.to_mom(@clazz) node.to_mom(@clazz)
end end
Mom::MomCompiler.new(method_compilers) Mom::MomCompiler.new(method_compilers)
end end

View File

@ -72,8 +72,7 @@ module Vool
# - Setting up the next message, with receiver, arguments, and (importantly) return address # - 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 CachedCall , or a SimpleCall, depending on wether the receiver type can be determined
def to_mom( compiler ) def to_mom( compiler )
@parfait_block = self.block.to_mom(compiler) if self.block @receiver = SelfExpression.new(compiler.method.self_type) if @receiver.is_a?(SelfExpression)
@receiver = SelfExpression.new(compiler.method.for_type) if @receiver.is_a?(SelfExpression)
if(@receiver.ct_type) if(@receiver.ct_type)
simple_call(compiler) simple_call(compiler)
else else

View File

@ -39,10 +39,6 @@ module Vool
flat flat
end end
def create_objects
@statements.each{ |s| s.create_objects }
end
def each(&block) def each(&block)
block.call(self) block.call(self)
@statements.each{|a| a.each(&block)} @statements.each{|a| a.each(&block)}

View File

@ -48,7 +48,7 @@ module Vool
# - a SimpleCall, # - a SimpleCall,
def to_mom( compiler ) def to_mom( compiler )
@parfait_block = @block.to_mom(compiler) if @block @parfait_block = @block.to_mom(compiler) if @block
@receiver = SelfExpression.new(compiler.for_type) if @receiver.is_a?(SelfExpression) @receiver = SelfExpression.new(compiler.self_type) if @receiver.is_a?(SelfExpression)
if(@receiver.ct_type) if(@receiver.ct_type)
simple_call(compiler) simple_call(compiler)
else else

View File

@ -30,7 +30,7 @@ module Risc
def produce_instructions def produce_instructions
assert @expect , "No output given" assert @expect , "No output given"
linker = RubyX::RubyXCompiler.new(as_test_main).ruby_to_risc(:interpreter) linker = RubyX::RubyXCompiler.new(as_test_main).ruby_to_risc(:interpreter)
compiler = linker.assemblers.find{|c| c.method.name == :main and c.method.for_type.object_class.name == :Test} compiler = linker.assemblers.find{|c| c.method.name == :main and c.method.self_type.object_class.name == :Test}
compiler.instructions compiler.instructions
end end
def check_nil def check_nil

View File

@ -16,7 +16,7 @@ module Parfait
end end
def test_class_for def test_class_for
assert_equal :Object , @method.for_type.object_class.name assert_equal :Object , @method.self_type.object_class.name
end end
def test_arg1 def test_arg1

View File

@ -63,8 +63,8 @@ module Risc
in_test_vool("def meth; @ivar = 5; @ibar = 4;end") in_test_vool("def meth; @ivar = 5; @ibar = 4;end")
test = Parfait.object_space.get_class_by_name(:Test) test = Parfait.object_space.get_class_by_name(:Test)
method = test.instance_type.get_method(:meth) method = test.instance_type.get_method(:meth)
assert_equal 1, method.for_type.variable_index(:ivar) assert_equal 1, method.self_type.variable_index(:ivar)
assert_equal 2, method.for_type.variable_index(:ibar) assert_equal 2, method.self_type.variable_index(:ibar)
end end
def test_typed_method_has_one_local def test_typed_method_has_one_local
in_test_vool("def meth; local = 5 ; a = 6;end") in_test_vool("def meth; local = 5 ; a = 6;end")

View File

@ -33,7 +33,7 @@ module MomCompile
def compile_first_method( input ) def compile_first_method( input )
ret = compile_method( as_test_main( input )) ret = compile_method( as_test_main( input ))
assert_equal Mom::MomCompiler , ret.class assert_equal Mom::MomCompiler , ret.class
compiler = ret.method_compilers.find{|c| c.method.name == :main and c.method.for_type.object_class.name == :Test} compiler = ret.method_compilers.find{|c| c.method.name == :main and c.method.self_type.object_class.name == :Test}
assert_equal Risc::MethodCompiler , compiler.class assert_equal Risc::MethodCompiler , compiler.class
@method.source.to_mom( compiler ) @method.source.to_mom( compiler )
end end