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/space"
require_relative "builtin/integer" require_relative "builtin/integer"
require_relative "builtin/object" require_relative "builtin/object"
require_relative "builtin/word" require_relative "builtin/word"
module Risc module Mom
module Builtin module Builtin
# classes have booted, now create a minimal set of functions # classes have booted, now create a minimal set of functions
# minimal means only that which can not be coded in ruby # minimal means only that which can not be coded in ruby
# Methods are grabbed from respective modules by sending the method name. # 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) # 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) # 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(add_main = false) def self.boot_functions()
# TODO go through the virtual parfait layer and adjust function names # TODO go through the virtual parfait layer and adjust function names
# to what they really are # to what they really are
compilers = [] compilers = []
@ -25,8 +37,8 @@ module Risc
end end
obj_type = space.get_type_by_class_name(:Object) obj_type = space.get_type_by_class_name(:Object)
[ :get_internal_word , :set_internal_word , :_method_missing, [ :__init__ , :exit , :_method_missing, :get_internal_word ,
:exit , :__init__].each do |f| :set_internal_word ].each do |f|
compilers << compiler_for( obj_type , Object , f) compilers << compiler_for( obj_type , Object , f)
end end
@ -39,7 +51,7 @@ module Risc
Risc.operators.each do |op| Risc.operators.each do |op|
compilers << operator_compiler( int_type , op) compilers << operator_compiler( int_type , op)
end 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) compilers << compiler_for( int_type , Integer , f)
end end
compilers 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 module Builtin
class Space class Space
module ClassMethods module ClassMethods
@ -8,7 +8,6 @@ module Risc
# defined here as empty, to be redefined # defined here as empty, to be redefined
def main(context) def main(context)
compiler = compiler_for(:Space , :main ,{args: :Integer}) compiler = compiler_for(:Space , :main ,{args: :Integer})
compiler.add_mom( Mom::ReturnSequence.new)
return compiler return compiler
end 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 class Instruction
include Util::List 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. # 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). # 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 # In other words Mom is the higher abstraction and so mom instructions convert
@ -27,18 +38,18 @@ module Mom
end end
require_relative "label" require_relative "instruction/label"
require_relative "check" require_relative "instruction/check"
require_relative "basic_values" require_relative "instruction/basic_values"
require_relative "simple_call" require_relative "instruction/simple_call"
require_relative "dynamic_call" require_relative "instruction/dynamic_call"
require_relative "block_yield" require_relative "instruction/block_yield"
require_relative "resolve_method" require_relative "instruction/resolve_method"
require_relative "truth_check" require_relative "instruction/truth_check"
require_relative "not_same_check" require_relative "instruction/not_same_check"
require_relative "jump" require_relative "instruction/jump"
require_relative "return_jump" require_relative "instruction/return_jump"
require_relative "slot_load" require_relative "instruction/slot_load"
require_relative "return_sequence" require_relative "instruction/return_sequence"
require_relative "message_setup" require_relative "instruction/message_setup"
require_relative "argument_transfer" require_relative "instruction/argument_transfer"

View File

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

View File

@ -6,7 +6,10 @@ module Mom
class BlockYield < Instruction class BlockYield < Instruction
attr :arg_index 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 @arg_index = index
end end
@ -14,6 +17,7 @@ module Mom
"BlockYield[#{arg_index}] " "BlockYield[#{arg_index}] "
end end
# almost as simple as a SimpleCall, use a dynamic_jump to get there
def to_risc(compiler) def to_risc(compiler)
return_label = Risc.label("block_yield", "continue_#{object_id}") return_label = Risc.label("block_yield", "continue_#{object_id}")
index = arg_index index = arg_index

View File

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

View File

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

View File

@ -15,7 +15,11 @@ module Mom
class ResolveMethod < Instruction class ResolveMethod < Instruction
attr :cache_entry , :name 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 @name = name
@cache_entry = cache_entry @cache_entry = cache_entry
end end
@ -61,7 +65,7 @@ module Mom
# temporary, need to raise really. # temporary, need to raise really.
factory! << Parfait.object_space.get_factory_for(:Integer) factory! << Parfait.object_space.get_factory_for(:Integer)
integer_tmp! << factory[:reserve] 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 add_code ok_label
cache_entry[:cached_method] << callable_method cache_entry[:cached_method] << callable_method

View File

@ -7,8 +7,20 @@ module Mom
# #
class ReturnJump < Instruction 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) 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
end end

View File

