several larger changes came together, bit of cleaning too

- all code must be in functions (which must be in classes).
— changes a fair few tests
— also changes api, as method is not recursive, not passed around
- all state in instance vars in compiler (no accessors)
- class is another such variable, surely more coming
all green again
This commit is contained in:
Torsten Ruger 2015-10-06 00:27:13 +03:00
parent 3d36fd1746
commit f4a4ccb98e
37 changed files with 302 additions and 205 deletions

View File

@ -1,9 +1,7 @@
module Bosl
class Compiler < AST::Processor
attr_reader :method
def initialize(method)
@method = method
def initialize()
end
def handler_missing node
raise "No handler on_#{node.type}(node)"
@ -24,8 +22,8 @@ module Bosl
# This makes the dispatch extensible, ie Expressions may be added by external code,
# as long as matching compile methods are supplied too.
#
def self.compile expression , method
compiler = Compiler.new method
def self.compile expression
compiler = Compiler.new
compiler.process expression
end

View File

@ -19,11 +19,16 @@ Similarly, the result of compiling is two-fold: a static and a dynamic part.
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.
Each ast class gets a compile method that does the compilation.
The compiler has a method for each type for ast, named along on_xxx with xxx as the type
#### MethodSource and Instructions
#### Compiler holds scope
The Compiler instance can hold arbitrary scope needed during the compilation. Since we compile bosl
(a static language) things have become more simple.
A class statement sets the current @clazz scope , a method definition the @method.
If either are not set when needed compile errors will follow. So easy, so nice.
The first argument to the compile method is the MethodSource.
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.
@ -57,29 +62,17 @@ The objects the machine works on are:
and working on means, these are the only objects which the machine accesses.
Ie all others would have to be moved first.
When a Method needs to make a call, or send a Message, it creates a NewMessage object.
Messages contain return addresses and arguments.
Then the machine must find the method to call.
This is a function of the virtual machine and is implemented in ruby.
Then a new Method receives the Message, creates a Frame for local and temporary variables
and continues execution.
When a Method 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.
And interestingly we can partly use ruby to find the method, so in a way it is not just a top
down transformation. Instead the sending goes back up and then down again.
### Distinclty future proof
The Message object is the second parameter to the compile method, the run-time part as it were.
Why? Since it only exists at runtime: to make compile time analysis possible
(it is after all the Virtual version, not Parfait. ie compile-time, not run-time).
Especially for those times when we can resolve the method at compile time.
Bosl is designed to be used as an implementation language for a higher oo language. Some, or
even many, features may not make sense on their own. But these features, like several return
addresses are important to implement the higher language.
*
As ruby is a dynamic language, it also compiles at run-time. This line of thought does not help
though as it sort of mixes the seperate times up, even they are not.
Even in a running ruby programm the stages of compile and run are seperate.
Similarly it does not help to argue that the code is static too, not dynamic,
as that leaves us with a worse working model.
In fact, Bosl main purpose is not even to be written. The main purpose is to have a language to
compile ruby to. In the same way that the assembler layer in salama is not designed to be written,
we just need it to create our layers.

View File

@ -14,25 +14,25 @@ module Bosl
def on_int expression
int = expression.first
to = Virtual::Return.new(Virtual::Integer , int)
method.source.add_code Virtual::Set.new( int , to )
@method.source.add_code Virtual::Set.new( int , to )
to
end
def on_true expression
to = Virtual::Return.new(Virtual::Reference , true )
method.source.add_code Virtual::Set.new( true , to )
@method.source.add_code Virtual::Set.new( true , to )
to
end
def on_false expression
to = Virtual::Return.new(Virtual::Reference , false)
method.source.add_code Virtual::Set.new( false , to )
@method.source.add_code Virtual::Set.new( false , to )
to
end
def on_nil expression
to = Virtual::Return.new(Virtual::Reference , nil)
method.source.add_code Virtual::Set.new( nil , to )
@method.source.add_code Virtual::Set.new( nil , to )
to
end
@ -40,8 +40,8 @@ module Bosl
# Clearly a TODO here to implement strings rather than reusing symbols
value = expression.first.to_sym
to = Virtual::Return.new(Virtual::Reference , value)
method.source.constants << value
method.source.add_code Virtual::Set.new( value , to )
@method.source.constants << value
@method.source.add_code Virtual::Set.new( value , to )
to
end
end

