cleaning out stash

most of the stuff is now reimplemented
fragments still open (as interpreter is not working yet)
This commit is contained in:
Torsten Ruger 2018-03-19 16:25:27 +05:30
parent 7953ef3e39
commit 28ae1de59f
80 changed files with 0 additions and 2561 deletions

View File

@ -1,4 +1,3 @@
require_relative '../helper'
require "risc/interpreter"
require "parser/ruby22"
require "yaml"

View File

@ -1,33 +0,0 @@
class List
def initialize
@next = nil
end
def empty?
@next.nil?
end
def get(key)
@next ? @next.get(key) : nil
end
def set(key , value)
@next ? @next.set(key,value) : @next = Node.new(key,value)
value
end
end
class Node < List
def initialize(key,value)
@key = key
@value = value
end
def get(key)
@key == key ? @value : super(key)
end
def set(key,value)
@key == key ? @value = value : super(key,value)
end
end
# https://www.youtube.com/watch?v=HJ-719EGIts

View File

@ -1,25 +0,0 @@
module Rubyx
module Passes
class LocalsCollector < TotalProcessor
def initialize
@locals = {}
end
def collect(statement)
process statement
@locals
end
def on_lvasgn statement
add_local( statement )
end
def add_local(statement)
var = statement.children[0]
@locals[var] = :Object #not really used right now
end
end
end
end

View File

@ -1,38 +0,0 @@
module Rubyx
module Passes
class MethodCollector < TotalProcessor
def initialize
@methods = []
end
def collect(statement)
process statement
@methods
end
def on_def(statement)
name , args , body = *statement
args_type = make_type(args)
frame_type = make_locals(body)
@methods << Vool::VoolMethod.new(name , args_type , frame_type , body )
end
private
def make_type( statement )
type_hash = {}
statement.children.each do |arg|
type_hash[arg.children[0]] = :Object
end
Parfait::NamedList.type_for( type_hash )
end
def make_locals(body)
type_hash = LocalsCollector.new.collect(body)
Parfait::NamedList.type_for( type_hash )
end
end
end
end

View File

@ -1,61 +0,0 @@
module Rubyx
module Passes
class MethodCompiler < AST::Processor
def initialize( ruby_method )
@ruby_method = ruby_method
end
def get_code
process(@ruby_method.source)
end
def on_ivasgn(statement)
name , value = *statement
w = Vm::Tree::Assignment.new()
w.name = Vm::Tree::InstanceName.new( name[1..-1].to_sym)
w.value = process(value)
w
end
def on_ivar( var )
name = var.children.first
w = Vm::Tree::FieldAccess.new()
w.receiver = Vm::Tree::KnownName.new(:self)
w.field = Vm::Tree::InstanceName.new( name[1..-1].to_sym)
w
end
def on_send( statement )
receiver , name , args = *statement
w = Vm::Tree::CallSite.new()
w.name = name
w.arguments = process(args) || []
w.receiver = process(receiver)
w
end
def on_lvar(statement)
name = statement.children.first.to_sym
if(@ruby_method.args_type.variable_index(name))
return Vm::Tree::ArgumentName.new(name)
end
raise "Not found #{name}"
end
def on_str( string )
Vm::Tree::StringExpression.new(string.children.first)
end
def on_int( expression)
Vm::Tree::IntegerExpression.new(expression.children.first)
end
def handler_missing(node)
raise "No handler for #{node}"
end
end
end
end

View File

@ -1,12 +0,0 @@
module Rubyx
module Passes
class Normalizer < AST::Processor
def initialize( ruby_method )
@ruby_method
end
end
end
end

View File

@ -1,14 +0,0 @@
module Rubyx
module Passes
class TotalProcessor < AST::Processor
def handler_missing(node)
node.children.each do |kid |
process(kid) if kid.is_a?(AST::Node)
end
end
end
end
end

View File

@ -1,29 +0,0 @@
module Rubyx
module Passes
class TypeCollector < TotalProcessor
def initialize
@ivars = {}
end
def collect(statement)
process statement
@ivars
end
def on_ivar(statement)
add_ivar(statement)
end
def on_ivasgn( statement )
add_ivar(statement)
end
def add_ivar(statement)
var = statement.children[0].to_s[1..-1].to_sym
@ivars[var] = :Object #guess, can maybe guess better
end
end
end
end

View File

@ -1,64 +0,0 @@
require "parser/ruby22"
require_relative "passes/total_processor"
require_relative "passes/type_collector"
require_relative "passes/method_collector"
require_relative "passes/method_compiler"
require_relative "passes/locals_collector"
require_relative "passes/normalizer"
require "vool/vool_method"
module Rubyx
class RubyxCompiler < Passes::TotalProcessor
def self.compile( input )
ast = Parser::Ruby22.parse( input )
self.new.process( ast )
end
def on_class statement
name , sup , body = *statement
class_name = get_name(name)
clazz = Parfait.object_space.get_class_by_name(class_name )
if(clazz)
#FIXME super class check with "sup"
else #existing class, don't overwrite type (parfait only?)
clazz = Parfait.object_space.create_class(class_name , get_name(sup) )
ivar_hash = Passes::TypeCollector.new.collect(body)
clazz.set_instance_type( Parfait::Type.for_hash( clazz , ivar_hash ) )
end
methods = create_methods(clazz , body)
compile_methods(clazz,methods)
end
def create_methods(clazz , body)
methods = Passes::MethodCollector.new.collect(body)
methods.each do |method|
clazz.add_method( method )
normalizer = Passes::Normalizer.new(method)
method.normalize_source { |sourc| normalizer.process( sourc ) }
end
methods
end
def compile_methods(clazz , methods)
methods.each do |method|
code = Passes::MethodCompiler.new(method).get_code
typed_method = method.create_parfait_method(clazz.instance_type)
Vm::MethodCompiler.new( typed_method ).init_method.process( code )
end
end
private
def get_name( statement )
return nil unless statement
raise "Not const #{statement}" unless statement.type == :const
name = statement.children[1]
raise "Not symbol #{name}" unless name.is_a? Symbol
name
end
end
end

View File

@ -1,20 +0,0 @@
module Risc
# A slot is a slot in an object. It is the storage location for a value.
# (Remember, values are typed)
# From a memory perspective a slot is an index into an array (the object)
# The mapping into arrays is a straightforward matter, but happens in the
# next level down, the register machine.
# Four known objects exist and those correspond to subclasses:
# - the message that has been received: MessageSlot
# - the named_list of the method that is executing (local variables): NamedListSlot
# - self as an object: SelfsSlot
# - a message that will be sent, NewMessageSlot
# additionally named_list, self and return are slots in Message and NewMessage
# Slot has a lot of small subclasses
# Names for the slots avoid indexes
end

View File

@ -1,20 +0,0 @@
require_relative '../helper'
# Fragments are small programs that we run through the interpreter and really only check
# - the no. of instructions processed
# - the stdout output
module Fragments
include RuntimeTests
# define setup to NOT load parfait.
def setup
@stdout = ""
@machine = Risc.machine.boot
end
def main()
@string_input
end
end

View File

@ -1,24 +0,0 @@
require_relative 'helper'
module Risc
class TestBasicClass < MiniTest::Test
include Fragments
def test_class_def
@string_input = <<HERE
class Bar
int buh()
return 1
end
end
class Space
int main()
return 1
end
end
HERE
@length = 15
check
end
end
end

View File

@ -1,65 +0,0 @@
require_relative 'helper'
class TestFunctions < MiniTest::Test
include Fragments
def test_functions
@string_input = <<HERE
class Space
int times(int a, int b)
if_zero( b + 0)
a = 0
else
int m = b - 1
int t = times(a, m)
a = a + t
end
return a
end
int t_seven()
int tim = times(8,10)
tim.putint()
return tim
end
int main()
return t_seven()
end
end
HERE
@length = 505
check 80
end
def test_class_method
@string_input = <<HERE
class Space
int self.some()
return 5
end
int main()
return Object.some()
end
end
HERE
@length = 33
check 5
end
def test_class_method_fails
@string_input = <<HERE
class Space
int main()
return Object.som()
end
end
HERE
assert_raises {check}
end
end

View File

