move parfait into register

register layer uses parfait models to generate object space
so parfait  should be part of register (not above it)
This commit is contained in:
Torsten Ruger
2015-11-18 12:04:25 +02:00
parent 2bc2d4486f
commit 979660f282
23 changed files with 18 additions and 18 deletions

View File

@ -1,45 +0,0 @@
## Notice of change
The stuff below, like the whole of Parfait, was written before soml. Ie before there was a seperate
language to compile a higher language to. Soml is not so dynamic, could do without much of the
ObjectSpace that is the core of Parfait.
So things will change. How will become clear when soml is finished.
### Parfait: a thin layer
Parfait is the run-time of the **vm**.
To be more precise, it is that part of the run-time needed to boot soml.
The run-time needs to contain quite a lot of functionality for a dynamic system.
And a large part of that functionality must actually be used at compile time too.
We reuse the Parfait code at compile-time, to create the data for the compiled vm.
To do this the vm (re) defines the object memory (in parfait_adapter).
To do the actual compiling we parse and compile the parfait code and inline it to
appropriate places.
A work in progress that started from here : http://salama.github.io/2014/06/10/more-clarity.html
went on here http://salama.github.io/2014/07/05/layers-vs-passes.html
A step back: the code (program) we compile runs at run - time.
And so does parfait. So all we have to do is compile it with the program.
And thus parfait can be used at run-time.
It's too simple: just slips off the mind like a fish into water.
Parfait has a brother, the Builtin module. Builtin contains everything that can not be coded in ruby,
but we still need (things like List access).
### Vm vs language- core
Parfait is not the language core library. Core library functionality differs between
languages and so the language core lib must be on top of the vm parfait.
To make this point clear, i have started using different names for the core classes. Hopefully
more sensible ones, ie List instead of Array, Dictionary instead of Hash.
Also Parfait is meant to be as thin as humanly possibly, so extra (nice to have) functionality
will be in future modules.

View File

@ -1,79 +0,0 @@
# described in the ruby language book as the eigenclass, what you get with
# class MyClass
# class << self <--- this is called the eigenclass, or metaclass, and really is just
# .... the class object but gives us the ability to use the
# syntax as if it were a class
#
module Parfait
module Behaviour
def self.included(base)
base.attribute :instance_methods
end
def initialize
super()
self.instance_methods = List.new
end
def methods
m = self.instance_methods
return m if m
self.instance_methods = List.new
end
def method_names
names = List.new
self.methods.each do |method|
names.push method.name
end
names
end
def add_instance_method method
raise "not a method #{method.class} #{method.inspect}" unless method.is_a? Method
raise "syserr #{method.name.class}" unless method.name.is_a? Symbol
if self.is_a?(Class) and (method.for_class != self)
raise "Adding to wrong class, should be #{method.for_class}"
end
found = get_instance_method( method.name )
if found
self.methods.delete(found)
end
self.methods.push method
#puts "#{self.name} add #{method.name}"
method
end
def remove_instance_method method_name
found = get_instance_method( method_name )
if found
self.methods.delete(found)
else
raise "No such method #{method_name} in #{self.name}"
end
return true
end
def get_instance_method fname
raise "get_instance_method #{fname}.#{fname.class}" unless fname.is_a?(Symbol)
#if we had a hash this would be easier. Detect or find would help too
self.methods.each do |m|
return m if(m.name == fname )
end
nil
end
# get the method and if not found, try superclasses. raise error if not found
def resolve_method m_name
raise "resolve_method #{m_name}.#{m_name.class}" unless m_name.is_a?(Symbol)
method = get_instance_method(m_name)
return method if method
if( self.super_class_name )
method = self.super_class.resolve_method(m_name)
end
method
end
end
end

View File

@ -1,17 +0,0 @@
# A method object is a description of the method, it's name etc
#
# But the code that the method represents, the binary, is held as an array
# in one of these.
#
module Parfait
# obviously not a "Word" but a ByteArray , but no such class yet
# As on the other hand has no encoding (yet) it is close enough
class BinaryCode < Word
def to_s
"BinaryCode #{self.char_length}"
end
end
end

View File

