Introduce singleton types
Just for future, as this gives us a way to know immediately in the type, which represent normal, and which singleton classes Also instantiate singleton class lazily (with singleton type) This makes the type of class single, ie unique, immediately when it is used, ie methods or variables defined. Fixes a design mistake, where all singletonn classes shared the same type, and thus unique methods per class were impossible (Also some misc in commit)
This commit is contained in:
parent
ba83affd8c
commit
2dcb2a9a72
@ -69,7 +69,7 @@ module Mom
|
||||
return ["arg#{index}".to_sym]
|
||||
end
|
||||
index = @callable.frame_type.variable_index(name)
|
||||
raise "no such local or argument #{name}" unless index
|
||||
raise "no such local or argument #{name} for #{callable.name}:#{callable.frame_type.hash}" unless index
|
||||
return ["local#{index}".to_sym]
|
||||
end
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
module Parfait
|
||||
class Class < Behaviour
|
||||
|
||||
attr_reader :name , :super_class_name , :single_class
|
||||
attr_reader :name , :super_class_name
|
||||
|
||||
def self.type_length
|
||||
6
|
||||
@ -30,7 +30,11 @@ module Parfait
|
||||
super(instance_type)
|
||||
@name = name
|
||||
@super_class_name = superclass
|
||||
@single_class = SingletonClass.new( self , self.type || @name)
|
||||
end
|
||||
|
||||
def single_class
|
||||
return @single_class if @single_class
|
||||
@single_class = SingletonClass.new( self )
|
||||
end
|
||||
|
||||
def rxf_reference_name
|
||||
|
@ -25,16 +25,21 @@ module Parfait
|
||||
8
|
||||
end
|
||||
|
||||
def initialize( clazz , clazz_type)
|
||||
raise "No type for #{clazz.name}" unless clazz_type
|
||||
super( clazz_type )
|
||||
def initialize( clazz )
|
||||
clazz_hash = clazz.type.to_hash
|
||||
@clazz = clazz
|
||||
super( Type.for_hash(clazz_hash , self , 1) )
|
||||
@clazz.set_type( @instance_type )
|
||||
end
|
||||
|
||||
def rxf_reference_name
|
||||
@clazz.name
|
||||
end
|
||||
|
||||
def name
|
||||
:"#{clazz.name}.Single"
|
||||
end
|
||||
|
||||
def inspect
|
||||
"SingletonClass(#{@clazz.name})"
|
||||
end
|
||||
|
@ -4,7 +4,7 @@ module Parfait
|
||||
# you want to store values by name (instance variable names).
|
||||
#
|
||||
# One could (like mri), store the names in each object, but that is wasteful in both
|
||||
# time and space.
|
||||
# time and space (time for access, space to store implicitly known names ).
|
||||
# Instead we store only the values, and access them by index (bit like c++).
|
||||
# The Type allows the mapping of names to index.
|
||||
|
||||
@ -17,20 +17,27 @@ module Parfait
|
||||
# for every Type instance.
|
||||
|
||||
# But, as we want every Object to have a class, the Type carries that class.
|
||||
# So the type of type has an entry "object_class"
|
||||
# So the type of type has an entry "object_class", ir Type has an instance object_class.
|
||||
|
||||
# But Objects must also be able to carry methods themselves (ruby calls singleton_methods)
|
||||
# and those too are stored in the Type (both type and class include behaviour)
|
||||
# and those too are stored in the Type. Those type instances are called singleton
|
||||
# types, in analogy to the singleton classes they represent.
|
||||
# In other words, "usually" a type represents a whole group of objects (instances of a
|
||||
# class at the time the type was the instance_type). But for Singletons, ie objects
|
||||
# that have a singleton class, the type is only for that object.
|
||||
|
||||
# The object is an "List" (memory location) of values of length n
|
||||
# An object is an "List" (memory location) of values of length n
|
||||
# The Type is a list of n names and n types that describe the values stored in an
|
||||
# actual object.
|
||||
# Together they turn the object into a hash like structure
|
||||
|
||||
# For types to be a useful concept, they have to be unique and immutable. Any "change",
|
||||
# like adding a name/type pair, will result in a new type instance.
|
||||
# Type identity can be checked by the hash function, so two types are the same when their
|
||||
# hashes are the same. The hash is made up of hashing all instance names and the class
|
||||
# name.
|
||||
|
||||
# The Type class carries a hash of types of the systems, which is used to ensure that
|
||||
# The Space class carries a hash of types of the systems, which is used to ensure that
|
||||
# there is only one instance of every type. Hash and equality are defined on type
|
||||
# for this to work.
|
||||
|
||||
@ -39,24 +46,31 @@ module Parfait
|
||||
attr_reader :object_class , :names , :types , :methods
|
||||
|
||||
def self.type_length
|
||||
5
|
||||
6
|
||||
end
|
||||
|
||||
def self.for_hash( hash , object_class = :Object)
|
||||
# This is the default way to create new type, because we add it to the
|
||||
# global list, to space.
|
||||
# The hash (actually the keys of the hash) and the object_class define the
|
||||
# identity of the type, which can be checked with the hash function.
|
||||
# single is by default 0, meaning you have to specify explicitly (1) for
|
||||
# it to be a "singleton" type (see class description)
|
||||
def self.for_hash( hash , object_class = :Object , single = 0)
|
||||
name = object_class
|
||||
if(object_class.is_a?(Symbol))
|
||||
object_class = Object.object_space.get_class_by_name(object_class)
|
||||
end
|
||||
raise "No such class #{name}" unless object_class
|
||||
hash = {type: object_class.name }.merge(hash) unless hash[:type]
|
||||
new_type = Type.new( object_class , hash)
|
||||
new_type = Type.new( object_class , hash , single)
|
||||
Object.object_space.add_type(new_type)
|
||||
end
|
||||
|
||||
# should not be called directly. Use Type.for_hash instead, that adds the
|
||||
# type to the global list
|
||||
def initialize( object_class , hash )
|
||||
# type to the global list and does sym->class conversion if neccessary
|
||||
def initialize( object_class , hash , single )
|
||||
super()
|
||||
@is_single = single
|
||||
@object_class = object_class
|
||||
@methods = nil
|
||||
@names = List.new
|
||||
@ -68,6 +82,10 @@ module Parfait
|
||||
end
|
||||
end
|
||||
|
||||
def is_single?
|
||||
@is_single == 1
|
||||
end
|
||||
|
||||
def class_name
|
||||
@object_class&.name
|
||||
end
|
||||
|
@ -20,6 +20,7 @@ module Parfait
|
||||
@args_type = args_type
|
||||
@frame_type = frame_type
|
||||
@source = source
|
||||
#raise source.to_s if name == :type_length
|
||||
raise "Name must be symbol" unless name.is_a?(Symbol)
|
||||
raise "args_type must be type" unless args_type.is_a?(Parfait::Type)
|
||||
raise "frame_type must be type" unless frame_type.is_a?(Parfait::Type)
|
||||
|
@ -67,6 +67,9 @@ module Risc
|
||||
def self.position!( objekt )
|
||||
return false if Position.set?(objekt)
|
||||
return true if objekt.is_a? ::Integer
|
||||
return true if objekt.is_a? ::NilClass
|
||||
return true if objekt.is_a? ::TrueClass
|
||||
return true if objekt.is_a? ::FalseClass
|
||||
return true if objekt.is_a?( Risc::Label)
|
||||
#puts "ADD #{objekt.class.name}"
|
||||
unless objekt.is_a?( Parfait::Object) or objekt.is_a?( Symbol)
|
||||
|
@ -29,7 +29,7 @@ module Parfait
|
||||
space = Space.new( )
|
||||
type_names.each do |name , ivars |
|
||||
ivars[:type] = :Type
|
||||
instance_type = Type.new(name , ivars)
|
||||
instance_type = Type.new(name , ivars , 0)
|
||||
space.add_type instance_type
|
||||
space.classes[name] = Class.new(name , nil , instance_type)
|
||||
end
|
||||
@ -51,7 +51,6 @@ module Parfait
|
||||
classes.each do |name , cl|
|
||||
object_type = Parfait.object_space.get_type_by_class_name(name)
|
||||
raise "nil type" unless object_type
|
||||
cl.single_class.instance_eval{ @instance_type = class_type}
|
||||
cl.instance_eval{ @instance_type = object_type}
|
||||
cl.instance_eval{ @super_class_name = super_names[name] || :Object}
|
||||
object_type.instance_eval{ @object_class = cl }
|
||||
@ -140,7 +139,8 @@ module Parfait
|
||||
true_object: :TrueClass, false_object: :FalseClass , nil_object: :NilClass},
|
||||
TrueClass: {},
|
||||
Type: {names: :List , types: :List ,
|
||||
object_class: :Class, methods: :CallableMethod } ,
|
||||
object_class: :Class, methods: :CallableMethod ,
|
||||
is_single: :Object} ,
|
||||
VoolMethod: { name: :Word , args_type: :Type , frame_type: :Type } ,
|
||||
Word: {char_length: :Integer , next_word: :Word} ,
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ module Vool
|
||||
|
||||
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(depth + 1)}end")
|
||||
at_depth(depth , "def self.#{name}(#{arg_str})\n#{@body.to_s(1)}\nend")
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -50,7 +50,7 @@ module Vool
|
||||
|
||||
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(depth + 1)}\nend")
|
||||
at_depth(depth , "def #{name}(#{arg_str})\n#{@body.to_s(1)}\nend")
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -72,12 +72,8 @@ module Vool
|
||||
first = stats.shift.to_mom(compiler)
|
||||
while( nekst = stats.shift )
|
||||
next_mom = nekst.to_mom(compiler)
|
||||
if next_mom.is_a?(Mom::BlockCompiler)
|
||||
compiler.block_compilers << next_mom
|
||||
else
|
||||
first.append next_mom
|
||||
end
|
||||
end
|
||||
first
|
||||
end
|
||||
|
||||
|
@ -48,6 +48,20 @@ module Parfait
|
||||
@space.get_class.add_instance_variable(:counter , :Integer)
|
||||
assert before != @space.get_class.instance_type.hash
|
||||
end
|
||||
|
||||
def test_has_single
|
||||
assert_equal SingletonClass , @try.single_class.class
|
||||
end
|
||||
def test_before_not_single_type
|
||||
assert_equal false , @try.type.is_single?
|
||||
end
|
||||
def test_single_type_not_class
|
||||
hash_after = @try.single_class.instance_type.hash
|
||||
assert_equal @try.type.hash , hash_after
|
||||
end
|
||||
def test_single_type_not_class_before
|
||||
hash_before = @try.type.hash
|
||||
hash_after = @try.single_class.instance_type.hash
|
||||
refute_equal hash_before , hash_after
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -49,5 +49,11 @@ module Parfait
|
||||
@try.add_instance_variable(:counter , :Integer)
|
||||
assert_equal @try.clazz.type , @try.instance_type
|
||||
end
|
||||
def test_name
|
||||
assert_equal :"Try.Single" , @try.name
|
||||
end
|
||||
def test_type_is_single
|
||||
assert_equal true , @try.instance_type.is_single?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -13,35 +13,29 @@ module Parfait
|
||||
def test_type_index
|
||||
assert_equal @mess.get_type , @mess.get_internal_word(Parfait::TYPE_INDEX) , "mess"
|
||||
end
|
||||
|
||||
def test_type_is_first
|
||||
type = @mess.get_type
|
||||
assert_equal 0 , type.variable_index(:type)
|
||||
end
|
||||
|
||||
def test_length
|
||||
assert @mess
|
||||
assert @mess.get_type
|
||||
assert_equal 31 , @mess.get_type.instance_length , @mess.get_type.inspect
|
||||
end
|
||||
|
||||
def test_names
|
||||
assert @type.names
|
||||
end
|
||||
def test_types
|
||||
assert @type.types
|
||||
end
|
||||
|
||||
def test_type_length
|
||||
assert_equal 31 , @mess.get_type.instance_length , @mess.get_type.inspect
|
||||
end
|
||||
|
||||
def test_type_length_index
|
||||
type = @mess.get_type.get_type
|
||||
assert_equal 4 , type.variable_index(:methods)
|
||||
assert_equal type.object_class , type.get_internal_word(3)
|
||||
end
|
||||
|
||||
def test_no_index_below_0
|
||||
type = @mess.get_type
|
||||
names = type.names
|
||||
@ -50,12 +44,10 @@ module Parfait
|
||||
assert type.variable_index(n) >= 0
|
||||
end
|
||||
end
|
||||
|
||||
def test_attribute_set
|
||||
@mess.set_receiver( 55)
|
||||
assert_equal 55 , @mess.receiver
|
||||
end
|
||||
|
||||
def test_variable_index
|
||||
assert_equal 1 , @type.variable_index(:next_message)
|
||||
end
|
||||
@ -68,7 +60,6 @@ module Parfait
|
||||
def test_type_for
|
||||
assert_equal :Message , @type.type_for(:next_message)
|
||||
end
|
||||
|
||||
def test_remove_me
|
||||
type = @mess.get_type
|
||||
assert_equal type , @mess.get_internal_word(0)
|
||||
@ -83,5 +74,9 @@ module Parfait
|
||||
int_class = @space.get_type_by_class_name(:Integer)
|
||||
assert_equal :Integer, int_class.object_class.name
|
||||
end
|
||||
def test_create_single
|
||||
single = Type.for_hash( {} , :Object ,1)
|
||||
assert_equal true , single.is_single?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -38,7 +38,7 @@ module Risc
|
||||
ret = main_ticks(49)
|
||||
assert_equal FunctionReturn , ret.class
|
||||
assert_equal :r3 , ret.register.symbol
|
||||
assert_equal 38236 , @interpreter.get_register(ret.register)
|
||||
assert_equal 36540 , @interpreter.get_register(ret.register)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -38,7 +38,7 @@ module Risc
|
||||
end
|
||||
|
||||
def len
|
||||
1479
|
||||
1426
|
||||
end
|
||||
|
||||
def test_collect_all_types
|
||||
@ -70,7 +70,7 @@ module Risc
|
||||
end
|
||||
|
||||
def len
|
||||
2959
|
||||
2906
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -54,7 +54,7 @@ module Risc
|
||||
end
|
||||
def test_pc
|
||||
@interpreter.tick
|
||||
assert_equal t = 37800 , @interpreter.pc
|
||||
assert_equal t = 36104 , @interpreter.pc
|
||||
@interpreter.tick
|
||||
assert_equal t + 4 , @interpreter.pc
|
||||
end
|
||||
|
@ -24,7 +24,7 @@ module Risc
|
||||
assert_equal 0 , Position.get(@linker.cpu_init).at
|
||||
end
|
||||
def test_cpu_at
|
||||
assert_equal "0x941c" , Position.get(@linker.cpu_init.first).to_s
|
||||
assert_equal "0x8d7c" , Position.get(@linker.cpu_init.first).to_s
|
||||
end
|
||||
def test_cpu_label
|
||||
assert_equal Position , Position.get(@linker.cpu_init.first).class
|
||||
|
@ -1,7 +1,7 @@
|
||||
require_relative "../helper"
|
||||
|
||||
module RubyX
|
||||
class TestIntegerCompile# < MiniTest::Test
|
||||
class TestIntegerCompile < MiniTest::Test
|
||||
include ParfaitHelper
|
||||
def setup
|
||||
@compiler = compiler
|
||||
@ -26,14 +26,17 @@ module RubyX
|
||||
assert_equal :Data8 , vool[3].name
|
||||
end
|
||||
def test_mom
|
||||
mom = @compiler.ruby_to_mom source
|
||||
vool = @compiler.ruby_to_vool source
|
||||
vool.to_parfait
|
||||
#puts vool
|
||||
mom = vool.to_mom(nil)
|
||||
assert_equal Mom::MomCollection , mom.class
|
||||
end
|
||||
def test_risc
|
||||
def est_risc
|
||||
risc = compiler.ruby_to_risc source
|
||||
assert_equal Risc::RiscCollection , risc.class
|
||||
end
|
||||
def test_binary
|
||||
def est_binary
|
||||
risc = compiler.ruby_to_binary source , :interpreter
|
||||
assert_equal Risc::Linker , risc.class
|
||||
end
|
||||
|
@ -48,7 +48,7 @@ module RubyX
|
||||
end
|
||||
end
|
||||
end
|
||||
class TestObjectRtTest #< Minitest::Test
|
||||
class TestObjectRtTest < Minitest::Test
|
||||
self.class.include ParfaitHelper
|
||||
include Risc::Ticker
|
||||
|
||||
|
@ -40,7 +40,7 @@ module Vool
|
||||
assert_equal SimpleCall, @ins.next(2).class
|
||||
assert_equal :one_plus, @ins.next(2).method.name
|
||||
assert_equal Parfait::Type, @ins.next(2).method.self_type.class
|
||||
assert_equal :Class, @ins.next(2).method.self_type.object_class.name
|
||||
assert_equal :"Space.Single", @ins.next(2).method.self_type.object_class.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -4,9 +4,25 @@ module Vool
|
||||
class TestSendClassMom < MiniTest::Test
|
||||
include VoolCompile
|
||||
|
||||
def class_main
|
||||
<<-eos
|
||||
class Space
|
||||
def self.one_plus(one)
|
||||
return 1 + 1
|
||||
end
|
||||
end
|
||||
class Space
|
||||
def main(arg)
|
||||
return Space.one_plus(1)
|
||||
end
|
||||
end
|
||||
eos
|
||||
end
|
||||
|
||||
def setup
|
||||
@compiler = compile_main( "Object.get_internal_word(0)" , "Object.get" )
|
||||
@ins = @compiler.mom_instructions.next
|
||||
source = "class Integer < Data4;def +(other);X.int_operator(:+);end;end;" + class_main
|
||||
ret = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(source)
|
||||
@ins = ret.compilers.find_compiler_name(:main).mom_instructions.next
|
||||
end
|
||||
|
||||
def test_array
|
||||
@ -37,10 +53,10 @@ module Vool
|
||||
def test_call_is
|
||||
assert_equal SimpleCall, @ins.next(2).class
|
||||
assert_equal Parfait::CallableMethod, @ins.next(2).method.class
|
||||
assert_equal :get_internal_word, @ins.next(2).method.name
|
||||
assert_equal :one_plus, @ins.next(2).method.name
|
||||
end
|
||||
def test_call_has_right_receiver
|
||||
assert_equal "Class_Type", @ins.next(2).method.self_type.name
|
||||
assert_equal "Space.Single_Type", @ins.next(2).method.self_type.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -5,7 +5,7 @@ module Vool
|
||||
include VoolCompile
|
||||
|
||||
def class_code
|
||||
"class Space;def self.meth;return 1 ; end;end"
|
||||
"class Space;def self.meth; return meth(22 + 22) ; end;end"
|
||||
end
|
||||
def setup
|
||||
Parfait.boot!(Parfait.default_test_options)
|
||||
@ -33,5 +33,16 @@ module Vool
|
||||
m = clazz.single_class.instance_type.get_method(:meth)
|
||||
assert m , "no type method :meth"
|
||||
end
|
||||
def as_mom
|
||||
@clazz.to_parfait
|
||||
@clazz.to_mom(nil)
|
||||
end
|
||||
def test_mom
|
||||
assert_equal :meth , as_mom.method_compilers.callable.name
|
||||
end
|
||||
def test_mom_frame
|
||||
callable = as_mom.method_compilers.callable
|
||||
assert callable.frame_type.names.last.to_s.start_with?("tmp_") , "no tmp_ variable #{callable.frame_type.names}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user