@ -1,18 +0,0 @@
require_relative 'helper'
class TestHello < MiniTest::Test
include Fragments
def test_hello
@string_input = <<HERE
class Space
int main()
"Hello Raisa, I am rubyx".putstring()
end
end
HERE
@length = 37
@stdout = "Hello Raisa, I am rubyx"
check
end
end

View File

@ -1,60 +0,0 @@
require_relative 'helper'
class TestIf < MiniTest::Test
include Fragments
def test_if_plus
@string_input = <<HERE
class Space
int main()
int n = 10
if_plus( n - 12)
return 3
else
return 4
end
end
end
HERE
@length = 25
check 4
end
def test_if_zero
@string_input = <<HERE
class Space
int main()
int n = 10
if_zero(n - 10 )
"10".putstring()
end
end
end
HERE
@length = 47
@stdout = "10"
check
end
def test_if_minus
@string_input = <<HERE
class Space
int itest(int n)
if_minus( n - 12)
"then".putstring()
else
"else".putstring()
end
end
int main()
itest(20)
end
end
HERE
@length = 62
@stdout = "else"
check
end
end

View File

@ -1,22 +0,0 @@
require_relative 'helper'
class TestPutint < MiniTest::Test
include Fragments
def test_putint
@string_input = <<HERE
class Integer
int putint()
return 1
end
end
class Space
int main()
42.putint()
end
end
HERE
@length = 32
check
end
end

View File

@ -1,34 +0,0 @@
require_relative 'helper'
class TestRecursinveFibo < MiniTest::Test
include Fragments
def test_recursive_fibo
@string_input = <<HERE
class Space
int fibonaccir( int n )
if_plus( n - 2 )
int tmp
tmp = n - 1
int a = fibonaccir( tmp )
tmp = n - 2
int b = fibonaccir( tmp )
return a + b
else
return n
end
end
int fib_print(int n)
int fib = fibonaccir( n )
fib.putint()
return fib
end
int main()
return fib_print(8)
end
end
HERE
@length = 2525
check 21
end
end

View File

@ -1,51 +0,0 @@
require_relative 'helper'
class TestReturn < MiniTest::Test
include Fragments
def test_return1
@string_input = <<HERE
class Space
int main()
return 5
end
end
HERE
@length = 15
check 5
end
def test_return2
@string_input = <<HERE
class Space
int foo(int x)
return x
end
int main()
return foo( 5 )
end
end
HERE
@length = 35
check 5
end
def test_return3
@string_input = <<HERE
class Space
int foo(int x)
int a = 5
return a
end
int main()
return foo( 4 )
end
end
HERE
@length = 39
check 5
end
end

View File

@ -1,44 +0,0 @@
require_relative 'helper'
class TestWhileFragment < MiniTest::Test
include Fragments
def fibo num
@string_input = <<HERE
class Space
int fibonaccit(int n)
int a = 0
int b = 1
while_plus( n - 2)
n = n - 1
int tmp = a
a = b
b = tmp + b
end
b.putint()
return b
end
int main()
return fibonaccit( 100 )
end
end
HERE
@string_input.sub!( "100" , num.to_s )
end
def test_while_fibo48
fibo 48
@length = 1241
# this is not the correct fibo, just what comes from wrapping (smaller than below)
check 512559680
end
# highest 32 bit fibo
def test_while_fibo47
fibo 47
@length = 1216
check 2971215073
end
end

View File

@ -1,25 +0,0 @@
require_relative 'helper'
module Vm
class TestWord < MiniTest::Test
include Fragments
def test_word_new
@string_input = <<HERE
class Space
Word self.new()
return nil
end
end
class Space
int main()
Word w = Word.new()
end
end
HERE
@length = 34
@stdout = ""
check
end
end
end

View File

@ -1,98 +0,0 @@
require_relative '../helper'
require "register/interpreter"
require "rye"
Rye::Cmd.add_command :ld, '/usr/bin/ld'
Rye::Cmd.add_command :aout, './a.out'
# machinery to run a typed program in 2 ways
# - first by running it through the interpreter
# - second by assembling to arm , pushing the binary to a remote machine and executing it there
#
# The second obviously takes a fair bit of time so it's only done when an REMOTE_PI is set
# REMOTE_PI has to be set to user@machine:port or it will default to an emulator
# the minimum is REMOTE_PI=username , and off course ssh keys have to be set up
# btw can't test with ruby on a PI as code creation only works on 64bit
# that's because ruby nibbles 2 bits from a word, and typed code doesn't work around that
module RuntimeTests
def setup
@stdout = ""
end
def load_program
@machine = Risc.machine.boot
@machine.parse_and_compile main()
@machine.collect
end
def check ret = nil
i = check_local
check_r ret
i
end
def check_local ret = nil
load_program
interpreter = Risc::Interpreter.new
interpreter.start @machine.init
count = 0
begin
count += 1
#puts interpreter.instruction
interpreter.tick
end while( ! interpreter.instruction.nil?)
assert_equal @stdout , interpreter.stdout , "stdout wrong locally"
if ret
assert_equal Parfait::Message , interpreter.get_register(:r0).class
assert_equal ret , interpreter.get_register(:r0).return_value , "exit wrong #{@string_input}"
end
interpreter
end
def connected
return false if ENV["REMOTE_PI"].nil? or (ENV["REMOTE_PI"] == "")
return @@conn if defined?(@@conn)
puts "remote " + ENV["REMOTE_PI"]
user , rest = ENV["REMOTE_PI"].split("@")
machine , port = rest.to_s.split(":")
make_box machine , port , user
end
def make_box machine = nil , port = nil , user = nil
@@conn = Rye::Box.new(machine || "localhost" , :port => (port || 2222) , :user => (user || "pi"))
end
def check_r ret_val , dont_run = false
return unless box = connected
load_program
file = write_object_file
r_file = file.sub("./" , "ruby-x/")
box.file_upload file , r_file
print "\nfile #{file} "
return if dont_run
box.ld "-N", r_file
begin #need to rescue here as rye throws if no return 0
ret = box.aout # and we use return to mean something
rescue Rye::Err => e # so it's basically never 0
ret = e.rap
end
assert_equal @stdout , ret.stdout.join , "remote std was #{ret.stdout}" if @stdout
assert_equal "" , ret.stderr.join , "remote had error"
if ret_val
ret_val &= 0xFF # don't knwo why exit codes are restricted but there you are
assert_equal ret_val , ret.exit_status.to_i , "remote exit failed for #{@string_input}"
end
end
def write_object_file
file_name = caller(3).first.split("in ").last.chop.sub("`","")
return if file_name.include?("run")
file_name = "./tmp/" + file_name + ".o"
Risc.machine.translate_arm
writer = Elf::ObjectWriter.new
writer.save file_name
file_name
end
end

View File

@ -1 +0,0 @@
require_relative '../helper'

View File

@ -1 +0,0 @@
require_relative '../helper'

View File

@ -1,37 +0,0 @@
require_relative "helper"
module Rubyx
module Passes
class TestLocalsCollector #< MiniTest::Test
def setup
Risc.machine.boot unless Risc.machine.booted
end
def parse_collect( input )
ast = Parser::Ruby22.parse input
LocalsCollector.new.collect(ast)
end
def test_no_locals
locals = parse_collect "def meth; end"
assert locals.empty?
end
def test_method_is_not_local
locals = parse_collect("def meth2(arg1); foo ;end")
assert locals.empty?
end
def test_local_assign_one
locals = parse_collect("def meth2(arg1); foo = bar ;end")
assert locals[:foo] , locals.inspect
end
def test_local_assign_two
locals = parse_collect("def meth2(arg1); foo = bar ; groove = 1 + 2 ;end")
assert locals.length == 2 , locals.inspect
end
end
end
end

View File

@ -1,39 +0,0 @@
require_relative "helper"
module Rubyx
module Passes
class TestMethodCollector #< MiniTest::Test
def setup
Risc.machine.boot unless Risc.machine.booted
end
def parse_collect( input )
ast = Parser::Ruby22.parse input
MethodCollector.new.collect(ast)
end
def test_no_args
methods = parse_collect "def meth; @ivar;end"
assert methods.find{|m| m.name == :meth }
end
def test_one_arg
method = parse_collect("def meth2(arg1); 1;end").first
assert method.name == :meth2
assert_equal 2 , method.args_type.variable_index(:arg1) , method.args_type.inspect
end
def test_three_args
method = parse_collect("def meth3(yksi,kaksi,kolme); 1;end").first
assert method.args_type.variable_index(:kolme) , method.args_type.inspect
end
def test_one_local
method = parse_collect("def meth2(arg1); foo = 2 ;end").first
assert method.frame_type.variable_index(:foo) , method.frame_type.inspect
end
end
end
end