@ -1,86 +0,0 @@
# Class is mainly a list of methods with a name (for now)
# The memory layout of object is seperated into Layout
# A class describes the capabilities of a group of objects, ie what data it has
# and functions it responds to.
#
# So it is essential that the class (the object defining the class)
# can carry methods. It does so as instance variables.
# In fact this property is implemented in the Layout, as methods
# may be added to any object at run-time
# An Object carries the data for the instance variables it has
# The Layout lists the names of the instance variables
# The class keeps a list of instance methods, these have a name and code
module Parfait
class Class < Object
include Behaviour
attributes [:object_layout , :name , :super_class_name]
def initialize name , superclass
super()
self.name = name
self.super_class_name = superclass
# the layout for this class (class = object of type Class) carries the class
# as an instance. The relation is from an object through the Layout to it's class
# TODO the object layout should copy the stuff from superclass
self.object_layout = Layout.new(self)
end
def allocate_object
#space, and ruby allocate
end
def add_instance_name name
self.object_layout.push name
end
def sof_reference_name
name
end
def inspect
"Class(#{name})"
end
def create_instance_method method_name , arguments
raise "create_instance_method #{method_name}.#{method_name.class}" unless method_name.is_a?(Symbol)
clazz = object_layout().object_class()
raise "??? #{method_name}" unless clazz
#puts "Self: #{self.class} clazz: #{clazz.name}"
add_instance_method Method.new( clazz , method_name , arguments )
end
# this needs to be done during booting as we can't have all the classes and superclassses
# instantiated. By that logic it should maybe be part of vm rather.
# On the other hand vague plans to load the hierachy from sof exist, so for now...
def set_super_class_name sup
raise "super_class_name must be a name, not #{sup}" unless sup.is_a?(Symbol)
self.super_class_name = sup
end
def super_class
raise "No super_class for class #{self.name}" unless self.super_class_name
s = Parfait::Space.object_space.get_class_by_name(self.super_class_name)
raise "superclass not found for class #{self.name} (#{self.super_class_name})" unless s
s
end
# ruby 2.1 list (just for reference, keep at bottom)
#:allocate, :new, :superclass
# + modules
# :<, :<=, :>, :>=, :included_modules, :include?, :name, :ancestors, :instance_methods, :public_instance_methods,
# :protected_instance_methods, :private_instance_methods, :constants, :const_get, :const_set, :const_defined?,
# :const_missing, :class_variables, :remove_class_variable, :class_variable_get, :class_variable_set,
# :class_variable_defined?, :public_constant, :private_constant, :singleton_class?, :include, :prepend,
# :module_exec, :class_exec, :module_eval, :class_eval, :method_defined?, :public_method_defined?,
# :private_method_defined?, :protected_method_defined?, :public_class_method, :private_class_method, :autoload,
# :autoload?, :instance_method, :public_instance_method
end
end

View File

@ -1,91 +0,0 @@
# almost simplest hash imaginable. make good use of Lists
module Parfait
class Dictionary < Object
attribute :keys
attribute :values
# only empty initialization for now
#
# internally we store keys and values in lists, which means this does **not** scale well
def initialize
super()
self.keys = List.new()
self.values = List.new()
end
# are there any key/value items in the list
def empty?
self.keys.empty?
end
# How many key/value pairs there are
def length()
return self.keys.get_length()
end
# get a value fot the given key
# key identity is checked with == not === (ie equals not identity)
# return nil if no such key
def get(key)
index = key_index(key)
if( index )
self.values.get(index)
else
nil
end
end
# same as get(key)
def [](key)
get(key)
end
# private method
def key_index(key)
len = self.keys.get_length()
index = 1
found = nil
while(index <= len)
if( self.keys.get(index) == key)
found = index
break
end
index += 1
end
found
end
# set key with value, returns value
def set(key , value)
index = key_index(key)
if( index )
self.keys.set(index , value)
else
self.keys.push(key)
self.values.push(value)
end
value
end
#same as set(k,v)
def []=(key,val)
set(key,val)
end
# yield to each key value pair
def each
index = 1
while index <= self.keys.get_length
key = self.keys.get(index)
value = self.values.get(index)
yield key , value
index = index + 1
end
self
end
def to_sof_node(writer , level , ref)
Sof.hash_to_sof_node( self , writer , level , ref)
end
end
end

View File

@ -1,26 +0,0 @@
# A Frame is set up by functions that use local variables or temporary variables
# in fact temporary variables are local variables named by the system
# It allows for access to those variables basically
# A Message and a Frame make up the two sides of message passing:
# A Message (see details there) is created by the caller and control is transferred
# A Frame is created by the receiver
# PS: it turns out that both messages and frames are created at compile, not run-time, and
# just constantly reused. Each message has a frame object ready and ist also linked
# to the next message.
# The better way to say above is that a message is *used* by the caller, and a frame by the callee.
# Also at runtime Messages and Frames remain completely "normal" objects. Ie have layouts and so on.
# Which resolves the dichotomy of objects on the stack or heap. Sama sama.
module Parfait
class Frame < Object
attribute :next_frame
include Indexed
self.offset(2) # 1 == the next_frame attributes above + layout. (indexed_length gets added)
end
end

