Merge branch 'new_mom'

This commit is contained in:
Torsten Rüger 2019-08-14 11:14:58 +03:00
commit 1dad6dee7a
150 changed files with 2368 additions and 921 deletions

60
lib/mom/block_compiler.rb Normal file
View File

@ -0,0 +1,60 @@
module Mom
# A BlockCompiler is much like a MehtodCompiler, exept for blocks
#
class BlockCompiler < CallableCompiler
attr_reader :block , :mom_instructions
alias :block :callable
def initialize( block , method)
@method = method
super(block)
end
def source_name
"#{@method.self_type.name}.init"
end
def to_risc(in_method)
risc_compiler = Risc::BlockCompiler.new(@callable , in_method , mom_instructions)
instructions_to_risc(risc_compiler)
#recursive blocks not done
risc_compiler
end
# resolve the type of the slot, by inferring from it's name, using the type
# scope related slots are resolved by the compiler by method/block
#
# This mainly calls super, and only for :caller adds extra info
# Using the info, means assuming that the block is not passed around (FIXME in 2020)
def slot_type( slot , type)
new_type = super
if slot == :caller
extra_info = { type_frame: @method.frame_type ,
type_arguments: @method.arguments_type ,
type_self: @method.self_type}
end
return new_type , extra_info
end
# determine how given name need to be accsessed.
# For blocks the options are args or frame
# or then the methods arg or frame
def slot_type_for(name)
if @callable.arguments_type.variable_index(name)
slot_def = [:arguments]
elsif @callable.frame_type.variable_index(name)
slot_def = [:frame]
elsif @method.arguments_type.variable_index(name)
slot_def = [:caller , :caller ,:arguments ]
elsif @method.frame_type.variable_index(name)
slot_def = [:caller ,:caller , :frame ]
elsif
raise "no variable #{name} , need to resolve at runtime"
end
slot_def << name
end
end
end

View File

@ -1,20 +1,32 @@
require_relative "builtin/compile_helper"
module Mom
module Builtin
module CompileHelper
def compiler_for( clazz_name , method_name , arguments , locals = {})
frame = Parfait::NamedList.type_for( locals )
args = Parfait::NamedList.type_for( arguments )
Mom::MethodCompiler.compiler_for_class(clazz_name , method_name , args, frame )
end
end
end
end
require_relative "builtin/space"
require_relative "builtin/integer"
require_relative "builtin/object"
require_relative "builtin/word"
module Risc
module Mom
module Builtin
# classes have booted, now create a minimal set of functions
# minimal means only that which can not be coded in ruby
# Methods are grabbed from respective modules by sending the method name.
# This should return the implementation of the method (ie a method object),
# This should return the implementation of the method (ie a method compiler),
# not actually try to implement it(as that's impossible in ruby)
#
# When no main has been compiled, we will add an empty main (for testing)
#
def self.boot_functions(add_main = false)
# We create an empty main for init to jump to, if no code is compiled, that just returns
# See Builtin directory readme and module
def self.boot_functions()
# TODO go through the virtual parfait layer and adjust function names
# to what they really are
compilers = []
@ -25,8 +37,8 @@ module Risc
end
obj_type = space.get_type_by_class_name(:Object)
[ :get_internal_word , :set_internal_word , :_method_missing,
:exit , :__init__].each do |f|
[ :__init__ , :exit , :_method_missing, :get_internal_word ,
:set_internal_word ].each do |f|
compilers << compiler_for( obj_type , Object , f)
end
@ -39,7 +51,7 @@ module Risc
Risc.operators.each do |op|
compilers << operator_compiler( int_type , op)
end
[:putint, :div4, :div10 , :<,:<= , :>=, :>].each do |f| #div4 is just a forward declaration
[ :div4, :<,:<= , :>=, :> , :div10 ].each do |f| #div4 is just a forward declaration
compilers << compiler_for( int_type , Integer , f)
end
compilers

View File

@ -0,0 +1,34 @@
module Mom
module Builtin
class Comparison < ::Mom::Instruction
attr_reader :operator
def initialize(name , operator)
super(name)
@operator = operator
end
def to_risc(compiler)
builder = compiler.builder(compiler.source)
operator = @operator # make accessible in block
builder.build do
integer! << message[:receiver]
integer.reduce_int
integer_reg! << message[:arguments]
integer_reg << integer_reg[Parfait::NamedList.type_length + 0] #"other" is at index 0
integer_reg.reduce_int
swap_names(:integer , :integer_reg) if(operator.to_s.start_with?('<') )
integer.op :- , integer_reg
if_minus false_label
if_zero( false_label ) if operator.to_s.length == 1
object! << Parfait.object_space.true_object
branch merge_label
add_code false_label
object << Parfait.object_space.false_object
add_code merge_label
message[:return_value] << object
end
return compiler
end
end
end
end

65
lib/mom/builtin/div10.rb Normal file
View File

@ -0,0 +1,65 @@
module Mom
module Builtin
class Div10 < ::Mom::Instruction
def to_risc(compiler)
s = "div_10 "
builder = compiler.builder(compiler.source)
integer_tmp = builder.allocate_int
builder.build do
integer_self! << message[:receiver]
integer_self.reduce_int
integer_1! << integer_self
integer_reg! << integer_self
integer_const! << 1
integer_1.op :>> , integer_const
integer_const << 2
integer_reg.op :>> , integer_const
integer_reg.op :+ , integer_1
integer_const << 4
integer_1 << integer_reg
integer_reg.op :>> , integer_1
integer_reg.op :+ , integer_1
integer_const << 8
integer_1 << integer_reg
integer_1.op :>> , integer_const
integer_reg.op :+ , integer_1
integer_const << 16
integer_1 << integer_reg
integer_1.op :>> , integer_const
integer_reg.op :+ , integer_1
integer_const << 3
integer_reg.op :>> , integer_const
integer_const << 10
integer_1 << integer_reg
integer_1.op :* , integer_const
integer_self.op :- , integer_1
integer_1 << integer_self
integer_const << 6
integer_1.op :+ , integer_const
integer_const << 4
integer_1.op :>> , integer_const
integer_reg.op :+ , integer_1
integer_tmp[Parfait::Integer.integer_index] << integer_reg
message[:return_value] << integer_tmp
end
return compiler
end
end
end
end

19
lib/mom/builtin/div4.rb Normal file
View File

@ -0,0 +1,19 @@
module Mom
module Builtin
class Div4 < ::Mom::Instruction
def to_risc(compiler)
builder = compiler.builder(compiler.source)
integer_tmp = builder.allocate_int
builder.build do
integer_self! << message[:receiver]
integer_self.reduce_int
integer_1! << 2
integer_self.op :>> , integer_1
integer_tmp[Parfait::Integer.integer_index] << integer_self
message[:return_value] << integer_tmp
end
return compiler
end
end
end
end

12
lib/mom/builtin/exit.rb Normal file
View File

@ -0,0 +1,12 @@
module Mom
module Builtin
class Exit < ::Mom::Instruction
def to_risc(compiler)
builder = compiler.builder(compiler.source)
builder.prepare_int_return # makes integer_tmp variable as return
Builtin.exit_sequence(builder)
return compiler
end
end
end
end

View File

@ -0,0 +1,20 @@
module Mom
module Builtin
class GetInternalByte < ::Mom::Instruction
def to_risc(compiler)
builder = compiler.builder(compiler.source)
integer_tmp = builder.allocate_int
builder.build do
object! << message[:receiver]
integer! << message[:arguments]
integer << integer[Parfait::NamedList.type_length + 0] #"at" is at index 0
integer.reduce_int
object <= object[integer]
integer_tmp[Parfait::Integer.integer_index] << object
message[:return_value] << integer_tmp
end
return compiler
end
end
end
end

View File

@ -0,0 +1,16 @@
module Mom
module Builtin
class GetInternalWord < ::Mom::Instruction
def to_risc(compiler)
compiler.builder(compiler.source).build do
object! << message[:receiver]
integer! << message[:arguments]
integer << integer[Parfait::NamedList.type_length + 0] #"at" is at index 0
integer.reduce_int
object << object[integer]
message[:return_value] << object
end
end
end
end
end

36
lib/mom/builtin/init.rb Normal file
View File

@ -0,0 +1,36 @@
module Mom
module Builtin
class Init < ::Mom::Instruction
def to_risc(compiler)
builder = compiler.builder(compiler.source)
builder.build do
factory! << Parfait.object_space.get_factory_for(:Message)
message << factory[:next_object]
next_message! << message[:next_message]
factory[:next_object] << next_message
end
Mom::MessageSetup.new(Parfait.object_space.get_main).build_with( builder )
builder.build do
message << message[:next_message]
space? << Parfait.object_space
message[:receiver] << space
end
exit_label = Risc.label(compiler.source , "#{compiler.receiver_type.object_class.name}.#{compiler.source.name}" )
ret_tmp = compiler.use_reg(:Label).set_builder(builder)
builder.build do
ret_tmp << exit_label
message[:return_address] << ret_tmp
add_code Risc.function_call( "__init__ issue call" , Parfait.object_space.get_main)
add_code exit_label
end
compiler.reset_regs
Builtin.exit_sequence(builder)
return compiler
end
end
end
end

View File

@ -0,0 +1,85 @@
require_relative "div4"
require_relative "div10"
require_relative "operator"
require_relative "comparison"
module Mom
module Builtin
# integer related kernel functions
# all these functions (return the function they implement) assume interger input
# Also the returned integer object has to be passed in to avoid having to allocate it.
#
# This means the methods will have to be renamed at some point and wrapped
module Integer
module ClassMethods
include CompileHelper
# div by 4, ie shift right by 2
# Mostly created for testing at this point, as it is short
# return new int with result
def div4(context)
compiler = compiler_for(:Integer,:div4 ,{})
compiler.add_code Div4.new("div4")
return compiler
end
# implemented by the comparison
def >( context )
comparison( :> )
end
# implemented by the comparison
def <( context )
comparison( :< )
end
# implemented by the comparison
def <=( context )
comparison( :<= )
end
# implemented by the comparison
def >=( context )
comparison( :>= )
end
# all (four) comparison operation are quite similar and implemented here
# - reduce the ints (assume int as input)
# - subtract the fixnums
# - check for minus ( < and > )
# - also check for zero (<= and >=)
# - load true or false object into return, depending on check
# - return
def comparison( operator )
compiler = compiler_for(:Integer, operator ,{other: :Integer })
compiler.add_code Comparison.new("comparison" , operator)
return compiler
end
# implemented all known binary operators that map straight to machine codes
# this function (similar to comparison):
# - unpacks the intergers to fixnum
# - applies the operator (at a risc level)
# - gets a new integer and stores the result
# - returns the new int
def operator_method( op_sym )
compiler = compiler_for(:Integer, op_sym ,{other: :Integer })
compiler.add_code Operator.new("operator" , op_sym)
return compiler
end
# as the name suggests, this devides the integer (self) by ten
#
# This version is lifted from some arm assembler tricks and is _much_
# faster than the general div versions. I think it was about three
# times less instructions. Useful for itos
#
# In fact it is possible to generate specific div function for any given
# integer and some are even more faster (as eg div4).
def div10( context )
compiler = compiler_for(:Integer,:div10 ,{})
compiler.add_code Div10.new("div10")
return compiler
end
end
extend ClassMethods
end
end
end

View File

@ -0,0 +1,12 @@
module Mom
module Builtin
class MethodMissing < ::Mom::Instruction
def to_risc(compiler)
builder = compiler.builder(compiler.source)
builder.prepare_int_return # makes integer_tmp variable as return
Builtin.emit_syscall( builder , :exit )
return compiler
end
end
end
end

107
lib/mom/builtin/object.rb Normal file
View File

@ -0,0 +1,107 @@
require_relative "get_internal_word"
require_relative "set_internal_word"
require_relative "method_missing"
require_relative "init"
require_relative "exit"
module Mom
module Builtin
class Object
module ClassMethods
include CompileHelper
# self[index] basically. Index is the first arg
# return is stored in return_value
def get_internal_word( context )
compiler = compiler_for(:Object , :get_internal_word ,{at: :Integer})
compiler.add_code GetInternalWord.new("get_internal_word")
return compiler
end
# self[index] = val basically. Index is the first arg , value the second
# return the value passed in
def set_internal_word( context )
compiler = compiler_for(:Object , :set_internal_word , {at: :Integer, value: :Object} )
compiler.add_code SetInternalWord.new("set_internal_word")
return compiler
end
# every object needs a method missing.
# Even if it's just this one, sys_exit (later raise)
def _method_missing( context )
compiler = compiler_for(:Object,:method_missing ,{})
compiler.add_code MethodMissing.new("missing")
return compiler
end
# this is the really really first place the machine starts (apart from the jump here)
# it isn't really a function, ie it is jumped to (not called), exits and may not return
# so it is responsible for initial setup:
# - load fist message, set up Space as receiver
# - call main, ie set up message for that etc
# - exit (exit_sequence) which passes a machine int out to c
def __init__( context )
compiler = Mom::MethodCompiler.compiler_for_class(:Object,:__init__ ,
Parfait::NamedList.type_for({}) , Parfait::NamedList.type_for({}))
compiler.add_code Init.new("missing")
return compiler
end
# the exit function
# mainly calls exit_sequence
def exit( context )
compiler = compiler_for(:Object,:exit ,{})
compiler.add_code Exit.new("exit")
return compiler
end
end
extend ClassMethods
end
# emit the syscall with given name
# there is a Syscall instruction, but the message has to be saved and restored
def self.emit_syscall( builder , name )
save_message( builder )
builder.add_code Risc::Syscall.new("emit_syscall(#{name})", name )
restore_message(builder)
return unless (@clazz and @method)
builder.add_code Risc.label( "#{@clazz.name}.#{@message.name}" , "return_syscall" )
end
# a sort of inline version of exit method.
# Used by exit and __init__ (so it doesn't have to call it)
# Assumes int return value and extracts the fixnum for process exit code
def self.exit_sequence(builder)
save_message( builder )
builder.build do
message << message[:return_value]
message.reduce_int
add_code Risc::Syscall.new("emit_syscall(exit)", :exit )
end
end
# save the current message, as the syscall destroys all context
#
# This relies on linux to save and restore all registers
#
def self.save_message(builder)
r8 = Risc::RegisterValue.new( :r8 , :Message).set_builder(builder)
builder.build {r8 << message}
end
# restore the message that we save in r8
# before th restore, the syscall return, a fixnum, is saved
# The caller of this method is assumed to caal prepare_int_return
# so that the return value already has an integer instance
# This instance is filled with os return value
def self.restore_message(builder)
r8 = Risc::RegisterValue.new( :r8 , :Message)
builder.build do
integer_reg! << message
message << r8
integer_2! << message[:return_value]
integer_2[Parfait::Integer.integer_index] << integer_reg
end
end
end
end

View File

@ -0,0 +1,28 @@
module Mom
module Builtin
class Operator < ::Mom::Instruction
attr_reader :operator
def initialize(name , operator)
super(name)
@operator = operator
end
def to_risc(compiler)
builder = compiler.builder(compiler.source)
integer_tmp = builder.allocate_int
operator = @operator # make accessible in block
builder.build do
integer! << message[:receiver]
integer.reduce_int
integer_reg! << message[:arguments]
integer_reg << integer_reg[Parfait::NamedList.type_length + 0] #"other" is at index 0
integer_reg.reduce_int
integer.op operator , integer_reg
integer_tmp[Parfait::Integer.integer_index] << integer
message[:return_value] << integer_tmp
end
return compiler
end
end
end
end

View File

@ -0,0 +1,16 @@
module Mom
module Builtin
class Putstring < ::Mom::Instruction
def to_risc(compiler)
builder = compiler.builder(compiler.source)
builder.prepare_int_return # makes integer_tmp variable as return
builder.build do
word! << message[:receiver]
integer! << word[Parfait::Word.get_length_index]
end
Mom::Builtin.emit_syscall( builder , :putstring )
compiler
end
end
end
end

View File

@ -0,0 +1,19 @@
module Mom
module Builtin
class SetInternalByte < ::Mom::Instruction
def to_risc(compiler)
compiler.builder(compiler.source).build do
word! << message[:receiver]
integer! << message[:arguments]
integer_reg! << integer[Parfait::NamedList.type_length + 1] #"value" is at index 1
message[:return_value] << integer_reg
integer << integer[Parfait::NamedList.type_length + 0] #"at" is at index 0
integer.reduce_int
integer_reg.reduce_int
word[integer] <= integer_reg
end
return compiler
end
end
end
end

View File