@ -22,13 +22,14 @@ module Mom
# SlotDefinition (see there) # SlotDefinition (see there)
# #
# @right: A SlotDefinition with slots or a Mom::Constant # @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) # risc instructions. (Because SlotLoad is often used internally in mom)
class SlotLoad < Instruction class SlotLoad < Instruction
attr_reader :left , :right , :original_source 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 , @right = left , right
@left = SlotDefinition.new(@left.shift , @left) if @left.is_a? Array @left = SlotDefinition.new(@left.shift , @left) if @left.is_a? Array
@right = SlotDefinition.new(@right.shift , @right) if @right.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. # Machine capabilities (instructions) for basic operations. Use of macros for higher level.
module Mom module Mom
# boot bubiltin function (subject to change)
def self.boot!
Builtin.boot_functions
end
end end
require_relative "instruction/instruction.rb" require_relative "instruction.rb"
require_relative "mom_compiler" 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,7 +3,7 @@ module Parfait
# A Block is a callable object, much like a CallableMethod. # A Block is a callable object, much like a CallableMethod.
# Surprisingly similar in fact, as the block is really only missing the name. # 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 # Also both have a list of blocks defined in their scope. But this is
# not implemented for blocks yet # not implemented for blocks yet

View File

@ -1,14 +1,15 @@
module Parfait 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). # 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. # One could (like mri), store the names in each object, but that is wasteful in both
# Instead we store only the values, and access them by index. # 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 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 # The Type of an object describes the memory layout of the object. In a c analogy,
# information defined in a struct. # 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). # 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** # 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) # 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) # 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 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
# The Type is a list of n names and n types that describe the values stored in an actual object. # actual object.
# Together they turn the object into a hash like structure # 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 # For types to be a useful concept, they have to be unique and immutable. Any "change",
# a name/type pair, will result in a new instance. # 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 # 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 # 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 # Type objects are already created for args and locals, but the main attribute
# is the source, which is a Vool::Statement # 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 # 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 # as it changes during the course of it's life. Types do not change. Objects have
# type, and so only indirectly a class. # type, and so only indirectly a class.
@ -34,9 +34,9 @@ module Parfait
def compiler_for(self_type) def compiler_for(self_type)
callable_method = create_callable_method(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 ) head = @source.to_mom( compiler )
compiler.add_mom(head) compiler.add_code(head)
compiler compiler
end end
end end

View File

@ -12,15 +12,11 @@ class Class
end end
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 # See risc/Readme
module Risc module Risc
# module method to reset, and init # module method to reset, and init
def self.boot! def self.boot!
Position.clear_positions Position.clear_positions
Builtin.boot_functions
end end
end end
@ -35,6 +31,7 @@ require_relative "risc/callable_compiler"
require_relative "risc/method_compiler" require_relative "risc/method_compiler"
require_relative "risc/block_compiler" require_relative "risc/block_compiler"
require_relative "risc/assembler" require_relative "risc/assembler"
require_relative "risc/risc_collection"
class Integer class Integer
def fits_u8? def fits_u8?
@ -46,5 +43,4 @@ end
require_relative "risc/instruction" require_relative "risc/instruction"
require_relative "risc/register_value" require_relative "risc/register_value"
require_relative "risc/text_writer" require_relative "risc/text_writer"
require_relative "risc/builtin"
require_relative "risc/builder" require_relative "risc/builder"

View File

@ -4,16 +4,15 @@ module Risc
# #
class BlockCompiler < CallableCompiler class BlockCompiler < CallableCompiler
attr_reader :block , :risc_instructions , :constants attr_reader :block , :risc_instructions , :constants , :in_method
alias :block :callable
def initialize( block , method) def initialize( block , in_method , mom_label)
@method = method @in_method = in_method
super(block) super(block , mom_label)
end end
def source_name def source_name
"#{@method.self_type.name}.init" "#{@in_method.self_type.name}.init"
end end
# resolve the type of the slot, by inferring from it's name, using the type # 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) def slot_type( slot , type)
new_type = super new_type = super
if slot == :caller if slot == :caller
extra_info = { type_frame: @method.frame_type , extra_info = { type_frame: @in_method.frame_type ,
type_arguments: @method.arguments_type , type_arguments: @in_method.arguments_type ,
type_self: @method.self_type} type_self: @in_method.self_type}
end end
return new_type , extra_info return new_type , extra_info
end end
@ -38,9 +37,9 @@ module Risc
slot_def = [:arguments] slot_def = [:arguments]
elsif @callable.frame_type.variable_index(name) elsif @callable.frame_type.variable_index(name)
slot_def = [:frame] slot_def = [:frame]
elsif @method.arguments_type.variable_index(name) elsif @in_method.arguments_type.variable_index(name)
slot_def = [:caller , :caller ,:arguments ] 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 ] slot_def = [:caller ,:caller , :frame ]
elsif elsif
raise "no variable #{name} , need to resolve at runtime" 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. # 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 class Builder
attr_reader :built , :compiler 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 # class shared by BlockCompiler and MethodCompiler
# - risc_instructions: The sequence of risc level instructions that mom was compiled to # - 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 # 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 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 @callable = callable
@regs = [] @regs = []
@constants = [] @constants = []
@block_compilers = [] @block_compilers = []
@risc_instructions = Risc.label(source_name, source_name) @current = @risc_instructions = mom_label.risc_label(self)
@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
reset_regs reset_regs
end end
attr_reader :risc_instructions , :constants , :block_compilers , :callable , :current attr_reader :risc_instructions , :constants , :block_compilers , :callable , :current
@ -32,20 +30,6 @@ module Risc
end end
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) # add a constant (which get created during compilation and need to be linked)
def add_constant(const) def add_constant(const)
raise "Must be Parfait #{const}" unless const.is_a?(Parfait::Object) raise "Must be Parfait #{const}" unless const.is_a?(Parfait::Object)