View File

@ -4,7 +4,7 @@ module Bosl
def on_call expression
name , arguments , receiver = *expression
name = name.to_a.first
raise "not inside method " unless @method
if receiver
me = process( receiver.to_a.first )
else
@ -12,9 +12,9 @@ module Bosl
end
## need two step process, compile and save to frame
# then move from frame to new message
method.source.add_code Virtual::NewMessage.new
method.source.add_code Virtual::Set.new( me , Virtual::NewSelf.new(me.type))
method.source.add_code Virtual::Set.new( name.to_sym , Virtual::NewMessageName.new(:int))
@method.source.add_code Virtual::NewMessage.new
@method.source.add_code Virtual::Set.new( me , Virtual::NewSelf.new(me.type))
@method.source.add_code Virtual::Set.new( name.to_sym , Virtual::NewMessageName.new(:int))
compiled_args = []
arguments.to_a.each_with_index do |arg , i|
#compile in the running method, ie before passing control
@ -24,7 +24,7 @@ module Bosl
# so the next free is +1
to = Virtual::NewArgSlot.new(i + 1 ,val.type , val)
# (doing this immediately, not after the loop, so if it's a return it won't get overwritten)
method.source.add_code Virtual::Set.new( val , to )
@method.source.add_code Virtual::Set.new( val , to )
compiled_args << to
end
#method.source.add_code Virtual::MessageSend.new(name , me , compiled_args) #and pass control
@ -41,7 +41,7 @@ module Bosl
elsif( me.is_a? Fixnum )
name = :plus if name == :+
method = Virtual.machine.space.get_class_by_name(:Integer).get_instance_method(name)
#puts Virtual.machine.space.get_class_by_name(:Integer).method_names.to_a
puts Virtual.machine.space.get_class_by_name(:Integer).method_names.to_a
raise "Method not implemented Integer.#{name}" unless method
@method.source.add_code Virtual::MethodCall.new( method )
else

View File

@ -5,7 +5,8 @@ module Bosl
#puts expression.inspect
type , name , value = *expression
for_class = self.method.for_class
for_class = @clazz
raise "no class" unless for_class
index = for_class.object_layout.variable_index(name)
#raise "class field already defined:#{name} for class #{for_class.name}" if index
puts "Define field #{name} on class #{for_class.name}"

View File

@ -9,11 +9,10 @@ module Bosl
case receiver
when :self
for_class = self.method.for_class
index = for_class.object_layout.variable_index(field_name)
raise "field access, but no such field:#{field_name} for class #{for_class.name}" unless index
index = @clazz.object_layout.variable_index(field_name)
raise "field access, but no such field:#{field_name} for class #{@clazz.name}" unless index
value = Virtual::Return.new(:int)
method.source.add_code Virtual::Set.new( Virtual::SelfSlot.new(index, :int ) , value )
@method.source.add_code Virtual::Set.new( Virtual::SelfSlot.new(index, :int ) , value )
when :message
#message Slot
raise "message not yet"

View File

@ -5,7 +5,7 @@ module Bosl
#puts expression.inspect
type , name , value = *expression
index = method.ensure_local( name , type )
index = @method.ensure_local( name , type )
if value
value = process( value )

View File

@ -1,6 +1,6 @@
module Bosl
Compiler.class_eval do
# function attr_reader :name, :params, :body , :receiver
def on_function expression
#puts expression.inspect
return_type , name , parameters, kids , receiver = *expression
@ -24,23 +24,30 @@ module Bosl
end
end
else
r = Virtual::Self.new(:int)
class_name = method.for_class.name
r = @clazz
class_name = @clazz.name
end
new_method = Virtual::MethodSource.create_method(class_name, return_type, name , args )
new_method.source.receiver = r
new_method.for_class.add_instance_method new_method
raise "Already in method #{@method}" if @method
@method = @clazz.get_instance_method( name )
if(@method)
puts "Warning, redefining method #{name}" unless name == :main
#TODO check args / type compatibility
@method.source.init @method
else
@method = Virtual::MethodSource.create_method(class_name, return_type, name , args )
@method.for_class.add_instance_method @method
end
@method.source.receiver = r
puts "compile method #{@method.name}"
old_method = @method
@method = new_method
#frame = frame.new_frame
kids.to_a.each do |ex|
return_type = process(ex)
raise return_type.inspect if return_type.is_a? Virtual::Instruction
end
@method = old_method
new_method.source.return_type = return_type
@method.source.return_type = return_type
@method = nil
Virtual::Return.new(return_type)
end
end