@ -0,0 +1,18 @@
module Mom
module Builtin
class SetInternalWord < ::Mom::Instruction
def to_risc(compiler)
compiler.builder(compiler.source).build do
object! << message[:receiver]
integer! << message[:arguments]
object_reg! << integer[Parfait::NamedList.type_length + 1] #"value" is at index 1
integer << integer[Parfait::NamedList.type_length + 0] #"at" is at index 0
integer.reduce_int
object[integer] << object_reg
message[:return_value] << object_reg
end
return compiler
end
end
end
end

View File

@ -1,4 +1,4 @@
module Risc
module Mom
module Builtin
class Space
module ClassMethods
@ -8,7 +8,6 @@ module Risc
# defined here as empty, to be redefined
def main(context)
compiler = compiler_for(:Space , :main ,{args: :Integer})
compiler.add_mom( Mom::ReturnSequence.new)
return compiler
end

43
lib/mom/builtin/word.rb Normal file
View File

@ -0,0 +1,43 @@
require_relative "get_internal_byte"
require_relative "set_internal_byte"
require_relative "putstring"
module Mom
module Builtin
module Word
module ClassMethods
include CompileHelper
# wrapper for the syscall
# io/file currently hardcoded to stdout
# set up registers for syscall, ie
# - pointer in r1
# - length in r2
# - emit_syscall (which does the return of an integer, see there)
def putstring( context)
compiler = compiler_for(:Word , :putstring ,{})
compiler.add_code Putstring.new("putstring")
return compiler
end
# self[index] basically. Index is the first arg > 0
# return a word sized new int, in return_value
#
# Note: no index (or type) checking. Method should be internal and check before.
# Which means the returned integer could be passed in, instead of allocated.
def get_internal_byte( context)
compiler = compiler_for(:Word , :get_internal_byte , {at: :Integer})
compiler.add_code GetInternalByte.new("get_internal_byte")
return compiler
end
# self[index] = val basically. Index is the first arg ( >0 , unchecked),
# value the second, which is also returned
def set_internal_byte( context )
compiler = compiler_for(:Word, :set_internal_byte , {at: :Integer , value: :Integer} )
compiler.add_code SetInternalByte.new("set_internal_byte")
return compiler
end
end
extend ClassMethods
end
end
end

View File

@ -0,0 +1,104 @@
module Mom
# CallableCompiler is used to generate mom instructions. It is an abstact base
# class shared by BlockCompiler and MethodCompiler
# - mom_instructions: The sequence of mom level instructions that mom was compiled to
# Instructions derive from class Instruction and form a linked list
class CallableCompiler
def initialize( callable )
@callable = callable
@constants = []
@block_compilers = []
@mom_instructions = Label.new(source_name, source_name)
@current = start = @mom_instructions
add_code Label.new( source_name, "return_label")
add_code Mom::ReturnSequence.new(source_name)
add_code Label.new( source_name, "unreachable")
@current = start
end
attr_reader :mom_instructions , :constants , :block_compilers , :callable , :current
def return_label
@mom_instructions.each do |ins|
next unless ins.is_a?(Label)
return ins if ins.name == "return_label"
end
end
# add a constant (which get created during compilation and need to be linked)
def add_constant(const)
raise "Must be Parfait #{const}" unless const.is_a?(Parfait::Object)
@constants << const
end
# translate to Risc, ie a Risc level CallableCompiler
# abstract functon that needs to be implemented by Method/BlockCompiler
def to_risc
raise "abstract in #{self.class}"
end
# add a risc instruction after the current (insertion point)
# the added instruction will become the new insertion point
def add_code( instruction )
raise "Not an instruction:#{instruction.to_s}:#{instruction.class.name}" unless instruction.is_a?(Mom::Instruction)
new_current = instruction.last #after insertion this point is lost
@current.insert(instruction) #insert after current
@current = new_current
self
end
# resolve the type of the slot, by inferring from it's name, using the type
# scope related slots are resolved by the compiler by method/block
def slot_type( slot , type)
case slot
when :frame
new_type = self.frame_type
when :arguments
new_type = self.arg_type
when :receiver
new_type = self.receiver_type
when Symbol
new_type = type.type_for(slot)
raise "Not found object #{slot}: in #{type}" unless new_type
else
raise "Not implemented object #{slot}:#{slot.class}"
end
#puts "RESOLVE in #{@type.class_name} #{slot}->#{type}"
return new_type
end
# return the frame type, ie the blocks frame type
def frame_type
@callable.frame_type
end
# return the frame type, ie the blocks arguments type
def arg_type
@callable.arguments_type
end
# return the frame type, ie the blocks self_type
def receiver_type
@callable.self_type
end
private
# convert al instruction to risc
# method is called by Method/BlockCompiler from to_risc
def instructions_to_risc(risc_compiler)
instruction = mom_instructions.next
while( instruction )
raise "whats this a #{instruction}" unless instruction.is_a?(Mom::Instruction)
#puts "adding mom #{instruction.to_s}:#{instruction.next.to_s}"
instruction.to_risc( risc_compiler )
risc_compiler.reset_regs
#puts "adding risc #{risc.to_s}:#{risc.next.to_s}"
instruction = instruction.next
end
end
end
end

View File

@ -13,6 +13,17 @@ module Mom
class Instruction
include Util::List
def initialize( source , nekst = nil )
@source = source
@next = nekst
return unless source
unless source.is_a?(String) or
source.is_a?(Vool::Statement)
raise "Source must be string or Instruction, not #{source.class}"
end
end
attr_reader :source
# to_risc, like the name says, converts the instruction to it's Risc equivalent.
# The Risc machine is basically a simple register machine (kind of arm).
# In other words Mom is the higher abstraction and so mom instructions convert
@ -27,18 +38,18 @@ module Mom
end
require_relative "label"
require_relative "check"
require_relative "basic_values"
require_relative "simple_call"
require_relative "dynamic_call"
require_relative "block_yield"
require_relative "resolve_method"
require_relative "truth_check"
require_relative "not_same_check"
require_relative "jump"
require_relative "return_jump"
require_relative "slot_load"
require_relative "return_sequence"
require_relative "message_setup"
require_relative "argument_transfer"
require_relative "instruction/label"
require_relative "instruction/check"
require_relative "instruction/basic_values"
require_relative "instruction/simple_call"
require_relative "instruction/dynamic_call"
require_relative "instruction/block_yield"
require_relative "instruction/resolve_method"
require_relative "instruction/truth_check"
require_relative "instruction/not_same_check"
require_relative "instruction/jump"
require_relative "instruction/return_jump"
require_relative "instruction/slot_load"
require_relative "instruction/return_sequence"
require_relative "instruction/message_setup"
require_relative "instruction/argument_transfer"

View File

@ -22,7 +22,8 @@ module Mom
# receiver is a slot_definition
# arguments is an array of SlotLoads
def initialize( receiver,arguments )
def initialize( source , receiver,arguments )
super(source)
@receiver , @arguments = receiver , arguments
raise "Receiver not SlotDefinition #{@receiver}" unless @receiver.is_a?(SlotDefinition)
@arguments.each{|a| raise "args not SlotLoad #{a}" unless a.is_a?(SlotLoad)}
@ -35,7 +36,7 @@ module Mom
# load receiver and then each arg into the new message
# delegates to SlotLoad for receiver and to the actual args.to_risc
def to_risc(compiler)
transfer = SlotLoad.new([:message , :next_message , :receiver] , @receiver, self).to_risc(compiler)
transfer = SlotLoad.new(self.source ,[:message , :next_message , :receiver] , @receiver, self).to_risc(compiler)
compiler.reset_regs
@arguments.each do |arg|
arg.to_risc(compiler)

View File

@ -6,7 +6,10 @@ module Mom
class BlockYield < Instruction
attr :arg_index
def initialize(index)
# pass in the source (vool statement) and the index.
# The index is the argument index of the block that we call
def initialize(source , index)
super(source)
@arg_index = index
end
@ -14,6 +17,7 @@ module Mom
"BlockYield[#{arg_index}] "
end
# almost as simple as a SimpleCall, use a dynamic_jump to get there
def to_risc(compiler)
return_label = Risc.label("block_yield", "continue_#{object_id}")
index = arg_index

View File

@ -23,7 +23,8 @@ module Mom
class Label < Instruction
attr_reader :name
def initialize(name)
def initialize(source , name)
super(source)
@name = name
end
@ -32,9 +33,9 @@ module Mom
end
# generate the risc label lazily
def risc_label(comiler)
def risc_label(compiler)
@risc_label ||= Risc.label(self,name)
comiler.add_constant(@risc_label.address)
compiler.add_constant(@risc_label.address)
@risc_label
end

View File

@ -22,6 +22,7 @@ module Mom
attr_reader :method_source
def initialize(method_source)
raise "no nil" unless method_source
@method_source = method_source
end

View File

@ -15,7 +15,11 @@ module Mom
class ResolveMethod < Instruction
attr :cache_entry , :name
def initialize(name , cache_entry)
# pass in source (VoolStatement)
# name of the method (don't knwow the actaual method)
# and the cache_entry
def initialize(source , name , cache_entry)
super(source)
@name = name
@cache_entry = cache_entry
end
@ -61,7 +65,7 @@ module Mom
# temporary, need to raise really.
factory! << Parfait.object_space.get_factory_for(:Integer)
integer_tmp! << factory[:reserve]
Risc::Builtin::Object.emit_syscall( builder , :exit ) #uses integer_tmp
Mom::Builtin.emit_syscall( builder , :exit ) #uses integer_tmp
add_code ok_label
cache_entry[:cached_method] << callable_method

View File

@ -7,8 +7,20 @@ module Mom
#
class ReturnJump < Instruction
attr_reader :return_label
# pass in the source_name (string/vool_instruction) for accounting purposes
# and the return_label, where we actually jump to. This is set up by the
# method_compiler, so it is easy to find (see return_label in compiler)
def initialize( source , label )
super(source)
@return_label = label
end
# the jump quite simple resolves to an uncondition risc Branch
# we use the label that is passed in at creation
def to_risc(compiler)
compiler.add_code Risc::Branch.new(self , compiler.return_label)
compiler.add_code Risc::Branch.new(self , return_label.risc_label(compiler))
end
end

View File

@ -22,13 +22,14 @@ module Mom
# SlotDefinition (see there)
#
# @right: A SlotDefinition with slots or a Mom::Constant
# original_source: optinally another mom instruction that wil be passed down to created
# original_source: optinally another mom instruction that will be passed down to created
# risc instructions. (Because SlotLoad is often used internally in mom)
class SlotLoad < Instruction
attr_reader :left , :right , :original_source
def initialize(left , right, original_source = nil)
def initialize(source , left , right, original_source = nil)
super(source)
@left , @right = left , right
@left = SlotDefinition.new(@left.shift , @left) if @left.is_a? Array
@right = SlotDefinition.new(@right.shift , @right) if @right.is_a? Array

View File

@ -0,0 +1,91 @@
module Mom
# MethodCompiler is used to generate Mom instructions for methods
# and to instantiate the methods correctly.
class MethodCompiler < CallableCompiler
def initialize( method )
super(method)
end
def source_name
"#{@callable.self_type.name}.#{@callable.name}"
end
def get_method
@callable
end
# sometimes the method is used as source (tb reviewed)
def source
@callable
end
# drop down to risc by converting this compilers instructions to risc.
# and the doing the same for any block_compilers
def to_risc
risc_compiler = Risc::MethodCompiler.new(@callable , mom_instructions)
instructions_to_risc(risc_compiler)
block_compilers.each do |m_comp|
risc_compiler.block_compilers << m_comp.to_risc(@callable)
end
risc_compiler
end
# helper method for builtin mainly
# the class_name is a symbol, which is resolved to the instance_type of that class
#
# return compiler_for_type with the resolved type
#
def self.compiler_for_class( class_name , method_name , args , frame )
raise "create_method #{class_name}.#{class_name.class}" unless class_name.is_a? Symbol
clazz = Parfait.object_space.get_class_by_name! class_name
compiler_for_type( clazz.instance_type , method_name , args , frame)
end
def add_method_to( target )
target.add_method( @callable )
end
def create_block(arg_type , frame_type)
@callable.create_block(arg_type ,frame_type)
end
# create a method for the given type ( Parfait type object)
# method_name is a Symbol
# args a hash that will be converted to a type
# the created method is set as the current and the given type too
# return the compiler
def self.compiler_for_type( type , method_name , args , frame)
raise "create_method #{type.inspect} is not a Type" unless type.is_a? Parfait::Type
raise "Args must be Type #{args}" unless args.is_a?(Parfait::Type)
raise "create_method #{method_name}.#{method_name.class}" unless method_name.is_a? Symbol
method = type.create_method( method_name , args , frame)
self.new(method)
end
# determine how given name need to be accsessed.
# For methods the options are args or frame
def slot_type_for(name)
if @callable.arguments_type.variable_index(name)
type = :arguments
else
type = :frame
end
[type , name]
end
def add_block_compiler(compiler)
@block_compilers << compiler
end
# return true or false if the given name is in scope (arg/local)
def in_scope?(name)
ret = true if @callable.arguments_type.variable_index(name)
ret = @callable.frame_type.variable_index(name) unless ret
ret
end
end
end

View File

@ -12,7 +12,15 @@
# Machine capabilities (instructions) for basic operations. Use of macros for higher level.
module Mom
# boot bubiltin function (subject to change)
def self.boot!
Builtin.boot_functions
end
end
require_relative "instruction/instruction.rb"
require_relative "mom_compiler"
require_relative "instruction.rb"
require_relative "mom_collection"
require_relative "callable_compiler"
require_relative "method_compiler"
require_relative "block_compiler"
require_relative "builtin"

47
lib/mom/mom_collection.rb Normal file
View File

@ -0,0 +1,47 @@
module Mom
# The Compiler/Collection for the Mom level is a collection of Mom level Method
# compilers These will transform to Risc MethodCompilers on the way down.
#
# As RubyCompiler pools source at the vool level, when several classes are compiled
# from vool to mom, several MomCompilers get instantiated. They must be merged before
# proceeding with translate. Thus we have a append method.
#
class MomCollection
attr_reader :method_compilers
# Initialize with an array of risc MethodCompilers
def initialize(compilers = [])
@method_compilers = compilers
end
# lazily instantiate the compilers for boot functions
# (in the hope of only booting the functions once)
def boot_compilers
@boot_compilers ||= Mom::Builtin.boot_functions
end
# Return all compilers, namely the MethodCompilers passed in, plus the
# boot_function's compilers (boot_compilers)
def compilers
@method_compilers + boot_compilers
end
# Append another MomCompilers method_compilers to this one.
def append(mom_compiler)
@method_compilers += mom_compiler.method_compilers
self
end
def to_risc( )
riscs = compilers.collect do | mom_c |
mom_c.to_risc
end
# to_risc all compilers
# for each suffling constnts and fist label, then all instructions (see below)
# then create risc collection
Risc::RiscCollection.new(riscs)
end
end
end

View File

@ -3,10 +3,10 @@ module Parfait
# A Block is a callable object, much like a CallableMethod.
# Surprisingly similar in fact, as the block is really only missing the name.
#
# The difference lies mostly in the way they are compiled
# The difference lies mostly in the way they are compiled (scope and return)
#
# Also both have a list of blocks defined in their scope. But this is
# notimplemented for blocks yet
# not implemented for blocks yet
#
class Block < Callable

View File

@ -1,14 +1,15 @@
module Parfait
# An Object is really a hash like structure. It is dynamic and
# An Object is conceptually 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 in both time and space.
# Instead we store only the values, and access them by index.
# One could (like mri), store the names in each object, but that is wasteful in both
# time and space.
# Instead we store only the values, and access them by index (bit like c++).
# The Type allows the mapping of names to index.
# The Type of an object describes the memory layout of the object. In a c analogy, it is the
# information defined in a struct.
# The Type of an object describes the memory layout of the object. In a c analogy,
# it is the information defined in a struct.
# The Type is a list of the names of instance variables, and their value types (int etc).
#
# Every object has a Type to describe it, so it's *first* instance variable is **always**
@ -21,14 +22,13 @@ module Parfait
# But Objects must also be able to carry methods themselves (ruby calls singleton_methods)
# and those too are stored in the Type (both type and class include behaviour)
# The object is an List of values of length n
# The Type is a list of n names and n types that describe the values stored in an actual object.
# The object is an "List" (memory location) of values of length n
# The Type is a list of n names and n types that describe the values stored in an
# actual object.
# Together they turn the object into a hash like structure
# For types to be a useful concept, they have to be unique and immutable. Any "change", like adding
# a name/type pair, will result in a new instance.
# For types to be a useful concept, they have to be unique and immutable. Any "change",
# like adding a name/type pair, will result in a new type instance.
# The Type class carries a hash of types of the systems, which is used to ensure that
# there is only one instance of every type. Hash and equality are defined on type

View File