View File

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

View File

@ -1,16 +1,8 @@
module Mom module Risc
# The Compiler for the Mom level is a collection of Risc level Method compilers, # 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. # plus functions to translate from the risc to cpu specific code.
# #
# Builtin functions are created here, lazily, when translate is called. class RiscCollection
# 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
attr_reader :method_compilers attr_reader :method_compilers
# Initialize with an array of risc MethodCompilers # Initialize with an array of risc MethodCompilers
@ -18,21 +10,9 @@ module Mom
@method_compilers = compilers @method_compilers = compilers
end 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 # collects constants from all compilers into one array
def constants def constants
compilers.inject([]){|sum ,comp| sum + comp.constants } method_compilers.inject([]){|sum ,comp| sum + comp.constants }
end end
# Append another MomCompilers method_compilers to this one. # 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 # go through all methods and translate them to cpu, given the translator
def translate_methods(translator) def translate_methods(translator)
compilers.collect do |compiler| method_compilers.collect do |compiler|
#log.debug "Translate method #{compiler.method.name}" #log.debug "Translate method #{compiler.method.name}"
translate_method(compiler , translator) translate_method(compiler , translator)
end.flatten 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 "rx-file"
require "util/logging" require "util/logging"
require "util/list"
require_relative "elf/object_writer" require_relative "elf/object_writer"
require_relative "risc" require_relative "risc"
require_relative "mom/mom"
require_relative "arm/arm_machine" require_relative "arm/arm_machine"
require_relative "arm/arm_platform" require_relative "arm/arm_platform"
require_relative "vool/statement" require_relative "vool/statement"
require_relative "ruby" require_relative "ruby"
require_relative "rubyx/rubyx_compiler" require_relative "rubyx/rubyx_compiler"
require_relative "mom/mom"

View File