View File

@ -1,203 +0,0 @@
# various classes would derive from array in ruby, ie have indexed variables
#
# But for our memory layout we need the variable part of an object to be after
# the fixed, ie the instance variables
#
# Just using ruby derivation will not allow us to offset the index, so instead the
# function will be generated and included to the classes that need them.
#
# Using ruby include does not work for similar reasons, so Indexed.at is the main
# function that generates the methods
# ( do have to use a marker module so we can test with is_a?)
module Parfait
module Indexed # marker module
def self.included(base)
base.extend(Methods)
base.attribute :indexed_length
end
# include? means non nil index
def include? item
return index_of(item) != nil
end
# index of item, remeber first item has index 1
# return nil if no such item
def index_of item
max = self.get_length
#puts "length #{max} #{max.class}"
counter = 1
while( counter <= max )
if( get(counter) == item)
return counter
end
counter = counter + 1
end
return nil
end
# push means add to the end
# this automatically grows the List
def push value
to = self.get_length + 1
set( to , value)
to
end
def delete value
index = index_of value
return false unless index
delete_at index
end
def delete_at index
# TODO bounds check
while(index < self.get_length)
set( index , get(index + 1))
index = index + 1
end
set_length( self.get_length - 1)
true
end
def first
return nil if empty?
get(1)
end
def last
return nil if empty?
get(get_length())
end
def empty?
self.get_length == 0
end
def equal? other
# this should call parfait get_class, alas that is not implemented yet
return false if other.class != self.class
return false if other.get_length != self.get_length
index = self.get_length
while(index > 0)
return false if other.get(index) != self.get(index)
index = index - 1
end
return true
end
# above, correct, implementation causes problems in the machine object space
# because when a second empty (newly created) list is added, it is not actually
# added as it exists already. TODO, but hack with below identity function
def == other
self.object_id == other.object_id
end
# word length (padded) is the amount of space taken by the object
# For your basic object this means the number of instance variables as determined by layout
# This is off course 0 for a list, unless someone squeezed an instance variable in
# but additionally, the amount of data comes on top.
# unfortuntely we can't just use super because of the Padding
def padded_length
padded_words( get_layout().instance_length + get_length() )
end
def each
# not sure how to do this with define_method, because of the double block issue.
# probably some clever way around that, but not important
index = 1
while index <= self.get_length
item = get(index)
yield item
index = index + 1
end
self
end
def set_length len
was = self.get_length
return if was == len
if(was < len)
grow_to len
else
shrink_to len
end
end
def inspect
index = 1
ret = ""
while index <= self.get_length
item = get(index)
ret += item.inspect
ret += "," unless index == self.get_length
index = index + 1
end
ret
end
module Methods
def offset( offset )
offset += 1 # for the attribute we add (indexed_length)
# define methods on the class that includes.
# weird syntax, but at least it's possible
(class << self;self;end).send :define_method , :get_length_index do
offset
end
(class << self;self;end).send :define_method , :get_indexed do |index|
offset + index
end
define_method :get_offset do
offset
end
define_method :get_length do
r = get_internal( offset ) #one for layout
r.nil? ? 0 : r
end
# set the value at index.
# Lists start from index 1
define_method :set do | index , value|
raise "Only positive indexes #{index}" if index <= 0
if index > self.get_length
grow_to(index)
end
# start one higher than offset, which is where the length is
set_internal( index + offset, value)
end
# set the value at index.
# Lists start from index 1
define_method :get do | index|
raise "Only positive indexes, #{index}" if index <= 0
ret = nil
if(index <= self.get_length)
# start one higher than offset, which is where the length is
ret = get_internal(index + offset )
end
ret
end
define_method :grow_to do | len|
raise "Only positive lenths, #{len}" if len < 0
old_length = self.get_length
return if old_length >= len
# raise "bounds error at #{len}" if( len + offset > 16 )
# be nice to use the indexed_length , but that relies on booted space
set_internal( offset , len) #one for layout
end
define_method :shrink_to do | len|
raise "Only positive lenths, #{len}" if len < 0
old_length = self.get_length
return if old_length <= len
set_internal( offset , len)
end
end
end
end
end

View File