@ -5,7 +5,7 @@ module Parfait
# Type objects are already created for args and locals, but the main attribute
# is the source, which is a Vool::Statement
#
# Classes store VoolMethods, while Types store CallableMethod
# Classes store VoolMethods, while Types store Risc::CallableMethod
# A Type referes to a Class , but a Class (interface) is implemented by many types
# as it changes during the course of it's life. Types do not change. Objects have
# type, and so only indirectly a class.
@ -14,7 +14,7 @@ module Parfait
attr :type, :name , :args_type , :frame_type
attr_reader :source
def initialize(name , args_type , frame_type , source )
self.name = name
self.args_type = args_type
@ -34,9 +34,9 @@ module Parfait
def compiler_for(self_type)
callable_method = create_callable_method(self_type)
compiler = Risc::MethodCompiler.new( callable_method )
compiler = Mom::MethodCompiler.new( callable_method )
head = @source.to_mom( compiler )
compiler.add_mom(head)
compiler.add_code(head)
compiler
end
end

View File

@ -12,15 +12,11 @@ class Class
end
end
# The Risc Machine, is an abstract machine with registers. Think of it as an arm machine with
# normal instruction names. It is not however an abstraction of existing hardware, but only
# of that subset that we need.
# See risc/Readme
module Risc
# module method to reset, and init
def self.boot!
Position.clear_positions
Builtin.boot_functions
end
end
@ -35,6 +31,7 @@ require_relative "risc/callable_compiler"
require_relative "risc/method_compiler"
require_relative "risc/block_compiler"
require_relative "risc/assembler"
require_relative "risc/risc_collection"
class Integer
def fits_u8?
@ -46,5 +43,4 @@ end
require_relative "risc/instruction"
require_relative "risc/register_value"
require_relative "risc/text_writer"
require_relative "risc/builtin"
require_relative "risc/builder"

View File

@ -4,16 +4,15 @@ module Risc
#
class BlockCompiler < CallableCompiler
attr_reader :block , :risc_instructions , :constants
alias :block :callable
attr_reader :block , :risc_instructions , :constants , :in_method
def initialize( block , method)
@method = method
super(block)
def initialize( block , in_method , mom_label)
@in_method = in_method
super(block , mom_label)
end
def source_name
"#{@method.self_type.name}.init"
"#{@in_method.self_type.name}.init"
end
# resolve the type of the slot, by inferring from it's name, using the type
@ -24,9 +23,9 @@ module Risc
def slot_type( slot , type)
new_type = super
if slot == :caller
extra_info = { type_frame: @method.frame_type ,
type_arguments: @method.arguments_type ,
type_self: @method.self_type}
extra_info = { type_frame: @in_method.frame_type ,
type_arguments: @in_method.arguments_type ,
type_self: @in_method.self_type}
end
return new_type , extra_info
end
@ -38,9 +37,9 @@ module Risc
slot_def = [:arguments]
elsif @callable.frame_type.variable_index(name)
slot_def = [:frame]
elsif @method.arguments_type.variable_index(name)
elsif @in_method.arguments_type.variable_index(name)
slot_def = [:caller , :caller ,:arguments ]
elsif @method.frame_type.variable_index(name)
elsif @in_method.frame_type.variable_index(name)
slot_def = [:caller ,:caller , :frame ]
elsif
raise "no variable #{name} , need to resolve at runtime"

View File

@ -4,6 +4,12 @@ module Risc
#
# The code is added to the method_compiler.
#
# Basically this allows to many Risc instructions with extremely readable code.
# example:
# space << Parfait.object_space # load constant
# message[:receiver] << space #make current message's (r0) receiver the space
# See http://ruby-x.org/rubyx/builder.html for details
#
class Builder
attr_reader :built , :compiler

View File

@ -1,14 +0,0 @@
module Risc
module Builtin
module CompileHelper
def compiler_for( clazz_name , method_name , arguments , locals = {})
frame = Parfait::NamedList.type_for( locals )
args = Parfait::NamedList.type_for( arguments )
MethodCompiler.compiler_for_class(clazz_name , method_name , args, frame )
end
end
end
end

View File

@ -1,183 +0,0 @@
module Risc
module Builtin
# integer related kernel functions
# all these functions (return the function they implement) assume interger input
# Also the returned integer object has to be passed in to avoid having to allocate it.
#
# This means the methods will have to be renamed at some point and wrapped
module Integer
module ClassMethods
include CompileHelper
# div by 4, ie shift right by 2
# Mostly created for testing at this point, as it is short
# return new int with result
def div4(context)
compiler = compiler_for(:Integer,:div4 ,{})
builder = compiler.builder(compiler.source)
integer_tmp = builder.allocate_int
builder.build do
integer_self! << message[:receiver]
integer_self.reduce_int
integer_1! << 2
integer_self.op :>> , integer_1
integer_tmp[Parfait::Integer.integer_index] << integer_self
message[:return_value] << integer_tmp
end
compiler.add_mom( Mom::ReturnSequence.new)
return compiler
end
# implemented by the comparison
def >( context )
comparison( :> )
end
# implemented by the comparison
def <( context )
comparison( :< )
end
# implemented by the comparison
def <=( context )
comparison( :<= )
end
# implemented by the comparison
def >=( context )
comparison( :>= )
end
# all (four) comparison operation are quite similar and implemented here
# - reduce the ints (assume int as input)
# - subtract the fixnums
# - check for minus ( < and > )
# - also check for zero (<= and >=)
# - load true or false object into return, depending on check
# - return
def comparison( operator )
compiler = compiler_for(:Integer, operator ,{other: :Integer })
builder = compiler.builder(compiler.source)
builder.build do
integer! << message[:receiver]
integer.reduce_int
integer_reg! << message[:arguments]
integer_reg << integer_reg[Parfait::NamedList.type_length + 0] #"other" is at index 0
integer_reg.reduce_int
swap_names(:integer , :integer_reg) if(operator.to_s.start_with?('<') )
integer.op :- , integer_reg
if_minus false_label
if_zero( false_label ) if operator.to_s.length == 1
object! << Parfait.object_space.true_object
branch merge_label
add_code false_label
object << Parfait.object_space.false_object
add_code merge_label
message[:return_value] << object
end
compiler.add_mom( Mom::ReturnSequence.new)
return compiler
end
# not implemented, would need a itos and that needs "new" (wip)
def putint(context)
compiler = compiler_for(:Integer,:putint ,{})
compiler.add_mom( Mom::ReturnSequence.new)
return compiler
end
# implemented all known binary operators that map straight to machine codes
# this function (similar to comparison):
# - unpacks the intergers to fixnum
# - applies the operator (at a risc level)
# - gets a new integer and stores the result
# - returns the new int
def operator_method( op_sym )
compiler = compiler_for(:Integer, op_sym ,{other: :Integer })
builder = compiler.builder(compiler.source)
integer_tmp = builder.allocate_int
builder.build do
integer! << message[:receiver]
integer.reduce_int
integer_reg! << message[:arguments]
integer_reg << integer_reg[Parfait::NamedList.type_length + 0] #"other" is at index 0
integer_reg.reduce_int
integer.op op_sym , integer_reg
integer_tmp[Parfait::Integer.integer_index] << integer
message[:return_value] << integer_tmp
end
compiler.add_mom( Mom::ReturnSequence.new)
return compiler
end
# as the name suggests, this devides the integer (self) by ten
#
# This version is lifted from some arm assembler tricks and is _much_
# faster than the general div versions. I think it was about three
# times less instructions. Useful for itos
#
# In fact it is possible to generate specific div function for any given
# integer and some are even more faster (as eg div4).
def div10( context )
s = "div_10 "
compiler = compiler_for(:Integer,:div10 ,{})
builder = compiler.builder(compiler.source)
integer_tmp = builder.allocate_int
builder.build do
integer_self! << message[:receiver]
integer_self.reduce_int
integer_1! << integer_self
integer_reg! << integer_self
integer_const! << 1
integer_1.op :>> , integer_const
integer_const << 2
integer_reg.op :>> , integer_const
integer_reg.op :+ , integer_1
integer_const << 4
integer_1 << integer_reg
integer_reg.op :>> , integer_1
integer_reg.op :+ , integer_1
integer_const << 8
integer_1 << integer_reg
integer_1.op :>> , integer_const
integer_reg.op :+ , integer_1
integer_const << 16
integer_1 << integer_reg
integer_1.op :>> , integer_const
integer_reg.op :+ , integer_1
integer_const << 3
integer_reg.op :>> , integer_const
integer_const << 10
integer_1 << integer_reg
integer_1.op :* , integer_const
integer_self.op :- , integer_1
integer_1 << integer_self
integer_const << 6
integer_1.op :+ , integer_const
integer_const << 4
integer_1.op :>> , integer_const
integer_reg.op :+ , integer_1
integer_tmp[Parfait::Integer.integer_index] << integer_reg
message[:return_value] << integer_tmp
end
compiler.add_mom( Mom::ReturnSequence.new)
return compiler
end
end
extend ClassMethods
end
end
end

View File

@ -1,148 +0,0 @@
module Risc
module Builtin
class Object
module ClassMethods
include CompileHelper
# self[index] basically. Index is the first arg
# return is stored in return_value
def get_internal_word( context )
compiler = compiler_for(:Object , :get_internal_word ,{at: :Integer})
compiler.builder(compiler.source).build do
object! << message[:receiver]
integer! << message[:arguments]
integer << integer[Parfait::NamedList.type_length + 0] #"at" is at index 0
integer.reduce_int
object << object[integer]
message[:return_value] << object
end
compiler.add_mom( Mom::ReturnSequence.new)
return compiler
end
# self[index] = val basically. Index is the first arg , value the second
# return the value passed in
def set_internal_word( context )
compiler = compiler_for(:Object , :set_internal_word , {at: :Integer, value: :Object} )
compiler.builder(compiler.source).build do
object! << message[:receiver]
integer! << message[:arguments]
object_reg! << integer[Parfait::NamedList.type_length + 1] #"value" is at index 1
integer << integer[Parfait::NamedList.type_length + 0] #"at" is at index 0
integer.reduce_int
object[integer] << object_reg
message[:return_value] << object_reg
end
compiler.add_mom( Mom::ReturnSequence.new)
return compiler
end
# every object needs a method missing.
# Even if it's just this one, sys_exit (later raise)
def _method_missing( context )
compiler = compiler_for(:Object,:method_missing ,{})
builder = compiler.builder(compiler.source)
builder.prepare_int_return # makes integer_tmp variable as return
emit_syscall( builder , :exit )
return compiler
end
# this is the really really first place the machine starts (apart from the jump here)
# it isn't really a function, ie it is jumped to (not called), exits and may not return
# so it is responsible for initial setup:
# - load fist message, set up Space as receiver
# - call main, ie set up message for that etc
# - exit (exit_sequence) which passes a machine int out to c
def __init__( context )
compiler = MethodCompiler.compiler_for_class(:Object,:__init__ ,
Parfait::NamedList.type_for({}) , Parfait::NamedList.type_for({}))
builder = compiler.builder(compiler.source)
builder.build do
factory! << Parfait.object_space.get_factory_for(:Message)
message << factory[:next_object]
next_message! << message[:next_message]
factory[:next_object] << next_message
end
Mom::MessageSetup.new(Parfait.object_space.get_main).build_with( builder )
builder.build do
message << message[:next_message]
space? << Parfait.object_space
message[:receiver] << space
end
exit_label = Risc.label(compiler.source , "#{compiler.receiver_type.object_class.name}.#{compiler.source.name}" )
ret_tmp = compiler.use_reg(:Label).set_builder(builder)
builder.build do
ret_tmp << exit_label
message[:return_address] << ret_tmp
add_code Risc.function_call( "__init__ issue call" , Parfait.object_space.get_main)
add_code exit_label
end
compiler.reset_regs
exit_sequence(builder)
return compiler
end
# the exit function
# mainly calls exit_sequence
def exit( context )
compiler = compiler_for(:Object,:exit ,{})
builder = compiler.builder(compiler.source)
builder.prepare_int_return # makes integer_tmp variable as return
exit_sequence(builder)
return compiler
end
# a sort of inline version of exit method.
# Used by exit and __init__ (so it doesn't have to call it)
# Assumes int return value and extracts the fixnum for process exit code
def exit_sequence(builder)
save_message( builder )
builder.build do
message << message[:return_value]
message.reduce_int
add_code Syscall.new("emit_syscall(exit)", :exit )
end
end
# emit the syscall with given name
# there is a Syscall instruction, but the message has to be saved and restored
def emit_syscall( builder , name )
save_message( builder )
builder.add_code Syscall.new("emit_syscall(#{name})", name )
restore_message(builder)
return unless (@clazz and @method)
builder.add_code Risc.label( "#{@clazz.name}.#{@message.name}" , "return_syscall" )
end
# save the current message, as the syscall destroys all context
#
# This relies on linux to save and restore all registers
#
def save_message(builder)
r8 = RegisterValue.new( :r8 , :Message).set_builder(builder)
builder.build {r8 << message}
end
# restore the message that we save in r8
# before th restore, the syscall return, a fixnum, is saved
# The caller of this method is assumed to caal prepare_int_return
# so that the return value already has an integer instance
# This instance is filled with os return value
def restore_message(builder)
r8 = RegisterValue.new( :r8 , :Message)
builder.build do
integer_reg! << message
message << r8
integer_2! << message[:return_value]
integer_2[Parfait::Integer.integer_index] << integer_reg
end
end
end
extend ClassMethods
end
end
end

View File

@ -1,70 +0,0 @@
module Risc
module Builtin
module Word
module ClassMethods
include CompileHelper
# wrapper for the syscall
# io/file currently hardcoded to stdout
# set up registers for syscall, ie
# - pointer in r1
# - length in r2
# - emit_syscall (which does the return of an integer, see there)
def putstring( context)
compiler = compiler_for(:Word , :putstring ,{})
builder = compiler.builder(compiler.source)
builder.prepare_int_return # makes integer_tmp variable as return
builder.build do
word! << message[:receiver]
integer! << word[Parfait::Word.get_length_index]
end
Risc::Builtin::Object.emit_syscall( builder , :putstring )
compiler.add_mom( Mom::ReturnSequence.new)
compiler
end
# self[index] basically. Index is the first arg > 0
# return a word sized new int, in return_value
#
# Note: no index (or type) checking. Method should be internal and check before.
# Which means the returned integer could be passed in, instead of allocated.
def get_internal_byte( context)
compiler = compiler_for(:Word , :get_internal_byte , {at: :Integer})
builder = compiler.builder(compiler.source)
integer_tmp = builder.allocate_int
builder.build do
object! << message[:receiver]
integer! << message[:arguments]
integer << integer[Parfait::NamedList.type_length + 0] #"at" is at index 0
integer.reduce_int
object <= object[integer]
integer_tmp[Parfait::Integer.integer_index] << object
message[:return_value] << integer_tmp
end
compiler.add_mom( Mom::ReturnSequence.new)
return compiler
end
# self[index] = val basically. Index is the first arg ( >0 , unchecked),
# value the second, which is also returned
def set_internal_byte( context )
compiler = compiler_for(:Word, :set_internal_byte , {at: :Integer , value: :Integer} )
compiler.builder(compiler.source).build do
word! << message[:receiver]
integer! << message[:arguments]
integer_reg! << integer[Parfait::NamedList.type_length + 1] #"value" is at index 1
message[:return_value] << integer_reg
integer << integer[Parfait::NamedList.type_length + 0] #"at" is at index 0
integer.reduce_int
integer_reg.reduce_int
word[integer] <= integer_reg
end
compiler.add_mom( Mom::ReturnSequence.new)
return compiler
end
end
extend ClassMethods
end
end
end

View File

@ -4,23 +4,21 @@ module Risc
# class shared by BlockCompiler and MethodCompiler
# - risc_instructions: The sequence of risc level instructions that mom was compiled to
# - cpu_instructions: The sequence of cpu specific instructions that the
# risc_instructions was compiled to
# Instructions derive from class Instruction and form a linked list
# - constants is an array of Parfait objects that need to be available
# - callable is a Method of Block
# - current instruction is where addidion happens
#
class CallableCompiler
def initialize( callable )
# Must pass the callable (method/block)
# Also start instuction, usually a label is mandatory
def initialize( callable , mom_label)
@callable = callable
@regs = []
@constants = []
@block_compilers = []
@risc_instructions = Risc.label(source_name, source_name)
@current = start = @risc_instructions
add_code Risc.label( source_name, "return_label")
Mom::ReturnSequence.new.to_risc(self)
add_code Risc.label( source_name, "unreachable")
@current = start
@current = @risc_instructions = mom_label.risc_label(self)
reset_regs
end
attr_reader :risc_instructions , :constants , :block_compilers , :callable , :current
@ -32,20 +30,6 @@ module Risc
end
end
# convert the given mom instruction to_risc and then add it (see add_code)
# continue down the instruction chain unti depleted
# (adding moves the insertion point so the whole mom chain is added as a risc chain)
def add_mom( instruction )
while( instruction )
raise "whats this a #{instruction}" unless instruction.is_a?(Mom::Instruction)
#puts "adding mom #{instruction.to_s}:#{instruction.next.to_s}"
instruction.to_risc( self )
reset_regs
#puts "adding risc #{risc.to_s}:#{risc.next.to_s}"
instruction = instruction.next
end
end
# add a constant (which get created during compilation and need to be linked)
def add_constant(const)
raise "Must be Parfait #{const}" unless const.is_a?(Parfait::Object)