View File

@ -8,28 +8,28 @@ module Bosl
# to execute the logic as the if states it, the blocks are the other way around
# so we can the jump over the else if true ,
# and the else joins unconditionally after the true_block
merge_block = method.source.new_block "if_merge" # last one, created first
true_block = method.source.new_block "if_true" # second, linked in after current, before merge
false_block = method.source.new_block "if_false" # directly next in order, ie if we don't jump we land here
merge_block = @method.source.new_block "if_merge" # last one, created first
true_block = @method.source.new_block "if_true" # second, linked in after current, before merge
false_block = @method.source.new_block "if_false" # directly next in order, ie if we don't jump we land here
is = process(condition )
# TODO should/will use different branches for different conditions.
# just a scetch : cond_val = cond_val.is_true?(method) unless cond_val.is_a? BranchCondition
method.source.add_code Virtual::IsTrueBranch.new( true_block )
@method.source.add_code Virtual::IsTrueBranch.new( true_block )
# compile the true block (as we think of it first, even it is second in sequential order)
method.source.current true_block
@method.source.current true_block
last = process_all(if_true).last
# compile the false block
method.source.current false_block
@method.source.current false_block
last = process_all(if_false).last if if_false
method.source.add_code Virtual::UnconditionalBranch.new( merge_block )
@method.source.add_code Virtual::UnconditionalBranch.new( merge_block )
#puts "compiled if: end"
method.source.current merge_block
@method.source.current merge_block
#TODO should return the union of the true and false types
last

View File

@ -9,10 +9,11 @@ module Bosl
def on_class expression
#puts expression.inspect
name , derives , expressions = *expression
clazz = Parfait::Space.object_space.get_class_by_name! name
#puts "Compiling class #{clazz.name.inspect}"
raise "classes dont yet play babushka, get coding #{name}" if @clazz
@clazz = Parfait::Space.object_space.get_class_by_name! name
puts "Compiling class #{@clazz.name.inspect}"
expression_value = process_all(expressions).last
@clazz = nil
return expression_value
end
end

View File

@ -7,15 +7,15 @@ module Bosl
# whichever way this goes the result is stored in the return slot (as all compiles)
def on_name expression
name = expression.to_a.first
return Virtual::Self.new( Virtual::Reference.new(method.for_class)) if name == :self
return Virtual::Self.new( Virtual::Reference.new(@clazz)) if name == :self
# either an argument, so it's stored in message
ret = Virtual::Return.new :int
if( index = method.has_arg(name))
method.source.add_code Virtual::Set.new( Virtual::ArgSlot.new(index,:int ) , ret)
if( index = @method.has_arg(name))
@method.source.add_code Virtual::Set.new( Virtual::ArgSlot.new(index,:int ) , ret)
else # or a local so it is in the frame
index = method.has_local( name )
index = @method.has_local( name )
if(index)
method.source.add_code Virtual::Set.new(Virtual::FrameSlot.new(index,:int ) , ret )
@method.source.add_code Virtual::Set.new(Virtual::FrameSlot.new(index,:int ) , ret )
else
raise "must define variable #{name} before using it"
end

View File