@ -1,25 +0,0 @@
# Integer class for representing mathods on Integers
# Integers are Values (not Objects),
# - they have fixed value
# - they are immutable
# you can *not* assign instance variables or methods
# TODO how this idea works with Numeric ?
module Parfait
class Integer < Value
# :integer?, :odd?, :even?, :upto, :downto, :times, :succ, :next, :pred, :chr, :ord, :to_i, :to_int, :floor,
# :ceil, :truncate, :round, :gcd, :lcm, :gcdlcm, :numerator, :denominator, :to_r, :rationalize,
# :singleton_method_added, :coerce, :i, :+@, :-@, :fdiv, :div, :divmod, :%, :modulo, :remainder, :abs, :magnitude,
# :real?, :zero?, :nonzero?, :step, :quo, :to_c, :real, :imaginary, :imag, :abs2, :arg, :angle, :phase,
# :rectangular, :rect, :polar, :conjugate, :conj, :>, :>=, :<, :<=, :between?
#
# Numeric
# :singleton_method_added, :coerce, :i, :+@, :-@, :fdiv, :div, :divmod, :%, :modulo, :remainder, :abs, :magnitude,
# :to_int, :real?, :integer?, :zero?, :nonzero?, :floor, :ceil, :round, :truncate, :step, :numerator, :denominator,
# :quo, :to_c, :real, :imaginary, :imag, :abs2, :arg, :angle, :phase, :rectangular, :rect, :polar, :conjugate, :conj,
# :>, :>=, :<, :<=, :between?
end
end

View File

@ -1,105 +0,0 @@
# An Object is really a hash like structure. It is dynamic and
# you want to store values by name (instance variable names).
#
# One could (like mri), store the names in each object, but that is wasteful
# Instead we store only the values, and access them by index.
# The Layout allows the mapping of names to index.
# The Layout of an object describes the memory layout of the object
# The Layout is a simple list of the names of instance variables.
#
# As every object has a Layout to describe it, the name "layout" is the
# first name in the list for every Layout.
# But as we want every Object to have a class, the Layout carries that class.
# So the layout of layout has an entry "object_class"
# But Objects must also be able to carry methods themselves (ruby calls singleton_methods)
# and those too are stored in the Layout (both layout and class include behaviour)
# In other words, the Layout is a list of names that describe
# the values stored in an actual object.
# The object is an List of values of length n and
# the Layout is an List of names of length n , plus class reference and methods reference
# Together they turn the object into a hash like structure
module Parfait
class Layout < Object
attribute :object_class
include Behaviour
include Indexed
self.offset(3)
def initialize( object_class )
super()
add_instance_variable :layout ,:Layout
self.object_class = object_class
end
def == other
self.object_id == other.object_id
end
# add the name of an instance variable
# The index will be returned and can subsequently be searched with index_of
# The index of the name is the index of the data in the object
#
# TODO , later we would need to COPY the layout to keep the old constant
# but now we are concerned with booting, ie getting a working structure
def add_instance_variable name , type
raise "Name shouldn't be nil" unless name
raise "Type shouldn't be nil" unless type
self.push(name)
self.push(type)
self.get_length
end
def instance_names
names = List.new
name = true
each do |item|
names.push(item) if name
name = ! name
end
names
end
def instance_length
(self.get_length / 2).to_i # to_i for opal
end
alias :super_index :index_of
def index_of(name)
raise "Use variable_index instead"
end
# index of the variable when using get_internal
# (get_internal is 1 based and 1 is always the layout)
def variable_index name
has = super_index(name)
return nil unless has
raise "internal error #{name}:#{has}" if has < 1
(1 + has / 2).to_i # to_i for opal
end
def type_at index
type_index = index * 2
get(type_index)
end
def inspect
"Layout[#{super}]"
end
def sof_reference_name
"#{self.object_class.name}_Layout"
end
alias :name :sof_reference_name
def super_class_name
nil # stop resolve recursing up metaclasses
end
end
end

View File

@ -1,52 +0,0 @@
require_relative "indexed"
# A List, or rather an ordered list, is just that, a list of items.
# For a programmer this may be a little strange as this new start goes with trying to break old
# bad habits. A List would be an array in some languages, but list is a better name, closer to
# common language.
# Another habit is to start a list from 0. This is "just" programmers lazyness, as it goes
# with the standard c implementation. But it bends the mind, and in oo we aim not to.
# If you have a list of three items, you they will be first, second and third, ie 1,2,3
#
# For the implementation we use Objects memory which is index addressable
# But, objects are also lists where indexes start with 1, except 1 is taken for the Layout
# so all incoming/outgoing indexes have to be shifted one up/down
module Parfait
class List < Object
include Indexed
self.offset(1)
def initialize( )
super()
end
alias :[] :get
def to_sof_node(writer , level , ref )
Sof.array_to_sof_node(self , writer , level , ref )
end
def to_a
array = []
index = 1
while( index <= self.get_length)
array[index - 1] = get(index)
index = index + 1
end
array
end
end
# new list from ruby array to be precise
def self.new_list array
list = Parfait::List.new
list.set_length array.length
index = 1
while index <= array.length do
list.set(index , array[index - 1])
index = index + 1
end
list
end
end