View File

@ -1,16 +0,0 @@
require_relative "helper"
module Rubyx
module Passes
class TestNormalizer < MiniTest::Test
def setup
Risc.machine.boot unless Risc.machine.booted
end
def test_no_thing
assert true
end
end
end
end

View File

@ -1,40 +0,0 @@
require_relative "helper"
module Rubyx
module Passes
class TestTypeCollector #< MiniTest::Test
def setup
Risc.machine.boot
end
def parse_collect( input )
ast = Parser::Ruby22.parse input
TypeCollector.new.collect(ast)
end
def test_ivar_name
hash = parse_collect "def meth; @ivar;end"
assert hash[:ivar] , hash
end
def test_ivar_assign
hash = parse_collect "def meth; @ivar = 5;end"
assert hash[:ivar] , hash
end
def test_ivar_operator_assign
hash = parse_collect "def meth; @ivar += 5;end"
assert hash[:ivar] , hash
end
def test_compile_class
RubyxCompiler.compile "class TestIvar < Object ; def meth; @ivar;end; end"
itest = Parfait.object_space.get_class_by_name(:TestIvar)
assert itest
assert itest.instance_type.names.include?(:ivar) , itest.instance_type.names.inspect
end
end
end
end

View File

@ -1,46 +0,0 @@
require_relative "helper"
module Rubyx
class TestCompiler #< MiniTest::Test
def setup
Risc.machine.boot
end
def test_doesnt_create_existing_clas
space_class = Parfait.object_space.get_class_by_name(:Space)
RubyxCompiler.compile "class Space ; end"
clazz = Parfait.object_space.get_class_by_name(:Space)
assert_equal clazz , space_class
end
def test_creates_class_without_deriviation
RubyxCompiler.compile "class Testing ; end"
clazz = Parfait.object_space.get_class_by_name(:Testing)
assert clazz , "No classes created"
assert_equal :Object , clazz.super_class_name
end
def test_creates_class_with_deriviation
RubyxCompiler.compile "class Test2 < List ;end"
clazz = Parfait.object_space.get_class_by_name(:Test2)
assert clazz, "No classes created"
assert_equal :List , clazz.super_class_name
end
def test_space_is_unchanged_by_compile
space1 = Parfait.object_space.get_class_by_name(:Space)
RubyxCompiler.compile "class Space ;end"
space2 = Parfait.object_space.get_class_by_name(:Space)
assert_equal space1 , space2
end
def test_space_type_is_unchanged_by_compile
space1 = Parfait.object_space.get_class_by_name(:Space).instance_type
RubyxCompiler.compile "class Space ;end"
space2 = Parfait.object_space.get_class_by_name(:Space).instance_type
assert_equal space1 , space2
end
end
end

View File

@ -1,72 +0,0 @@
require_relative "helper"
module Rubyx
class TestRubyMethod < MiniTest::Test
include CompilerHelper
def setup
Risc.machine.boot
end
def create_method
Vool::VoolCompiler.compile in_Test("def meth; @ivar ;end")
test = Parfait.object_space.get_class_by_name(:Test)
test.get_method(:meth)
end
def create_method_arg
Vool::VoolCompiler.compile in_Test("def meth_arg(arg); arg ;end")
test = Parfait.object_space.get_class_by_name(:Test)
test.get_method(:meth_arg)
end
def create_method_local
Vool::VoolCompiler.compile in_Test("def meth_local(arg); local = 5 ;end")
test = Parfait.object_space.get_class_by_name(:Test)
test.get_method(:meth_local)
end
def test_creates_method_in_class
method = create_method
assert method , "No method created"
end
def test_method_has_source
method = create_method
assert_equal Vool::ScopeStatement, method.source.class
assert_equal Vool::InstanceVariable, method.source.first.class
end
def test_method_has_no_args
method = create_method
assert_equal 1 , method.args_type.instance_length
end
def test_method_has_no_locals
method = create_method
assert_equal 1 , method.frame_type.instance_length
end
def test_method_has_args
method = create_method_arg
assert_equal 2 , method.args_type.instance_length
end
def test_method_has_locals
method = create_method_local
assert_equal 2 , method.frame_type.instance_length
end
def test_method_create_tmp
name = create_method.create_tmp
assert_equal :tmp_1 , name
end
def test_method_add_tmp
method = create_method_local
method.create_tmp
assert_equal 3 , method.frame_type.instance_length
end
end
end

View File

@ -1,3 +0,0 @@
require_relative "../helper"
Risc.machine.boot unless Risc.machine.booted

View File

@ -1,82 +0,0 @@
require_relative '../helper'
module Risc
module SpaceHack
# test hack to in place change object type
def add_space_field(name,type)
class_type = Parfait.object_space.get_class_by_name(:Space).instance_type
class_type.send(:private_add_instance_variable, name , type)
end
end
module ExpressionHelper
include SpaceHack
def check
Risc.machine.boot unless Risc.machine.booted
compiler = Vm::MethodCompiler.new Parfait.object_space.get_main
code = Vm.ast_to_code @input
assert code.to_s , @input
produced = compiler.process( code )
assert @output , "No output given"
assert_equal produced.class , @output , "Wrong class"
produced
end
end
module Statements
include AST::Sexp
include CleanCompile
include SpaceHack
def setup
Risc.machine.boot # force boot to reset main
end
def preamble
[Label, SlotToReg , LoadConstant, RegToSlot, LoadConstant,RegToSlot, LoadConstant, SlotToReg, SlotToReg ]
end
def postamble
[ Label, FunctionReturn]
end
def check_nil
assert @expect , "No output given"
compiler = Vm::MethodCompiler.new(:main)
code = Vm.ast_to_code( @input )
assert code.to_s , @input
produced = compiler.process( code )
produced = Parfait.object_space.get_main.instructions
compare_instructions produced , @expect
end
def check_return
was = check_nil
raise was if was
Parfait.object_space.get_main.instructions
end
def compare_instructions( instruction , expect )
index = 0
all = instruction.to_arr
full_expect = preamble + expect + postamble
full_expect = expect
begin
should = full_expect[index]
return "No instruction at #{index}" unless should
return "Expected at #{index+1}\n#{should(all)}" unless instruction.class == should
index += 1
instruction = instruction.next
end while( instruction )
nil
end
def should( all )
#preamble.each {all.shift}
#postamble.each {all.pop}
str = all.to_s.gsub("Risc::","")
ret = ""
str.split(",").each_slice(6).each do |line|
ret += " " + line.join(",") + " ,\n"
end
ret
end
end
end

View File

