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 module Bosl
class Compiler < AST::Processor class Compiler < AST::Processor
attr_reader :method
def initialize(method) def initialize()
@method = method
end end
def handler_missing node def handler_missing node
raise "No handler on_#{node.type}(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, # This makes the dispatch extensible, ie Expressions may be added by external code,
# as long as matching compile methods are supplied too. # as long as matching compile methods are supplied too.
# #
def self.compile expression , method def self.compile expression
compiler = Compiler.new method compiler = Compiler.new
compiler.process expression compiler.process expression
end 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 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. 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. 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, Instructions are stored as a list of Blocks, and Blocks are the smallest unit of code,
which is always linear. 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. and working on means, these are the only objects which the machine accesses.
Ie all others would have to be moved first. 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. When a Method needs to make a call, it creates a NewMessage object.
Messages contain return addresses and arguments. Messages contain return addresses (yes, plural) 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.
The important thing here is that Messages and Frames are normal objects. 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 ### Distinclty future proof
down transformation. Instead the sending goes back up and then down again.
The Message object is the second parameter to the compile method, the run-time part as it were. Bosl is designed to be used as an implementation language for a higher oo language. Some, or
Why? Since it only exists at runtime: to make compile time analysis possible even many, features may not make sense on their own. But these features, like several return
(it is after all the Virtual version, not Parfait. ie compile-time, not run-time). addresses are important to implement the higher language.
Especially for those times when we can resolve the method at compile time.
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,
As ruby is a dynamic language, it also compiles at run-time. This line of thought does not help we just need it to create our layers.
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.

View File

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

View File

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

View File

@ -5,7 +5,8 @@ module Bosl
#puts expression.inspect #puts expression.inspect
type , name , value = *expression 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) index = for_class.object_layout.variable_index(name)
#raise "class field already defined:#{name} for class #{for_class.name}" if index #raise "class field already defined:#{name} for class #{for_class.name}" if index
puts "Define field #{name} on class #{for_class.name}" puts "Define field #{name} on class #{for_class.name}"

View File

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

View File

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

View File

@ -1,6 +1,6 @@
module Bosl module Bosl
Compiler.class_eval do Compiler.class_eval do
# function attr_reader :name, :params, :body , :receiver
def on_function expression def on_function expression
#puts expression.inspect #puts expression.inspect
return_type , name , parameters, kids , receiver = *expression return_type , name , parameters, kids , receiver = *expression
@ -24,23 +24,30 @@ module Bosl
end end
end end
else else
r = Virtual::Self.new(:int) r = @clazz
class_name = method.for_class.name class_name = @clazz.name
end end
new_method = Virtual::MethodSource.create_method(class_name, return_type, name , args ) raise "Already in method #{@method}" if @method
new_method.source.receiver = r @method = @clazz.get_instance_method( name )
new_method.for_class.add_instance_method new_method 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 #frame = frame.new_frame
kids.to_a.each do |ex| kids.to_a.each do |ex|
return_type = process(ex) return_type = process(ex)
raise return_type.inspect if return_type.is_a? Virtual::Instruction raise return_type.inspect if return_type.is_a? Virtual::Instruction
end end
@method = old_method @method.source.return_type = return_type
new_method.source.return_type = return_type @method = nil
Virtual::Return.new(return_type) Virtual::Return.new(return_type)
end end
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 # 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 , # so we can the jump over the else if true ,
# and the else joins unconditionally after the true_block # and the else joins unconditionally after the true_block
merge_block = method.source.new_block "if_merge" # last one, created first 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 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 false_block = @method.source.new_block "if_false" # directly next in order, ie if we don't jump we land here
is = process(condition ) is = process(condition )
# TODO should/will use different branches for different conditions. # 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 # 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) # 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 last = process_all(if_true).last
# compile the false block # compile the false block
method.source.current false_block @method.source.current false_block
last = process_all(if_false).last if if_false 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" #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 #TODO should return the union of the true and false types
last last

View File

@ -9,10 +9,11 @@ module Bosl
def on_class expression def on_class expression
#puts expression.inspect #puts expression.inspect
name , derives , expressions = *expression name , derives , expressions = *expression
clazz = Parfait::Space.object_space.get_class_by_name! name raise "classes dont yet play babushka, get coding #{name}" if @clazz
#puts "Compiling class #{clazz.name.inspect}" @clazz = Parfait::Space.object_space.get_class_by_name! name
puts "Compiling class #{@clazz.name.inspect}"
expression_value = process_all(expressions).last expression_value = process_all(expressions).last
@clazz = nil
return expression_value return expression_value
end end
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) # whichever way this goes the result is stored in the return slot (as all compiles)
def on_name expression def on_name expression
name = expression.to_a.first 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 # either an argument, so it's stored in message
ret = Virtual::Return.new :int ret = Virtual::Return.new :int
if( index = method.has_arg(name)) if( index = @method.has_arg(name))
method.source.add_code Virtual::Set.new( Virtual::ArgSlot.new(index,:int ) , ret) @method.source.add_code Virtual::Set.new( Virtual::ArgSlot.new(index,:int ) , ret)
else # or a local so it is in the frame else # or a local so it is in the frame
index = method.has_local( name ) index = @method.has_local( name )
if(index) 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 else
raise "must define variable #{name} before using it" raise "must define variable #{name} before using it"
end end

View File

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

View File

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

View File

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

View File