View File

@ -1,35 +0,0 @@
# A message is what is sent when you invoke a method. Args and stuff are packed up in to a Message
# and the Message is sent to the receiver.
# Part of the housekeeping (see attributes) makes messages a double linked list (next_message and
# caller) , and maybe surprisingly this means that we can create all messages at runtime
# and link them up and never have to touch that list again.
# All the args and receiver data changes, but the list of messages stays constant.
module Parfait
class Message < Object
attributes [:next_message , :receiver , :frame , :return_address ]
attributes [:return_value, :caller , :name ]
include Indexed
self.offset(8) # 8 == the seven attributes above + layout. (indexed_length gets added)
def initialize next_m
self.next_message = next_m
self.frame = Frame.new()
self.caller = nil
super()
end
def set_caller caller
self.caller = caller
end
def get_type_for(name)
index = @layout.get_index(name)
get_at(index)
end
end
end

View File

@ -1,68 +0,0 @@
module Parfait
# class that acts like a class, but is really the object
# described in the ruby language book as the eigenclass, what you get with
# class MyClass
# class << self <--- this is called the eigenclass, or metaclass, and really is just
# .... the class object but gives us the ability to use the
# syntax as if it were a class
# While the "real" metaclass is the layout, we need to honor the constancy of the layout
# So the layout needs to be copied and replaced anytime it is edited.
# And then changed in the original object, and thus we need this level of indirection
# Basically we implement the Behaviour protocol, by forwarding to the layout
class MetaClass < Object
include Logging
attribute :object
def initialize(object)
super()
self.object = object
end
def name
self.object.get_layout.name
end
# first part of the protocol is read, just forward to self.object.layout
def methods
self.object.get_layout.methods
end
def method_names
self.object.get_layout.method_names
end
def get_instance_method fname
self.object.get_layout.get_instance_method fname
end
def resolve_method m_name
self.object.get_layout.resolve_method m_name
end
# the modifying part creates a new layout
# forwards the action and replaces the layout
def add_instance_method method
layout = self.object.get_layout.dup
ret = layout.add_instance_method(method)
self.object.set_layout layout
ret
end
def remove_instance_method method_name
layout = self.object.get_layout.dup
ret = layout.remove_instance_method(method_name)
self.object.set_layout layout
ret
end
def create_instance_method method_name , arguments
raise "create_instance_method #{method_name}.#{method_name.class}" unless method_name.is_a?(Symbol)
log.debug "Add_method: #{method_name} clazz: #{self.name}"
add_instance_method Method.new( self , method_name , arguments )
end
end
end

View File

@ -1,83 +0,0 @@
# A Method (at runtime , sis in Parfait) is static object that primarily holds the executable
# code.
# For reflection also holds arguments and such
#
module Parfait
# static description of a method
# name
# arguments
# known local variable names
# executable code
# ps, the compiler injects its own info, see Register::MethodSource
class Method < Object
def initialize clazz , name , arguments
super()
raise "No class #{name}" unless clazz
self.for_class = clazz
self.name = name
self.binary = BinaryCode.new 0
raise "Wrong type, expect List not #{arguments.class}" unless arguments.is_a? List
arguments.each do |var|
raise "Must be variable argument, not #{var}" unless var.is_a? Variable
end
self.arguments = arguments
self.locals = List.new
end
attributes [:name , :source , :instructions , :binary ,:arguments , :for_class, :locals ]
# not part of the parfait model, hence ruby accessor
attr_accessor :source
# determine whether this method has an argument by the name
def has_arg name
raise "has_arg #{name}.#{name.class}" unless name.is_a? Symbol
max = self.arguments.get_length
counter = 1
while( counter <= max )
if( self.arguments.get(counter).name == name)
return counter
end
counter = counter + 1
end
return nil
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
max = self.locals.get_length
counter = 1
while( counter <= max )
if( self.locals.get(counter).name == name)
return counter
end
counter = counter + 1
end
return nil
end
def ensure_local name , type
index = has_local name
return index if index
var = Variable.new( type , name)
self.locals.push var
self.locals.get_length
end
def sof_reference_name
"Method: " + self.name.to_s
end
def inspect
"#{self.for_class.name}:#{name}(#{arguments.inspect})"
end
end
end

View File