View File

@ -1,13 +1,14 @@
module Risc
# MethodCompiler (old name) is used to generate risc instructions for methods
# and to instantiate the methods correctly. Most of the init is typed layer stuff,
# but there is some logic too.
# MethodCompiler is used to generate risc instructions for methods
# and to instantiate the methods correctly.
class MethodCompiler < CallableCompiler
def initialize( method )
super(method)
# Methods starts with a Label, both in risc and mom.
# Pass in the callable(method) and the mom label that the method starts with
def initialize( method , mom_label)
super(method , mom_label)
end
#include block_compilers constants
@ -33,7 +34,7 @@ module Risc
#
# return compiler_for_type with the resolved type
#
def self.compiler_for_class( class_name , method_name , args , frame )
def self.compiler_for_clazz( class_name , method_name , args , frame )
raise "create_method #{class_name}.#{class_name.class}" unless class_name.is_a? Symbol
clazz = Parfait.object_space.get_class_by_name! class_name
compiler_for_type( clazz.instance_type , method_name , args , frame)
@ -52,7 +53,7 @@ module Risc
# args a hash that will be converted to a type
# the created method is set as the current and the given type too
# return the compiler
def self.compiler_for_type( type , method_name , args , frame)
def self.compiler_for_typez( type , method_name , args , frame)
raise "create_method #{type.inspect} is not a Type" unless type.is_a? Parfait::Type
raise "Args must be Type #{args}" unless args.is_a?(Parfait::Type)
raise "create_method #{method_name}.#{method_name.class}" unless method_name.is_a? Symbol

View File

@ -186,7 +186,7 @@ module Risc
# populate the position caches (forward and revese) with the given position
# forward caches object -> position
# reverse caches position.at > position
# Labels do not participatein reverse cache
# Labels do not participate in reverse cache
def self.set_cache( position , to)
postest = Position.positions[position.object] unless to < 0
raise "Mismatch #{position}" if postest and postest != position

View File

@ -1,16 +1,8 @@
module Mom
# The Compiler for the Mom level is a collection of Risc level Method compilers,
module Risc
# The Collection for the Risc level is a collection of Risc level Method compilers,
# plus functions to translate from the risc to cpu specific code.
#
# Builtin functions are created here, lazily, when translate is called.
# Instantiating builtin functions results in a MethodCompiler for that function, and
# to avoid confusion, these should be instantiated only once.
#
# As RubyCompiler pools source at the vool level, when several classes are compiled
# from vool to mom, several MomCompilers get instantiated. They must be merged before
# proceeding with translate. Thus we have a append method.
#
class MomCompiler
class RiscCollection
attr_reader :method_compilers
# Initialize with an array of risc MethodCompilers
@ -18,21 +10,9 @@ module Mom
@method_compilers = compilers
end
# lazily instantiate the compilers for boot functions
# (in the hope of only booting the functions once)
def boot_compilers
@boot_compilers ||= Risc::Builtin.boot_functions
end
# Return all compilers, namely the MethodCompilers passed in, plus the
# boot_function's compilers (boot_compilers)
def compilers
@method_compilers + boot_compilers
end
# collects constants from all compilers into one array
def constants
compilers.inject([]){|sum ,comp| sum + comp.constants }
method_compilers.inject([]){|sum ,comp| sum + comp.constants }
end
# Append another MomCompilers method_compilers to this one.
@ -55,7 +35,7 @@ module Mom
# go through all methods and translate them to cpu, given the translator
def translate_methods(translator)
compilers.collect do |compiler|
method_compilers.collect do |compiler|
#log.debug "Translate method #{compiler.method.name}"
translate_method(compiler , translator)
end.flatten

31
lib/ruby/README.md Normal file
View File

@ -0,0 +1,31 @@
# Ruby
A representation of the ruby code.
## Concrete Syntax tree
Ruby is parsed by the parser gem (big thanks to whitequark). Own attempts at
this task have failed utterly, because ruby is a _huge_ language (and not well
defined at that)
Alas, the parser gem creates an abstract syntax tree, meaning one class is used
to represent all node types.
Imho object oriented languages have a class system to do just that, ie represent
what we may loosely call type here, ie the kind of statement.
The ruby layer is really all about defining classes for every kind of statement,
thus "typing" the syntax tree, and making it concrete.
## to Vool
In our nice layers, we are ont the way down to Vool, a simplified version of oo.
It has proven handy to have this layer, so the code for transforming each object
is in the class representing that object. (As one does in oo, again imho).
## Parfait objects
The compilation process ends up creating (parfait) objects to represent
things like classes, types and constants. This is not done here yet, but in
the vool layer.

View File

@ -1,11 +1,12 @@
require "rx-file"
require "util/logging"
require "util/list"
require_relative "elf/object_writer"
require_relative "risc"
require_relative "mom/mom"
require_relative "arm/arm_machine"
require_relative "arm/arm_platform"
require_relative "vool/statement"
require_relative "ruby"
require_relative "rubyx/rubyx_compiler"
require_relative "mom/mom"

View File

@ -1,5 +1,14 @@
module RubyX
# The RubyXCompiler provides the main interface to create binaries
# The RubyXCompiler provides the main interface to create binaries, and also
# give helper functions to create any intermediate layer.
# Layers are:
# - ruby , always needed as input, string
# - vool - intermediate language layer
# - mom - intermediate machine layer
# - risc - "last" intermediate machine layer
# - target - arm or interpreter binary code
# - binary - "linked" code, everything need to create an elf binary
#
#
# There are methods to go from ruby to any of the layers in the system
# (mainly for testing). ruby_to_binary creates actual binary code
@ -21,6 +30,7 @@ module RubyX
# initialize boots Parfait and Risc (ie load Builin)
def initialize(options)
Parfait.boot!(options[:parfait] || {})
Mom.boot!
Risc.boot!
end
@ -37,42 +47,58 @@ module RubyX
to_binary(platform)
end
# ruby_to_target creates Target instructions (but does not link)
#
# After creating vool, we call to_target
# Return a Linker
def ruby_to_target(ruby , platform)
ruby_to_vool(ruby)
to_target( platform )
end
# ruby_to_risc creates Risc instructions
#
# After creating vool, we call to_risc
# Return a RiscCollection
def ruby_to_risc(ruby)
ruby_to_vool(ruby)
to_risc()
end
# Transform the incoming ruby source (string) to mom
#
# The vool is stored using ruby_to_vool,the to_mom is called
# Return Mom Statement
def ruby_to_mom(ruby)
ruby_to_vool(ruby)
to_mom
end
# Process previously stored vool source to binary.
# Binary code is generated byu calling to_risc, then positioning and calling
# Binary code is generated by calling to_risc, then positioning and calling
# create_binary on the linker. The linker may then be used to creat a binary file.
# The biary the method name refers to is binary code in memory, or in BinaryCode
# objects to be precise.
def to_binary(platform)
linker = to_risc(platform)
linker = to_target(platform)
linker.position_all
linker.create_binary
linker
end
# ruby_to_risc creates Risc instructions (as the name implies), but also
# translates those to the platform given
#
# After creating vool, we call to_risc
def ruby_to_risc(ruby, platform)
ruby_to_vool(ruby)
to_risc(platform)
# transform stored vool to target code
# return a linker
def to_target(platform)
raise "No platform given" unless platform
collection = to_risc
collection.translate(platform)
end
# Process previously stored vool source. First to mom, then to platform.
# Translating to platform returns a linker that is returned and can be used
# to generate binaries
def to_risc(platform)
# Process previously stored vool source to risc.
# return a Risc::RiscCollection , a collection of MethodCompilers
def to_risc()
mom = to_mom
mom.translate(platform)
end
# ruby_to_mom does exactly that, it transform the incoming ruby source (string)
# to mom
# The vool is stored using ruby_to_vool, and if there was previous source,
# this will also be momed
def ruby_to_mom(ruby)
ruby_to_vool(ruby)
to_mom
mom.to_risc()
end
# return mom for the previously stored vool source.
@ -108,9 +134,7 @@ module RubyX
compiler = RubyXCompiler.new(options)
compiler.load_parfait if options[:load_parfait]
compiler.ruby_to_vool(ruby)
platform = options[:platform]
raise "No platform given" unless platform
compiler.to_binary(platform)
compiler.to_binary(options[:platform])
end
end
end

View File

@ -9,16 +9,14 @@ Possibly later other languages can compile to this level and use rx-file as code
## Syntax tree
Vool is the layer of concrete syntax tree. The Parser gem is used to parse ruby. It creates
an abstract syntax tree which is then transformed.
Vool is a layer with concrete syntax tree, just like the ruby layer above.
Vool is just simplified, without fluff, see below.
The next layer down is the Mom, Minimal object Machine, which uses an instruction tree.
That is on the way down we create instructions, but stays in tree format. Only the next step
down to the Risc layer moves to an instruction stream.
The next layer down is the Mom, Minimal object Machine, which uses an instruction list.
The nodes of the syntax tree are all the things one would expect from a language, if statements
and the like. There is no context yet, and actual objects, representing classes and
methods, will be created on the way down.
The nodes of the syntax tree are all the things one would expect from a language,
if statements and the like. There is no context yet, and actual objects,
representing classes and methods, will be created on the way down.
## Fluff
@ -30,3 +28,9 @@ existence of until, which really means if not. Other examples, some more impactf
- no case
- no elseif (no unless, no ternary operator)
- no global variables.
## Parfait objects
The compilation process ends up creating (parfait) objects to represent
things like classes, types and constants. This is done in this layer,
on the way down to MOM (ie not during init)

View File

@ -3,6 +3,8 @@ module Vool
class Assignment < Statement
attr_reader :name , :value
def initialize(name , value )
raise "Name nil #{self}" unless name
raise "Value nil #{self}" unless value
@name , @value = name , value
end

View File

@ -21,9 +21,9 @@ module Vool
# to the method compiler for further processing
def to_mom( compiler )
parfait_block = self.parfait_block(compiler)
block_compiler = Risc::BlockCompiler.new( parfait_block , compiler.get_method )
block_compiler = Mom::BlockCompiler.new( parfait_block , compiler.get_method )
head = body.to_mom( block_compiler )
block_compiler.add_mom(head)
block_compiler.add_code(head)
block_compiler
end

View File

@ -11,6 +11,7 @@ module Vool
raise "not meta" unless clazz.class == Parfait::MetaClass
raise( "no class in #{self}") unless clazz
method = clazz.add_method_for(name , make_arg_type , make_frame , body )
#VoolMethod
compiler = method.compiler_for(clazz.instance_type)
each {|node| raise "Blocks not implemented" if node.is_a?(BlockStatement)}
compiler

View File

@ -1,4 +1,13 @@
module Vool
# This represents a class at the vool level. Vool is a syntax tree,
# so here the only child (or children) is a body.
# Body may either be a MethodStatement, or Statements (either empty or
# containing MethodStatement)
#
# We store the class name and the parfait class
#
# The Parfait class gets created lazily on the way down to mom, ie the clazz
# attribute will only be set after to_mom, or a direct call to create_class
class ClassStatement < Statement
attr_reader :name, :super_class_name , :body
attr_reader :clazz
@ -17,6 +26,10 @@ module Vool
end
end
# This create the Parfait class, and then transforms every method
#
# As there is no class equivalnet in code, a MomCollection is returned,
# which is just a list of Mom::MethodCompilers
def to_mom( _ )
create_class_object
method_compilers = body.statements.collect do |node|
@ -29,7 +42,7 @@ module Vool
raise "Only methods for now #{node.class}:#{node}"
end
end
Mom::MomCompiler.new(method_compilers)
Mom::MomCollection.new(method_compilers)
end
def each(&block)
@ -37,6 +50,10 @@ module Vool
@body.each(&block) if @body
end
# This creates the Parfait class. But doesn not hadle reopening yet, so only new classes
# Creating the class involves creating the instance_type (or an initial version)
# which means knowing all used names. So we go through the code looking for
# InstanceVariables or InstanceVariable Assignments, to do that.
def create_class_object
@clazz = Parfait.object_space.get_class_by_name(@name )
if(@clazz)
@ -53,6 +70,7 @@ module Vool
ivar_hash[node.name] = :Object
end
@clazz.set_instance_type( Parfait::Type.for_hash( @clazz , ivar_hash ) )
@clazz
end
end

View File

@ -15,8 +15,8 @@ module Vool
end
def simple_if(compiler)
true_label = Mom::Label.new( "true_label_#{object_id.to_s(16)}")
merge_label = Mom::Label.new( "merge_label_#{object_id.to_s(16)}")
true_label = Mom::Label.new( self,"true_label_#{object_id.to_s(16)}")
merge_label = Mom::Label.new( self,"merge_label_#{object_id.to_s(16)}")
head = Mom::TruthCheck.new(condition.slot_definition(compiler) , merge_label)
head << true_label
@ -25,9 +25,9 @@ module Vool
end
def full_if(compiler)
true_label = Mom::Label.new( "true_label_#{object_id.to_s(16)}")
false_label = Mom::Label.new( "false_label_#{object_id.to_s(16)}")
merge_label = Mom::Label.new( "merge_label_#{object_id.to_s(16)}")
true_label = Mom::Label.new( self , "true_label_#{object_id.to_s(16)}")
false_label = Mom::Label.new( self , "false_label_#{object_id.to_s(16)}")
merge_label = Mom::Label.new( self , "merge_label_#{object_id.to_s(16)}")
head = Mom::TruthCheck.new(condition.slot_definition(compiler) , false_label)
head << true_label

View File

@ -5,7 +5,7 @@ module Vool
def to_mom( compiler )
to = Mom::SlotDefinition.new(:message ,[ :receiver , @name])
from = @value.slot_definition(compiler)
return chain_assign( Mom::SlotLoad.new(to,from) , compiler)
return chain_assign( Mom::SlotLoad.new(self,to,from) , compiler)
end
end

View File

@ -6,9 +6,9 @@ module Vool
slot_def = compiler.slot_type_for(@name)
to = Mom::SlotDefinition.new(:message ,slot_def)
from = @value.slot_definition(compiler)
return chain_assign( Mom::SlotLoad.new(to,from) , compiler)
return chain_assign( Mom::SlotLoad.new(self,to,from) , compiler)
end
end
end

View File

@ -9,7 +9,7 @@ module Vool
def to_mom(clazz)
raise( "no class in #{self}") unless clazz
method = clazz.add_method_for(name , make_arg_type , make_frame , body )
method = make_method(clazz)
compiler = method.compiler_for(clazz.instance_type)
each do |node| ## TODO: must account for nested blocks (someday)
next unless node.is_a?(BlockStatement)
@ -18,6 +18,15 @@ module Vool
compiler
end
# Class to be passed in is a Parfait class
# return VoolMethod
#
# extracted call to create the VoolMethod as this is the place
# where we have all the info. Used in testing.
def make_method(clazz)
clazz.add_method_for(name , make_arg_type , make_frame , body )
end
def each(&block)
block.call(self)
@body.each(&block)

View File