@ -1,5 +1,14 @@
module RubyX 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 # 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 # (mainly for testing). ruby_to_binary creates actual binary code
@ -21,6 +30,7 @@ module RubyX
# initialize boots Parfait and Risc (ie load Builin) # initialize boots Parfait and Risc (ie load Builin)
def initialize(options) def initialize(options)
Parfait.boot!(options[:parfait] || {}) Parfait.boot!(options[:parfait] || {})
Mom.boot!
Risc.boot! Risc.boot!
end end
@ -37,42 +47,58 @@ module RubyX
to_binary(platform) to_binary(platform)
end 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. # 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. # 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 # The biary the method name refers to is binary code in memory, or in BinaryCode
# objects to be precise. # objects to be precise.
def to_binary(platform) def to_binary(platform)
linker = to_risc(platform) linker = to_target(platform)
linker.position_all linker.position_all
linker.create_binary linker.create_binary
linker linker
end end
# ruby_to_risc creates Risc instructions (as the name implies), but also # transform stored vool to target code
# translates those to the platform given # return a linker
# def to_target(platform)
# After creating vool, we call to_risc raise "No platform given" unless platform
def ruby_to_risc(ruby, platform) collection = to_risc
ruby_to_vool(ruby) collection.translate(platform)
to_risc(platform)
end end
# Process previously stored vool source. First to mom, then to platform. # Process previously stored vool source to risc.
# Translating to platform returns a linker that is returned and can be used # return a Risc::RiscCollection , a collection of MethodCompilers
# to generate binaries def to_risc()
def to_risc(platform)
mom = to_mom mom = to_mom
mom.translate(platform) mom.to_risc()
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
end end
# return mom for the previously stored vool source. # return mom for the previously stored vool source.
@ -108,9 +134,7 @@ module RubyX
compiler = RubyXCompiler.new(options) compiler = RubyXCompiler.new(options)
compiler.load_parfait if options[:load_parfait] compiler.load_parfait if options[:load_parfait]
compiler.ruby_to_vool(ruby) compiler.ruby_to_vool(ruby)
platform = options[:platform] compiler.to_binary(options[:platform])
raise "No platform given" unless platform
compiler.to_binary(platform)
end end
end 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 ## Syntax tree
Vool is the layer of concrete syntax tree. The Parser gem is used to parse ruby. It creates Vool is a layer with concrete syntax tree, just like the ruby layer above.
an abstract syntax tree which is then transformed. Vool is just simplified, without fluff, see below.
The next layer down is the Mom, Minimal object Machine, which uses an instruction tree. The next layer down is the Mom, Minimal object Machine, which uses an instruction list.
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 nodes of the syntax tree are all the things one would expect from a language, if statements The nodes of the syntax tree are all the things one would expect from a language,
and the like. There is no context yet, and actual objects, representing classes and if statements and the like. There is no context yet, and actual objects,
methods, will be created on the way down. representing classes and methods, will be created on the way down.
## Fluff ## Fluff
@ -30,3 +28,9 @@ existence of until, which really means if not. Other examples, some more impactf
- no case - no case
- no elseif (no unless, no ternary operator) - no elseif (no unless, no ternary operator)
- no global variables. - 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 class Assignment < Statement
attr_reader :name , :value attr_reader :name , :value
def initialize(name , value ) def initialize(name , value )
raise "Name nil #{self}" unless name
raise "Value nil #{self}" unless value
@name , @value = name , value @name , @value = name , value
end end

View File

@ -21,9 +21,9 @@ module Vool
# to the method compiler for further processing # to the method compiler for further processing
def to_mom( compiler ) def to_mom( compiler )
parfait_block = self.parfait_block(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 ) head = body.to_mom( block_compiler )
block_compiler.add_mom(head) block_compiler.add_code(head)
block_compiler block_compiler
end end

View File

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

View File

@ -1,4 +1,13 @@
module Vool 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 class ClassStatement < Statement
attr_reader :name, :super_class_name , :body attr_reader :name, :super_class_name , :body
attr_reader :clazz attr_reader :clazz
@ -17,6 +26,10 @@ module Vool
end end
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( _ ) def to_mom( _ )
create_class_object create_class_object
method_compilers = body.statements.collect do |node| method_compilers = body.statements.collect do |node|
@ -29,7 +42,7 @@ module Vool
raise "Only methods for now #{node.class}:#{node}" raise "Only methods for now #{node.class}:#{node}"
end end
end end
Mom::MomCompiler.new(method_compilers) Mom::MomCollection.new(method_compilers)
end end
def each(&block) def each(&block)
@ -37,6 +50,10 @@ module Vool
@body.each(&block) if @body @body.each(&block) if @body
end 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 def create_class_object
@clazz = Parfait.object_space.get_class_by_name(@name ) @clazz = Parfait.object_space.get_class_by_name(@name )
if(@clazz) if(@clazz)
@ -53,6 +70,7 @@ module Vool
ivar_hash[node.name] = :Object ivar_hash[node.name] = :Object
end end
@clazz.set_instance_type( Parfait::Type.for_hash( @clazz , ivar_hash ) ) @clazz.set_instance_type( Parfait::Type.for_hash( @clazz , ivar_hash ) )
@clazz
end end
end end

View File

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

View File

@ -5,7 +5,7 @@ module Vool
def to_mom( compiler ) def to_mom( compiler )
to = Mom::SlotDefinition.new(:message ,[ :receiver , @name]) to = Mom::SlotDefinition.new(:message ,[ :receiver , @name])
from = @value.slot_definition(compiler) 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 end

View File

@ -6,7 +6,7 @@ module Vool
slot_def = compiler.slot_type_for(@name) slot_def = compiler.slot_type_for(@name)
to = Mom::SlotDefinition.new(:message ,slot_def) to = Mom::SlotDefinition.new(:message ,slot_def)
from = @value.slot_definition(compiler) 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 end

View File

@ -9,7 +9,7 @@ module Vool
def to_mom(clazz) def to_mom(clazz)
raise( "no class in #{self}") unless 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) compiler = method.compiler_for(clazz.instance_type)
each do |node| ## TODO: must account for nested blocks (someday) each do |node| ## TODO: must account for nested blocks (someday)
next unless node.is_a?(BlockStatement) next unless node.is_a?(BlockStatement)
@ -18,6 +18,15 @@ module Vool
compiler compiler
end 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) def each(&block)
block.call(self) block.call(self)
@body.each(&block) @body.each(&block)