@ -1,141 +0,0 @@
# to be precise, this should be an ObjectReference, as the Reference is a Value
# but we don't want to make that distinction all the time , so we don't.
# that does lead to the fact that we have Reference functions on the Object though
# Objects are arranged or layed out (in memory) according to their Layout
# every object has a Layout. Layout objects are immutalbe and may be resued for a group/class
# off objects.
# The Layout of an object may change, but then a new Layout is created
# The Layout also defines the class of the object
# The Layout is **always** the first entry (index 1) in an object, but the type word is index 0
module Parfait
LAYOUT_INDEX = 1
class Object < Value
def self.new *args
object = self.allocate
#HACK, but used to do the adapter in the init, bu that is too late now
object.fake_init if object.respond_to?(:fake_init) # at compile, not run-time
# have to grab the class, because we are in the ruby class not the parfait one
cl = Space.object_space.get_class_by_name( self.name.split("::").last.to_sym)
# and have to set the layout before we let the object do anything. otherwise boom
object.set_layout cl.object_layout
object.send :initialize , *args
object
end
# Objects memory functions. Object memory is 1 based
# but we implement it with ruby array (0 based) and don't use 0
# These are the same functions that Builtin implements for run-time
include Padding
include Positioned
def fake_init
@memory = Array.new(16)
@position = nil
self # for chaining
end
# 1 -based index
def get_internal(index)
@memory[index]
end
# 1 -based index
def set_internal(index , value)
raise "failed init for #{self.class}" unless @memory
raise "Word[#{index}] = " if((self.class == Parfait::Word) and value.nil? )
@memory[index] = value
value
end
def self.attributes names
names.each{|name| attribute(name) }
end
def self.attribute name
define_method(name) { get_instance_variable(name) }
define_method("#{name}=".to_sym) { |value| set_instance_variable(name , value) }
end
def == other
self.object_id == other.object_id
end
# This is the crux of the object system. The class of an object is stored in the objects
# memory (as opposed to an integer that has no memory and so always has the same class)
#
# In Salama we store the class in the Layout, and so the Layout is the only fixed
# data that every object carries.
def get_class()
l = get_layout()
#puts "Layout #{l.class} in #{self.class} , #{self}"
l.object_class()
end
# private
def set_layout(layout)
# puts "Layout was set for #{self.class}"
raise "Nil layout" unless layout
set_internal(LAYOUT_INDEX , layout)
end
# so we can keep the raise in get_layout
def has_layout?
! get_internal(LAYOUT_INDEX).nil?
end
def get_layout()
l = get_internal(LAYOUT_INDEX)
#puts "get layout for #{self.class} returns #{l.class}"
raise "No layout #{self.object_id.to_s(16)}:#{self.class} " unless l
return l
end
# return the metaclass
def meta
MetaClass.new self
end
def get_instance_variables
get_layout().instance_names
end
def get_instance_variable name
index = instance_variable_defined(name)
#puts "getting #{name} at #{index}"
return nil if index == nil
return get_internal(index)
end
def set_instance_variable name , value
index = instance_variable_defined(name)
return nil if index == nil
return set_internal(index , value)
end
def instance_variable_defined name
get_layout().variable_index(name)
end
def padded_length
padded_words( get_layout().instance_length )
end
# parfait versions are deliberately called different, so we "relay"
# have to put the "@" on the names for sof to take them off again
def instance_variables
get_instance_variables.to_a.collect{ |n| "@#{n}".to_sym }
end
# name comes in as a ruby @var name
def instance_variable_get name
var = get_instance_variable name.to_s[1 .. -1].to_sym
#puts "getting #{name} #{var}"
var
end
end
end

View File

@ -1,15 +0,0 @@
# A Page (from the traditionally a memory page) represents a collection of
# objects in a physically form. Ie the page holds the memory or data, that
# the objects are made up of.
# Pages have a total size, but more importantly and object size.
# All objects of a Page are same sized, and multiples of the smallest
# object. The smallest object is usually a cache line, 16 bytes or
# an exponent of two larger.
module Parfait
class Page < Object
end
end

View File