@ -16,9 +16,9 @@ module Vool
# - store the given return value, this is a SlotMove
# - activate return sequence (reinstantiate old message and jump to return address)
def to_mom( compiler )
ret = Mom::SlotLoad.new( [:message , :return_value] ,
ret = Mom::SlotLoad.new( self , [:message , :return_value] ,
@return_value.slot_definition(compiler) )
ret << Mom::ReturnJump.new
ret << Mom::ReturnJump.new(self , compiler.return_label )
end
def to_s(depth = 0)

View File

@ -50,9 +50,9 @@ module Vool
arg_target = [:message , :next_message , :arguments]
args = []
@arguments.each_with_index do |arg , index| # +1 because of type
args << Mom::SlotLoad.new( arg_target + [index + 1] , arg.slot_definition(compiler))
args << Mom::SlotLoad.new(self, arg_target + [index + 1] , arg.slot_definition(compiler))
end
setup << Mom::ArgumentTransfer.new( mom_receive , args )
setup << Mom::ArgumentTransfer.new(self, mom_receive , args )
end
def simple_call(compiler)
@ -73,10 +73,10 @@ module Vool
# if not, change and find method for the type (simple_call to resolve_method)
# conceptually easy in ruby, but we have to compile that "easy" ruby
def cache_check(compiler)
ok = Mom::Label.new("cache_ok_#{self.object_id}")
ok = Mom::Label.new(self,"cache_ok_#{self.object_id}")
check = build_condition(ok, compiler) # if cached_type != current_type
check << Mom::SlotLoad.new([dynamic_call.cache_entry, :cached_type] , receiver_type_definition(compiler))
check << Mom::ResolveMethod.new( @name , dynamic_call.cache_entry )
check << Mom::SlotLoad.new(self,[dynamic_call.cache_entry, :cached_type] , receiver_type_definition(compiler))
check << Mom::ResolveMethod.new(self, @name , dynamic_call.cache_entry )
check << ok
end

View File

@ -10,8 +10,8 @@ module Vool
end
def to_mom( compiler )
merge_label = Mom::Label.new( "merge_label_#{object_id.to_s(16)}")
cond_label = Mom::Label.new( "cond_label_#{object_id.to_s(16)}")
merge_label = Mom::Label.new(self, "merge_label_#{object_id.to_s(16)}")
cond_label = Mom::Label.new(self, "cond_label_#{object_id.to_s(16)}")
codes = cond_label
codes << @hoisted.to_mom(compiler) if @hoisted
codes << Mom::TruthCheck.new(condition.slot_definition(compiler) , merge_label)

View File

@ -33,13 +33,13 @@ module Vool
# this needs run-time variable resolution, which is just not done.
# we brace ourselves with the check, and exit (later raise) if . . .
def method_check(compiler)
ok_label = Mom::Label.new("method_ok_#{self.object_id}")
ok_label = Mom::Label.new(self,"method_ok_#{self.object_id}")
compile_method = Mom::SlotDefinition.new( compiler.get_method , [])
runtime_method = Mom::SlotDefinition.new( :message , [ :method] )
check = Mom::NotSameCheck.new(compile_method , runtime_method, ok_label)
# TODO? Maybe create mom instructions for this
#builder = compiler.builder("yield")
#Risc::Builtin::Object.exit_sequence(builder)
#Risc::Builtin.exit_sequence(builder)
#check << builder.built
check << ok_label
end
@ -53,10 +53,10 @@ module Vool
arg_target = [:message , :next_message , :arguments]
args = []
@arguments.each_with_index do |arg , index| # +1 because of type
args << Mom::SlotLoad.new( arg_target + [index + 1] , arg.slot_definition(compiler))
args << Mom::SlotLoad.new(self, arg_target + [index + 1] , arg.slot_definition(compiler))
end
setup << Mom::ArgumentTransfer.new( mom_receive , args )
setup << Mom::BlockYield.new( arg_index )
setup << Mom::ArgumentTransfer.new( self , mom_receive , args )
setup << Mom::BlockYield.new( self , arg_index )
end
end

View File

@ -6,9 +6,7 @@ module Elf
def setup
super
@linker = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_risc(as_main("return 1"),:arm)
@linker.position_all
@linker.create_binary
@linker = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_binary(as_main("return 1"),:arm)
end
def test_empty_translate

View File

@ -14,7 +14,7 @@ end
require "minitest/color"
require "minitest/autorun"
require "minitest/fail_fast" unless ENV["TEST_ALL"]
#require "minitest/fail_fast" unless ENV["TEST_ALL"]
require 'minitest/profile'
#require "minitest/reporters"
#Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new

View File

@ -0,0 +1,21 @@
require_relative "../helper"
module Risc
class TestBlockAssign < MiniTest::Test
include Statements
def setup
super
@input = as_block("a = 5")
@expect = [LoadConstant, SlotToReg, RegToSlot]
end
def test_send_instructions
assert_nil msg = check_nil(:main_block) , msg
end
def test_load_5
produced = produce_block.next
assert_load( produced , Parfait::Integer)
assert_equal 5 , produced.constant.value
end
end
end

View File

@ -0,0 +1,32 @@
require_relative "../helper"
module Risc
class TestBlockSetupBlock < MiniTest::Test
include Statements
def setup
super
@input = as_block("return 5")
@mom = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(as_test_main)
end
def main_risc
@mom.to_risc.method_compilers.find{|c| c.callable.name == :main }
end
def test_mom
assert_equal Mom::MomCollection , @mom.class
end
def test_mom_block_comp
assert_equal 1 , @mom.method_compilers.first.block_compilers.length
end
def test_risc
assert_equal Risc::RiscCollection , @mom.to_risc.class
end
def test_risc_comp
assert_equal :main , main_risc.callable.name
end
def test_risc_block_comp
assert_equal 1 , main_risc.block_compilers.length
end
end
end

View File

@ -0,0 +1,63 @@
require_relative "../helper"
module Risc
class TestBlockSetup < MiniTest::Test
include Statements
def setup
super
@input = as_block("return 5")
@expect = [LoadConstant, SlotToReg, RegToSlot, LoadConstant, SlotToReg, #4
RegToSlot, LoadConstant, LoadConstant, SlotToReg, SlotToReg, #9
RegToSlot, RegToSlot, RegToSlot, RegToSlot, SlotToReg, #14
SlotToReg, RegToSlot, SlotToReg, SlotToReg, SlotToReg, #19
SlotToReg, RegToSlot, LoadConstant, SlotToReg, RegToSlot, #24
SlotToReg, FunctionCall, Label]
end
def test_send_instructions
assert_nil msg = check_nil(:main) , msg
end
def test_load_5
produced = produce_block.next
assert_load( produced , Parfait::Integer)
assert_equal 5 , produced.constant.value
end
def test_load_5
produced = produce_body
assert_load( produced , Parfait::Integer)
assert_equal 5 , produced.constant.value
end
def test_load_block
produced = produce_body.next(3)
assert_load( produced , Parfait::Block)
assert_equal :main_block , produced.constant.name
end
def test_load_method_to_call
produced = produce_body.next(6)
assert_load( produced , Parfait::CallableMethod)
assert_equal :main , produced.constant.name
end
def test_load_next_message
produced = produce_body.next(7)
assert_load( produced , Parfait::Factory)
assert_equal "Message_Type" , produced.constant.for_type.name
end
def test_load_return
produced = produce_body.next(22)
assert_load( produced , Label)
assert produced.constant.name.start_with?("continue_")
end
def test_function_call
produced = produce_body.next(26)
assert_equal FunctionCall , produced.class
assert_equal :main , produced.method.name
end
def test_check_continue
produced = produce_body.next(27)
assert_equal Label , produced.class
assert produced.name.start_with?("continue_")
end
end
end

View File

@ -0,0 +1,10 @@
# Builtin Testing
At the Module level (word/object/integer) mostly testing that
- functions exist
- they compile
- basic length tests (no "contents")
Minimal tests for risc compilation, and again length only (should be array too)
Functionality is tested by interpreter over in interpreter dir

View File

@ -0,0 +1,15 @@
require_relative "../helper"
module Mom
module Builtin
class BootTest < MiniTest::Test
def setup
Parfait.boot!(Parfait.default_test_options)
@functions = Builtin.boot_functions
end
def get_compiler( name )
@functions.each.find{|meth| meth.callable.name == name}
end
end
end
end

View File

@ -0,0 +1,30 @@
require_relative "helper"
module Mom
module Builtin
class TestIntComp1Risc < BootTest
def setup
super
@method = get_compiler(:<)
end
def test_compile
assert_equal Risc::MethodCompiler , @method.to_risc.class
end
def test_risc_length
assert_equal 28 , @method.to_risc.risc_instructions.length
end
end
class TestIntComp2Risc < BootTest
def setup
super
@method = get_compiler(:>=)
end
def test_compile
assert_equal Risc::MethodCompiler , @method.to_risc.class
end
def test_risc_length
assert_equal 27 , @method.to_risc.risc_instructions.length
end
end
end
end

View File

@ -0,0 +1,18 @@
require_relative "helper"
module Mom
module Builtin
class TestIntDiv10Risc < BootTest
def setup
super
@method = get_compiler(:div10)
end
def test_compile
assert_equal Risc::MethodCompiler , @method.to_risc.class
end
def test_risc_length
assert_equal 76 , @method.to_risc.risc_instructions.length
end
end
end
end

View File

@ -0,0 +1,18 @@
require_relative "helper"
module Mom
module Builtin
class TestIntDiv4Risc < BootTest
def setup
super
@method = get_compiler(:div4)
end
def test_compile
assert_equal Risc::MethodCompiler , @method.to_risc.class
end
def test_risc_length
assert_equal 47 , @method.to_risc.risc_instructions.length
end
end
end
end

View File

@ -0,0 +1,18 @@
require_relative "helper"
module Mom
module Builtin
class TestObjectExitRisc < BootTest
def setup
super
@method = get_compiler(:exit)
end
def test_compile
assert_equal Risc::MethodCompiler , @method.to_risc.class
end
def test_risc_length
assert_equal 46 , @method.to_risc.risc_instructions.length
end
end
end
end

View File

@ -0,0 +1,18 @@
require_relative "helper"
module Mom
module Builtin
class TestWordGetRisc < BootTest
def setup
super
@method = get_compiler(:get_internal_byte)
end
def test_compile
assert_equal Risc::MethodCompiler , @method.to_risc.class
end
def test_risc_length
assert_equal 48 , @method.to_risc.risc_instructions.length
end
end
end
end

View File

@ -0,0 +1,18 @@
require_relative "helper"
module Mom
module Builtin
class TestWordGetRisc < BootTest
def setup
super
@method = get_compiler(:get_internal_byte)
end
def test_compile
assert_equal Risc::MethodCompiler , @method.to_risc.class
end
def test_risc_length
assert_equal 48 , @method.to_risc.risc_instructions.length
end
end
end
end

View File

@ -0,0 +1,18 @@
require_relative "helper"
module Mom
module Builtin
class TestObjectInitRisc < BootTest
def setup
super
@method = get_compiler(:__init__)
end
def test_compile
assert_equal Risc::MethodCompiler , @method.to_risc.class
end
def test_risc_length
assert_equal 37 , @method.to_risc.risc_instructions.length
end
end
end
end

View File

@ -0,0 +1,71 @@
require_relative "helper"
module Mom
module Builtin
class TestIntDiv4 < BootTest
def setup
super
@method = get_compiler(:div4)
end
def test_has_get_internal
assert_equal Mom::MethodCompiler , @method.class
end
def test_mom_length
assert_equal 5 , @method.mom_instructions.length
end
end
class TestIntDiv10 < BootTest
def setup
super
@method = get_compiler(:div10)
end
def test_has_get_internal
assert_equal Mom::MethodCompiler , @method.class
end
def test_mom_length
assert_equal 5 , @method.mom_instructions.length
end
end
class TestIntComp1 < BootTest
def setup
super
@method = get_compiler(:<)
end
def test_has_get_internal
assert_equal Mom::MethodCompiler , @method.class
end
def test_mom_length
assert_equal 5 , @method.mom_instructions.length
end
end
class TestIntComp2 < BootTest
def setup
super
@method = get_compiler(:>=)
end
def test_has_get_internal
assert_equal Mom::MethodCompiler , @method.class
end
def test_mom_length
assert_equal 5 , @method.mom_instructions.length
end
end
class TestIntOperators < BootTest
def setup
super
end
def each_method &block
Risc.operators.each do |name|
method = get_compiler(name)
block.yield(method)
end
end
def test_has_get_internal
each_method do |method|
assert_equal Mom::MethodCompiler , method.class
assert_equal 5 , method.mom_instructions.length
end
end
end
end
end

View File

@ -0,0 +1,18 @@
require_relative "helper"
module Mom
module Builtin
class TestObjectMissingRisc < BootTest
def setup
super
@method = get_compiler(:method_missing)
end
def test_compile
assert_equal Risc::MethodCompiler , @method.to_risc.class
end
def test_risc_length
assert_equal 48 , @method.to_risc.risc_instructions.length
end
end
end
end

View File

@ -0,0 +1,66 @@
require_relative "helper"
module Mom
module Builtin
class TestObjectGet < BootTest
def setup
super
@method = get_compiler(:get_internal_word)
end
def test_has_get_internal
assert_equal Mom::MethodCompiler , @method.class
end
def test_mom_length
assert_equal 5 , @method.mom_instructions.length
end
end
class TestObjectSet < BootTest
def setup
super
@method = get_compiler(:set_internal_word)
end
def test_has_get_internal
assert_equal Mom::MethodCompiler , @method.class
end
def test_mom_length
assert_equal 5 , @method.mom_instructions.length
end
end
class TestObjectMissing < BootTest
def setup
super
@method = get_compiler(:method_missing)
end
def test_has_get_internal
assert_equal Mom::MethodCompiler , @method.class
end
def test_mom_length
assert_equal 5 , @method.mom_instructions.length
end
end
class TestObjectExit < BootTest
def setup
super
@method = get_compiler(:exit)
end
def test_has_get_internal
assert_equal Mom::MethodCompiler , @method.class
end
def test_mom_length
assert_equal 5 , @method.mom_instructions.length
end
end
class TestObjectInit < BootTest
def setup
super
@method = get_compiler(:__init__)
end
def test_has_get_internal
assert_equal Mom::MethodCompiler , @method.class
end
def test_mom_length
assert_equal 5 , @method.mom_instructions.length
end
end
end
end

View File

@ -0,0 +1,27 @@
require_relative "helper"
module Mom
module Builtin
class TestIntOperatorsRisc < BootTest
def setup
super
end
def each_method &block
Risc.operators.each do |name|
method = get_compiler(name)
block.yield(method)
end
end
def test_compile
each_method do |method|
assert_equal Risc::MethodCompiler , method.to_risc.class
end
end
def test_risc_length
each_method do |method|
assert_equal 49 , method.to_risc.risc_instructions.length
end
end
end
end
end

View File

@ -0,0 +1,18 @@
require_relative "helper"
module Mom
module Builtin
class TestWordPutRisc < BootTest
def setup
super
@method = get_compiler(:putstring)
end
def test_compile
assert_equal Risc::MethodCompiler , @method.to_risc.class
end
def test_risc_length
assert_equal 50 , @method.to_risc.risc_instructions.length
end
end
end
end

View File

@ -0,0 +1,18 @@
require_relative "helper"
module Mom
module Builtin
class TestWordSetRisc < BootTest
def setup
super
@method = get_compiler(:set_internal_byte)
end
def test_compile
assert_equal Risc::MethodCompiler , @method.to_risc.class
end
def test_risc_length
assert_equal 22 , @method.to_risc.risc_instructions.length
end
end
end
end

View File

@ -0,0 +1,18 @@
require_relative "helper"
module Mom
module Builtin
class TestWordSetRisc < BootTest
def setup
super
@method = get_compiler(:set_internal_byte)
end
def test_compile
assert_equal Risc::MethodCompiler , @method.to_risc.class
end
def test_risc_length
assert_equal 22 , @method.to_risc.risc_instructions.length
end
end
end
end

View File

@ -0,0 +1,42 @@
require_relative "helper"
module Mom
module Builtin
class TestWordPut < BootTest
def setup
super
@method = get_compiler(:putstring)
end
def test_has_get_internal
assert_equal Mom::MethodCompiler , @method.class
end
def test_mom_length
assert_equal 5 , @method.mom_instructions.length
end
end
class TestWordGet < BootTest
def setup
super
@method = get_compiler(:get_internal_byte)
end
def test_has_get_internal
assert_equal Mom::MethodCompiler , @method.class
end
def test_mom_length
assert_equal 5 , @method.mom_instructions.length
end
end
class TestWordSet < BootTest
def setup
super
@method = get_compiler(:set_internal_byte)
end
def test_has_get_internal
assert_equal Mom::MethodCompiler , @method.class
end
def test_mom_length
assert_equal 5 , @method.mom_instructions.length
end
end
end
end

View File

@ -0,0 +1,43 @@
require_relative "../helper"
module Risc
class TestClassCallSimple < MiniTest::Test
include Statements
def setup
super
@class_input = "def self.simple_return; return 1 ; end;"
@input = "return Test.simple_return"
@expect = [LoadConstant, RegToSlot, Branch]
end
def test_send_instructions
assert_nil msg = check_nil(:simple_return) , msg
end
def test_load_simple
produced = produce_target(:simple_return).next(1)
assert_load( produced , Parfait::Integer)
assert_equal 1 , produced.constant.value
end
# The normal send
def test_load_5
produced = produce_body.next(8)
assert_load( produced , Parfait::Class)
assert_equal :Test , produced.constant.name
end
def test_load_label
produced = produce_body.next(11)
assert_load( produced , Label)
end
def test_function_call
produced = produce_body.next(15)
assert_equal FunctionCall , produced.class
assert_equal :simple_return , produced.method.name
end
def test_check_continue
produced = produce_body.next(16)
assert_equal Label , produced.class
assert produced.name.start_with?("continue_")
end
end
end

View File

@ -14,28 +14,37 @@ module Risc
SlotToReg, RegToSlot, RegToSlot, SlotToReg, SlotToReg,
SlotToReg , FunctionReturn, Label]
end
# test hack to in place change object type
def add_space_field(name,type)
class_type = Parfait.object_space.get_type_by_class_name(:Space)
class_type.send(:private_add_instance_variable, name , type)
end
def produce_body
produced = produce_instructions
produced = produce_main
preamble.each{ produced = produced.next }
produced
end
def as_block( block_input , method_input = "main_local = 5")
"#{method_input} ; self.main{|val| #{block_input}}"
end
def as_test_main
"class Test; def main(arg);#{@input};end;end"
"class Test; #{@class_input if @class_input};def main(arg);#{@input};end;end"
end
def produce_instructions
def to_target
assert @expect , "No output given"
linker = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_risc(as_test_main,:interpreter)
compiler = linker.assemblers.find{|c| c.callable.name == :main and c.callable.self_type.object_class.name == :Test}
compiler.instructions
RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_target(as_test_main,:interpreter)
end
def check_nil
produced = produce_instructions
compare_instructions( produced , @expect)
def produce_main
produce_target(:main)
end
def produce_block
produce_target(:main_block)
end
def produce_target(name = :main_block)
linker = to_target
block = linker.assemblers.find {|c| c.callable.name == name }
assert_equal Risc::Assembler , block.class
block.instructions
end
def check_nil( name = :main )
produced = produce_target( name )
compare_instructions( produced , @expect )
end
def check_return
was = check_nil