View File

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

View File

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

View File

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

View File

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

View File

@ -14,7 +14,7 @@ end
require "minitest/color" require "minitest/color"
require "minitest/autorun" require "minitest/autorun"
require "minitest/fail_fast" unless ENV["TEST_ALL"] #require "minitest/fail_fast" unless ENV["TEST_ALL"]
require 'minitest/profile' require 'minitest/profile'
#require "minitest/reporters" #require "minitest/reporters"
#Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new #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,27 +14,36 @@ module Risc
SlotToReg, RegToSlot, RegToSlot, SlotToReg, SlotToReg, SlotToReg, RegToSlot, RegToSlot, SlotToReg, SlotToReg,
SlotToReg , FunctionReturn, Label] SlotToReg , FunctionReturn, Label]
end 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 def produce_body
produced = produce_instructions produced = produce_main
preamble.each{ produced = produced.next } preamble.each{ produced = produced.next }
produced produced
end end
def as_block( block_input , method_input = "main_local = 5")
"#{method_input} ; self.main{|val| #{block_input}}"
end
def as_test_main 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 end
def produce_instructions def to_target
assert @expect , "No output given" assert @expect , "No output given"
linker = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_risc(as_test_main,:interpreter) RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_target(as_test_main,:interpreter)
compiler = linker.assemblers.find{|c| c.callable.name == :main and c.callable.self_type.object_class.name == :Test}
compiler.instructions
end end
def check_nil def produce_main
produced = produce_instructions 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 ) compare_instructions( produced , @expect )
end end
def check_return def check_return

View File

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

View File

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

View File

@ -5,7 +5,7 @@ module Mom
def setup def setup
Parfait.boot!(Parfait.default_test_options) 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 @compiler = Risc::FakeCompiler.new
load.to_risc(@compiler) load.to_risc(@compiler)
@instructions = @compiler.instructions @instructions = @compiler.instructions

View File

@ -6,7 +6,7 @@ module Mom
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
@compiler = Risc::FakeCompiler.new @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) load.to_risc(@compiler)
@instructions = @compiler.instructions @instructions = @compiler.instructions
end end

View File

@ -8,7 +8,7 @@ module Mom
method = make_method method = make_method
@compiler = Risc::FakeCompiler.new @compiler = Risc::FakeCompiler.new
@cache_entry = Parfait::CacheEntry.new(method.frame_type, method) @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) load.to_risc(@compiler)
@instructions = @compiler.instructions @instructions = @compiler.instructions
end 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 } ")) @ret = compile_mom( as_test_main("self.main {|elem| elem = 5 } "))
end end
def test_is_compiler def test_is_compiler
assert_equal Mom::MomCompiler , @ret.class assert_equal Mom::MomCollection , @ret.class
end end
def test_has_method def test_has_method
assert_equal Parfait::CallableMethod , @ret.method_compilers.first.get_method.class assert_equal Parfait::CallableMethod , @ret.method_compilers.first.get_method.class

View File

@ -1,22 +1,23 @@
require_relative "helper" require_relative "helper"
module Risc module Mom
class TestBuiltinFunction < MiniTest::Test class TestBuiltinFunction < MiniTest::Test
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
@functions = Builtin.boot_functions
end end
def test_has_boot_function def test_has_boot_function
assert Builtin.boot_functions assert @functions
end end
def test_boot_function_type def test_boot_function_type
assert_equal Array, Builtin.boot_functions.class assert_equal Array, @functions.class
end end
def test_boot_function_length def test_boot_function_length
assert_equal 23, Builtin.boot_functions.length assert_equal 22, @functions.length
end end
def test_boot_function_first def test_boot_function_first
assert_equal MethodCompiler, Builtin.boot_functions.first.class assert_equal Mom::MethodCompiler, @functions.first.class
end end
end 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" require_relative "helper"
module Vool module Vool
class TestClassStatement < MiniTest::Test class TestClassStatementMom < MiniTest::Test
include MomCompile include MomCompile
def setup def setup
@ -11,10 +11,10 @@ module Vool
end end
def test_return_class def test_return_class
assert_equal Mom::MomCompiler , @ret.class assert_equal Mom::MomCollection , @ret.class
end end
def test_has_compilers def test_has_compilers
assert_equal Risc::MethodCompiler , @ret.method_compilers.first.class assert_equal Mom::MethodCompiler , @ret.method_compilers.first.class
end end
def test_constant 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 super
@input = "5.div4" @input = "5.div4"
@expect = "something" @expect = "something"
@produced = produce_instructions
end end
def instruction(num) # 18 is the main, see length in test/mom/send/test_setup_simple.rb 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 end
def test_postamble_classes def test_postamble_classes
postamble.each_with_index do |ins , index| postamble.each_with_index do |ins , index|

