homing in on line length 100

This commit is contained in:
Torsten Ruger
2015-05-30 12:20:39 +03:00
parent 33d464f032
commit e651b57d08
29 changed files with 159 additions and 108 deletions

View File

@ -15,8 +15,8 @@ module Virtual
# They also have local variables.
# Code-wise Methods are made up from a list of Blocks, in a similar way blocks are made up of Instructions
# The function starts with one block, and that has a start and end (return)
# Code-wise Methods are made up from a list of Blocks, in a similar way blocks are made up of
# Instructions. The function starts with one block, and that has a start and end (return)
# Blocks can be linked in two ways:
# -linear: flow continues from one to the next as they are sequential both logically and
@ -59,7 +59,9 @@ module Virtual
# add an instruction after the current (insertion point)
# the added instruction will become the new insertion point
def add_code instruction
raise instruction.inspect unless (instruction.is_a?(Instruction) or instruction.is_a?(Register::Instruction))
unless (instruction.is_a?(Instruction) or instruction.is_a?(Register::Instruction))
raise instruction.inspect
end
@current.add_code(instruction) #insert after current
self
end
@ -79,21 +81,24 @@ module Virtual
used.uniq
end
# control structures need to see blocks as a graph, but they are stored as a list with implict branches
# control structures need to see blocks as a graph, but they are stored as a list with implict
# branches
# So when creating a new block (with new_block), it is only added to the list, but instructions
# still go to the current one
# With this function one can change the current block, to actually code it.
# This juggling is (unfortunately) neccessary, as all compile functions just keep puring their code into the
# method and don't care what other compiles (like if's) do.
# This juggling is (unfortunately) neccessary, as all compile functions just keep puring their
# code into the method and don't care what other compiles (like if's) do.
# Example: while, needs 2 extra blocks
# 1 condition code, must be its own blockas we jump back to it
# - the body, can actually be after the condition as we don't need to jump there
# 2 after while block. Condition jumps here
# After block 2, the function is linear again and the calling code does not need to know what happened
# After block 2, the function is linear again and the calling code does not need to know what
# happened
# But subsequent statements are still using the original block (self) to add code to
# So the while expression creates the extra blocks, adds them and the code and then "moves" the insertion point along
# So the while expression creates the extra blocks, adds them and the code and then "moves"
# the insertion point along
def current block
@current = block
self

View File

@ -79,7 +79,9 @@ module Virtual
#attr_reader :left, :right
def self.compile_assignment expression , method
raise "must assign to NameExpression , not #{expression.left}" unless expression.left.instance_of? Ast::NameExpression
unless expression.left.instance_of? Ast::NameExpression
raise "must assign to NameExpression , not #{expression.left}"
end
r = Compiler.compile(expression.right , method )
raise "oh noo, nil from where #{expression.right.inspect}" unless r
index = method.has_arg(Virtual.new_word name)

View File

@ -4,7 +4,8 @@ module Virtual
def self.compile_if expression , method
# to execute the logic as the if states it, the blocks are the other way around
# so we can the jump over the else if true ,and the else joins unconditionally after the true_block
# so we can the jump over the else if true ,
# and the else joins unconditionally after the true_block
merge_block = method.info.new_block "if_merge" # last one, created first
true_block = method.info.new_block "if_true" # second, linked in after current, before merge
false_block = method.info.new_block "if_false" # directly next in order, ie if we don't jump we land here

View File

@ -10,7 +10,8 @@ module Virtual
puts "Created class #{clazz.name.inspect}"
expression.expressions.each do |expr|
# check if it's a function definition and add
# if not, execute it, but that does means we should be in salama (executable), not ruby. ie throw an error for now
# if not, execute it, but that does means we should be in salama (executable), not ruby.
# ie throw an error for now
raise "only functions for now #{expr.inspect}" unless expr.is_a? Ast::FunctionExpression
#puts "compiling expression #{expression}"
expression_value = Compiler.compile(expr,method )

View File

@ -15,7 +15,9 @@ module Virtual
if expression_value.is_a?(IntegerConstant) or expression_value.is_a?(ObjectConstant)
return_reg.load into , expression_value
else
return_reg.move( into, expression_value ) if expression_value.register_symbol != return_reg.register_symbol
if expression_value.register_symbol != return_reg.register_symbol
return_reg.move( into, expression_value )
end
end
#function.set_return return_reg
return return_reg

View File

@ -1,10 +1,11 @@
module Virtual
# the first instruction we need is to stop. Off course in a real machine this would be a syscall, but that is just
# an implementation (in a programm it would be a function).
# the first instruction we need is to stop. Off course in a real machine this would be a syscall,
# but that is just an implementation (in a programm it would be a function).
# But in a virtual machine, not only do we need this instruction,
# it is indeed the first instruction as just this instruction is the smallest possible programm for the machine.
# it is indeed the first instruction as just this instruction is the smallest possible programm
# for the machine.
# As such it is the next instruction for any first instruction that we generate.
class Halt < Instruction
end