View File

@ -2,5 +2,8 @@ require_relative '../helper'
module Mom
class InstructionMock < Instruction
def initialize
super("mocking")
end
end
end

View File

@ -4,17 +4,17 @@ module Mom
class TestSlotLoadBasics < MiniTest::Test
def test_ins_ok
assert SlotLoad.new( [:message, :caller] , [:receiver,:type] )
assert SlotLoad.new("test", [:message, :caller] , [:receiver,:type] )
end
def test_ins_fail1
assert_raises {SlotLoad.new( [:message, :caller] , nil )}
assert_raises {SlotLoad.new( "test",[:message, :caller] , nil )}
end
def test_fail_on_right
@load = SlotLoad.new( [:message, :caller] , [:receiver,:type] )
@load = SlotLoad.new( "test",[:message, :caller] , [:receiver,:type] )
assert_raises {@load.to_risc(Risc::FakeCompiler.new)}
end
def test_fail_on_left_long
@load = SlotLoad.new( [:message, :caller , :type] , [:receiver,:type] )
@load = SlotLoad.new("test", [:message, :caller , :type] , [:receiver,:type] )
assert_raises {@load.to_risc(Risc::FakeCompiler.new)}
end
end

View File

@ -5,7 +5,7 @@ module Mom
def setup
Parfait.boot!(Parfait.default_test_options)
load = SlotLoad.new( [:message, :caller] , [:message,:type] )
load = SlotLoad.new("test", [:message, :caller] , [:message,:type] )
@compiler = Risc::FakeCompiler.new
load.to_risc(@compiler)
@instructions = @compiler.instructions

View File

@ -6,7 +6,7 @@ module Mom
def setup
Parfait.boot!(Parfait.default_test_options)
@compiler = Risc::FakeCompiler.new
load = SlotLoad.new( [:message, :caller, :type] , [:message, :caller , :type] )
load = SlotLoad.new( "test",[:message, :caller, :type] , [:message, :caller , :type] )
load.to_risc(@compiler)
@instructions = @compiler.instructions
end

View File

@ -8,7 +8,7 @@ module Mom
method = make_method
@compiler = Risc::FakeCompiler.new
@cache_entry = Parfait::CacheEntry.new(method.frame_type, method)
load = SlotLoad.new( [@cache_entry , :cached_type] , [:message, :type] )
load = SlotLoad.new("test", [@cache_entry , :cached_type] , [:message, :type] )
load.to_risc(@compiler)
@instructions = @compiler.instructions
end

View File

@ -0,0 +1,23 @@
require_relative "helper"
module Mom
class TestBlockCompiler < MiniTest::Test
include ScopeHelper
def setup
code = as_test_main_block("return 5" , "a = 1")
@risc = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_risc(code)
end
def test_collection
assert_equal Risc::RiscCollection, @risc.class
end
def test_main_compiler
assert_equal :main , @risc.method_compilers.first.callable.name
end
def test_main_block_compiler
assert_equal :main , @risc.method_compilers.first.block_compilers.first.in_method.name
assert_equal :main_block , @risc.method_compilers.first.block_compilers.first.callable.name
end
end
end

View File

@ -10,7 +10,7 @@ module Vool
@ret = compile_mom( as_test_main("self.main {|elem| elem = 5 } "))
end
def test_is_compiler
assert_equal Mom::MomCompiler , @ret.class
assert_equal Mom::MomCollection , @ret.class
end
def test_has_method
assert_equal Parfait::CallableMethod , @ret.method_compilers.first.get_method.class

View File

@ -1,22 +1,23 @@
require_relative "helper"
module Risc
module Mom
class TestBuiltinFunction < MiniTest::Test
def setup
Parfait.boot!(Parfait.default_test_options)
@functions = Builtin.boot_functions
end
def test_has_boot_function
assert Builtin.boot_functions
assert @functions
end
def test_boot_function_type
assert_equal Array, Builtin.boot_functions.class
assert_equal Array, @functions.class
end
def test_boot_function_length
assert_equal 23, Builtin.boot_functions.length
assert_equal 22, @functions.length
end
def test_boot_function_first
assert_equal MethodCompiler, Builtin.boot_functions.first.class
assert_equal Mom::MethodCompiler, @functions.first.class
end
end
end

View File

@ -0,0 +1,32 @@
require_relative "helper"
class FakeCallable
end
module Mom
class FakeCallableCompiler < CallableCompiler
def source_name
"luke"
end
end
class TestCallableCompiler < MiniTest::Test
def setup
@compiler = FakeCallableCompiler.new(FakeCallable.new)
end
def test_ok
assert @compiler
end
def test_current
assert @compiler.current
end
def test_current_label
assert_equal Label , @compiler.current.class
assert_equal @compiler.source_name , @compiler.current.name
end
def test_mom
assert @compiler.mom_instructions
end
def test_const
assert_equal Array , @compiler.constants.class
end
end
end

View File

@ -2,7 +2,7 @@
require_relative "helper"
module Vool
class TestClassStatement < MiniTest::Test
class TestClassStatementMom < MiniTest::Test
include MomCompile
def setup
@ -11,10 +11,10 @@ module Vool
end
def test_return_class
assert_equal Mom::MomCompiler , @ret.class
assert_equal Mom::MomCollection , @ret.class
end
def test_has_compilers
assert_equal Risc::MethodCompiler , @ret.method_compilers.first.class
assert_equal Mom::MethodCompiler , @ret.method_compilers.first.class
end
def test_constant

View File

@ -0,0 +1,20 @@
require_relative "helper"
module Mom
class TestInstruction < MiniTest::Test
def test_instantiates
assert Instruction.new("Hi")
end
def test_string_source
assert_equal "Hi" ,Instruction.new("Hi").source
end
def test_nil_next
assert_nil Instruction.new("Hi").next
end
def test_raise
assert_raises {Instruction.new(5)}
end
end
end

View File

@ -0,0 +1,36 @@
require_relative "helper"
module Mom
class TestMethodCompiler < MiniTest::Test
include ScopeHelper
def setup
end
def in_test_vool(body = "@ivar = 5")
code = in_Test("def meth; #{body};end")
RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(code)
end
def test_method
in_test_vool()
method = Parfait.object_space.get_class_by_name(:Test).get_method(:meth)
assert_equal Parfait::VoolMethod , method.class
end
def test_method_mom_col
mom = in_test_vool()
assert_equal Mom::MomCollection , mom.class
assert_equal Mom::MethodCompiler , mom.compilers.first.class
end
def test_compiles_risc
compiler = in_test_vool().compilers.first.to_risc
assert_equal Risc::MethodCompiler , compiler.class
assert_equal Risc::Label , compiler.risc_instructions.class
end
def test_compiles_all_risc
compiler = in_test_vool().compilers.first.to_risc
assert_equal Risc::LoadConstant , compiler.risc_instructions.next.class
assert_equal 17 , compiler.risc_instructions.length
end
end
end

View File

@ -0,0 +1,54 @@
require_relative "helper"
module Mom
class TestMomCollection < MiniTest::Test
include MomCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@comp = compile_mom( "class Test ; def main(); return 'Hi'; end; end;")
end
def test_class
assert_equal MomCollection , @comp.class
end
def test_compilers
assert_equal 22 , @comp.compilers.length
end
def test_boot_compilers
assert_equal 21 , @comp.boot_compilers.length
end
def test_compilers_bare
assert_equal 21 , MomCollection.new.compilers.length
end
def test_append_class
assert_equal MomCollection, (@comp.append @comp).class
end
def test_append_length
assert_equal 2 , @comp.append(@comp).method_compilers.length
end
end
class TestMomCollectionToRisc < MiniTest::Test
include MomCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@comp = compile_mom( "class Test ; def main(); return 'Hi'; end; end;")
@collection = @comp.to_risc()
end
def compiler
@collection.method_compilers.first
end
def test_has_to_risc
assert_equal Risc::RiscCollection, @collection.class
end
def test_has_risc_compiler
assert_equal Risc::MethodCompiler, compiler.class
assert_equal 22, @collection.method_compilers.length
end
def test_has_risc_instructions
assert_equal Risc::Label, compiler.risc_instructions.class
assert_equal 17, compiler.risc_instructions.length
end
end
end

View File

@ -1,40 +0,0 @@
require_relative "helper"
module Mom
class TestMomCompiler < MiniTest::Test
include MomCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@comp = compile_mom( "class Test ; def main(); return 'Hi'; end; end;")
end
def test_class
assert_equal MomCompiler , @comp.class
end
def test_compilers
assert_equal 23 , @comp.compilers.length
end
def test_boot_compilers
assert_equal 22 , @comp.boot_compilers.length
end
def test_compilers_bare
assert_equal 22 , MomCompiler.new.compilers.length
end
def test_returns_constants
assert_equal Array , @comp.constants.class
end
def test_has_constant
assert_equal "Hi" , @comp.constants[1].to_string
end
def test_has_translate
assert @comp.translate(:interpreter)
end
def test_append_class
assert_equal MomCompiler, (@comp.append @comp).class
end
def test_append_length
assert_equal 2 , @comp.append(@comp).method_compilers.length
end
end
end

View File

@ -8,10 +8,9 @@ module Risc
super
@input = "5.div4"
@expect = "something"
@produced = produce_instructions
end
def instruction(num) # 18 is the main, see length in test/mom/send/test_setup_simple.rb
@produced.next( 18 + num)
produce_main.next( 18 + num)
end
def test_postamble_classes
postamble.each_with_index do |ins , index|

View File

@ -4,11 +4,11 @@ module Parfait
class TestMethods < ParfaitTest
def setup
super
Risc::Builtin.boot_functions
Mom.boot!
end
def test_integer
int = Parfait.object_space.get_class_by_name :Integer
assert_equal 14, int.instance_type.method_names.get_length
assert_equal 13, int.instance_type.method_names.get_length
end
def test_methods_booted
word = @space.get_type_by_class_name(:Word)

View File

@ -0,0 +1,32 @@
require_relative "helper"
module Vool
class TestVoolMethod < MiniTest::Test
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
ruby_tree = Ruby::RubyCompiler.compile( as_test_main("a = 5") )
@clazz = ruby_tree.to_vool
end
def method
@clazz.body.first
end
def test_setup
assert_equal ClassStatement , @clazz.class
assert_equal Statements , @clazz.body.class
assert_equal MethodStatement , method.class
end
def test_class
assert_equal Parfait::Class , @clazz.create_class_object.class
end
def test_method
clazz = @clazz.create_class_object
assert_equal Parfait::VoolMethod , method.make_method(clazz).class
end
#create CallableMethod
#create Compiler
end
end

View File

@ -1,11 +0,0 @@
require_relative "../helper"
module Risc
module Builtin
class BuiltinTest < MiniTest::Test
include Ticker
def setup
end
end
end
end

View File

@ -1,8 +1,12 @@
require_relative "helper"
require_relative "../helper"
# TODO move these to interpreter dir
module Risc
module Builtin
class IntCmp < BuiltinTest
class IntCmp < Minitest::Test
include Ticker
def setup
end
def test_smaller_true
run_main_return "4 < 5"

View File

@ -1,8 +1,11 @@
require_relative "helper"
require_relative "../helper"
module Risc
module Builtin
class IntMath < BuiltinTest
class IntMath < Minitest::Test
include Ticker
def setup
end
def test_add
run_main_return "5 + 5"

View File

@ -41,7 +41,7 @@ module Risc
ret = main_ticks(68)
assert_equal FunctionReturn , ret.class
assert_equal :r1 , ret.register.symbol
assert_equal 24204 , @interpreter.get_register(ret.register)
assert_equal 23196 , @interpreter.get_register(ret.register)
end
end
end

View File

@ -5,7 +5,7 @@ module Risc
def setup
Parfait.boot!(Parfait.default_test_options)
Risc.boot!
@linker = Mom::MomCompiler.new.translate(:interpreter)
@linker = Mom::MomCollection.new.to_risc.translate(:interpreter)
@binary = Parfait::BinaryCode.new(1)
@method = Parfait.object_space.types.values.first.methods
@label = Risc.label("hi","ho")

View File

@ -4,8 +4,9 @@ module Risc
class TestMachinePositions < MiniTest::Test
def setup_for(platform)
Parfait.boot!(Parfait.default_test_options)
Mom.boot!
Risc.boot!
@linker = Mom::MomCompiler.new.translate(platform)
@linker = Mom::MomCollection.new.to_risc.translate(platform)
@linker.position_all
end
def test_cpu_init

View File

@ -5,9 +5,10 @@ module Risc
def setup
Parfait.boot!(Parfait.default_test_options)
Mom.boot!
Risc.boot!
init = Parfait.object_space.get_init
@builder = Risc::MethodCompiler.new( init ).builder(init)
@builder = Risc::MethodCompiler.new( init ,Mom::Label.new( "source_name", "return_label")).builder(init)
@label = Risc.label("source","name")
@start = @builder.compiler.current
end

View File

@ -5,9 +5,10 @@ module Risc
def setup
Parfait.boot!(Parfait.default_test_options)
Mom.boot!
Risc.boot!
@init = Parfait.object_space.get_init
@compiler = Risc::MethodCompiler.new( @init )
@compiler = Risc::MethodCompiler.new( @init , Mom::Label.new( "source_name", "return_label"))
@builder = @compiler.builder(@init)
end
def test_inserts_built
@ -42,7 +43,7 @@ module Risc
end
def test_allocate_len
int = @builder.allocate_int
assert_equal 41 , @builder.compiler.risc_instructions.length
assert_equal 28 , @builder.compiler.risc_instructions.length
end
end
end

View File

@ -5,9 +5,10 @@ module Risc
def setup
Parfait.boot!(Parfait.default_test_options)
Mom.boot!
Risc.boot!
@init = Parfait.object_space.get_init
@compiler = Risc::MethodCompiler.new( @init )
@compiler = Risc::MethodCompiler.new( @init, Mom::Label.new( "source_name", "return_label") )
@builder = @compiler.builder(@init)
end
def test_list

View File

@ -0,0 +1,37 @@
require_relative "helper"
module Risc
class FakeCallable
end
class FakeCallableCompiler < CallableCompiler
def initialize(a,c)
super(a,c)
end
def source_name
"luke"
end
end
class TestCallableCompiler < MiniTest::Test
def setup
Parfait.boot!({})
label = Mom::Label.new("hi","ho")
@compiler = FakeCallableCompiler.new(FakeCallable.new , label)
end
def test_ok
assert @compiler
end
def test_current
assert @compiler.current
end
def test_current_label
assert_equal Label , @compiler.current.class
assert_equal "ho" , @compiler.current.name
end
def test_mom
assert @compiler.risc_instructions
end
def test_const
assert_equal Array , @compiler.constants.class
end
end
end

View File

@ -6,12 +6,12 @@ module Risc
def setup
Parfait.boot!(Parfait.default_test_options)
Risc.boot!
@linker = Mom::MomCompiler.new.translate(:arm)
@linker = Mom::MomCollection.new.to_risc.translate(:arm)
end
def test_simple_collect
objects = Collector.collect_space(@linker)
assert_equal 600 , objects.length , objects.length.to_s
assert_equal 621 , objects.length , objects.length.to_s
end
def test_collect_all_types
@ -47,15 +47,15 @@ module Risc
def setup
opt = Parfait.default_test_options
opt[:factory] = 4000
opt[:factory] = 400
Parfait.boot!(opt)
Risc.boot!
@linker = Mom::MomCompiler.new.translate(:arm)
@linker = Mom::MomCollection.new.to_risc.translate(:arm)
end
def test_simple_collect
objects = Collector.collect_space(@linker)
assert_equal 20329, objects.length , objects.length.to_s
assert_equal 2421, objects.length , objects.length.to_s
end
def test_integer_positions