View File

@ -4,11 +4,11 @@ module Parfait
class TestMethods < ParfaitTest class TestMethods < ParfaitTest
def setup def setup
super super
Risc::Builtin.boot_functions Mom.boot!
end end
def test_integer def test_integer
int = Parfait.object_space.get_class_by_name :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 end
def test_methods_booted def test_methods_booted
word = @space.get_type_by_class_name(:Word) 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 Risc
module Builtin module Builtin
class IntCmp < BuiltinTest class IntCmp < Minitest::Test
include Ticker
def setup
end
def test_smaller_true def test_smaller_true
run_main_return "4 < 5" run_main_return "4 < 5"

View File

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

View File

@ -41,7 +41,7 @@ module Risc
ret = main_ticks(68) ret = main_ticks(68)
assert_equal FunctionReturn , ret.class assert_equal FunctionReturn , ret.class
assert_equal :r1 , ret.register.symbol 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 end
end end

View File

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

View File

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

View File

@ -5,9 +5,10 @@ module Risc
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
Mom.boot!
Risc.boot! Risc.boot!
init = Parfait.object_space.get_init 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") @label = Risc.label("source","name")
@start = @builder.compiler.current @start = @builder.compiler.current
end end

View File

@ -5,9 +5,10 @@ module Risc
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
Mom.boot!
Risc.boot! Risc.boot!
@init = Parfait.object_space.get_init @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) @builder = @compiler.builder(@init)
end end
def test_inserts_built def test_inserts_built
@ -42,7 +43,7 @@ module Risc
end end
def test_allocate_len def test_allocate_len
int = @builder.allocate_int int = @builder.allocate_int
assert_equal 41 , @builder.compiler.risc_instructions.length assert_equal 28 , @builder.compiler.risc_instructions.length
end end
end end
end end

View File

@ -5,9 +5,10 @@ module Risc
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
Mom.boot!
Risc.boot! Risc.boot!
@init = Parfait.object_space.get_init @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) @builder = @compiler.builder(@init)
end end
def test_list 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 def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
Risc.boot! Risc.boot!
@linker = Mom::MomCompiler.new.translate(:arm) @linker = Mom::MomCollection.new.to_risc.translate(:arm)
end end
def test_simple_collect def test_simple_collect
objects = Collector.collect_space(@linker) objects = Collector.collect_space(@linker)
assert_equal 600 , objects.length , objects.length.to_s assert_equal 621 , objects.length , objects.length.to_s
end end
def test_collect_all_types def test_collect_all_types
@ -47,15 +47,15 @@ module Risc
def setup def setup
opt = Parfait.default_test_options opt = Parfait.default_test_options
opt[:factory] = 4000 opt[:factory] = 400
Parfait.boot!(opt) Parfait.boot!(opt)
Risc.boot! Risc.boot!
@linker = Mom::MomCompiler.new.translate(:arm) @linker = Mom::MomCollection.new.to_risc.translate(:arm)
end end
def test_simple_collect def test_simple_collect
objects = Collector.collect_space(@linker) objects = Collector.collect_space(@linker)
assert_equal 20329, objects.length , objects.length.to_s assert_equal 2421, objects.length , objects.length.to_s
end end
def test_integer_positions def test_integer_positions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,10 +5,6 @@ module RubyX
module RubyXHelper module RubyXHelper
def setup def setup
end 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 = {}) def ruby_to_vool(input, options = {})
options = RubyX.default_test_options.merge(options) options = RubyX.default_test_options.merge(options)
RubyXCompiler.new(options).ruby_to_vool(input) RubyXCompiler.new(options).ruby_to_vool(input)

View File

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

View File

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

View File