View File

@ -1,7 +1,9 @@
module Virtual
# Get a instance variable by _name_ . So we have to resolve the name to an index to trnsform into a Slot
# The slot may the be used in a set on left or right hand. The transformation is done by GetImplementation
# Get a instance variable by _name_ . So we have to resolve the name to an index to
# transform into a Slot
# The slot may the be used in a set on left or right hand.
# The transformation is done by GetImplementation
class InstanceGet < Instruction
def initialize name
@name = name.to_sym

View File

@ -1,8 +1,8 @@
module Virtual
# class for Set instructions, A set is basically a mem move.
# to and from are indexes into the known objects(frame,message,self and new_message), these are represented as slots
# (see there)
# to and from are indexes into the known objects(frame,message,self and new_message),
# these are represented as slots (see there)
# from may be a Constant (Object,Integer,String,Class)
class Set < Instruction
def initialize to , from

View File

@ -1,34 +1,37 @@
module Virtual
# The Virtual Machine is a value based virtual machine in which ruby is implemented. While it is value based,
# it resembles oo in basic ways of object encapsulation and method invokation, it is a "closed" / static sytem
# in that all types are know and there is no dynamic dispatch (so we don't bite our tail here).
# The Virtual Machine is a value based virtual machine in which ruby is implemented.
# While it is value based, it resembles oo in basic ways of object encapsulation and method
# invocation, it is a "closed" / static sytem in that all types are know and there is no
# dynamic dispatch (so we don't bite our tail here).
#
# It is minimal and realistic and low level
# - minimal means that if one thing can be implemented by another, it is left out. This is quite the opposite from
# ruby, which has several loops, many redundant if forms and the like.
# - realistic means it is easy to implement on a 32 bit machine (arm) and possibly 64 bit. Memory access, a stack,
# some registers of same size are the underlying hardware. (not ie byte machine)
# - low level means it's basic instructions are realively easily implemented in a register machine. ie send is not
# a an instruction but a function.
# - minimal means that if one thing can be implemented by another, it is left out. This is quite
# the opposite from ruby, which has several loops, many redundant if forms and the like.
# - realistic means it is easy to implement on a 32 bit machine (arm) and possibly 64 bit.
# Memory access,some registers of same size are the underlying hardware. (not ie byte machine)
# - low level means it's basic instructions are realively easily implemented in a register machine.
# ie send is not a an instruction but a function.
#
# So the memory model of the machine allows for indexed access into an "object" . A fixed number of objects exist
# (ie garbage collection is reclaming, not destroying and recreating) although there may be a way to increase that number.
# So the memory model of the machine allows for indexed access into an "object" .
# A fixed number of objects exist (ie garbage collection is reclaming, not destroying and
# recreating) although there may be a way to increase that number.
#
# The ast is transformed to virtaul-machine objects, some of which represent code, some data.
#
# The next step transforms to the register machine layer, which is what actually executes.
#
# More concretely, a virtual machine is a sort of oo turing machine, it has a current instruction, executes the
# instructions, fetches the next one and so on.
# More concretely, a virtual machine is a sort of oo turing machine, it has a current instruction,
# executes the instructions, fetches the next one and so on.
# Off course the instructions are not soo simple, but in oo terms quite so.
#
# The machine is virtual in the sense that it is completely modeled in software,
# it's complete state explicitly available (not implicitly by walking stacks or something)
# The machine has a no register, but local variables, a scope at each point in time.
# Scope changes with calls and blocks, but is saved at each level. In terms of lower level implementation this means
# that the the model is such that what is a variable in ruby, never ends up being just on the pysical stack.
# Scope changes with calls and blocks, but is saved at each level. In terms of lower level
# implementation this means that the the model is such that what is a variable in ruby,
# never ends up being just on the pysical stack.
#
class Machine

View File

@ -1,21 +1,23 @@
module Virtual
# So when an object calls a method, or sends a message, this is what it sends: a Message
# A message contains the sender, return and exceptional return addresses,the arguments, and a slot for the frame.
# A message contains the sender, return and exceptional return addresses,the arguments,
# and a slot for the frame.
# As such it is a very run-time object, deep in the machinery as it were, and does not have meaningful
# methods you could call at compile time.
# As such it is a very run-time object, deep in the machinery as it were, and does not have
# meaningful methods you could call at compile time.
# The methods that are there, are nevertheless meant to be called at compile time and generate code, rather than
# executing it.
# The methods that are there, are nevertheless meant to be called at compile time and generate
# code, rather than executing it.
# The caller creates the Message and passes control to the receiver's method
# The receiver create a new Frame to hold local and temporary variables and (later) creates default values for
# arguments that were not passed
# The receiver create a new Frame to hold local and temporary variables and (later) creates
# default values for arguments that were not passed
# How the actual finding of the method takes place (acording to the ruby rules) is not simple, but as there is a
# guaranteed result (be it method_missing) it does not matter to the passing mechanism described
# How the actual finding of the method takes place (acording to the ruby rules) is not simple,
# but as there is a guaranteed result (be it method_missing) it does not matter to the passing
# mechanism described
# During compilation Message and frame objects are created to do type analysis

