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:
Torsten Rüger 2019-09-30 17:09:13 +03:00
parent ba83affd8c
commit 2dcb2a9a72
22 changed files with 124 additions and 52 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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} ,
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -48,7 +48,7 @@ module RubyX
end
end
end
class TestObjectRtTest #< Minitest::Test
class TestObjectRtTest < Minitest::Test
self.class.include ParfaitHelper
include Risc::Ticker

View File

@ -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

View File

@ -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

View File

@ -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