@ -16,43 +16,34 @@ module ScopeHelper
def as_test_main( statements ) def as_test_main( statements )
in_Test("def main(arg) ; #{statements}; end") in_Test("def main(arg) ; #{statements}; end")
end end
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 MomCompile end
module VoolCompile
include ScopeHelper include ScopeHelper
include Mom
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
end
def compile_first_method( input ) def compile_first_method( input )
ret = compile_method( as_test_main( input )) input = as_test_main( input )
assert_equal Mom::MomCompiler , ret.class collection = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(input)
compiler = ret.method_compilers.find{|c| c.get_method.name == :main and c.get_method.self_type.object_class.name == :Test} assert collection.is_a?(Mom::MomCollection) , collection.class.name
assert_equal Risc::MethodCompiler , compiler.class compiler = collection.compilers.first
@method.source.to_mom( compiler ) assert compiler.is_a?(Mom::MethodCompiler)
assert_equal Mom::MethodCompiler , compiler.class
compiler
end end
def compile_first_block( block_input , method_input = "main_local = 5") def compile_first_block( block_input , method_input = "main_local = 5")
source = "#{method_input} ; self.main{|val| #{block_input}}" source = as_test_main("#{method_input} ; self.main{|val| #{block_input}}")
vool = Ruby::RubyCompiler.compile( as_test_main(source) ).to_vool mom_col = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom( source )
mom_c = vool.to_mom(nil) compiler = mom_col.method_compilers.find{|c| c.get_method.name.to_s.start_with?("main") }
compiler = mom_c.method_compilers.find{|c| c.get_method.name == :main and c.get_method.self_type.object_class.name == :Test} block = compiler.block_compilers.first
block = nil
vool.each {|b| block = b if b.is_a?(Vool::BlockStatement)}
assert block assert block
block_c = compiler.block_compilers.first block.mom_instructions.next
assert block_c
block.body.to_mom(block_c)
end end
def compile_mom(input)
RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(input)
end
def check_array( should , is ) def check_array( should , is )
index = 0 index = 0
test = is test = is
@ -79,26 +70,11 @@ module MomCompile
end end
module MomCompile
include ScopeHelper
def compile_mom(input)
RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(input)
end
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
end
end end

View File

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

View File

@ -2,7 +2,6 @@ require_relative "../helper"
module VoolBlocks module VoolBlocks
class TestClassAssignMom < MiniTest::Test class TestClassAssignMom < MiniTest::Test
include MomCompile
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
@ -21,7 +20,7 @@ module VoolBlocks
end end
def test_assign_compiles def test_assign_compiles
vool = Ruby::RubyCompiler.compile( as_class_method("val = 0") ).to_vool 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 end
end end

View File

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

View File

@ -2,8 +2,7 @@ require_relative "helper"
module VoolBlocks module VoolBlocks
class TestSimpleWhileMom < MiniTest::Test class TestSimpleWhileMom < MiniTest::Test
include MomCompile include VoolCompile
include Mom
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
@ -20,7 +19,8 @@ module VoolBlocks
assert_equal SlotDefinition , @ins.next.condition.class , @ins assert_equal SlotDefinition , @ins.next.condition.class , @ins
end end
def test_array 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 end
end end

View File

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

View File

@ -17,16 +17,8 @@ module Vool
end end
def setup def setup
statements = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_vool(class_main) ret = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(class_main)
assert_equal Vool::ClassStatement, statements.class @ins = ret.compilers.first.mom_instructions.next
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 )
end end
def test_any def test_any
@ -35,7 +27,7 @@ module Vool
def test_no_arg def test_no_arg
assert_equal Mom::ArgumentTransfer, @ins.next(1).class 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 end
def test_call_two def test_call_two
assert_equal SimpleCall, @ins.next(2).class assert_equal SimpleCall, @ins.next(2).class

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,6 @@ require_relative "helper"
module Vool module Vool
class TestSendSelfMom < MiniTest::Test class TestSendSelfMom < MiniTest::Test
include SimpleSendHarness include SimpleSendHarness
include Mom
def send_method def send_method
"self.get_internal_word(0)" "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 assert_equal 2, @ins.next(1).arguments[1].right.known_object.value
end end
def test_array def test_array
check_array [Mom::MessageSetup,Mom::ArgumentTransfer,Mom::SimpleCall] , @ins check_array [MessageSetup,ArgumentTransfer,SimpleCall, Label, ReturnSequence ,
Label] , @ins
end end
end end
end end

View File