View File

@ -4,8 +4,9 @@ module Risc
class TestInterpreterBasics < MiniTest::Test
def setup
Parfait.boot!(Parfait.default_test_options)
Mom.boot!
Risc.boot!
@linker = Mom::MomCompiler.new.translate(:interpreter)
@linker = Mom::MomCollection.new.to_risc.translate(:interpreter)
end
def test_class
@ -54,7 +55,7 @@ module Risc
end
def test_pc1
@interpreter.tick
assert_equal 23672 , @interpreter.pc
assert_equal 22856 , @interpreter.pc
end
def test_tick2
@interpreter.tick
@ -68,7 +69,7 @@ module Risc
def test_pc2
@interpreter.tick
@interpreter.tick
assert_equal 23676 , @interpreter.pc
assert_equal 22860 , @interpreter.pc
end
def test_tick_14_jump
14.times {@interpreter.tick}

View File

@ -6,7 +6,7 @@ module Risc
def setup
Parfait.boot!(Parfait.default_test_options)
Risc.boot!
@linker = Mom::MomCompiler.new.translate(:arm)
@linker = Mom::MomCollection.new.to_risc.translate(:arm)
end
def test_objects
objects = @linker.object_positions
@ -25,7 +25,7 @@ module Risc
assert_equal 0 , Position.get(@linker.cpu_init).at
end
def test_cpu_at
assert_equal "0x569c" , Position.get(@linker.cpu_init.first).to_s
assert_equal "0x563c" , Position.get(@linker.cpu_init.first).to_s
end
def test_cpu_label
assert_equal Position , Position.get(@linker.cpu_init.first).class

View File

@ -4,8 +4,7 @@ module Risc
class TestMachinePos < MiniTest::Test
def setup
code = "class Space; def main(arg);a = 1;return a;end;end"
@linker = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_risc(code,:arm)
@linker.position_all
@linker = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_binary(code, :arm)
end
def test_positions_set
@linker.object_positions.each do |obj , position|
@ -17,7 +16,7 @@ module Risc
assert_equal 1 , mains.length
end
def test_assembler_num
assert_equal 23 , @linker.assemblers.length
assert_equal 22 , @linker.assemblers.length
end
end
end

View File

@ -12,9 +12,6 @@ module Risc
vool.to_mom(nil)
vool
end
def in_test_mom(str)
FIXMERubyX::RubyXCompiler.new(in_Test(str)).ruby_to_mom()
end
def create_method(body = "@ivar = 5")
in_test_vool("def meth; #{body};end")
test = Parfait.object_space.get_class_by_name(:Test)
@ -65,19 +62,11 @@ module Risc
end
def constant_setup(input)
mom = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(in_Test(input))
assert_equal Mom::MomCompiler , mom.class
assert_equal Mom::MomCollection , mom.class
compiler = mom.method_compilers.first
assert_equal MethodCompiler , compiler.class
assert_equal Mom::MethodCompiler , compiler.class
compiler
end
def test_has_method_constant
compiler = constant_setup("def meth; return 'Hi';end")
assert compiler.constants.include?("Hi")
end
def test_has_block_constant
compiler = constant_setup("def meth; meth{return 'Ho'};return 'Hi';end")
assert compiler.constants.include?("Ho")
end
def test_return_label
compiler = constant_setup("def meth; return 'Hi';end")
assert_equal "return_label", compiler.return_label.name

View File

@ -1,17 +1,17 @@
require_relative "helper"
module Mom
module Risc
class TestMomCompilerTranslate < MiniTest::Test
include MomCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@comp = compile_mom( "class Test ; def main(); main{return 'Ho'};return 'Hi'; end; end;")
@linker = @comp.translate(:interpreter)
@linker = @comp.to_risc.translate(:interpreter)
end
def test_translate_class
assert_equal Risc::Linker , @linker.class
assert_equal Linker , @linker.class
end
def test_linker_has_constants
assert_equal Array , @linker.constants.class
@ -26,16 +26,16 @@ module Mom
assert @linker.constants.include?("Ho")
end
def test_translate_platform
assert_kind_of Risc::Platform , @linker.platform
assert_kind_of Platform , @linker.platform
end
def test_translate_assemblers
assert_equal Risc::Assembler , @linker.assemblers.first.class
assert_equal Assembler , @linker.assemblers.first.class
end
def test_assembler_code
assert_equal Risc::Label , @linker.assemblers.first.instructions.class
assert_equal Label , @linker.assemblers.first.instructions.class
end
def test_assembler_assembled
assert_equal Risc::LoadConstant , @linker.assemblers.first.instructions.next.class
assert_equal LoadConstant , @linker.assemblers.first.instructions.next.class
end
def test_no_loops_in_chain
@linker.assemblers.each do |asm|

View File

@ -0,0 +1,58 @@
require_relative "helper"
module Risc
class TestMomCompilerTranslate < MiniTest::Test
include MomCompile
def setup
@comp = compile_mom( "class Test ; def main(); main{return 'Ho'};return 'Hi'; end; end;")
@linker = @comp.to_risc.translate(:interpreter)
end
def test_translate_class
assert_equal Linker , @linker.class
end
def test_linker_has_constants
assert_equal Array , @linker.constants.class
end
def test_linker_constants_not_empty
assert !@linker.constants.empty?
end
def test_linker_constants_contains_hi
assert @linker.constants.include?("Hi")
end
def test_linker_constants_contains_ho
assert @linker.constants.include?("Ho")
end
def test_translate_platform
assert_kind_of Platform , @linker.platform
end
def test_translate_assemblers
assert_equal Assembler , @linker.assemblers.first.class
end
def test_assembler_code
assert_equal Label , @linker.assemblers.first.instructions.class
end
def test_assembler_assembled
assert_equal LoadConstant , @linker.assemblers.first.instructions.next.class
end
def test_no_loops_in_chain
@linker.assemblers.each do |asm|
all = []
asm.instructions.each do |ins|
assert !all.include?(ins) , "Double in #{asm.callable.name}:#{ins}"
all << ins
end
end
end
def test_no_risc
@linker.position_all
@linker.create_binary
@linker.assemblers.each do |asm|
asm.instructions.each do |ins|
ins.assemble(Util::DevNull.new)
end # risc instruction don't have an assemble
end
end
end
end

View File

@ -6,7 +6,7 @@ module Risc
def setup
Parfait.boot!(Parfait.default_test_options)
Risc.boot!
@linker = Mom::MomCompiler.new.translate(:arm)
@linker = Mom::MomCollection.new.to_risc.translate(:arm)
end
def test_init
@text_writer = TextWriter.new(@linker)
@ -21,7 +21,7 @@ module Risc
def setup
Parfait.boot!(Parfait.default_test_options)
Risc.boot!
@linker = Mom::MomCompiler.new.translate(:arm)
@linker = Mom::MomCollection.new.to_risc.translate(:arm)
@linker.position_all
@linker.create_binary
@text_writer = TextWriter.new(@linker)

View File

@ -5,10 +5,6 @@ module RubyX
module RubyXHelper
def setup
end
def ruby_to_risc(input , options = {})
mom = ruby_to_mom(input , options)
mom.translate(options[:platform] || :interpreter)
end
def ruby_to_vool(input, options = {})
options = RubyX.default_test_options.merge(options)
RubyXCompiler.new(options).ruby_to_vool(input)

View File

@ -8,29 +8,35 @@ module RubyX
def setup
super
code = "class Space ; def main(arg);return arg;end; end"
@linker = ruby_to_risc(code)
@comp = RubyXCompiler.new(load_parfait: true )
@collection = @comp.ruby_to_risc(code)
end
def test_to_risc
assert_equal Risc::Linker , @linker.class
assert_equal Risc::RiscCollection , @collection.class
end
def test_linker
assert_equal Risc::Linker , @collection.translate(:interpreter).class
end
def test_method
assert_equal :main , @linker.assemblers.first.callable.name
linker = @collection.translate(:interpreter)
assert_equal :main , linker.assemblers.first.callable.name
end
def test_asm_len
assert_equal 23 , @linker.assemblers.length
linker = @collection.translate(:interpreter)
assert_equal 22 , linker.assemblers.length
end
end
class TestRubyXCompilerParfait < MiniTest::Test
class TestRubyXCompilerParfait #< MiniTest::Test
include ScopeHelper
include RubyXHelper
def setup
super
code = "class Space ; def self.class_method; return 1; end;def main(arg);return Space.class_method;end; end"
@comp = RubyXCompiler.ruby_to_binary(code , load_parfait: true , platform: :interpreter)
code = "class Space ; def self.class_method(); return 1; end;def main(arg);return Space.class_method;end; end"
@comp = RubyXCompiler.ruby_to_risc(code , load_parfait: true)# , platform: :interpreter)
end
def pest_load
def test_load
object = Parfait.object_space.get_class_by_name(:Object)
assert_equal Parfait::Class , object.class
object = object.instance_type

View File

@ -33,9 +33,10 @@ module RubyX
compiler = RubyXCompiler.new(RubyX.default_test_options)
compiler.ruby_to_vool(space_source_for("main"))
compiler.ruby_to_vool(space_source_for("twain"))
assert_equal 2 , compiler.vool.length
linker = compiler.to_binary(:interpreter)
assert_equal Risc::Linker , linker.class
assert_equal 24 , linker.assemblers.length
assert_equal 23 , linker.assemblers.length
end
end
end

View File

@ -16,43 +16,34 @@ module ScopeHelper
def as_test_main( statements )
in_Test("def main(arg) ; #{statements}; end")
end
end
module MomCompile
include ScopeHelper
def compile_method(input)
statements = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_vool(input)
assert statements.is_a?(Vool::ClassStatement)
ret = statements.to_mom(nil)
assert_equal Parfait::Class , statements.clazz.class , statements
@method = statements.clazz.get_method(:main)
assert_equal Parfait::VoolMethod , @method.class
ret
def as_test_main_block( block_input = "return 5", method_input = "main_local = 5")
as_test_main("#{method_input} ; self.main{|val| #{block_input}}")
end
end
module VoolCompile
include ScopeHelper
include Mom
def compile_first_method( input )
ret = compile_method( as_test_main( input ))
assert_equal Mom::MomCompiler , ret.class
compiler = ret.method_compilers.find{|c| c.get_method.name == :main and c.get_method.self_type.object_class.name == :Test}
assert_equal Risc::MethodCompiler , compiler.class
@method.source.to_mom( compiler )
input = as_test_main( input )
collection = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(input)
assert collection.is_a?(Mom::MomCollection) , collection.class.name
compiler = collection.compilers.first
assert compiler.is_a?(Mom::MethodCompiler)
assert_equal Mom::MethodCompiler , compiler.class
compiler
end
def compile_first_block( block_input , method_input = "main_local = 5")
source = "#{method_input} ; self.main{|val| #{block_input}}"
vool = Ruby::RubyCompiler.compile( as_test_main(source) ).to_vool
mom_c = vool.to_mom(nil)
compiler = mom_c.method_compilers.find{|c| c.get_method.name == :main and c.get_method.self_type.object_class.name == :Test}
block = nil
vool.each {|b| block = b if b.is_a?(Vool::BlockStatement)}
source = as_test_main("#{method_input} ; self.main{|val| #{block_input}}")
mom_col = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom( source )
compiler = mom_col.method_compilers.find{|c| c.get_method.name.to_s.start_with?("main") }
block = compiler.block_compilers.first
assert block
block_c = compiler.block_compilers.first
assert block_c
block.body.to_mom(block_c)
block.mom_instructions.next
end
def compile_mom(input)
RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(input)
end
def check_array( should , is )
index = 0
test = is
@ -79,26 +70,11 @@ module MomCompile
end
module MomCompile
include ScopeHelper
class Ignored
def == other
return false unless other.class == self.class
Sof::Util.attributes(self).each do |a|
begin
left = send(a)
rescue NoMethodError
next # not using instance variables that are not defined as attr_readers for equality
end
begin
right = other.send(a)
rescue NoMethodError
return false
end
return false unless left.class == right.class
return false unless left == right
end
return true
def compile_mom(input)
RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(input)
end
end

View File

@ -2,13 +2,12 @@ require_relative "../helper"
module VoolBlocks
class TestAssignMom < MiniTest::Test
include MomCompile
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_block( "local = 5")
@ins = compile_first_block( "local = 5" )
end
def test_block_compiles
assert_equal Mom::SlotLoad , @ins.class , @ins
end
@ -30,7 +29,7 @@ module VoolBlocks
end
class TestAssignMomInstanceToLocal < MiniTest::Test
include MomCompile
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_block( "local = @a" , "@a = 5") #second arg in method scope
@ -47,7 +46,7 @@ module VoolBlocks
end
class TestAssignToArg < MiniTest::Test
include MomCompile
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ -66,7 +65,7 @@ module VoolBlocks
end
class TestAssignMomToInstance < MiniTest::Test
include MomCompile
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
end

View File

@ -2,7 +2,6 @@ require_relative "../helper"
module VoolBlocks
class TestClassAssignMom < MiniTest::Test
include MomCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ -21,7 +20,7 @@ module VoolBlocks
end
def test_assign_compiles
vool = Ruby::RubyCompiler.compile( as_class_method("val = 0") ).to_vool
assert_equal Mom::MomCompiler , vool.to_mom(nil).class
assert_equal Mom::MomCollection , vool.to_mom(nil).class
end
end
end

View File

@ -2,12 +2,11 @@ require_relative "helper"
module VoolBlocks
class TestConditionIfMom < MiniTest::Test
include MomCompile
include Mom
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
Risc::Builtin.boot_functions
Mom::Builtin.boot_functions
@ins = compile_first_block( "if(5.div4) ; @a = 6 ; else; @a = 5 ; end")
end
@ -23,7 +22,8 @@ module VoolBlocks
end
def test_array
check_array [MessageSetup, ArgumentTransfer, SimpleCall, SlotLoad, TruthCheck, Label ,
SlotLoad, Jump, Label, SlotLoad, Label] , @ins
SlotLoad, Jump, Label, SlotLoad, Label,
Label, ReturnSequence, Label] , @ins
end
end

View File

@ -2,8 +2,7 @@ require_relative "helper"
module VoolBlocks
class TestSimpleWhileMom < MiniTest::Test
include MomCompile
include Mom
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ -20,7 +19,8 @@ module VoolBlocks
assert_equal SlotDefinition , @ins.next.condition.class , @ins
end
def test_array
check_array [Label, TruthCheck, SlotLoad, Jump, Label], @ins
check_array [Label, TruthCheck, SlotLoad, Jump, Label,
Label, ReturnSequence, Label], @ins
end
end
end

View File

@ -4,12 +4,13 @@ module Vool
# relies on @ins and receiver_type method
module ClassHarness
include MomCompile
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
Risc::Builtin.boot_functions
@ins = compile_first_method( send_method )
Risc.boot!
@compiler = compile_first_method( send_method )
@ins = @compiler.mom_instructions.next
end
def test_first_not_array
@ -19,7 +20,7 @@ module Vool
assert_equal Mom::MessageSetup , @ins.class , @ins
end
def test_two_instructions_are_returned
assert_equal 3 , @ins.length , @ins
assert_equal 6 , @ins.length , @ins
end
def test_receiver_move_class
assert_equal Mom::ArgumentTransfer, @ins.next(1).class
@ -37,7 +38,8 @@ module Vool
assert_equal Parfait::CallableMethod, @ins.next(2).method.class
end
def test_array
check_array [Mom::MessageSetup,Mom::ArgumentTransfer,Mom::SimpleCall] , @ins
check_array [MessageSetup,ArgumentTransfer,SimpleCall,Label,
ReturnSequence , Label] , @ins
end
end

View File

@ -17,16 +17,8 @@ module Vool
end
def setup
statements = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_vool(class_main)
assert_equal Vool::ClassStatement, statements.class
ret = statements.to_mom(nil)
assert_equal Parfait::Class , statements.clazz.class , statements
@method = statements.clazz.get_method(:main)
assert_equal Parfait::VoolMethod , @method.class
assert_equal Mom::MomCompiler , ret.class
compiler = ret.method_compilers.find{|c| c.get_method.name == :main }
assert_equal Risc::MethodCompiler , compiler.class
@ins = @method.source.to_mom( compiler )
ret = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(class_main)
@ins = ret.compilers.first.mom_instructions.next
end
def test_any
@ -35,7 +27,7 @@ module Vool
def test_no_arg
assert_equal Mom::ArgumentTransfer, @ins.next(1).class
assert_equal 0, @ins.next(1).arguments.length
assert_equal 1, @ins.next(1).arguments.length
end
def test_call_two
assert_equal SimpleCall, @ins.next(2).class