View File

@ -25,7 +25,9 @@ module FakeMem
end
#TODO, this is copied from module Positioned, maybe avoid duplication ?
def position
raise "position accessed but not set at #{mem_length} for #{self.inspect[0...1000]}" if @position == nil
if @position == nil
raise "position accessed but not set at #{mem_length} for #{self.inspect[0...1000]}"
end
@position
end
def set_position pos

View File

@ -3,14 +3,18 @@ module Virtual
# Frames and Message are very similar apart from the class name
# - All existing instances are stored in the space for both
# - Size is currently 2, ie 16 words (TODO a little flexibility here would not hurt, but the mountain is big)
# - Unused instances for a linked list with their first instance variable. This is HARD coded to avoid any lookup
# - Size is currently 2, ie 16 words
# (TODO a little flexibility here would not hurt, but the mountain is big)
# - Unused instances for a linked list with their first instance variable.
# This is HARD coded to avoid any lookup
# Just as a reminder: a message object is created before a send and holds return address/message and arguemnts + self
# frames are created upon entering a method and hold local and temporary variables
# as a result one of each is created for every single method call. A LOT, so make it fast luke
# Note: this is off course the reason for stack based implementations that just increment a known pointer/register or
# something. But i think most programs are memory bound and a few extra instructions don't hurt.
# Just as a reminder: a message object is created before a send and holds return address/message
# and arguemnts + self frames are created upon entering a method and hold local and temporary
# variables as a result one of each is created for every single method call.
# A LOT, so make it fast luke
# Note: this is off course the reason for stack based implementations that just increment
# a known pointer/register or something. But i think most programs are memory bound
# and a few extra instructions don't hurt.
# After all, we are buying a big prize:oo, otherwise known as sanity.
class FrameImplementation

View File

@ -1,7 +1,9 @@
module Virtual
# This implements instance variable get (not the opposite of Set, such a thing does not exists, their slots)
# This implements instance variable get (not the opposite of Set,
# such a thing does not exists, their slots)
# Ivar get needs to acces the layout, find the index of the name, and shuffle the data to return register
# Ivar get needs to acces the layout, find the index of the name,
# and shuffle the data to return register
# In short it's so complicated we implement it in ruby and stick the implementation here
class GetImplementation
def run block

View File

@ -4,9 +4,11 @@ module Virtual
# That off course opens up an endless loop possibility that we stop by
# implementing Class and Module methods
# Note: I find it slightly unsemetrical that the NewMessage object needs to be created before this instruction
# This is because all expressions create a (return) value and that return value is overwritten by the next
# expression unless saved. And since the message is the place to save it it needs to exist. qed
# Note: I find it slightly unsymmetrical that the NewMessage object needs to be created
# before this instruction.
# This is because all expressions create a (return) value and that return value is
# overwritten by the next expression unless saved.
# And since the message is the place to save it it needs to exist. qed
class SendImplementation
def run block
block.codes.dup.each do |code|

View File

@ -4,18 +4,22 @@ module Virtual
# Data in a Block is usefull in the same way data in objects is. Plocks being otherwise just code.
#
# But the concept is not quite straigtforwrd: If one thinks of a Plock embedded in a normal method,
# the a data in the Plock would be static data. In OO terms this comes quite close to a Proc, if the data is the local
# variables. Quite possibly they shall be used to implement procs, but that is not the direction now.
# the a data in the Plock would be static data. In OO terms this comes quite close to a Proc,
# if the data is the local variables.
# Quite possibly they shall be used to implement procs, but that is not the direction now.
#
# For now we use Plocks behaind the scenes as it were. In the code that you never see, method invocation mainly.
#
# In terms of implementation the Plock is a Block with data (Not too much data, mainly a couple of references).
# The block writes it's instructions as normal, but a jump is inserted as the last instruction. The jump is to the
# next block, over the data that is inserted after the block code (and so before the next)
# For now we use Plocks behaind the scenes as it were. In the code that you never see,
# method invocation mainly.
#
# In terms of implementation the Plock is a Block with data
# (Not too much data, mainly a couple of references).
# The block writes it's instructions as normal, but a jump is inserted as the last instruction.
# The jump is to the next block, over the data that is inserted after the block code
# (and so before the next)
#
# It follows that Plocks should be linear blocks.
class Plock < Block
def initialize(name , method , next_block )
super
@data = []

View File

@ -2,7 +2,9 @@ require_relative "type"
module Positioned
def position
raise "position accessed but not set at #{mem_length} for #{self.inspect[0...500]}" if @position == nil
if @position == nil
raise "position accessed but not set at #{mem_length} for #{self.inspect[0...500]}"
end
@position
end
def set_position pos