@ -32,6 +32,7 @@ module Parfait
message = Message.new(nil) message = Message.new(nil)
5.times do 5.times do
self.first_message = Message.new message 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.set_caller self.first_message
message = self.first_message message = self.first_message
end end

View File

@ -111,7 +111,7 @@ module Register
begin begin
code.assemble( stream ) code.assemble( stream )
rescue => e 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) puts Sof.write(code)
raise e raise e
end end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,15 @@ require_relative "compiler_helper"
class TestBasic < MiniTest::Test class TestBasic < MiniTest::Test
def check 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 ) if( expressions.first.is_a? Virtual::Self )
expressions.first.type.instance_variable_set :@of_class , nil expressions.first.type.instance_variable_set :@of_class , nil
end end

View File

@ -7,7 +7,7 @@ class CompilerTest < MiniTest::Test
Virtual.machine.boot Virtual.machine.boot
end end
def check 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}" assert res.is_a?(Virtual::Slot) , "compiler must compile to slot, not #{res.inspect}"
end end
def ttest_if_expression def ttest_if_expression
@ -21,9 +21,12 @@ class CompilerTest < MiniTest::Test
check check
end end
def test_function_expression def test_function_expression
@expression = s(:function, :int, s(:name, :foo), @expression = s(:class, :Foo,
s(:derives, :Object),
s(:expressions,
s(:function, :int, s(:name, :foo),
s(:parameters, s(:parameter, :ref, :x)), s(:parameters, s(:parameter, :ref, :x)),
s(:expressions, s(:int, 5))) s(:expressions, s(:int, 5)))))
check check
end end
end end

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@ require_relative '../helper'
module Fragments module Fragments
def check 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 | @expect.each_with_index do | should , i |
exp_i = expressions[i] exp_i = expressions[i]
assert exp_i.is_a?(Virtual::Slot) , "compiles should return #{should}, not #{exp_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 def test_foo2
@string_input = <<HERE @string_input = <<HERE
int foo(int x) class Object
int foo(int x)
int a = 5 int a = 5
return a return a
end
int main()
foo( 4 )
end
end end
foo( 4 )
HERE HERE
@expect = [ Virtual::Return ] @expect = [ Virtual::Return ]
check check

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,11 +6,18 @@ class AddTest < MiniTest::Test
def setup def setup
Virtual.machine.boot Virtual.machine.boot
code = s(:call, 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(:name, :plus),
s(:arguments , s(:int , 5)), s(:arguments , s(:int , 5)),
s(:receiver, s(:int, 2))) s(:receiver, s(:int, 2)))))))
Bosl::Compiler.compile( code , Virtual.machine.space.get_main ) Bosl::Compiler.compile( code )
Virtual.machine.run_before "Register::CallImplementation" Virtual.machine.run_before "Register::CallImplementation"
@interpreter = Interpreter::Interpreter.new @interpreter = Interpreter::Interpreter.new
@interpreter.start Virtual.machine.init @interpreter.start Virtual.machine.init

View File

@ -2,6 +2,7 @@ require_relative "helper"
class AddTest < MiniTest::Test class AddTest < MiniTest::Test
include Ticker include Ticker
include AST::Sexp
def test_puti def test_puti
@string_input = <<HERE @string_input = <<HERE
@ -23,10 +24,19 @@ class Integer < Object
return add_string( start ) return add_string( start )
end end
end end
class Object
int main()
5.to_string()
end
end
HERE HERE
expressions = Virtual.machine.boot.compile_main @string_input Virtual.machine.boot
puts expressions 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 ) # Bosl::Compiler.compile( expressions , Virtual.machine.space.get_main )
Virtual.machine.run_before "Register::CallImplementation" Virtual.machine.run_before "Register::CallImplementation"
@interpreter = Interpreter::Interpreter.new @interpreter = Interpreter::Interpreter.new
@ -38,7 +48,7 @@ HERE
"LoadConstant" , "SetSlot" , "RegisterTransfer" , "GetSlot" , "FunctionCall" , "LoadConstant" , "SetSlot" , "RegisterTransfer" , "GetSlot" , "FunctionCall" ,
"SaveReturn" , "GetSlot", "OperatorInstruction" , "RegisterTransfer" , "GetSlot" , "GetSlot" , "SaveReturn" , "GetSlot", "OperatorInstruction" , "RegisterTransfer" , "GetSlot" , "GetSlot" ,
"GetSlot" , "FunctionReturn" ,"RegisterTransfer" , "Syscall", "NilClass"].each_with_index do |name , index| "GetSlot" , "FunctionReturn" ,"RegisterTransfer" , "Syscall", "NilClass"].each_with_index do |name , index|
return if index == 10 return if index == 11
got = ticks(1) got = ticks(1)
puts got puts got
assert got.class.name.index(name) , "Wrong class for #{index+1}, expect #{name} , got #{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 include Ticker
def setup def setup
Virtual.machine.boot Virtual.machine.boot
code = s(:call, 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(:name, :putstring),
s(:arguments), s(:arguments),
s(:receiver, s(:receiver,
s(:string, "Hello again"))) s(:string, "Hello again")))))))
Bosl::Compiler.compile( code , Virtual.machine.space.get_main ) Bosl::Compiler.compile( code )
Virtual.machine.run_before "Register::CallImplementation" Virtual.machine.run_before "Register::CallImplementation"
@interpreter = Interpreter::Interpreter.new @interpreter = Interpreter::Interpreter.new
@interpreter.start Virtual.machine.init @interpreter.start Virtual.machine.init