View File

@ -2,7 +2,6 @@ require_relative "helper"
module Vool
class ClassSendHarness < MiniTest::Test
include MomCompile
include ClassHarness
def send_method

View File

@ -3,7 +3,6 @@ require_relative "helper"
module Vool
class TestSendClassMom < MiniTest::Test
include ClassHarness
include Mom
def send_method
"Object.get_internal_word(0)"

View File

@ -3,28 +3,30 @@ require_relative "../helper"
module Vool
# relies on @ins and receiver_type method
module SimpleSendHarness
include MomCompile
include VoolCompile
include Mom
def setup
Parfait.boot!(Parfait.default_test_options)
Risc::Builtin.boot_functions
@ins = compile_first_method( send_method )
Mom.boot!
@compiler = compile_first_method( send_method )
@ins = @compiler.mom_instructions.next
end
def test_first_not_array
assert Array != @ins.class , @ins
end
def test_class_compiles
assert_equal Mom::MessageSetup , @ins.class , @ins
assert_equal MessageSetup , @ins.class , @ins
end
def test_two_instructions_are_returned
assert_equal 3 , @ins.length , @ins
assert_equal 6 , @ins.length , @ins
end
def test_receiver_move_class
assert_equal Mom::ArgumentTransfer, @ins.next(1).class
assert_equal ArgumentTransfer, @ins.next(1).class
end
def test_receiver_move
assert_equal Mom::SlotDefinition, @ins.next.receiver.class
assert_equal SlotDefinition, @ins.next.receiver.class
end
def test_receiver
type , value = receiver
@ -32,13 +34,14 @@ module Vool
assert_equal value, @ins.next.receiver.known_object.value
end
def test_call_is
assert_equal Mom::SimpleCall, @ins.next(2).class
assert_equal SimpleCall, @ins.next(2).class
end
def test_call_has_method
assert_equal Parfait::CallableMethod, @ins.next(2).method.class
end
def test_array
check_array [Mom::MessageSetup,Mom::ArgumentTransfer,Mom::SimpleCall] , @ins
check_array [MessageSetup,ArgumentTransfer,SimpleCall,Label, ReturnSequence ,
Label] , @ins
end
end
end

View File

@ -2,18 +2,19 @@ require_relative "helper"
module Vool
class TestSendArgsSendMom < MiniTest::Test
include MomCompile
include Mom
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
Risc.boot!
@ins = compile_first_method( "a = main(1 + 2)" )
@compiler = compile_first_method( "a = main(1 + 2)" )
@ins = @compiler.mom_instructions.next
end
def test_array
check_array [MessageSetup, ArgumentTransfer, SimpleCall, SlotLoad, MessageSetup ,
ArgumentTransfer, SimpleCall, SlotLoad] , @ins
ArgumentTransfer, SimpleCall, SlotLoad ,Label, ReturnSequence ,
Label] , @ins
end
def test_one_call

View File

@ -2,12 +2,12 @@ require_relative "../helper"
module Vool
class TestSendCachedSimpleMom < MiniTest::Test
include MomCompile
include Mom
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_method( "a = 5; a.div4")
@compiler = compile_first_method( "a = 5; a.div4")
@ins = @compiler.mom_instructions.next
end
def test_check_type
assert_equal NotSameCheck , @ins.next.class , @ins
@ -23,12 +23,13 @@ module Vool
assert_equal ResolveMethod , @ins.next(3).class , @ins
end
def test_dynamic_call_last
assert_equal DynamicCall , @ins.last.class , @ins
assert_equal DynamicCall , @ins.next(7).class , @ins
end
def test_array
check_array [SlotLoad, NotSameCheck, SlotLoad, ResolveMethod, Label, MessageSetup ,
ArgumentTransfer, DynamicCall] , @ins
ArgumentTransfer, DynamicCall, Label, ReturnSequence ,
Label] , @ins
end
end

View File

@ -3,7 +3,6 @@ require_relative "helper"
module Vool
class TestSendSelfMom < MiniTest::Test
include SimpleSendHarness
include Mom
def send_method
"self.get_internal_word(0)"

View File

@ -20,7 +20,8 @@ module Vool
assert_equal 2, @ins.next(1).arguments[1].right.known_object.value
end
def test_array
check_array [Mom::MessageSetup,Mom::ArgumentTransfer,Mom::SimpleCall] , @ins
check_array [MessageSetup,ArgumentTransfer,SimpleCall, Label, ReturnSequence ,
Label] , @ins
end
end
end

View File

@ -2,11 +2,12 @@ require_relative "helper"
module Vool
class TestAssignMom < MiniTest::Test
include MomCompile
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_method( "local = 5")
@compiler = compile_first_method( "local = 5")
@ins = @compiler.mom_instructions.next
end
def test_class_compiles
@ -34,10 +35,11 @@ module Vool
#otherwise as above, but assigning instance, so should get a SlotLoad
class TestAssignMomInstanceToLocal < MiniTest::Test
include MomCompile
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_method( "@a = 5 ; local = @a")
@compiler = compile_first_method( "@a = 5 ; local = @a")
@ins = @compiler.mom_instructions.next
end
def test_class_compiles
assert_equal Mom::SlotLoad , @ins.next.class , @ins
@ -46,11 +48,12 @@ module Vool
#compiling to an argument should result in different second parameter in the slot array
class TestAssignToArg < MiniTest::Test
include MomCompile
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_method( "arg = 5")
@compiler = compile_first_method( "arg = 5")
@ins = @compiler.mom_instructions.next
end
def test_class_compiles
@ -71,17 +74,19 @@ module Vool
end
class TestAssignMomToInstance < MiniTest::Test
include MomCompile
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
end
def test_assigns_const
@ins = compile_first_method( "@a = 5")
@compiler = compile_first_method( "@a = 5")
@ins = @compiler.mom_instructions.next
assert_equal Mom::SlotLoad , @ins.class , @ins
assert_equal Mom::IntegerConstant , @ins.right.known_object.class , @ins
end
def test_assigns_move
@ins = compile_first_method( "@a = arg")
@compiler = compile_first_method( "@a = arg")
@ins = @compiler.mom_instructions.next
assert_equal Mom::SlotLoad , @ins.class , @ins
assert_equal Mom::SlotDefinition , @ins.right.class , @ins
end

View File

@ -0,0 +1,55 @@
require_relative "helper"
module Vool
class TestClassStatement < MiniTest::Test
include ScopeHelper
def setup
Parfait.boot!({})
ruby_tree = Ruby::RubyCompiler.compile( as_test_main("a = 5") )
@vool = ruby_tree.to_vool
end
def test_class
assert_equal ClassStatement , @vool.class
end
def test_method
assert_equal MethodStatement , @vool.body.first.class
end
def test_create_class
assert_equal Parfait::Class , @vool.create_class_object.class
end
def test_create_class
assert_equal :Test , @vool.create_class_object.name
end
end
class TestClassStatementCompile < MiniTest::Test
include VoolCompile
def setup
@compiler = compile_first_method( "if(@a) ; @a = 5 ; else; @a = 6 ; end")
@ins = @compiler.mom_instructions
end
def test_label
assert_equal Label , @ins.class , @ins
assert_equal "Test_Type.main" , @ins.name , @ins
end
def test_condition_compiles_to_check
assert_equal TruthCheck , @ins.next.class , @ins
end
def test_condition_is_slot
assert_equal SlotDefinition , @ins.next.condition.class , @ins
end
def test_label_after_check
assert_equal Label , @ins.next(2).class , @ins
end
def test_label_last
assert_equal Label , @ins.last.class , @ins
end
def test_array
check_array [Label, TruthCheck, Label, SlotLoad, Jump ,
Label, SlotLoad, Label, Label, ReturnSequence ,
Label] , @ins
end
end
end

View File

@ -2,13 +2,12 @@ require_relative "helper"
module Vool
class TestConditionIfMom < MiniTest::Test
include MomCompile
include Mom
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
Risc::Builtin.boot_functions
@ins = compile_first_method( "if(5.div4) ; @a = 6 ; else; @a = 5 ; end")
@compiler = compile_first_method( "if(5.div4) ; @a = 6 ; else; @a = 5 ; end")
@ins = @compiler.mom_instructions.next
end
def test_condition
@ -22,8 +21,9 @@ module Vool
assert_equal :div4 , @ins.next(2).method.name
end
def test_array
check_array [MessageSetup, ArgumentTransfer, SimpleCall, SlotLoad, TruthCheck, Label ,
SlotLoad, Jump, Label, SlotLoad, Label] , @ins
check_array [MessageSetup, ArgumentTransfer, SimpleCall, SlotLoad, TruthCheck,
Label ,SlotLoad, Jump, Label, SlotLoad, Label,
Label, ReturnSequence, Label ] , @ins
end
end

View File

@ -3,12 +3,12 @@ require_relative "helper"
module Vool
class TestIfNoElse < MiniTest::Test
include MomCompile
include Mom
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_method( "if(@a) ; @a = 5 ; end")
@compiler = compile_first_method( "if(@a) ; @a = 5 ; end")
@ins = @compiler.mom_instructions.next
end
def test_condition_compiles_to_check
@ -24,7 +24,8 @@ module Vool
assert_equal Label , @ins.last.class , @ins
end
def test_array
check_array [TruthCheck, Label, SlotLoad, Label], @ins
check_array [TruthCheck, Label, SlotLoad, Label, Label ,
ReturnSequence, Label], @ins
end
end
end

View File

@ -3,12 +3,12 @@ require_relative "helper"
module Vool
class TestSimpleIfMom < MiniTest::Test
include MomCompile
include Mom
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_method( "if(@a) ; @a = 5 ; else; @a = 6 ; end")
@compiler = compile_first_method( "if(@a) ; @a = 5 ; else; @a = 6 ; end")
@ins = @compiler.mom_instructions.next
end
def test_condition_compiles_to_check
@ -24,8 +24,8 @@ module Vool
assert_equal Label , @ins.last.class , @ins
end
def test_array
check_array [TruthCheck, Label, SlotLoad, Jump, Label, SlotLoad ,
Label] , @ins
check_array [TruthCheck, Label, SlotLoad, Jump, Label ,
SlotLoad, Label, Label, ReturnSequence, Label], @ins
end
end
end

View File

@ -2,11 +2,12 @@ require_relative "helper"
module Vool
class TestIvarMom < MiniTest::Test
include MomCompile
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_method( "@a = 5")
@compiler = compile_first_method( "@a = 5")
@ins = @compiler.mom_instructions.next
end
def test_compiles_not_array

View File

@ -2,11 +2,12 @@ require_relative "helper"
module Vool
class TestLocalMom < MiniTest::Test
include MomCompile
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_method( "a = 5")
@compiler = compile_first_method( "a = 5")
@ins = @compiler.mom_instructions.next
end
def test_compiles_not_array

View File

@ -0,0 +1,29 @@
require_relative "helper"
module Vool
class TestMethodStatement < MiniTest::Test
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
ruby_tree = Ruby::RubyCompiler.compile( as_test_main("a = 5") )
@clazz = ruby_tree.to_vool
end
def method
@clazz.body.first
end
def test_setup
assert_equal ClassStatement , @clazz.class
assert_equal Statements , @clazz.body.class
assert_equal MethodStatement , method.class
end
def test_class
assert_equal Parfait::Class , @clazz.create_class_object.class
end
def test_method
clazz = @clazz.create_class_object
assert_equal Parfait::VoolMethod , method.make_method(clazz).class
end
end
end

View File

@ -2,57 +2,57 @@ require_relative "helper"
module Vool
class TestReturnMom < MiniTest::Test
include MomCompile
include Mom
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@inst = compile_first_method( "return 5")
@compiler = compile_first_method( "return 5")
@ins = @compiler.mom_instructions.next
end
def test_class_compiles
assert_equal SlotLoad , @inst.class , @inst
assert_equal SlotLoad , @ins.class , @ins
end
def test_slot_is_set
assert @inst.left
assert @ins.left
end
def test_two_instructions_are_returned
assert_equal 2 , @inst.length
end
def test_second_is_return
assert_equal ReturnJump, @inst.last.class
assert_equal 5 , @ins.length
end
def test_slot_starts_at_message
assert_equal :message , @inst.left.known_object
assert_equal :message , @ins.left.known_object
end
def test_slot_gets_return
assert_equal :return_value , @inst.left.slots[0]
assert_equal :return_value , @ins.left.slots[0]
end
def test_slot_assigns_something
assert @inst.right
assert @ins.right
end
def test_slot_assigns_int
assert_equal Mom::IntegerConstant , @inst.right.known_object.class
assert_equal Mom::IntegerConstant , @ins.right.known_object.class
end
def test_second_is_return
assert_equal ReturnJump, @ins.next(1).class
end
def test_array
check_array [SlotLoad,ReturnSequence] , @ins
check_array [SlotLoad, ReturnJump, Label, ReturnSequence, Label], @ins
end
end
class TestReturnSendMom < MiniTest::Test
include MomCompile
include Mom
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
Risc.boot!
@ins = compile_first_method( "return 5.div4")
@compiler = compile_first_method( "return 5.div4")
@ins = @compiler.mom_instructions.next
end
def test_return_is_last
assert_equal ReturnJump , @ins.last.class
assert_equal ReturnJump , @ins.next(5).class
end
def test_array
check_array [MessageSetup,ArgumentTransfer,SimpleCall,SlotLoad,SlotLoad,ReturnJump] , @ins
check_array [MessageSetup,ArgumentTransfer,SimpleCall,SlotLoad,
SlotLoad,ReturnJump, Label, ReturnSequence, Label] , @ins
end
end
end

View File

@ -2,12 +2,12 @@ require_relative "helper"
module Vool
class TestSimpleWhileMom < MiniTest::Test
include MomCompile
include Mom
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_method( "while(@a) ; @a = 5 ; end")
@compiler = compile_first_method( "while(@a) ; @a = 5 ; end")
@ins = @compiler.mom_instructions.next
end
def test_compiles_as_while
@ -20,7 +20,8 @@ module Vool
assert_equal SlotDefinition , @ins.next.condition.class , @ins
end
def test_array
check_array [Label, TruthCheck, SlotLoad, Jump, Label], @ins
check_array [Label, TruthCheck, SlotLoad, Jump, Label ,
Label, ReturnSequence, Label], @ins
end
end
end

View File

@ -3,13 +3,12 @@ require_relative "helper"
module Vool
class TestWhileConditionMom < MiniTest::Test
include MomCompile
include Mom
include VoolCompile
def setup
Parfait.boot!(Parfait.default_test_options)
Risc::Builtin.boot_functions
@ins = compile_first_method( "while(5.div4) ; 5.div4 ; end")
@compiler = compile_first_method( "while(5.div4) ; 5.div4 ; end")
@ins = @compiler.mom_instructions.next
end
def test_condition_compiles_to_check
@ -31,7 +30,7 @@ module Vool
def test_array
check_array [Label, MessageSetup, ArgumentTransfer, SimpleCall, SlotLoad ,
TruthCheck, MessageSetup, ArgumentTransfer, SimpleCall, Jump ,
Label] , @ins
Label, Label, ReturnSequence, Label] , @ins
end
end

View File

@ -55,17 +55,19 @@ module Vool
end
end
class TestYieldArgsSendMom < MiniTest::Test
include MomCompile
include VoolCompile
include YieldBasics
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_method( "return yield(1)" )
@compiler = compile_first_method( "return yield(1)" )
@ins = @compiler.mom_instructions.next
end
def test_array
check_array [NotSameCheck, Label, MessageSetup, ArgumentTransfer, BlockYield ,
SlotLoad, SlotLoad, ReturnJump] , @ins
SlotLoad, SlotLoad, ReturnJump, Label, ReturnSequence ,
Label] , @ins
end
def test_transfer
assert_equal ArgumentTransfer, @ins.next(3).class
@ -79,16 +81,17 @@ module Vool
end
end
class TestYieldNoArgsSendMom < MiniTest::Test
include MomCompile
include Mom
include VoolCompile
include YieldBasics
def setup
Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_method( "return yield" )
@compiler = compile_first_method( "return yield" )
@ins = @compiler.mom_instructions.next
end
def test_array
check_array [NotSameCheck, Label, MessageSetup, ArgumentTransfer, BlockYield ,
SlotLoad, SlotLoad, ReturnJump] , @ins
SlotLoad, SlotLoad, ReturnJump, Label, ReturnSequence ,
Label] , @ins
end
def test_transfer
assert_equal ArgumentTransfer, @ins.next(3).class