@ -3,6 +3,7 @@ module Bosl
# operator attr_reader :operator, :left, :right
def on_operator expression
operator , left , right = *expression
#raise "not quite there"
Virtual::Return.new(:int)
end
@ -10,13 +11,13 @@ module Bosl
name , value = *expression
name = name.to_a.first
v = process(value)
index = method.has_local( name )
index = @method.has_local( name )
if(index)
method.source.add_code Virtual::Set.new(Virtual::FrameSlot.new(:int,index ) , v )
@method.source.add_code Virtual::Set.new(Virtual::FrameSlot.new(:int,index ) , v )
else
index = method.has_arg( name )
index = @method.has_arg( name )
if(index)
method.source.add_code Virtual::Set.new(Virtual::ArgSlot.new(:int,index ) , v )
@method.source.add_code Virtual::Set.new(Virtual::ArgSlot.new(:int,index ) , v )
else
raise "must define variable #{name} before using it in #{@method.inspect}"
end

View File

@ -7,22 +7,22 @@ module Bosl
condition = condition.first
# this is where the while ends and both branches meet
merge = method.source.new_block("while merge")
merge = @method.source.new_block("while merge")
# this comes after the current and beofre the merge
start = method.source.new_block("while_start" )
method.source.current start
start = @method.source.new_block("while_start" )
@method.source.current start
cond = process(condition)
method.source.add_code Virtual::IsTrueBranch.new(merge)
@method.source.add_code Virtual::IsTrueBranch.new(merge)
last = process_all(expressions).last
# unconditionally branch to the start
method.source.add_code Virtual::UnconditionalBranch.new(start)
@method.source.add_code Virtual::UnconditionalBranch.new(start)
# continue execution / compiling at the merge block
method.source.current merge
@method.source.current merge
last
end
end

View File

@ -90,6 +90,7 @@ module Parfait
return internal_object_get(index + 1)
end
end
alias :[] :get
def empty?
self.get_length == 0

View File

@ -32,6 +32,7 @@ module Parfait
message = Message.new(nil)
5.times do
self.first_message = Message.new message
#puts "INIT caller #{message.object_id} to #{self.first_message.object_id}"
message.set_caller self.first_message
message = self.first_message
end

View File

@ -111,7 +111,7 @@ module Register
begin
code.assemble( stream )
rescue => e
puts "Method error #{method.name}\n#{Sof.write(method.source.blocks).to_s[0...2000]}"
puts "Assembly error #{method.name}\n#{Sof.write(method.source.blocks).to_s[0...2000]}"
puts Sof.write(code)
raise e
end

View File

@ -132,11 +132,11 @@ module Virtual
self
end
def compile_main bytes
def parse_and_compile bytes
syntax = @parser.parse_with_debug(bytes)
parts = Parser::Transform.new.apply(syntax)
#puts parts.inspect
Bosl::Compiler.compile( parts , @space.get_main )
Bosl::Compiler.compile( parts )
end
private

View File

@ -38,23 +38,27 @@ module Virtual
clazz = Virtual.machine.space.get_class_by_name class_name
raise "No such class #{class_name}" unless clazz
return_type = Virtual::Type.from_sym return_type
arguemnts = []
arguments = []
args.each_with_index do | arg , index |
unless arg.is_a? Parfait::Variable
raise "not type #{arg}:#{arg.class}" unless arg == :int || arg == :ref
arg = Parfait::Variable.new arg , "arg#{index}".to_sym
end
arguemnts << arg
arguments << arg
end
method = clazz.create_instance_method( method_name , Virtual.new_list(arguemnts))
method = clazz.create_instance_method( method_name , Virtual.new_list(arguments))
method.source = MethodSource.new(method , return_type)
method
end
# just passing the method object in for Instructions to make decisions (later)
def initialize method , return_type
init( method , return_type)
end
def init method , return_type = nil
# first block we have to create with .new , as new_block assumes a current
enter = Block.new( "enter" , method ).add_code(MethodEnter.new( method ))
@return_type = return_type
@return_type = return_type if return_type
@blocks = [enter]
@current = enter
new_block("return").add_code(MethodReturn.new(method))

View File

@ -1,6 +1,6 @@
module CodeChecker
def check
Virtual.machine.boot.compile_main @string_input
Virtual.machine.boot.parse_and_compile @string_input
produced = Virtual.machine.space.get_main.source
assert @output , "No output given"
assert_equal @output.length , produced.blocks.length , "Block length"

View File