@ -1,94 +0,0 @@
# A Space is a collection of pages. It stores objects, the data for the objects,
# not references. See Page for more detail.
# Pages are stored by the object size they represent in a hash.
# Space and Page work together in making *new* objects available.
# "New" is slightly misleading in that normal operation only ever
# recycles objects.
module Parfait
# The Space contains all objects for a program. In functional terms it is a program, but in oo
# it is a collection of objects, some of which are data, some classes, some functions
# The main entry is a function called (of all things) "main".
# This _must be supplied by the compled code (similar to c)
# There is a start and exit block that call main, which receives an List of strings
# While data ususally would live in a .data section, we may also "inline" it into the code
# in an oo system all data is represented as objects
class Space < Object
def initialize
raise "Space can not be instantiated by new, you'd need a space to do so. Chicken and egg"
end
attributes [:classes , :first_message]
# need a two phase init for the object space (and generally parfait) because the space
# is an interconnected graph, so not everthing is ready
def late_init
message = Message.new(nil)
50.times do
self.first_message = Message.new message
#puts "INIT caller #{message.object_id} to #{self.first_message.object_id}"
message.set_caller self.first_message
message = self.first_message
end
end
@@object_space = nil
# Make the object space globally available
def self.object_space
@@object_space
end
# TODO Must get rid of the setter
def self.set_object_space space
@@object_space = space
end
def get_main
kernel = get_class_by_name :Object
kernel.get_instance_method :main
end
def get_init
kernel = get_class_by_name :Kernel
kernel.get_instance_method :__init__
end
# get a class by name (symbol)
# return nili if no such class. Use bang version if create should be implicit
def get_class_by_name name
raise "get_class_by_name #{name}.#{name.class}" unless name.is_a?(Symbol)
c = self.classes[name]
#puts "MISS, no class #{name} #{name.class}" unless c # " #{self.classes}"
#puts "CLAZZ, #{name} #{c.get_layout.get_length}" if c
c
end
# get or create the class by the (symbol) name
# notice that this method of creating classes implies Object superclass
def get_class_by_name! name
c = get_class_by_name(name)
return c if c
create_class name , get_class_by_name(:Object)
end
# this is the way to instantiate classes (not Parfait::Class.new)
# so we get and keep exactly one per name
def create_class name , superclass
raise "create_class #{name.class}" unless name.is_a? Symbol
c = Class.new(name , superclass)
self.classes[name] = c
end
def sof_reference_name
"space"
end
end
# ObjectSpace
# :each_object, :garbage_collect, :define_finalizer, :undefine_finalizer, :_id2ref, :count_objects
end

View File

@ -1,43 +0,0 @@
class Symbol
include Positioned
include Padding
def has_layout?
true
end
def get_layout
l = Register.machine.space.classes[:Word].object_layout
#puts "LL #{l.class}"
l
end
def padded_length
padded to_s.length + 4
end
# not the prettiest addition to the game, but it wasn't me who decided symbols are frozen in 2.x
def cache_positions
unless defined?(@@symbol_positions)
@@symbol_positions = {}
end
@@symbol_positions
end
def position
pos = cache_positions[self]
if pos == nil
str = "position accessed but not set, "
str += "Machine has object=#{Register.machine.objects.has_key?(self.object_id)} "
raise str + " for Symbol:#{self}"
end
pos
end
def position= pos
# resetting of position used to be error, but since relink and dynamic instruction size it is ok.
# in measures (of 32)
old = cache_positions[self]
if old != nil and ((old - pos).abs > 20000)
raise "position set again #{pos}!=#{old} for #{self}"
end
cache_positions[self] = pos
end
end

View File

@ -1,39 +0,0 @@
# this is not a "normal" ruby file, ie it is not required by salama
# instead it is parsed by salama to define part of the program that runs
# Values are _not_ objects. Specifically they have the following properties not found in objects:
# - they are immutable
# - equality implies identity == is ===
# - they have type, not class
# To make them useful in an oo system, we assign a class to each basic type
# This makes them look more like objects, but they are not.
# Value is an abstract class that unifies the "has a type" concept
# Types are not "objectified", but are represented as integer constants
module Parfait
class Value
# to make the machine work, the constants need
# - to start at 0
# - be unique
# - be smaller enough to fit into the 2-4 bits available
INTEGER_VALUE = 0
OBJECT_VALUE = 1
# the type is what identifies the value
def get_type()
raise "abstract clalled on #{self}"
end
# Get class works on the type. The value's type is stored by the machine.
# So this function is not overriden in derived classes, because it is used
# to espablish what class a value has! (it's that "fake" oo i mentioned)
# def get_class()
# type = self.get_type()
# return Integer if( type == INTEGER_VALUE )
# raise "invalid type"
# end
end
end

View File

@ -1,19 +0,0 @@
module Parfait
class Variable < Object
def initialize type , name , value = nil
raise "not type #{type}(#{type.class})" unless Register.machine.space.get_class_by_name(type)
self.type , self.name , self.value = type , name , value
self.value = 0 if self.type == :Integer and value == nil
raise "must give name for variable" unless name
end
attributes [:type , :name, :value]
def to_s
"Variable(#{self.type} ,#{self.name})"
end
def inspect
to_s
end
end
end

View File

