sorting mom instructions and statements into separate dirs
This commit is contained in:
29
lib/mom/instruction/argument_transfer.rb
Normal file
29
lib/mom/instruction/argument_transfer.rb
Normal file
@ -0,0 +1,29 @@
|
||||
module Mom
|
||||
|
||||
# Transering the arguments from the current frame into the next frame
|
||||
#
|
||||
# This could be _done_ at this level, and in fact used to be.
|
||||
# The instructions was introduced to
|
||||
# 1. make optimisations easier
|
||||
# 2. localise the inevitable change
|
||||
#
|
||||
# 1. The optimal implementation for this loads old and new frames into registers
|
||||
# and does a whole bunch of transfers
|
||||
# But if we do individual SlotMoves here, each one has to load the frames,
|
||||
# thus making advanced analysis/optimisation neccessary to achieve the same effect.
|
||||
#
|
||||
# 2. Closures will have to have access to variables after the frame goes out of scope
|
||||
# and in fact be able to change the parents variables. This design does not allow for
|
||||
# this, and so will have to be change in the not so distant future.
|
||||
#
|
||||
class ArgumentTransfer < Instruction
|
||||
|
||||
attr_reader :receiver , :arguments
|
||||
|
||||
def initialize( receiver,arguments )
|
||||
@receiver , @arguments = receiver , arguments
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
55
lib/mom/instruction/basic_values.rb
Normal file
55
lib/mom/instruction/basic_values.rb
Normal file
@ -0,0 +1,55 @@
|
||||
module Mom
|
||||
# just name scoping the same stuff to mom
|
||||
# so we know we are on the way down, keeping our layers seperated
|
||||
# and we can put constant adding into the to_risc methods (instead of on vool classes)
|
||||
class Constant
|
||||
end
|
||||
|
||||
class IntegerConstant < Constant
|
||||
attr_reader :value
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
def ct_type
|
||||
Parfait.object_space.get_class_by_name(:Integer).instance_type
|
||||
end
|
||||
end
|
||||
class FloatConstant < Constant
|
||||
attr_reader :value
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
def ct_type
|
||||
true
|
||||
end
|
||||
end
|
||||
class TrueConstant < Constant
|
||||
def ct_type
|
||||
Parfait.object_space.get_class_by_name(:True).instance_type
|
||||
end
|
||||
end
|
||||
class FalseConstant < Constant
|
||||
def ct_type
|
||||
Parfait.object_space.get_class_by_name(:False).instance_type
|
||||
end
|
||||
end
|
||||
class NilConstant < Constant
|
||||
def ct_type
|
||||
Parfait.object_space.get_class_by_name(:Nil).instance_type
|
||||
end
|
||||
end
|
||||
class StringConstant < Constant
|
||||
attr_reader :value
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
def ct_type
|
||||
Parfait.object_space.get_class_by_name(:Word).instance_type
|
||||
end
|
||||
end
|
||||
class SymbolConstant < String
|
||||
def ct_type
|
||||
Parfait.object_space.get_class_by_name(:Word).instance_type
|
||||
end
|
||||
end
|
||||
end
|
24
lib/mom/instruction/dynamic_call.rb
Normal file
24
lib/mom/instruction/dynamic_call.rb
Normal file
@ -0,0 +1,24 @@
|
||||
module Mom
|
||||
|
||||
# A dynamic call calls a method at runtime. This off course implies that we don't know the
|
||||
# method at compile time and so must "find" it. Resolving, or finding the method, is a
|
||||
# a seperate step though, and here we assume that we know this Method instance.
|
||||
#
|
||||
# Both (to be called) Method instance and the type of receiver are stored as
|
||||
# variables here. The type is used to check before calling.
|
||||
#
|
||||
# Setting up the method is not part of the instructions scope. That setup
|
||||
# includes the type check and any necccessay method resolution.
|
||||
# See vool send statement
|
||||
#
|
||||
class DynamicCall < Instruction
|
||||
attr :cached_type
|
||||
attr :cached_method
|
||||
|
||||
def initialize(type = nil, method = nil)
|
||||
@cached_type = type
|
||||
@cached_method = method
|
||||
end
|
||||
end
|
||||
|
||||
end
|
34
lib/mom/instruction/instruction.rb
Normal file
34
lib/mom/instruction/instruction.rb
Normal file
@ -0,0 +1,34 @@
|
||||
module Mom
|
||||
|
||||
# Base class for MOM instructions
|
||||
class Instruction
|
||||
include Common::List
|
||||
|
||||
# implement flatten as noop to avoid condition
|
||||
def flatten( options = {} )
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
# A label with a name
|
||||
class Label < Instruction
|
||||
attr_reader :name
|
||||
def initialize(name)
|
||||
@name = name
|
||||
end
|
||||
def to_risc(compiler)
|
||||
Risc::Label.new(self,name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require_relative "basic_values"
|
||||
require_relative "simple_call"
|
||||
require_relative "dynamic_call"
|
||||
require_relative "truth_check"
|
||||
require_relative "not_same_check"
|
||||
require_relative "jump"
|
||||
require_relative "slot_load"
|
||||
require_relative "return_sequence"
|
||||
require_relative "message_setup"
|
||||
require_relative "argument_transfer"
|
14
lib/mom/instruction/jump.rb
Normal file
14
lib/mom/instruction/jump.rb
Normal file
@ -0,0 +1,14 @@
|
||||
module Mom
|
||||
|
||||
# unconditional jump to the instruction given as target
|
||||
#
|
||||
class Jump < Instruction
|
||||
attr_reader :target
|
||||
|
||||
def initialize(target)
|
||||
@target = target
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
27
lib/mom/instruction/message_setup.rb
Normal file
27
lib/mom/instruction/message_setup.rb
Normal file
@ -0,0 +1,27 @@
|
||||
module Mom
|
||||
|
||||
# Preamble when entering the method body.
|
||||
# Acquiring the message basically.
|
||||
#
|
||||
# Currently messages are hardwired as a linked list,
|
||||
# but this does not account for continuations or closures and
|
||||
# so will have to be changed.
|
||||
#
|
||||
# With the current setup this maps to a single SlotMove, ie 2 risc Instructions
|
||||
# But clearer this way.
|
||||
#
|
||||
class MessageSetup < Instruction
|
||||
attr_reader :method
|
||||
|
||||
def initialize(method)
|
||||
@method = method
|
||||
end
|
||||
|
||||
def to_risc(compiler)
|
||||
Risc::Label.new(self,method.name)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
20
lib/mom/instruction/not_same_check.rb
Normal file
20
lib/mom/instruction/not_same_check.rb
Normal file
@ -0,0 +1,20 @@
|
||||
module Mom
|
||||
|
||||
# Mom internal check, as the name says to see if two values are not the same
|
||||
# In other words, we this checks identity, bit-values, pointers
|
||||
#
|
||||
# The values that are compared are defined as SlotDefinitions, ie can be anything
|
||||
# available to the machine through frame message or self
|
||||
#
|
||||
class NotSameCheck < Check
|
||||
attr_reader :left , :right
|
||||
|
||||
def initialize(left, right)
|
||||
@left , @right = left , right
|
||||
end
|
||||
|
||||
def to_risc(compiler)
|
||||
Risc::Label.new(self,"nosense")
|
||||
end
|
||||
end
|
||||
end
|
24
lib/mom/instruction/return_sequence.rb
Normal file
24
lib/mom/instruction/return_sequence.rb
Normal file
@ -0,0 +1,24 @@
|
||||
module Mom
|
||||
|
||||
# The ReturnSequence models the return from a method.
|
||||
#
|
||||
# This involves the jump to the return address stored in the message, and
|
||||
# the reinstantiation of the previous message.
|
||||
#
|
||||
# The machine (mom) only ever "knows" one message, the current message.
|
||||
# Messages are a double linked list, calling involves going forward,
|
||||
# returning means going back.
|
||||
#
|
||||
# The return value of the current message is transferred into the return value of the
|
||||
# callers return value during the swap of messages, and just before the jump.
|
||||
#
|
||||
# The callers perspective of a call is the magical apperance of a return_value
|
||||
# in it's message at the instruction after the call.
|
||||
#
|
||||
# The instruction is not parameterized as it translates to a constant
|
||||
# set of lower level instructions.
|
||||
#
|
||||
class ReturnSequence < Instruction
|
||||
end
|
||||
|
||||
end
|
18
lib/mom/instruction/simple_call.rb
Normal file
18
lib/mom/instruction/simple_call.rb
Normal file
@ -0,0 +1,18 @@
|
||||
module Mom
|
||||
|
||||
# A SimpleCall is just that, a simple call. This could be called a function call too,
|
||||
# meaning we managed to resolve the function at compile time and all we have to do is
|
||||
# actually call it.
|
||||
#
|
||||
# As the call setup is done beforehand (for both simple and cached call), the
|
||||
# calling really means just jumping to the address. Simple.
|
||||
#
|
||||
class SimpleCall < Instruction
|
||||
attr_reader :method
|
||||
|
||||
def initialize(method)
|
||||
@method = method
|
||||
end
|
||||
end
|
||||
|
||||
end
|
72
lib/mom/instruction/slot_load.rb
Normal file
72
lib/mom/instruction/slot_load.rb
Normal file
@ -0,0 +1,72 @@
|
||||
module Mom
|
||||
|
||||
# SlotLoad is an abstract base class for moving data into a slot
|
||||
# A Slot is basically an instance variable, but it must be of known type
|
||||
#
|
||||
# The value loaded can be a constant (SlotConstant) or come from another Slot (SlotMove)
|
||||
#
|
||||
# The Slot is the left hand side, the right hand side being determined by the subclass.
|
||||
# The only known object (*) for the left side is the current message, which is a bit like
|
||||
# the oo version of a PC (program Counter)
|
||||
# (* off course all class objects are global, and so they are allowed too)
|
||||
#
|
||||
# A maybe not immediately obvious corrolar of this design is the total absence of
|
||||
# general purpose instance variable accessors. Ie only inside an object's functions
|
||||
# can a method access instance variables, because only inside the method is the type
|
||||
# guaranteed.
|
||||
# From the outside a send is neccessary, both for get and set, (which goes through the method
|
||||
# resolution and guarantees the correct method for a type), in other words perfect data hiding.
|
||||
#
|
||||
# @left: is an array of symbols, that specifies the first the object, and then the Slot.
|
||||
# The first element is either a known type name (Capitalized symbol of the class name) ,
|
||||
# or the symbol :message
|
||||
# And subsequent symbols must be instance variables on the previous type.
|
||||
# Examples: [:message , :self] or [:Space : :next_message]
|
||||
#
|
||||
# @right: depends on the derived Class
|
||||
#
|
||||
class SlotLoad < Instruction
|
||||
attr_reader :left , :right
|
||||
def initialize(left , right)
|
||||
left = SlotDefinition.new(left.shift , left) if left.is_a? Array
|
||||
@left , @right = left , right
|
||||
raise "left not SlotDefinition, #{left}" unless left.is_a? SlotDefinition
|
||||
# raise "right not Mom, #{right.to_rxf}" unless right.class.name.include?("Mom")
|
||||
end
|
||||
|
||||
def to_risc(compiler)
|
||||
Risc::Label.new(self,"nosense")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# A SlotConstant moves a constant into a known Slot.
|
||||
# Eg when you write a = 5 , the 5 becomes a constant, and so the right side
|
||||
# the a is an instance variable on the current frame, and the frame is an instance
|
||||
# of the current message, so the effect is something like message.frame.a = 5
|
||||
# @left: See SlotLoad, an array of symbols
|
||||
# @right: A Constant from parse, ie an instance of classes in basc_value, like TrueStatement
|
||||
class SlotConstant < SlotLoad
|
||||
|
||||
def initialize(left , right)
|
||||
super
|
||||
raise "right not constant, #{right}" unless right.is_a? Mom::Constant
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#SlotMove is a SlotLoad where the right side is a slot, just like the left.
|
||||
class SlotMove < SlotLoad
|
||||
def to_risc(compiler)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
class SlotDefinition
|
||||
attr_reader :known_object , :slots
|
||||
def initialize( object , slots)
|
||||
@known_object , @slots = object , slots
|
||||
slot = [slot] unless slot.is_a?(Array)
|
||||
end
|
||||
end
|
||||
end
|
27
lib/mom/instruction/truth_check.rb
Normal file
27
lib/mom/instruction/truth_check.rb
Normal file
@ -0,0 +1,27 @@
|
||||
module Mom
|
||||
|
||||
# A base class for conditions in MOM
|
||||
# Just a marker, no real functionality for now
|
||||
|
||||
class Check < Instruction
|
||||
|
||||
end
|
||||
|
||||
# The funny thing about the ruby truth is that is is anything but false or nil
|
||||
#
|
||||
# To implement the normal ruby logic, we check for false or nil and jump
|
||||
# to the false branch. true_block follows implicitly
|
||||
#
|
||||
class TruthCheck < Check
|
||||
attr_reader :condition
|
||||
|
||||
def initialize(condition)
|
||||
@condition = condition
|
||||
end
|
||||
|
||||
def to_risc(compiler)
|
||||
Risc::Label.new(self,"nosense")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user