@ -1,112 +0,0 @@
require_relative 'helper'
module Risc
class TestAssignStatement < MiniTest::Test
include Statements
def test_assign_op
Parfait.object_space.get_main.add_local(:r , :Integer)
@input = s(:statements, s(:l_assignment, s(:local, :r), s(:operator_value, :+, s(:int, 10), s(:int, 1))))
@expect = [Label, LoadConstant, LoadConstant, OperatorInstruction, SlotToReg, RegToSlot ,
LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_assign_ivar_notpresent
@input =s(:statements, s(:i_assignment, s(:ivar, :r), s(:int, 5)))
@expect = []
assert_raises{ check_nil }
end
def test_assign_ivar
add_space_field(:r , :Integer)
@input =s(:statements, s(:i_assignment, s(:ivar, :r), s(:int, 5)))
@expect = [Label, LoadConstant, SlotToReg, RegToSlot, LoadConstant, SlotToReg ,
RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_assign_local_assign
Parfait.object_space.get_main.add_local(:r , :Integer)
@input = s(:statements, s(:l_assignment, s(:local, :r), s(:int, 5)))
@expect = [Label, LoadConstant, SlotToReg, RegToSlot, LoadConstant, SlotToReg ,
RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_assign_call
Parfait.object_space.get_main.add_local(:r , :Object)
@input = s(:statements, s(:l_assignment, s(:local, :r), s(:call, :main, s(:arguments))))
@expect = [Label, SlotToReg, SlotToReg, RegToSlot, LoadConstant, RegToSlot ,
LoadConstant, SlotToReg, RegToSlot, LoadConstant, RegToSlot, RiscTransfer ,
FunctionCall, Label, RiscTransfer, SlotToReg, SlotToReg, SlotToReg ,
RegToSlot, LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_named_list_get
Parfait.object_space.get_main.add_local(:r , :Integer)
@input = s(:statements, s(:l_assignment, s(:local, :r), s(:int, 5)), s(:return, s(:local, :r)))
@expect = [Label, LoadConstant, SlotToReg, RegToSlot, SlotToReg, SlotToReg ,
RegToSlot, LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn]
was = check_return
get = was.next(5)
assert_equal SlotToReg , get.class
assert_equal 1 + 1, get.index , "Get to named_list index must be offset, not #{get.index}"
end
def test_assign_local_int
Parfait.object_space.get_main.add_local(:r , :Integer)
@input = s(:statements, s(:l_assignment, s(:local, :r), s(:int, 5)) )
@expect = [Label, LoadConstant, SlotToReg, RegToSlot, LoadConstant, SlotToReg ,
RegToSlot, Label, FunctionReturn]
was = check_return
set = was.next(3)
assert_equal RegToSlot , set.class
assert_equal 1 + 1, set.index , "Set to named_list index must be offset, not #{set.index}"
end
def test_misassign_local
Parfait.object_space.get_main.add_local(:r , :Integer)
@input = s(:statements, s(:l_assignment, s(:local, :r), s(:string, "5")) )
@expect = [Label, LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn]
assert_raises {check }
end
def test_assign_arg
Parfait.object_space.get_main.add_argument(:blar , :Integer)
@input = s(:statements, s(:a_assignment, s(:arg, :blar), s(:int, 5)))
@expect = [Label, LoadConstant, SlotToReg, RegToSlot, LoadConstant, SlotToReg ,
RegToSlot, Label, FunctionReturn]
was = check_return
set = was.next(3)
assert_equal RegToSlot , set.class
assert_equal 1 + 1, set.index , "Set to args index must be offset, not #{set.index}"
end
def test_misassign_arg
Parfait.object_space.get_main.add_argument(:blar , :Integer)
@input = s(:statements, s(:a_assignment, s(:arg, :blar), s(:string, "5")))
@expect = [Label, LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn]
assert_raises {check }
end
def test_arg_get
# have to define bar externally, just because redefining main. Otherwise that would be automatic
Parfait.object_space.get_main.add_argument(:balr , :Integer)
@input = s(:statements, s(:return, s(:arg, :balr)))
@expect = [Label, SlotToReg, SlotToReg, RegToSlot, LoadConstant, SlotToReg ,
RegToSlot, Label, FunctionReturn]
was = check_return
get = was.next(2)
assert_equal SlotToReg , get.class
assert_equal 1 + 1, get.index , "Get to args index must be offset, not #{get.index}"
end
end
end

View File

@ -1,41 +0,0 @@
require_relative "helper"
module Risc
class TestBasic < MiniTest::Test
include ExpressionHelper
include AST::Sexp
def setup
Risc.machine.boot
@output = Risc::RiscValue
end
def test_number
@input = s(:int , 42)
assert_equal 42 , check.value
end
def test_true
@input = s(:true)
check
end
def test_false
@input = s(:false)
check
end
def test_nil
@input = s(:nil)
check
end
def test_self
@input = s(:known, :self)
check
end
def test_string
@input = s(:string , "hello")
check
end
end
end

View File

@ -1,43 +0,0 @@
require_relative "helper"
module Risc
class TestCall < MiniTest::Test
include ExpressionHelper
include AST::Sexp
def setup
Risc.machine.boot
@output = Risc::RiscValue
end
def test_call_main_plain
@input = s(:call , :main ,s(:arguments))
check
end
def test_call_main_int
Parfait.object_space.get_main.add_argument(:blar , :Integer)
@input =s(:call, :main ,s(:arguments , s(:int, 1)))
check
end
def test_call_main_string
Parfait.object_space.get_main.add_argument(:blar , :Word)
@input =s(:call, :main ,s(:arguments , s(:string, "1") ))
check
end
def test_call_main_op
Parfait.object_space.get_main.add_local(:bar , :Integer)
Parfait.object_space.get_main.add_argument(:blar , :Integer)
@input =s(:call, :main ,s(:arguments , s(:local, :bar) ))
check
end
def test_call_string_put
@input = s(:call, :putstring,s(:arguments),s(:receiver,s(:string, "Hello Raisa, I am rubyx")))
check
end
end
end

View File

@ -1,68 +0,0 @@
require_relative 'helper'
require_relative "test_call_expression"
module Risc
class TestCallStatement < MiniTest::Test
include Statements
def test_call_constant_int
clean_compile :Integer, :puti, {}, s(:statements, s(:return, s(:int, 1)))
@input = s(:call, :puti , s(:arguments), s(:receiver, s(:int, 42)))
@expect = [Label, SlotToReg, LoadConstant, RegToSlot, LoadConstant, RegToSlot, LoadConstant ,
SlotToReg, RegToSlot, LoadConstant, RegToSlot, RiscTransfer, FunctionCall, Label ,
RiscTransfer, SlotToReg, SlotToReg, LoadConstant, SlotToReg, RegToSlot, Label ,
FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_call_constant_string
clean_compile :Word, :putstr,{}, s(:statements, s(:return, s(:int, 1)))
@input =s(:call, :putstr, s(:arguments), s(:receiver, s(:string, "Hello")))
@expect = [Label, SlotToReg, LoadConstant, RegToSlot, LoadConstant, RegToSlot, LoadConstant ,
SlotToReg, RegToSlot, LoadConstant, RegToSlot, RiscTransfer, FunctionCall, Label ,
RiscTransfer, SlotToReg, SlotToReg, LoadConstant, SlotToReg, RegToSlot, Label ,
FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_call_local_int
Parfait.object_space.get_main.add_local(:testi , :Integer)
clean_compile :Integer, :putint, {}, s(:statements, s(:return, s(:int, 1)))
@input = s(:statements, s(:l_assignment, s(:local, :testi), s(:int, 20)), s(:call, :putint, s(:arguments), s(:receiver, s(:local, :testi))))
@expect = [Label, LoadConstant, SlotToReg, RegToSlot, SlotToReg, SlotToReg, SlotToReg ,
RegToSlot, LoadConstant, RegToSlot, LoadConstant, SlotToReg, RegToSlot, LoadConstant ,
RegToSlot, RiscTransfer, FunctionCall, Label, RiscTransfer, SlotToReg, SlotToReg ,
LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_call_local_class
Parfait.object_space.get_main.add_local(:test_l , :List)
clean_compile :List, :add, {}, s(:statements, s(:return, s(:int, 1)))
@input =s(:statements, s(:call, :add, s(:arguments), s(:receiver, s(:local, :test_l))))
@expect = [Label, SlotToReg, SlotToReg, SlotToReg, RegToSlot, LoadConstant, RegToSlot ,
LoadConstant, SlotToReg, RegToSlot, LoadConstant, RegToSlot, RiscTransfer, FunctionCall ,
Label, RiscTransfer, SlotToReg, SlotToReg, LoadConstant, SlotToReg, RegToSlot ,
Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_call_puts
clean_compile :Space, :putstr, {str: :Word}, s(:statements, s(:return, s(:arg, :str)))
@input =s(:call, :putstr , s(:arguments, s(:string, "Hello") ) )
@expect = [Label, SlotToReg, SlotToReg, RegToSlot, LoadConstant, RegToSlot ,
LoadConstant, SlotToReg, RegToSlot, LoadConstant, SlotToReg, RegToSlot ,
LoadConstant, RegToSlot, RiscTransfer, FunctionCall, Label, RiscTransfer ,
SlotToReg, SlotToReg, LoadConstant, SlotToReg, RegToSlot, Label ,
FunctionReturn]
was = check_return
set = was.next(8)
assert_equal RegToSlot , set.class
assert_equal 1, set.index , "Set to message must be offset, not #{set.index}"
end
end
end

View File

@ -1,34 +0,0 @@
require_relative "helper"
module Risc
class TestFields < MiniTest::Test
include ExpressionHelper
include AST::Sexp
def setup
Risc.machine.boot
end
def test_field_not_defined
@root = :field_access
@input = s(:field_access, s(:receiver, s(:known, :self)), s(:field, s(:ivar, :a)))
assert_raises(RuntimeError) { check }
end
def test_field_not_space
@root = :field_access
@input = s(:field_access, s(:receiver, s(:known, :self)), s(:field, s(:ivar, :space)))
assert_raises(RuntimeError) { check }
end
def test_field
add_space_field(:bro,:Object)
@root = :field_access
@input = s(:field_access,s(:receiver, s(:known, :self)),s(:field,s(:ivar, :bro)))
@output = Risc::RiscValue
check
end
end
end

View File

@ -1,42 +0,0 @@
require_relative 'helper'
module Risc
class TestFieldStatement < MiniTest::Test
include Statements
def test_field_named_list
Parfait.object_space.get_main.add_local( :m , :Message)
@input = s(:statements, s(:return, s(:field_access,
s(:receiver, s(:local, :m)), s(:field, s(:ivar, :name)))))
@expect = [Label, SlotToReg, SlotToReg, SlotToReg, RegToSlot, LoadConstant ,
SlotToReg, RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_field_arg
Parfait.object_space.get_main.add_local( :m , :Message)
clean_compile :Space, :get_name, { :main => :Message},
s(:statements, s(:return, s(:field_access,
s(:receiver, s(:arg, :main)), s(:field, s(:ivar, :name)))))
@input =s(:statements, s(:return, s(:call, :get_name, s(:arguments, s(:local, :m)))))
@expect = [Label, SlotToReg, SlotToReg, RegToSlot, LoadConstant, RegToSlot ,
LoadConstant, SlotToReg, RegToSlot, SlotToReg, SlotToReg, SlotToReg ,
RegToSlot, LoadConstant, RegToSlot, RiscTransfer, FunctionCall, Label ,
RiscTransfer, SlotToReg, SlotToReg, RegToSlot, LoadConstant, SlotToReg ,
RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_message_field
Parfait.object_space.get_main.add_local(:name , :Word)
@input = s(:statements, s(:l_assignment, s(:local, :name), s(:field_access, s(:receiver, s(:known, :message)), s(:field, s(:ivar, :name)))), s(:return, s(:local, :name)))
@expect = [Label, RiscTransfer, SlotToReg, SlotToReg, RegToSlot, SlotToReg ,
SlotToReg, RegToSlot, LoadConstant, SlotToReg, RegToSlot, Label ,
FunctionReturn]
assert_nil msg = check_nil , msg
end
end
end

View File

@ -1,36 +0,0 @@
require_relative 'helper'
module Risc
class TestIfStatement < MiniTest::Test
include Statements
def test_if_basicr
@input = s(:statements, s(:if_statement, :plus, s(:condition, s(:operator_value, :-, s(:int, 10), s(:int, 12))), s(:true_statements, s(:return, s(:int, 3))), s(:false_statements, s(:return, s(:int, 4)))))
@expect = [Label, LoadConstant, LoadConstant, OperatorInstruction, IsPlus, LoadConstant ,
RegToSlot, Branch, Label, LoadConstant, RegToSlot, Label ,
LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_if_small_minus
@input = s(:statements, s(:if_statement, :minus, s(:condition, s(:operator_value, :-, s(:int, 10), s(:int, 12))), s(:true_statements, s(:return, s(:int, 3))), s(:false_statements, nil)))
@expect = [Label, LoadConstant, LoadConstant, OperatorInstruction, IsMinus, Branch ,
Label, LoadConstant, RegToSlot, Label, LoadConstant, SlotToReg ,
RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_if_small_zero
@input = s(:statements, s(:if_statement, :zero, s(:condition, s(:operator_value, :-, s(:int, 10), s(:int, 12))), s(:true_statements, s(:return, s(:int, 3))), s(:false_statements, nil)))
@expect = [Label, LoadConstant, LoadConstant, OperatorInstruction, IsZero, Branch ,
Label, LoadConstant, RegToSlot, Label, LoadConstant, SlotToReg ,
RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
end
end

View File

@ -1,34 +0,0 @@
require_relative "helper"
module Risc
class TestFields < MiniTest::Test
include ExpressionHelper
include AST::Sexp
def setup
Risc.machine.boot
end
def test_local
Parfait.object_space.get_main.add_local(:bar , :Integer)
@input = s(:local, :bar)
@output = Risc::RiscValue
check
end
def test_space
@root = :name
@input = s(:known, :space)
@output = Risc::RiscValue
check
end
def test_args
Parfait.object_space.get_main.add_argument(:bar , :Integer)
@input = s(:arg, :bar)
@output = Risc::RiscValue
check
end
end
end

View File

@ -1,46 +0,0 @@
require_relative "helper"
module Risc
class TestOps < MiniTest::Test
include ExpressionHelper
include AST::Sexp
def setup
Risc.machine.boot
@root = :operator_value
@output = Risc::RiscValue
end
def operators
[:+ , :- , :* , :/ , :== ]
end
def test_ints
operators.each do |op|
@input = s(:operator_value, op , s(:int, 2), s(:int, 3))
check
end
end
def test_local_int
Parfait.object_space.get_main.add_local(:bar , :Integer)
@input = s(:operator_value, :+, s(:local, :bar), s(:int, 3))
check
end
def test_int_local
Parfait.object_space.get_main.add_local(:bar , :Integer)
@input = s(:operator_value, :+, s(:int, 3), s(:local, :bar))
check
end
def test_field_int
add_space_field(:bro,:Integer)
@input = s(:operator_value, :+, s(:field_access,s(:receiver, s(:known, :self)), s(:field, s(:ivar, :bro))), s(:int, 3))
check
end
def test_int_field
add_space_field(:bro,:Integer)
@input = s(:operator_value, :+, s(:int, 3), s(:field_access, s(:receiver, s(:known, :self)), s(:field,s(:ivar, :bro))))
check
end
end
end

View File

@ -1,47 +0,0 @@
require_relative 'helper'
module Risc
class TestReturnStatement < MiniTest::Test
include Statements
def test_return_int
@input = s(:statements, s(:return, s(:int, 5)))
@expect = [Label, LoadConstant, RegToSlot, LoadConstant, SlotToReg, RegToSlot ,
Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_return_local
Parfait.object_space.get_main.add_local(:runner , :Integer)
@input = s(:statements, s(:return, s(:local , :runner)))
@expect = [Label, SlotToReg, SlotToReg, RegToSlot, LoadConstant, SlotToReg ,
RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_return_local_assign
Parfait.object_space.get_main.add_local(:runner , :Integer)
@input = s(:statements, s(:l_assignment, s(:local, :runner), s(:int, 5)), s(:return, s(:local, :runner)))
@expect = [Label, LoadConstant, SlotToReg, RegToSlot, SlotToReg, SlotToReg ,
RegToSlot, LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_return_call
@input =s(:statements, s(:return, s(:call, :main, s(:arguments))))
@expect = [Label, SlotToReg, SlotToReg, RegToSlot, LoadConstant, RegToSlot ,
LoadConstant, SlotToReg, RegToSlot, LoadConstant, RegToSlot, RiscTransfer ,
FunctionCall, Label, RiscTransfer, SlotToReg, SlotToReg, RegToSlot ,
LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def pest_return_space_length # need to add runtime first
Parfait.object_space.get_main.add_local(:l , :Type)
@input = s(:statements, s(:l_assignment, s(:local, :l), s(:call, :get_type, s(:arguments), s(:receiver, s(:known, :space)))), s(:return, s(:field_access, s(:receiver, s(:known, :self)), s(:field, s(:ivar, :runner)))))
@expect = [Label, SlotToReg,SlotToReg ,RegToSlot,Label,FunctionReturn]
assert_nil msg = check_nil , msg
end
end
end

View File

@ -1,49 +0,0 @@
require_relative 'helper'
module Risc
class TestWhile < MiniTest::Test
include Statements
def test_while_mini
@input = s(:statements, s(:while_statement, :plus, s(:conditional, s(:int, 1)), s(:statements, s(:return, s(:int, 3)))))
@expect = [Label, Branch, Label, LoadConstant, RegToSlot, Label ,
LoadConstant, IsPlus, LoadConstant, SlotToReg, RegToSlot, Label ,
FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_while_assign
Parfait.object_space.get_main.add_local(:n , :Integer)
@input = s(:statements, s(:l_assignment, s(:local, :n), s(:int, 5)),
s(:while_statement, :plus, s(:conditional, s(:local, :n)),
s(:statements, s(:l_assignment, s(:local, :n),
s(:operator_value, :-, s(:local, :n), s(:int, 1))))),
s(:return, s(:local, :n)))
@expect = [Label, LoadConstant, SlotToReg, RegToSlot, Branch, Label ,
SlotToReg, SlotToReg, LoadConstant, OperatorInstruction, SlotToReg, RegToSlot ,
Label, SlotToReg, SlotToReg, IsPlus, SlotToReg, SlotToReg ,
RegToSlot, LoadConstant, SlotToReg, RegToSlot, Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
def test_while_return
Parfait.object_space.get_main.add_local(:n , :Integer)
@input = s(:statements, s(:l_assignment, s(:local, :n), s(:int, 10)), s(:while_statement, :plus, s(:conditional, s(:operator_value, :-, s(:local, :n), s(:int, 5))),
s(:statements, s(:l_assignment, s(:local, :n), s(:operator_value, :+, s(:local, :n), s(:int, 1))),
s(:return, s(:local, :n)))))
@expect = [Label, LoadConstant, SlotToReg, RegToSlot, Branch, Label ,
SlotToReg, SlotToReg, LoadConstant, OperatorInstruction, SlotToReg, RegToSlot ,
SlotToReg, SlotToReg, RegToSlot, Label, SlotToReg, SlotToReg ,
LoadConstant, OperatorInstruction, IsPlus, LoadConstant, SlotToReg, RegToSlot ,
Label, FunctionReturn]
assert_nil msg = check_nil , msg
end
end
end

View File

@ -1,55 +0,0 @@
require_relative "helper"
class ToCodeTest < MiniTest::Test
include AST::Sexp
def check clazz
tree = Vm.ast_to_code @statement
assert_equal tree.class , Vm::Tree.const_get( clazz )
end
def test_field_access
@statement = s(:field_access, s(:receiver, s(:ivar, :m)), s(:field, s(:ivar, :index)))
check "FieldAccess"
end
def test_simple_while
@statement = s(:while_statement, :false, s(:conditional,s(:int, 1)), s(:statements))
check "WhileStatement"
end
def test_l_assignment
@statement = s(:l_assignment, s(:local, :i), s(:int, 0))
check "LocalAssignment"
end
def test_a_assignment
@statement = s(:a_assignment, s(:arg, :i), s(:int, 0))
check "ArgAssignment"
end
def test_i_assignment
@statement = s(:i_assignment, s(:ivar, :i), s(:int, 0))
check "IvarAssignment"
end
def test_nil
@statement = s(:nil)
check "NilExpression"
end
def test_true
@statement = s(:true)
check "TrueExpression"
end
def test_false
@statement = s(:false)
check "FalseExpression"
end
def test_known
@statement = s(:known, :self)
check "KnownName"
end
def test_ivar
@statement = s(:ivar, :you)
check "InstanceName"
end
def test_class_name
@statement =s(:class_name, :FooBar)
check "ClassExpression"
end
end

View File

@ -1,57 +0,0 @@
### Compiling
The typed syntax tree is created by the ruby compiler.
The code in this directory compiles the typed tree to the register machine code, and
Parfait object structure.
If this were an interpreter, we would just walk the tree and do what it says.
Since it's not things are a little more difficult, especially in time.
When compiling we deal with two times, compile-time and run-time.
All the headache comes from mixing those two up.*
Similarly, the result of compiling is two-fold: a static and a dynamic part.
- the static part are objects like the constants, but also defined classes and their methods
- the dynamic part is the code, which is stored as streams of instructions in the MethodSource
Too make things a little simpler, we create a very high level instruction stream at first and then
run transformation and optimization passes on the stream to improve it.
The compiler has a method for each class of typed tree, named along on_xxx with xxx as the type
#### Compiler holds scope
The Compiler instance can hold arbitrary scope needed during the compilation.
A class statement sets the current @type scope , a method definition the @method.
If either are not set when needed compile errors will follow. So easy, so nice.
All code is encoded as a stream of Instructions in the MethodSource.
Instructions are stored as a list of Blocks, and Blocks are the smallest unit of code,
which is always linear.
Code is added to the method (using add_code), rather than working with the actual instructions.
This is so each compiling method can just do it's bit and be unaware of the larger structure
that is being created.
The general structure of the instructions is a graph
(with if's and whiles and breaks and what), but we build it to have one start and *one* end (return).
#### Messages and frames
Since the machine is oo we define it in objects.
Also it is important to define how instructions operate, which is is in a physical machine would
be by changing the contents of registers or some stack.
Our machine is not a register machine, but an object machine: it operates directly on objects and
also has no separate stack, only objects. There is only one object which is accessible,
basically meaning pinned to a register, the Message.
One can think of the Message as an oo replacement of the stack.
When a TypedMethod needs to make a call, it creates a NewMessage object.
Messages contain return addresses (yes, plural) and arguments.
The important thing here is that Messages and Frames are normal objects.

View File

@ -1,56 +0,0 @@
module Vm
module Assignment
def on_IvarAssignment( statement )
value = assignment_value(statement)
name = check_name(statement.name.name)
index = @method.for_type.variable_index( name)
raise "No such ivar #{name} #{@method.for_type}" unless index
value_type = @method.for_type.type_at( index )
raise "Argument Type mismatch #{value.type}!=#{value_type}" unless value.type == value_type
value_reg = use_reg(:value_type)
add_slot_to_reg(statement , :message , :receiver , value_reg )
add_reg_to_slot(statement , value , value_reg , index + 1 ) # one for type
end
def on_LocalAssignment( statement )
do_assignment_for( statement , :local )
end
def on_ArgAssignment( statement )
do_assignment_for( statement , :argument )
end
private
def do_assignment_for( statement , type )
value = assignment_value(statement)
name = check_name(statement.name.name)
index = @method.send( "has_#{type}" , name)
raise "No such #{type} #{name} #{@method.inspect}" unless index
value_type = @method.send("#{type}s_type" , index )
raise "Argument Type mismatch #{value.type}!=#{value_type}" unless value.type == value_type
move_reg(statement , "#{type}s".to_sym , value , index)
end
def move_reg(statement , type , value , index)
named_list = use_reg(:NamedList)
add_slot_to_reg(statement , :message , type , named_list )
add_reg_to_slot(statement , value , named_list , index + 1 ) # one for type
end
def assignment_value(statement)
reset_regs # statements reset registers, ie have all at their disposal
value = process(statement.value)
raise "Not register #{v}" unless value.is_a?(Risc::RiscValue)
value
end
# ensure the name given is not space and raise exception otherwise
# return the name
def check_name( name )
raise "space is a reserved name" if name == :space
name
end
end
end

View File

@ -1,52 +0,0 @@
module Vm
# collection of the simple ones, int and strings and such
module BasicValues
# Constant expressions can by definition be evaluated at compile time.
# But that does not solve their storage, ie they need to be accessible at runtime from _somewhere_
# So expressions move the data into a Risc.
# All expressions return registers
# But in the future (in the one that holds great things) we optimize those unneccesay moves away
def on_IntegerExpression expression
int = expression.value
reg = use_reg :Integer , int
add_load_constant( expression, int , reg )
return reg
end
def on_TrueExpression expression
reg = use_reg :Boolean
add_load_constant( expression, true , reg )
return reg
end
def on_FalseExpression expression
reg = use_reg :Boolean
add_load_constant( expression, false , reg )
return reg
end
def on_NilExpression expression
reg = use_reg :NilClass
add_load_constant( expression, nil , reg )
return reg
end
def on_StringExpression expression
value = Parfait.new_word expression.value.to_sym
reg = use_reg :Word
Risc.machine.constants << value
add_load_constant( expression, value , reg )
return reg
end
def on_ClassExpression expression
name = expression.value
raise "No meta class #{name}"
end
end
end

View File

@ -1,99 +0,0 @@
module Vm
module CallSite
def on_CallSite( statement )
raise "not inside method " unless @method
reset_regs
#move the new message (that we need to populate to make a call) to std register
load_new_message(statement)
me = get_me( statement )
type = get_my_type(me)
method = type.get_method(statement.name)
raise "Method not implemented for me:#{me} #{type.inspect}.#{statement.name}" unless method
# move our receiver there
add_reg_to_slot( statement , me , :new_message , :receiver)
set_message_details(method , statement , statement.arguments)
set_arguments(method , statement.arguments)
ret = use_reg( :Object ) #FIXME real return type
Risc.issue_call( self , method )
# the effect of the method is that the NewMessage Return slot will be filled, return it
# but move it into a register too
add_slot_to_reg(statement, :new_message , :return_value , ret )
ret
end
private
def load_new_message(statement)
new_message = Risc.resolve_to_register(:new_message)
add_slot_to_reg(statement, :message , :next_message , new_message )
new_message
end
def get_me( statement )
if statement.receiver
me = process( statement.receiver )
else
me = use_reg @method.for_type
add_slot_to_reg(statement, :message , :receiver , me )
end
me
end
def get_my_type( me )
# now we have to resolve the method name (+ receiver) into a callable method
case me.type
when Parfait::Type
type = me.type
when Symbol
type = Parfait.object_space.get_class_by_name(me.type).instance_type
else
raise me.inspect
end
raise "Not type #{type}" unless type.is_a? Parfait::Type
type
end
# load method name and set to new message (for exceptions/debug)
def set_message_details( method , name_s , arguments )
name = name_s.name
name_tmp = use_reg(:Word)
add_load_constant("#{name} load method name", name , name_tmp)
add_reg_to_slot( "#{name} store method name" , name_tmp , :new_message , :name)
# next arg type
args_reg = use_reg(:Type , method.arguments )
list_reg = use_reg(:NamedList , arguments )
add_load_constant("#{name} load arguments type", method.arguments , args_reg)
add_slot_to_reg( "#{name} get args from method" , :new_message , :arguments , list_reg )
add_reg_to_slot( "#{name} store args type in args" , args_reg , list_reg , 1 )
end
def set_arguments( method , arguments )
# reset tmp regs for each and load result into new_message
arg_type = method.arguments
message = "Arg number mismatch, method=#{arg_type.instance_length - 1} , call=#{arguments.length}"
raise message if (arg_type.instance_length - 1 ) != arguments.length
arguments.each_with_index do |arg , i |
store_arg_no(arguments , arg_type , arg , i + 1) #+1 for ruby(0 based)
end
end
def store_arg_no(arguments , arg_type , arg , i )
reset_regs
i = i + 1 # disregarding type field
val = process( arg) # processing should return the register with the value
raise "Not register #{val}" unless val.is_a?(Risc::RiscValue)
#FIXME definately needs some tests
raise "TypeMismatch calling with #{val.type} , instead of #{arg_type.type_at(i)}" if val.type != arg_type.type_at(i)
list_reg = use_reg(:NamedList , arguments )
add_slot_to_reg( "Set arg #{i}:#{arg}" , :new_message , :arguments , list_reg )
# which we load int the new_message at the argument's index
add_reg_to_slot( arg , val , list_reg , i ) #one for type and one for ruby
end
end
end

View File

@ -1,14 +0,0 @@
module Vm
module Collections
# attr_reader :values
def on_array statement, context
end
# attr_reader :key , :value
def on_association context
end
def on_hash context
end
end
end

View File

@ -1,27 +0,0 @@
module Vm
module FieldAccess
def on_FieldAccess statement
# receiver_ast , field_ast = *statement
receiver = process(statement.receiver)
type = receiver.type
if(type.is_a?(Symbol))
type = Parfait.object_space.get_class_by_name(type).instance_type
end
field_name = statement.field.name
index = type.variable_index(field_name)
raise "no such field:#{field_name} for class #{type.inspect}" unless index
value = use_reg(type.type_at(index))
add_slot_to_reg(statement , receiver , index, value)
value
end
def on_receiver expression
process expression.first
end
end
end

View File

@ -1,41 +0,0 @@
module Vm
module IfStatement
# an if evaluates the condition and jumps to the true block if true
# so the else block is automatically after that.
# But then the else needs to jump over the true block unconditionally.
def on_IfStatement( statement )
# branch_type , condition , if_true , if_false = *statement
true_block = compile_if_condition( statement )
merge = compile_if_false( statement )
add_code true_block
compile_if_true(statement)
add_code merge
nil # statements don't return anything
end
private
def compile_if_condition( statement )
reset_regs
process(statement.condition)
branch_class = Object.const_get "Risc::Is#{statement.branch_type.capitalize}"
true_block = Risc.label(statement, "if_true")
add_code branch_class.new( statement.condition , true_block )
return true_block
end
def compile_if_true( statement )
reset_regs
process(statement.if_true)
end
def compile_if_false( statement )
reset_regs
process(statement.if_false) if statement.if_false.statements
merge = Risc.label(statement , "if_merge")
add_code Risc::Branch.new(statement.if_false, merge )
merge
end
end
end

View File

@ -1,58 +0,0 @@
module Vm
module NameExpression
# attr_reader :name
# compiling name needs to check if it's a local variable
# or an argument
# whichever way this goes the result is stored in the return slot (as all compiles)
def on_KnownName statement
name = statement.name
[:self , :space , :message].each do |special|
return send(:"load_special_#{special}" , statement ) if name == special
end
raise "Unknow 'known' expression #{name}"
end
def on_ArgumentName(statement)
name = statement.name
index = @method.has_argument(name)
raise "no arg #{name}" unless index
named_list = use_reg :NamedList
ret = use_reg @method.arguments_type(index)
#puts "For #{name} at #{index} got #{@method.arguments.inspect}"
add_slot_to_reg("#{statement} load args" , :message , :arguments, named_list )
add_slot_to_reg("#{statement} load #{name}" , named_list , index + 1, ret )
return ret
end
def on_LocalName( statement )
name = statement.name
index = @method.has_local( name )
raise "must define local '#{name}' before using it" unless index
named_list = use_reg :NamedList
add_slot_to_reg("#{name} load locals" , :message , :frame , named_list )
ret = use_reg @method.frame_type( index )
add_slot_to_reg("#{name} load from locals" , named_list , index + 1, ret )
return ret
end
def load_special_self(statement)
ret = use_reg @type
add_slot_to_reg("#{statement} load self" , :message , :receiver , ret )
return ret
end
def load_special_space(statement)
space = Parfait.object_space
reg = use_reg :Space , space
add_load_constant( "#{statement} load space", space , reg )
return reg
end
def load_special_message(statement)
reg = use_reg :Message
add_transfer( "#{statement} load message", Risc.message_reg , reg )
return reg
end
end #module
end

View File

@ -1,15 +0,0 @@
module Vm
module OperatorExpression
def on_OperatorExpression statement
# operator , left_e , right_e = *statement
# left and right must be expressions. Expressions return a register when compiled
left_reg = process(statement.left_expression)
right_reg = process(statement.right_expression)
raise "Not register #{left_reg}" unless left_reg.is_a?(Risc::RiscValue)
raise "Not register #{right_reg}" unless right_reg.is_a?(Risc::RiscValue)
add_code Risc::OperatorInstruction.new(statement,statement.operator,left_reg,right_reg)
return left_reg # though this has wrong value attached
end
end
end

View File

@ -1,10 +0,0 @@
module Vm
module ReturnStatement
def on_ReturnStatement statement
reg = process(statement.return_value)
add_reg_to_slot( statement, reg , :message , :return_value)
nil # statements don't return
end
end
end

View File

@ -1,7 +0,0 @@
module Vm
module StatementList
def on_Statements statement
process_all( statement.statements )
end
end
end

View File

@ -1,42 +0,0 @@
module Vm
module WhileStatement
def on_WhileStatement statement
#branch_type , condition , statements = *statement
condition_label = compile_while_preamble( statement ) #jump there
start = compile_while_body( statement )
# This is where the loop starts, though in subsequent iterations it's in the middle
add_code condition_label
compile_while_condition( statement )
branch_class = Object.const_get "Risc::Is#{statement.branch_type.capitalize}"
# this is where the while ends and both branches meet
add_code branch_class.new( statement.condition , start )
nil # statements don't return anything
end
private
def compile_while_preamble( statement )
condition_label = Risc.label(statement.condition , "condition_label")
# unconditionally branch to the condition upon entering the loop
add_code Risc::Branch.new(statement.condition , condition_label)
condition_label
end
def compile_while_body( statement )
start = Risc.label(statement , "while_start" )
add_code start
reset_regs
process(statement.statements)
start
end
def compile_while_condition( statement )
reset_regs
process(statement.condition)
end
end
end

View File

@ -1,48 +0,0 @@
# Base class for Expresssion and Statement
module Vm
class Code ; end
class Statement < Code ; end
class Expression < Code ; end
module ValuePrinter
def to_s
@value.to_s
end
end
end
require_relative "tree/while_statement"
require_relative "tree/if_statement"
require_relative "tree/return_statement"
require_relative "tree/statements"
require_relative "tree/operator_expression"
require_relative "tree/field_access"
require_relative "tree/call_site"
require_relative "tree/basic_values"
require_relative "tree/assignment"
require_relative "tree/to_code"
AST::Node.class_eval do
# def [](name)
# #puts self.inspect
# children.each do |child|
# if child.is_a?(AST::Node)
# #puts child.type
# if (child.type == name)
# return child.children
# end
# else
# #puts child.class
# end
# end
# nil
# end
#
# def first_from( node_name )
# from = self[node_name]
# return nil unless from
# from.first
# end
end

View File

@ -1,30 +0,0 @@
module Vm
module Tree
class Assignment < Statement
attr_accessor :name , :value
def initialize(name , value = nil )
@name , @value = name , value
end
def to_s
"#{name} = #{value}\n"
end
end
class IvarAssignment < Assignment
def to_s
"@#{name} = #{value}\n"
end
end
class ArgAssignment < Assignment
end
class LocalAssignment < Assignment
end
end
end

View File

@ -1,66 +0,0 @@
module Vm
module Tree
class IntegerExpression < Expression
include ValuePrinter
attr_accessor :value
def initialize(value)
@value = value
end
end
class FloatExpression < Expression
include ValuePrinter
attr_accessor :value
def initialize(value)
@value = value
end
end
class TrueExpression < Expression
def to_s
"true"
end
end
class FalseExpression < Expression
def to_s
"false"
end
end
class NilExpression < Expression
def to_s
"nil"
end
end
class StringExpression < Expression
include ValuePrinter
attr_accessor :value
def initialize(value)
@value = value
end
end
class NameExpression < Expression
include ValuePrinter
attr_accessor :value
alias :name :value
def initialize(value)
@value = value
end
end
class LocalName < NameExpression
end
class ArgumentName < NameExpression
end
class InstanceName < NameExpression
end
class KnownName < NameExpression
end
class ClassExpression < Expression
include ValuePrinter
attr_accessor :value
def initialize(value)
@value = value
end
end
end
end

View File

@ -1,12 +0,0 @@
module Vm
module Tree
class CallSite < Expression
attr_accessor :name , :receiver , :arguments
def to_s
str = receiver ? "#{receiver}.#{name}" : name.to_s
str + arguments.collect{|a| a.to_s }.join(",")
end
end
end
end

View File

@ -1,10 +0,0 @@
module Vm
module Tree
class FieldAccess < Expression
attr_accessor :receiver , :field
def to_s
"#{receiver}.#{field}"
end
end
end
end

View File

@ -1,12 +0,0 @@
module Vm
module Tree
class IfStatement < Statement
attr_accessor :branch_type , :condition , :if_true , :if_false
def to_s
str = "if_#{branch_type}(#{condition}) \n #{if_true}\n"
str += "else\n #{if_false}\n" if if_false
str + "end\n"
end
end
end
end

View File

@ -1,10 +0,0 @@
module Vm
module Tree
class OperatorExpression < Expression
attr_accessor :operator , :left_expression , :right_expression
def to_s
"#{left_expression} #{operator} #{right_expression}"
end
end
end
end

View File

@ -1,11 +0,0 @@
module Vm
module Tree
class ReturnStatement < Statement
attr_accessor :return_value
def to_s
"return #{return_value}"
end
end
end
end

View File

@ -1,9 +0,0 @@
module Vm
class Statements < Statement
attr_accessor :statements
def to_s
return "" unless statements
statements.collect() { |s| s.to_s }.join
end
end
end

View File

@ -1,162 +0,0 @@
module Vm
def self.ast_to_code statement
compiler = ToCode.new
compiler.process statement
end
# ToCode converts an ast (from the ast gem) into the vm code expressions
# Code is the base class of the tree that is transformed to and
# Expression and Statement the next two subclasses.
# While it is an ast, it is NOT a ruby parser generated ast. Instead the ast is generated
# with s-expressions (also from the ast gem), mostly in tests, but also a little in
# the generation of functions (Builtin)
#
class ToCode < AST::Processor
def handler_missing node
raise "No handler on_#{node.type}(node)"
end
def on_parameters statement
params = {}
statement.children.each do |param , type , name|
type , name = *param
params[name] = type
end
params
end
def on_while_statement statement
branch_type , condition , statements = *statement
whil = Tree::WhileStatement.new()
whil.branch_type = branch_type
whil.condition = process(condition)
whil.statements = process(statements)
whil
end
def on_if_statement statement
branch_type , condition , if_true , if_false = *statement
iff = Tree::IfStatement.new()
iff.branch_type = branch_type
iff.condition = process(condition)
iff.if_true = process(if_true)
iff.if_false = process(if_false)
iff
end
def process_first code
raise "Too many children #{code.inspect}" if code.children.length != 1
process code.children.first
end
alias :on_conditional :process_first
alias :on_condition :process_first
alias :on_field :process_first
def on_statements( statement )
list = Statements.new()
kids = statement.children
return list unless kids
return list unless kids.first
list.statements = process_all(kids)
list
end
alias :on_true_statements :on_statements
alias :on_false_statements :on_statements
def on_return statement
ret = Tree::ReturnStatement.new()
ret.return_value = process(statement.children.first)
ret
end
def on_operator_value statement
operator , left_e , right_e = *statement
op = Tree::OperatorExpression.new()
op.operator = operator
op.left_expression = process(left_e)
op.right_expression = process(right_e)
op
end
def on_field_access statement
receiver_ast , field_ast = *statement
field = Tree::FieldAccess.new()
field.receiver = process(receiver_ast)
field.field = process(field_ast)
field
end
def on_receiver expression
process expression.children.first
end
def on_call statement
name , arguments , receiver = *statement
call = Tree::CallSite.new()
call.name = name
call.arguments = process_all(arguments)
call.receiver = process(receiver)
call
end
def on_int expression
Tree::IntegerExpression.new(expression.children.first)
end
def on_true _expression
Tree::TrueExpression.new
end
def on_false _expression
Tree::FalseExpression.new
end
def on_nil _expression
Tree::NilExpression.new
end
def on_arg statement
Tree::ArgumentName.new(statement.children.first)
end
def on_local statement
Tree::LocalName.new(statement.children.first)
end
def on_ivar statement
Tree::InstanceName.new(statement.children.first)
end
def on_known statement
Tree::KnownName.new(statement.children.first)
end
def on_string expressions
Tree::StringExpression.new(expressions.children.first)
end
def on_class_name expression
Tree::ClassExpression.new(expression.children.first)
end
def on_i_assignment statement
assignment_for( statement, Vm::Tree::IvarAssignment)
end
def on_a_assignment statement
assignment_for( statement, Vm::Tree::ArgAssignment)
end
def on_l_assignment( statement )
assignment_for( statement, Vm::Tree::LocalAssignment)
end
def assignment_for( statement , clazz)
name , value = *statement
p_name = process name
p_value = process(value)
clazz.new(p_name , p_value)
end
end
end

View File

@ -1,11 +0,0 @@
module Vm
module Tree
class WhileStatement < Statement
attr_accessor :branch_type , :condition , :statements
def to_s
str = "while_#{branch_type}(#{condition}) do\n"
str + statements.to_s + "\nend\n"
end
end
end
end