2015-10-22 17:16:29 +02:00
|
|
|
module Register
|
2015-05-19 19:29:33 +02:00
|
|
|
|
2016-12-06 14:08:29 +01:00
|
|
|
# Booting is complicated, so it is extracted into this file, even it has only one entry point
|
2015-05-24 12:31:33 +02:00
|
|
|
|
2016-12-29 17:53:24 +01:00
|
|
|
# a ruby object as a placeholder for the parfait Space during boot
|
|
|
|
class BootSpace
|
|
|
|
attr_reader :classes
|
|
|
|
def initialize
|
|
|
|
@classes = {}
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_class_by_name(name)
|
|
|
|
cl = @classes[name]
|
|
|
|
raise "No class for #{name}" unless cl
|
|
|
|
cl
|
|
|
|
end
|
|
|
|
end
|
|
|
|
# another ruby object to shadow the parfait, just during booting.
|
|
|
|
# all it needs is the type, which we make the Parfait type
|
|
|
|
class BootClass
|
|
|
|
attr_reader :instance_type
|
|
|
|
def initialize type
|
|
|
|
@instance_type = type
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-05-19 19:29:33 +02:00
|
|
|
class Machine
|
|
|
|
|
|
|
|
# The general idea is that compiling is creating an object graph. Functionally
|
|
|
|
# one tends to think of methods, and that is complicated enough, sure.
|
2015-07-28 15:18:32 +02:00
|
|
|
# But for an object system the graph includes classes and all instance variables
|
2015-05-19 19:29:33 +02:00
|
|
|
#
|
2015-07-28 15:18:32 +02:00
|
|
|
# And so we have a chicken and egg problem. At the end of the boot function we want to have a
|
2015-05-19 19:29:33 +02:00
|
|
|
# working Space object
|
|
|
|
# But that has instance variables (List and Dictionary) and off course a class.
|
2016-02-25 21:03:11 +01:00
|
|
|
# Or more precisely in salama, a Type, that points to a class.
|
|
|
|
# So we need a Type, but that has Type and Class too. hmmm
|
2015-05-19 19:29:33 +02:00
|
|
|
#
|
|
|
|
# The way out is to build empty shell objects and stuff the neccessary data into them
|
|
|
|
# (not use the normal initialize way)
|
2015-07-28 15:18:32 +02:00
|
|
|
# (PPS: The "real" solution is to read a sof graph and not do this by hand
|
|
|
|
# That graph can be programatically built and written (with this to boot that process :-))
|
|
|
|
|
2015-07-21 14:40:25 +02:00
|
|
|
# There are some helpers below, but the roadmap is something like:
|
2016-12-06 10:38:09 +01:00
|
|
|
# - create all the Type instances, with their basic types, but no classes
|
2015-07-21 14:40:25 +02:00
|
|
|
# - create a space by "hand" , using allocate, not new
|
2016-12-06 10:38:09 +01:00
|
|
|
# - create the Class objects and assign them to the types
|
2015-07-28 15:18:32 +02:00
|
|
|
def boot_parfait!
|
2016-12-29 17:53:24 +01:00
|
|
|
types = boot_types
|
|
|
|
boot_boot_space(types)
|
|
|
|
classes = boot_classes(types)
|
|
|
|
fix_types(types , classes)
|
2015-05-19 19:29:33 +02:00
|
|
|
|
2016-12-29 17:53:24 +01:00
|
|
|
@space = Parfait::Space.new(classes)
|
|
|
|
Parfait::Space.set_object_space @space
|
2015-07-28 15:18:32 +02:00
|
|
|
|
|
|
|
#puts Sof.write(@space)
|
|
|
|
boot_functions!
|
2015-07-21 14:40:25 +02:00
|
|
|
end
|
2015-07-28 15:18:32 +02:00
|
|
|
|
2016-12-29 17:53:24 +01:00
|
|
|
# types is where the snake bites its tail. Every chain ends at a type and then it
|
|
|
|
# goes around (circular references). We create them from the list below, just as empty
|
|
|
|
# shells, that we pass back, for the BootSpace to be created
|
2016-02-25 20:50:10 +01:00
|
|
|
def boot_types
|
2016-12-29 17:53:24 +01:00
|
|
|
types = {}
|
2016-02-25 20:50:10 +01:00
|
|
|
type_names.each do |name , ivars |
|
2016-12-29 17:53:24 +01:00
|
|
|
types[name] = Parfait::Type.allocate
|
2015-05-23 11:15:06 +02:00
|
|
|
end
|
2016-12-29 17:53:24 +01:00
|
|
|
type_type = types[:Type]
|
|
|
|
types.each do |name , type |
|
2016-02-25 20:50:10 +01:00
|
|
|
type.set_type(type_type)
|
2015-05-19 19:29:33 +02:00
|
|
|
end
|
2016-12-29 17:53:24 +01:00
|
|
|
types
|
2015-07-21 14:40:25 +02:00
|
|
|
end
|
|
|
|
|
2016-12-29 17:53:24 +01:00
|
|
|
def fix_types(types , classes)
|
|
|
|
type_names.each do |name , ivars |
|
|
|
|
type = types[name]
|
|
|
|
clazz = classes[name]
|
|
|
|
type.set_object_class( clazz )
|
|
|
|
type.init_lists(ivars)
|
|
|
|
end
|
2015-07-28 15:18:32 +02:00
|
|
|
end
|
|
|
|
|
2016-12-29 17:53:24 +01:00
|
|
|
# The BootSpace is an object that holds fake classes, that hold _real_ types
|
|
|
|
# Once we plug it in we can use .new
|
|
|
|
# then we need to create the parfait classes and fix the types before creating a Space
|
|
|
|
def boot_boot_space(types)
|
|
|
|
boot_space = BootSpace.new
|
|
|
|
types.each do |name , type|
|
|
|
|
clazz = BootClass.new(type)
|
|
|
|
boot_space.classes[name] = clazz
|
|
|
|
end
|
|
|
|
Parfait::Space.set_object_space boot_space
|
2016-12-07 22:34:45 +01:00
|
|
|
end
|
|
|
|
|
2016-12-29 17:53:24 +01:00
|
|
|
# superclasses other than default object
|
|
|
|
def super_class_names
|
|
|
|
{ :Object => :Kernel , :Kernel => :Value,
|
|
|
|
:Integer => :Value , :BinaryCode => :Word }
|
|
|
|
end
|
2016-02-25 20:50:10 +01:00
|
|
|
# when running code instantiates a class, a type is created automatically
|
|
|
|
# but even to get our space up, we have already instantiated all types
|
2015-07-28 15:18:32 +02:00
|
|
|
# so we have to continue and allocate classes and fill the data by hand
|
|
|
|
# and off cource we can't use space.create_class , but still they need to go there
|
2016-12-29 17:53:24 +01:00
|
|
|
def boot_classes(types)
|
|
|
|
classes = Parfait::Dictionary.new
|
2016-02-25 20:50:10 +01:00
|
|
|
type_names.each do |name , vars|
|
2016-12-29 17:53:24 +01:00
|
|
|
super_c = super_class_names[name] || :Object
|
|
|
|
classes[name] = Parfait::Class.new(name , super_c , types[name] )
|
2015-05-31 13:45:28 +02:00
|
|
|
end
|
2016-12-29 17:53:24 +01:00
|
|
|
classes
|
2015-07-21 14:40:25 +02:00
|
|
|
end
|
2015-05-30 10:55:46 +02:00
|
|
|
|
2016-12-29 17:53:24 +01:00
|
|
|
def set_ivars_for(type , name , ivars)
|
|
|
|
type.send(:private_add_instance_variable , :type , name)
|
|
|
|
ivars.each {|n,t| type.send(:private_add_instance_variable, n , t) }
|
2015-07-21 14:40:25 +02:00
|
|
|
end
|
2015-05-31 12:02:29 +02:00
|
|
|
|
2016-02-25 20:50:10 +01:00
|
|
|
# create an object with type (ie allocate it and assign type)
|
|
|
|
# meaning the lauouts have to be booted, @types filled
|
2015-07-21 14:40:25 +02:00
|
|
|
# here we pass the actual (ruby) class
|
2016-02-25 20:50:10 +01:00
|
|
|
def object_with_type(cl)
|
2016-12-29 17:53:24 +01:00
|
|
|
o = cl.allocate
|
2015-07-21 14:40:25 +02:00
|
|
|
name = cl.name.split("::").last.to_sym
|
2016-02-25 20:50:10 +01:00
|
|
|
o.set_type @types[name]
|
2015-07-21 14:40:25 +02:00
|
|
|
o
|
|
|
|
end
|
|
|
|
|
2015-07-28 15:18:32 +02:00
|
|
|
# the function really just returns a constant (just avoiding the constant)
|
|
|
|
# unfortuantely that constant condenses every detail about the system, class names
|
|
|
|
# and all instance variable names. Really have to find a better way
|
2016-02-25 20:50:10 +01:00
|
|
|
def type_names
|
2015-10-29 11:45:29 +01:00
|
|
|
{ :Word => {:char_length => :Integer} ,
|
|
|
|
:List => {:indexed_length => :Integer} ,
|
2016-12-21 18:01:42 +01:00
|
|
|
:Message => { :next_message => :Message, :receiver => :Object, :locals => :NamedList ,
|
2015-10-29 11:45:29 +01:00
|
|
|
:return_address => :Integer, :return_value => :Integer,
|
2016-12-21 21:35:36 +01:00
|
|
|
:caller => :Message , :name => :Word , :arguments => :NamedList },
|
2015-10-29 11:45:29 +01:00
|
|
|
:Integer => {},
|
|
|
|
:Object => {},
|
|
|
|
:Kernel => {}, #fix, kernel is a class, but should be a module
|
2015-11-14 14:04:04 +01:00
|
|
|
:BinaryCode => {:char_length => :Integer} ,
|
2016-12-07 22:34:45 +01:00
|
|
|
:Space => {:classes => :Dictionary , :types => :Dictionary , :first_message => :Message},
|
2016-12-28 17:08:07 +01:00
|
|
|
:NamedList => {},
|
2016-12-29 17:53:24 +01:00
|
|
|
:Type => {:names => :List , :types => :List ,
|
2016-12-30 12:33:07 +01:00
|
|
|
:object_class => :Class, :methods => :List } ,
|
2016-02-25 21:16:13 +01:00
|
|
|
:Class => {:instance_methods => :List, :instance_type => :Type, :name => :Word,
|
2016-12-18 19:04:40 +01:00
|
|
|
:super_class_name => :Word , :instance_names => :List },
|
2015-10-29 11:45:29 +01:00
|
|
|
:Dictionary => {:keys => :List , :values => :List } ,
|
2016-12-12 22:38:55 +01:00
|
|
|
:TypedMethod => {:name => :Word, :source => :Object, :instructions => :Object, :binary => :Object,
|
2016-12-14 12:23:46 +01:00
|
|
|
:arguments => :Type , :for_type => :Type, :locals => :Type } ,
|
2015-11-07 23:54:24 +01:00
|
|
|
:Value => {},
|
2015-07-21 14:40:25 +02:00
|
|
|
}
|
2015-05-19 19:29:33 +02:00
|
|
|
end
|
|
|
|
|
2015-05-24 12:31:33 +02:00
|
|
|
# classes have booted, now create a minimal set of functions
|
2015-05-19 19:29:33 +02:00
|
|
|
# minimal means only that which can not be coded in ruby
|
2015-05-24 12:31:33 +02:00
|
|
|
# Methods are grabbed from respective modules by sending the method name. This should return the
|
|
|
|
# implementation of the method (ie a method object), not actually try to implement it
|
|
|
|
# (as that's impossible in ruby)
|
2015-05-19 19:29:33 +02:00
|
|
|
def boot_functions!
|
|
|
|
# very fiddly chicken 'n egg problem. Functions need to be in the right order, and in fact we
|
2016-12-06 10:38:09 +01:00
|
|
|
# have to define some dummies, just for the others to compile
|
2015-05-31 17:34:18 +02:00
|
|
|
# TODO go through the virtual parfait layer and adjust function names to what they really are
|
2016-12-14 12:23:46 +01:00
|
|
|
space = @space.get_class_by_name(:Space)
|
2016-12-30 12:33:07 +01:00
|
|
|
space.instance_type.add_method Builtin::Space.send(:main, nil)
|
2015-11-30 15:09:12 +01:00
|
|
|
|
2015-07-21 14:40:25 +02:00
|
|
|
obj = @space.get_class_by_name(:Object)
|
2015-11-30 15:09:12 +01:00
|
|
|
[ :get_internal_word , :set_internal_word ].each do |f|
|
2016-12-30 12:33:07 +01:00
|
|
|
obj.instance_type.add_method Builtin::Object.send(f , nil)
|
2015-05-19 19:29:33 +02:00
|
|
|
end
|
2015-07-21 14:40:25 +02:00
|
|
|
obj = @space.get_class_by_name(:Kernel)
|
2015-07-28 15:18:32 +02:00
|
|
|
# create __init__ main first, __init__ calls it
|
2015-10-23 14:13:05 +02:00
|
|
|
[:exit , :__init__ ].each do |f|
|
2016-12-30 12:33:07 +01:00
|
|
|
obj.instance_type.add_method Builtin::Kernel.send(f , nil)
|
2015-05-19 19:29:33 +02:00
|
|
|
end
|
2015-05-26 19:17:03 +02:00
|
|
|
|
2015-11-19 09:08:41 +01:00
|
|
|
obj = @space.get_class_by_name(:Word)
|
|
|
|
[:putstring , :get_internal_byte , :set_internal_byte ].each do |f|
|
2016-12-30 12:33:07 +01:00
|
|
|
obj.instance_type.add_method Builtin::Word.send(f , nil)
|
2015-11-19 09:08:41 +01:00
|
|
|
end
|
2015-07-01 20:45:41 +02:00
|
|
|
|
2015-07-21 14:40:25 +02:00
|
|
|
obj = @space.get_class_by_name(:Integer)
|
2015-11-21 13:20:25 +01:00
|
|
|
[ :putint, :mod4, :div10].each do |f| #mod4 is just a forward declaration
|
2016-12-30 12:33:07 +01:00
|
|
|
obj.instance_type.add_method Builtin::Integer.send(f , nil)
|
2015-05-19 19:29:33 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|