@ -1,195 +0,0 @@
module Parfait
# A word is a a short sequence of characters
# Characters are not modeled as objects but as (small) integers
# The small means two of them have to fit into a machine word, utf16 or similar
#
# Words are constant, maybe like js strings, ruby symbols
# Words are short, but may have spaces
# Words are objects, that means they carry Layout as index 0
# So all indexes are offset by one in the implementation
# Object length is measured in non-layout cells though
class Word < Object
attribute :char_length
#semi "indexed" methods for interpreter
def self.get_length_index
2 # 2 is the amount of attributes, layout and char_length. the offset after which chars start
end
def self.get_indexed i
i + get_length_index
end
# initialize with length. For now we try to keep all non-parfait (including String) out
# String will contain spaces for non-zero length
# Register provides methods to create Parfait objects from ruby
def initialize len
super()
self.char_length = 0
raise "Must init with int, not #{len.class}" unless len.kind_of? Fixnum
raise "Must init with positive, not #{len}" if len < 0
set_length( len , 32 ) unless len == 0 #32 beeing ascii space
#puts "layout #{self.get_layout} #{self.object_id.to_s(16)}"
end
# return a copy of self
def copy
cop = Word.new( self.length )
index = 1
while( index <= self.length )
cop.set_char(index , self.get_char(index))
index = index + 1
end
cop
end
# return the number of characters
def length()
obj_len = self.char_length
return obj_len
end
# make every char equal the given one
def fill_with char
fill_from_with(0 , char)
end
def fill_from_with from , char
len = self.length()
return if from <= 0
while( from <= len)
set_char( from , char)
from = from + 1
end
from
end
# true if no characters
def empty?
return self.length == 0
end
# pad the string with the given character to the given length
#
def set_length(len , fill_char)
return if len <= 0
old = self.char_length
return if old >= len
self.char_length = len
check_length
fill_from_with( old + 1 , fill_char )
end
# set the character at the given index to the given character
# character must be an integer, as is the index
# the index starts at one, but may be negative to count from the end
# indexes out of range will raise an error
def set_char at , char
raise "char not fixnum #{char.class}" unless char.kind_of? Fixnum
index = range_correct_index(at)
word_index = (index - 1) / 4 + 1 + Word.get_length_index
rest = ((index - 1) % 4)
shifted = char << (rest * 8)
was = get_internal( word_index )
was = 0 unless was.is_a?(Numeric)
mask = 0xFF << (rest * 8)
mask = 0xFFFFFFFF - mask
masked = was & mask
put = masked + shifted
set_internal( word_index , put )
msg = "set index=#{index} word_index=#{word_index} rest=#{rest}= "
msg += "char=#{char.to_s(16)} shifted=#{shifted.to_s(16)} "
msg += "was=#{was.to_s(16)} masked=#{masked.to_s(16)} put=#{put.to_s(16)}"
#puts msg
char
end
# get the character at the given index (lowest 1)
# the index starts at one, but may be negative to count from the end
# indexes out of range will raise an error
#the return "character" is an integer
def get_char at
index = range_correct_index(at)
word_index = (index - 1 ) / 4 + 1 + Word.get_length_index
rest = ((index - 1) % 4)
char = get_internal(word_index)
char = 0 unless char.is_a?(Numeric)
shifted = char >> (8 * rest)
ret = shifted & 0xFF
msg = "get index=#{index} word_index=#{word_index} rest=#{rest}= "
msg += " char=#{char.to_s(16)} shifted=#{shifted.to_s(16)} ret=#{ret.to_s(16)}"
#puts msg
return ret
end
# private method to calculate negative indexes into positives
def range_correct_index at
index = at
index = self.length + at if at < 0
raise "index must be positive , not #{at}" if (index <= 0)
raise "index too large #{at} > #{self.length}" if (index > self.length )
return index
end
# compare the word to another
# currently checks for same class, though really identity of the characters
# in right order would suffice
def == other
return false if other.class != self.class
return false if other.length != self.length
len = self.length
while(len > 0)
return false if self.get_char(len) != other.get_char(len)
len = len - 1
end
return true
end
def == other
return false unless other.is_a?(String) or other.is_a?(Word)
as_string = self.to_string
unless other.is_a? String
other = other.to_string
end
as_string == other
end
def to_string
string = ""
index = 1
while( index <= self.char_length)
char = get_char(index)
string += char ? char.chr : "*"
index = index + 1
end
string
end
# as we answered is_value? with true, sof will create a basic node with this string
def to_sof
"'" + to_s + "'"
end
def padded_length
padded( 4 * get_layout().instance_length + self.char_length )
end
private
def check_length
raise "Length out of bounds #{self.char_length}" if self.char_length > 1000
end
end
# Word from string
def self.new_word( string )
string = string.to_s if string.is_a? Symbol
word = Parfait::Word.new( string.length )
string.codepoints.each_with_index do |code , index |
word.set_char(index + 1 , code)
end
word
end
end