@ -5,7 +5,7 @@ require 'parslet/convenience'
Bosl::Compiler.class_eval do
def check
Virtual.machine.boot.compile_main @string_input
Virtual.machine.boot.parse_and_compile @string_input
produced = Virtual.machine.space.get_main.source
assert_equal @output , produced
Virtual.machine.run_passes

View File

@ -1,4 +1,3 @@
require_relative "test_basic"
require_relative "test_methods"
require_relative "test_hello"
require_relative "test_compiler"

View File

@ -3,7 +3,15 @@ require_relative "compiler_helper"
class TestBasic < MiniTest::Test
def check
expressions = Virtual.machine.boot.compile_main @string_input
input = <<HERE
class Object
int main()
#{@string_input}
end
end
HERE
expressions = Virtual.machine.boot.parse_and_compile input
if( expressions.first.is_a? Virtual::Self )
expressions.first.type.instance_variable_set :@of_class , nil
end

View File

@ -7,11 +7,11 @@ class CompilerTest < MiniTest::Test
Virtual.machine.boot
end
def check
res = Bosl::Compiler.compile( @expression , Virtual.machine.space.get_main )
res = Bosl::Compiler.compile( @expression )
assert res.is_a?(Virtual::Slot) , "compiler must compile to slot, not #{res.inspect}"
end
def ttest_if_expression
#TODO review constant : all expressions return a slot
#TODO review constant : all expressions return a slot
@expression = s(:if,
s(:condition,
s(:int, 0)),
@ -21,9 +21,12 @@ class CompilerTest < MiniTest::Test
check
end
def test_function_expression
@expression = s(:function, :int, s(:name, :foo),
s(:parameters, s(:parameter, :ref, :x)),
s(:expressions, s(:int, 5)))
@expression = s(:class, :Foo,
s(:derives, :Object),
s(:expressions,
s(:function, :int, s(:name, :foo),
s(:parameters, s(:parameter, :ref, :x)),
s(:expressions, s(:int, 5)))))
check
end
end

View File

@ -7,10 +7,12 @@ module Virtual
def test_foo3
@string_input = <<HERE
field int a
int foo(int x)
int b = self.a
return b +x
class Object
field int a
int foo(int x)
int b = self.a
return b +x
end
end
HERE
@output = [ [Virtual::MethodEnter] , [Virtual::MethodReturn] ]

View File

@ -6,7 +6,7 @@ class HelloTest < MiniTest::Test
machine = Virtual.machine.boot
Parfait::Space.object_space.get_class_by_name(:Integer).remove_instance_method :plus
#TODO remove this hack: write proper aliases
expressions = machine.compile_main @string_input
expressions = machine.parse_and_compile @string_input
output_at = "Register::CallImplementation"
#{}"Register::CallImplementation"
machine.run_before output_at
@ -34,7 +34,11 @@ HERE
def test_string_put
@string_input = <<HERE
"Hello again\n".putstring()
class Object
int main()
"Hello again\n".putstring()
end
end
HERE
check
end

View File

@ -19,8 +19,10 @@ HERE
def test_simplest_function
@string_input = <<HERE
int foo(int x)
return x
class Object
int foo(int x)
return x
end
end
HERE
@output = [[MethodEnter] ,[MethodReturn]]
@ -29,8 +31,10 @@ HERE
def test_second_simplest_function
@string_input = <<HERE
ref foo(ref x)
return x
class Object
ref foo(ref x)
return x
end
end
HERE
@output = [[Virtual::MethodEnter],[Virtual::MethodReturn]]
@ -39,24 +43,28 @@ HERE
def test_puts_string
@string_input = <<HERE
int foo()
puts("Hello")
class Object
int main()
puts("Hello")
end
end
foo()
HERE
@output = [[Virtual::MethodEnter , Virtual::NewMessage, Virtual::Set, Virtual::Set, Virtual::MethodCall],
[Virtual::MethodReturn]]
@output = [[MethodEnter , NewMessage, Set, Set , Set, Set, MethodCall],[MethodReturn]]
check
end
def ttest_class_function
def test_int_function
@string_input = <<HERE
int self.length(int x)
self.length
class Integer < Object
int times(int x)
self * x
end
end
HERE
@output = nil
@output = [[Virtual::MethodEnter] , [Virtual::MethodReturn]]
check
cla = Virtual.machine.space.get_class_by_name :Integer
assert cla.get_instance_method( :times )
end
def ttest_function_ops
@ -106,11 +114,15 @@ HERE
def test_while
@string_input = <<HERE
while(1)
3
class Object
int foo()
while(1)
3
end
end
end
HERE
@output = [[MethodEnter],[Set,IsTrueBranch,Set,UnconditionalBranch],[],[MethodReturn]]
@output = [[Virtual::MethodEnter],[Virtual::MethodReturn]]
check
end

View File

@ -7,7 +7,7 @@ require_relative '../helper'
module Fragments
def check
expressions = Virtual.machine.boot.compile_main @string_input
expressions = Virtual.machine.boot.parse_and_compile @string_input
@expect.each_with_index do | should , i |
exp_i = expressions[i]
assert exp_i.is_a?(Virtual::Slot) , "compiles should return #{should}, not #{exp_i}"

View File

@ -5,11 +5,16 @@ class TestFoo < MiniTest::Test
def test_foo2
@string_input = <<HERE
int foo(int x)
int a = 5
return a
class Object
int foo(int x)
int a = 5
return a
end
int main()
foo( 4 )
end
end
foo( 4 )
HERE
@expect = [ Virtual::Return ]
check

View File

@ -5,30 +5,35 @@ class TestFunctions < MiniTest::Test
def test_functions
@string_input = <<HERE
int minus(int a,int b)
return a - b
end
class Object
int plus(int a, int b)
return a + b
end
int minus(int a,int b)
return a - b
end
int times(int a, int b)
if( b == 0 )
a = 0
else
int m = minus(b, 1)
int t = times(a, m)
a = plus(a,t)
int plus(int a, int b)
return a + b
end
int times(int a, int b)
if( b == 0 )
a = 0
else
int m = minus(b, 1)
int t = times(a, m)
a = plus(a,t)
end
end
int t_seven()
int tim = times(7,6)
tim.putint()
end
int main()
t_seven()
end
end
int t_seven()
int tim = times(7,6)
tim.putint()
end
t_seven()
HERE
@expect = [Virtual::Return ]
check

View File

@ -5,7 +5,11 @@ class TestHello < MiniTest::Test
def test_hello
@string_input = <<HERE
"Hello Raisa, I am salama".putstring()
class Object
int main()
"Hello Raisa, I am salama".putstring()
end
end
HERE
@expect = []
check

View File

@ -5,10 +5,14 @@ class TestIf < MiniTest::Test
def test_if_basic
@string_input = <<HERE
if( n < 12)
3
else
4
class Object
int main()
if( n < 12)
3
else
4
end
end
end
HERE
@expect = [Virtual::Return ]
@ -17,7 +21,11 @@ HERE
def test_return
@string_input = <<HERE
return 5
class Object
int main()
return 5
end
end
HERE
@expect = [Virtual::Return ]
check
@ -26,15 +34,19 @@ HERE
def test_if_function
@string_input = <<HERE
int itest(int n)
if( n < 12)
"then".putstring()
else
"else".putstring()
class Object
int itest(int n)
if( n < 12)
"then".putstring()
else
"else".putstring()
end
end
int main()
itest(20)
end
end
itest(20)
HERE
@expect = [Virtual::Return ]
check

View File

@ -5,7 +5,11 @@ class TestPutint < MiniTest::Test
def test_putint
@string_input = <<HERE
42.putint()
class Object
int main()
42.putint()
end
end
HERE
@expect = []
check

View File

@ -5,24 +5,29 @@ class TestRecursinveFibo < MiniTest::Test
def test_recursive_fibo
@string_input = <<HERE
int fib_print(int n)
int fib = fibonaccir( n )
fib.putint()
end
int fibonaccir( int n )
if( n <= 1 )
return n
else
int tmp
tmp = n - 1
int a = fibonaccir( tmp )
tmp = n - 2
int b = fibonaccir( tmp )
return a + b
class Integer
int fib_print(int n)
int fib = fibonaccir( n )
fib.putint()
end
int fibonaccir( int n )
if( n <= 1 )
return n
else
int tmp
tmp = n - 1
int a = fibonaccir( tmp )
tmp = n - 2
int b = fibonaccir( tmp )
return a + b
end
end
end
class Object
int main()
fib_print(10)
end
end
fib_print(10)
HERE
@expect = [Virtual::Return ]
check

View File

@ -5,20 +5,24 @@ class TestWhileFragment < MiniTest::Test
def test_while_fibo
@string_input = <<HERE
int fibonaccit(int n)
int a = 0
int b = 1
while( n > 1 )
int tmp = a
a = b
b = tmp + b
n = n - 1
end
b.putint()
return b
end
class Object
int fibonaccit(int n)
int a = 0
int b = 1
while( n > 1 )
int tmp = a
a = b
b = tmp + b
n = n - 1
end
b.putint()
return b
end
fibonaccit( 10 )
int main()
fibonaccit( 10 )
end
end
HERE
@expect = [Virtual::Return ]
check

View File

@ -3,14 +3,21 @@ require_relative "helper"
class AddTest < MiniTest::Test
include AST::Sexp
include Ticker
def setup
Virtual.machine.boot
code = s(:call,
s(:name, :plus),
s(:arguments , s(:int , 5)),
s(:receiver, s(:int, 2)))
Bosl::Compiler.compile( code , Virtual.machine.space.get_main )
code = s(:class, :Object,
s(:derives, nil),
s(:expressions,
s(:function, :int,
s(:name, :main),
s(:parameters),
s(:expressions,
s(:call,
s(:name, :plus),
s(:arguments , s(:int , 5)),
s(:receiver, s(:int, 2)))))))
Bosl::Compiler.compile( code )
Virtual.machine.run_before "Register::CallImplementation"
@interpreter = Interpreter::Interpreter.new
@interpreter.start Virtual.machine.init

View File

@ -2,6 +2,7 @@ require_relative "helper"
class AddTest < MiniTest::Test
include Ticker
include AST::Sexp
def test_puti
@string_input = <<HERE
@ -23,10 +24,19 @@ class Integer < Object
return add_string( start )
end
end
class Object
int main()
5.to_string()
end
end
HERE
expressions = Virtual.machine.boot.compile_main @string_input
puts expressions
Virtual.machine.boot
syntax = Parser::Salama.new.parse_with_debug(@string_input)
parts = Parser::Transform.new.apply(syntax)
puts parts.inspect
Bosl::Compiler.compile( parts )
# expressions = Virtual.machine.boot.parse_and_compile @string_input
# Bosl::Compiler.compile( expressions , Virtual.machine.space.get_main )
Virtual.machine.run_before "Register::CallImplementation"
@interpreter = Interpreter::Interpreter.new
@ -38,7 +48,7 @@ HERE
"LoadConstant" , "SetSlot" , "RegisterTransfer" , "GetSlot" , "FunctionCall" ,
"SaveReturn" , "GetSlot", "OperatorInstruction" , "RegisterTransfer" , "GetSlot" , "GetSlot" ,
"GetSlot" , "FunctionReturn" ,"RegisterTransfer" , "Syscall", "NilClass"].each_with_index do |name , index|
return if index == 10
return if index == 11
got = ticks(1)
puts got
assert got.class.name.index(name) , "Wrong class for #{index+1}, expect #{name} , got #{got}"

View File

@ -5,13 +5,20 @@ class TestPuts < MiniTest::Test
include Ticker
def setup
Virtual.machine.boot
code = s(:call,
s(:name, :putstring),
s(:arguments),
s(:receiver,
s(:string, "Hello again")))
code = s(:class, :Object,
s(:derives, nil),
s(:expressions,
s(:function, :int,
s(:name, :main),
s(:parameters),
s(:expressions,
s(:call,
s(:name, :putstring),
s(:arguments),
s(:receiver,
s(:string, "Hello again")))))))
Bosl::Compiler.compile( code , Virtual.machine.space.get_main )
Bosl::Compiler.compile( code )
Virtual.machine.run_before "Register::CallImplementation"
@interpreter = Interpreter::Interpreter.new
@interpreter.start Virtual.machine.init