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:
@ -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.
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
Reference in New Issue
Block a user