@ -2,11 +2,12 @@ require_relative "helper"
module Vool module Vool
class TestAssignMom < MiniTest::Test class TestAssignMom < MiniTest::Test
include MomCompile include VoolCompile
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_method( "local = 5") @compiler = compile_first_method( "local = 5")
@ins = @compiler.mom_instructions.next
end end
def test_class_compiles def test_class_compiles
@ -34,10 +35,11 @@ module Vool
#otherwise as above, but assigning instance, so should get a SlotLoad #otherwise as above, but assigning instance, so should get a SlotLoad
class TestAssignMomInstanceToLocal < MiniTest::Test class TestAssignMomInstanceToLocal < MiniTest::Test
include MomCompile include VoolCompile
def setup def setup
Parfait.boot!(Parfait.default_test_options) 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 end
def test_class_compiles def test_class_compiles
assert_equal Mom::SlotLoad , @ins.next.class , @ins 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 #compiling to an argument should result in different second parameter in the slot array
class TestAssignToArg < MiniTest::Test class TestAssignToArg < MiniTest::Test
include MomCompile include VoolCompile
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
@ins = compile_first_method( "arg = 5") @compiler = compile_first_method( "arg = 5")
@ins = @compiler.mom_instructions.next
end end
def test_class_compiles def test_class_compiles
@ -71,17 +74,19 @@ module Vool
end end
class TestAssignMomToInstance < MiniTest::Test class TestAssignMomToInstance < MiniTest::Test
include MomCompile include VoolCompile
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
end end
def test_assigns_const 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::SlotLoad , @ins.class , @ins
assert_equal Mom::IntegerConstant , @ins.right.known_object.class , @ins assert_equal Mom::IntegerConstant , @ins.right.known_object.class , @ins
end end
def test_assigns_move 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::SlotLoad , @ins.class , @ins
assert_equal Mom::SlotDefinition , @ins.right.class , @ins assert_equal Mom::SlotDefinition , @ins.right.class , @ins
end 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 module Vool
class TestConditionIfMom < MiniTest::Test class TestConditionIfMom < MiniTest::Test
include MomCompile include VoolCompile
include Mom
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
Risc::Builtin.boot_functions @compiler = compile_first_method( "if(5.div4) ; @a = 6 ; else; @a = 5 ; end")
@ins = compile_first_method( "if(5.div4) ; @a = 6 ; else; @a = 5 ; end") @ins = @compiler.mom_instructions.next
end end
def test_condition def test_condition
@ -22,8 +21,9 @@ module Vool
assert_equal :div4 , @ins.next(2).method.name assert_equal :div4 , @ins.next(2).method.name
end end
def test_array def test_array
check_array [MessageSetup, ArgumentTransfer, SimpleCall, SlotLoad, TruthCheck, Label , check_array [MessageSetup, ArgumentTransfer, SimpleCall, SlotLoad, TruthCheck,
SlotLoad, Jump, Label, SlotLoad, Label] , @ins Label ,SlotLoad, Jump, Label, SlotLoad, Label,
Label, ReturnSequence, Label ] , @ins
end end
end end

View File

@ -3,12 +3,12 @@ require_relative "helper"
module Vool module Vool
class TestIfNoElse < MiniTest::Test class TestIfNoElse < MiniTest::Test
include MomCompile include VoolCompile
include Mom
def setup def setup
Parfait.boot!(Parfait.default_test_options) 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 end
def test_condition_compiles_to_check def test_condition_compiles_to_check
@ -24,7 +24,8 @@ module Vool
assert_equal Label , @ins.last.class , @ins assert_equal Label , @ins.last.class , @ins
end end
def test_array def test_array
check_array [TruthCheck, Label, SlotLoad, Label], @ins check_array [TruthCheck, Label, SlotLoad, Label, Label ,
ReturnSequence, Label], @ins
end end
end end
end end

View File

@ -3,12 +3,12 @@ require_relative "helper"
module Vool module Vool
class TestSimpleIfMom < MiniTest::Test class TestSimpleIfMom < MiniTest::Test
include MomCompile include VoolCompile
include Mom
def setup def setup
Parfait.boot!(Parfait.default_test_options) 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 end
def test_condition_compiles_to_check def test_condition_compiles_to_check
@ -24,8 +24,8 @@ module Vool
assert_equal Label , @ins.last.class , @ins assert_equal Label , @ins.last.class , @ins
end end
def test_array def test_array
check_array [TruthCheck, Label, SlotLoad, Jump, Label, SlotLoad , check_array [TruthCheck, Label, SlotLoad, Jump, Label ,
Label] , @ins SlotLoad, Label, Label, ReturnSequence, Label], @ins
end end
end end
end end

View File

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

View File

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

View File

@ -2,12 +2,12 @@ require_relative "helper"
module Vool module Vool
class TestSimpleWhileMom < MiniTest::Test class TestSimpleWhileMom < MiniTest::Test
include MomCompile include VoolCompile
include Mom
def setup def setup
Parfait.boot!(Parfait.default_test_options) 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 end
def test_compiles_as_while def test_compiles_as_while
@ -20,7 +20,8 @@ module Vool
assert_equal SlotDefinition , @ins.next.condition.class , @ins assert_equal SlotDefinition , @ins.next.condition.class , @ins
end end
def test_array 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 end
end end

View File

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

View File

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