first interpreted tests, fix branch issues

whole branch logic wobbly
better syntax needed, but working(ish) for now
This commit is contained in:
Torsten Ruger 2015-10-19 14:46:12 +03:00
parent d767caf479
commit bdcd0f297d
12 changed files with 99 additions and 123 deletions

View File

@ -6,30 +6,23 @@ module Interpreter
# fire events for changed pc and register contents # fire events for changed pc and register contents
include Eventable include Eventable
# current instruction or pc attr_reader :instruction # current instruction or pc
attr_reader :instruction attr_reader :clock # current instruction or pc
# current instruction or pc
attr_reader :clock
# an (arm style) link register. store the return address to return to # an (arm style) link register. store the return address to return to
attr_reader :link attr_reader :link
# current executing block. since this is not a hardware simulator this is luxury # current executing block. since this is not a hardware simulator this is luxury
attr_reader :block attr_reader :block
attr_reader :registers # the registers, 16 (a hash, sym -> contents)
# the registers, 16 attr_reader :stdout # collect the output
attr_reader :registers attr_reader :state # running etc
attr_reader :flags # somewhat like the lags on a cpu, hash sym => bool (zero .. . )
# collect the output
attr_reader :stdout
attr_reader :state
def initialize def initialize
@state = "runnnig" @state = "runnnig"
@stdout = "" @stdout = ""
@registers = {} @registers = {}
@flags = { :zero => false , :positive => false ,
:negative=> false , :overflow => false }
@clock = 0 @clock = 0
(0...12).each do |r| (0...12).each do |r|
set_register "r#{r}".to_sym , "r#{r}:unknown" set_register "r#{r}".to_sym , "r#{r}:unknown"
@ -65,6 +58,13 @@ module Interpreter
def set_register reg , val def set_register reg , val
old = get_register( reg ) # also ensures format old = get_register( reg ) # also ensures format
unless val.is_a? String
@flags[:zero] = (val == 0)
@flags[:positive] = (val > 0)
@flags[:negative] = (val < 0)
#puts "Set_flags #{val} :zero=#{@flags[:zero]}"
end
return if old === val return if old === val
reg = reg.symbol if reg.is_a? Register::RegisterValue reg = reg.symbol if reg.is_a? Register::RegisterValue
@registers[reg] = val @registers[reg] = val
@ -96,17 +96,21 @@ module Interpreter
end end
# Instruction interpretation starts here # Instruction interpretation starts here
def execute_Branch def execute_AlwaysBranch
target = @instruction.block target = @instruction.block
set_block target set_block target
false false
end end
def execute_IsZeroBranch def execute_IsZeroBranch
puts @instruction.inspect #puts @instruction.inspect
target = @instruction.block if( @flags[:zero] )
set_block target target = @instruction.block
false set_block target
return false
else
return true
end
end end
def execute_LoadConstant def execute_LoadConstant
@ -192,18 +196,19 @@ module Interpreter
case @instruction.operator.to_s case @instruction.operator.to_s
when "+" when "+"
result = left + right result = left + right
when "/"
result = left / right
when "-" when "-"
result = left - right result = left - right
when "<" when "/"
result = left < right result = left / right
when "*"
#TODO set overflow, reduce result to int
result = left * right
when "==" when "=="
result = left == right result = (left == right) ? 1 : 0
else else
raise "unimplemented '#{@instruction.operator}' #{@instruction}" raise "unimplemented '#{@instruction.operator}' #{@instruction}"
end end
#puts "#{@instruction} == #{result}" #puts "#{@instruction} == #{result} (#{left}|#{right})"
right = set_register(@instruction.left , result) right = set_register(@instruction.left , result)
true true
end end

View File

@ -11,9 +11,9 @@ module Register
attr_reader :block attr_reader :block
def to_s def to_s
"Branch: #{block.name}" "#{self.class.name}: #{block.name}"
end end
alias :inspect :to_s alias :inspect :to_s
end end
class IsZeroBranch < Branch class IsZeroBranch < Branch

View File

@ -118,7 +118,7 @@ module Virtual
def boot def boot
boot_parfait! boot_parfait!
@init = Block.new("init", :__init__ ) @init = Block.new("init", :__init__ )
branch = Register::Branch.new( "__init__" , self.space.get_init.source.blocks.first ) branch = Register::AlwaysBranch.new( "__init__" , self.space.get_init.source.blocks.first )
@init.add_code branch @init.add_code branch
@booted = true @booted = true
self self

View File

@ -1,17 +0,0 @@
module CodeChecker
def check
machine = Virtual.machine.boot
machine.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"
produced.blocks.each_with_index do |b,i|
codes = @output[i]
assert codes , "No codes for block #{i}"
assert_equal b.codes.length , codes.length , "Code length for block #{i}"
b.codes.each_with_index do |c , ii |
assert_equal codes[ii] , c.class , "Block #{i} , code #{ii}"
end
end
end
end

View File

@ -1,54 +1,31 @@
require_relative '../../helper' require_relative '../../helper'
require "interpreter/interpreter"
# Fragments are small programs that we run through the interpreter and really only check
# - the no. of instructions processed
# - the stdout output
# simple tests to check parsing pworks and the first classes come out right.
#
# build up from small to check larger statements are correct
module Fragments module Fragments
def setup
@stdout = ""
end
def check def check
machine = Virtual.machine.boot machine = Virtual.machine.boot
machine.parse_and_compile @string_input machine.parse_and_compile @string_input
produced = Virtual.machine.space.get_main.source machine.collect
assert @expect , "No output given" interpreter = Interpreter::Interpreter.new
assert_equal @expect.length , produced.blocks.length , "Block length" interpreter.start machine.init
produced.blocks.each_with_index do |b,i| count = 0
codes = @expect[i] begin
assert codes , "No codes for block #{i}" count += 1
assert_equal b.codes.length , codes.length , "Code length for block #{i+1}" #puts interpreter.instruction
b.codes.each_with_index do |c , ii | interpreter.tick
assert_equal codes[ii] , c.class , "Block #{i+1} , code #{ii+1}" end while( ! interpreter.instruction.nil?)
end assert_equal @length , count
end assert_equal @stdout , interpreter.stdout
end
# helper to write the file
def write name
writer = Elf::ObjectWriter.new(@object_machine , Elf::Constants::TARGET_ARM)
assembly = writer.text
writer.save("#{name}.o")
function = @object_machine.classes[@target[0]]
assert function , "no class #{@target[0]}"
function = function.get_function(@target[1])
assert function , "no function #{@target[1]} for class #{@target[0]}"
io = StringIO.new
function.assemble io
assembly = io.string
# use this for getting the bytes to compare to :
puts bytes(assembly)
assembly.bytes.each_with_index do |byte , index|
is = @should[index]
assert_equal Fixnum , is.class , "@#{index.to_s(16)} = #{is}"
assert_equal byte , is , "@#{index.to_s(16)} #{byte.to_s(16)} != #{is.to_s(16)}"
end
if( RbConfig::CONFIG["build_cpu"] == "arm")
system "ld -N #{name}.o"
result = %x[./a.out]
assert_equal @output , result
end
end
def bytes string
"[" + string.bytes.collect{|b| "0x"+ b.to_s(16)}.join(",") + "]"
end end
end end

View File

@ -1,9 +1,9 @@
require_relative "test_foo"
require_relative "test_if" require_relative "test_if"
require_relative "test_hello" #require_relative "test_foo"
require_relative "test_class" #require_relative "test_hello"
require_relative "test_putint" #require_relative "test_class"
require_relative "test_functions" #require_relative "test_putint"
require_relative "test_recursive_fibo" #require_relative "test_functions"
require_relative "test_while_fibo" #require_relative "test_recursive_fibo"
#require_relative "test_while_fibo"

View File

@ -8,7 +8,7 @@ class TestIf < MiniTest::Test
class Object class Object
int main() int main()
int n = 10 int n = 10
if( n < 12) if( n - 12)
return 3 return 3
else else
return 4 return 4
@ -16,30 +16,32 @@ class Object
end end
end end
HERE HERE
@expect = [[SaveReturn,Virtual::Set,Virtual::Set,Register::GetSlot, @length = 17
Register::GetSlot,Register::OperatorInstruction,Register::IsZeroBranch] ,
[Virtual::Set,Register::AlwaysBranch] ,[Virtual::Set] ,[] ,[RegisterTransfer,GetSlot,FunctionReturn] ]
check check
end end
def test_return def test_if_small
@string_input = <<HERE @string_input = <<HERE
class Object class Object
int main() int main()
return 5 int n = 10
if(8 - n )
"10".putstring()
end
end end
end end
HERE HERE
@expect = [[SaveReturn,Virtual::Set] , [RegisterTransfer,GetSlot,FunctionReturn]] @length = 33
@stdout = "10"
check check
end end
def test_if_function def test_if_puts
@string_input = <<HERE @string_input = <<HERE
class Object class Object
int itest(int n) int itest(int n)
if( n < 12) if( n - 12)
"then".putstring() "then".putstring()
else else
"else".putstring() "else".putstring()
@ -51,8 +53,8 @@ class Object
end end
end end
HERE HERE
@expect = [ [SaveReturn,Register::GetSlot,Virtual::Set,Virtual::Set, @length = 40
Virtual::Set,Virtual::Set,RegisterTransfer,FunctionCall] ,[RegisterTransfer,GetSlot,FunctionReturn] ] @stdout = "else"
check check
end end
end end

View File

@ -2,4 +2,4 @@ require_relative "expressions/test_all"
require_relative "statements/test_all" require_relative "statements/test_all"
#require_relative "fragments/test_all" require_relative "fragments/test_all"

View File

@ -16,7 +16,7 @@ module Ticker
error = nil error = nil
tick = 1 tick = 1
begin begin
while true and (classes.length < 100) while true and (classes.length < 200)
cl = ticks(1).class cl = ticks(1).class
tick += 1 tick += 1
classes << cl classes << cl

View File

@ -26,7 +26,7 @@ class AddTest < MiniTest::Test
def test_branch def test_branch
was = @interpreter.block was = @interpreter.block
assert_equal Register::Branch , ticks(1).class assert_equal Register::AlwaysBranch , ticks(1).class
assert was != @interpreter.block assert was != @interpreter.block
end end
def test_load def test_load
@ -66,7 +66,7 @@ class AddTest < MiniTest::Test
def test_chain def test_chain
#show_ticks # get output of what is #show_ticks # get output of what is
["Branch","LoadConstant","GetSlot","SetSlot","RegisterTransfer", ["AlwaysBranch","LoadConstant","GetSlot","SetSlot","RegisterTransfer",
"FunctionCall","SaveReturn","LoadConstant","LoadConstant","OperatorInstruction", "FunctionCall","SaveReturn","LoadConstant","LoadConstant","OperatorInstruction",
"RegisterTransfer","GetSlot","FunctionReturn","RegisterTransfer","Syscall", "RegisterTransfer","GetSlot","FunctionReturn","RegisterTransfer","Syscall",
"NilClass"].each_with_index do |name , index| "NilClass"].each_with_index do |name , index|

View File

@ -29,7 +29,7 @@ class Integer < Object
div = self / 10 div = self / 10
int rest int rest
rest = self - div rest = self - div
if( rest < 0) if( rest - 0)
rest = self.digit( rest ) rest = self.digit( rest )
str = str + rest str = str + rest
else else
@ -58,8 +58,8 @@ HERE
# Phisol::Compiler.compile( statements , Virtual.machine.space.get_main ) # Phisol::Compiler.compile( statements , Virtual.machine.space.get_main )
@interpreter = Interpreter::Interpreter.new @interpreter = Interpreter::Interpreter.new
@interpreter.start Virtual.machine.init @interpreter.start Virtual.machine.init
#show_ticks # get output of what is # show_ticks # get output of what is
["Branch","LoadConstant","GetSlot","SetSlot","RegisterTransfer", ["AlwaysBranch","LoadConstant","GetSlot","SetSlot","RegisterTransfer",
"FunctionCall","SaveReturn","GetSlot","LoadConstant","SetSlot", "FunctionCall","SaveReturn","GetSlot","LoadConstant","SetSlot",
"LoadConstant","SetSlot","RegisterTransfer","FunctionCall","SaveReturn", "LoadConstant","SetSlot","RegisterTransfer","FunctionCall","SaveReturn",
"LoadConstant","GetSlot","SetSlot","GetSlot","GetSlot", "LoadConstant","GetSlot","SetSlot","GetSlot","GetSlot",
@ -68,14 +68,23 @@ HERE
"LoadConstant","OperatorInstruction","GetSlot","SetSlot","GetSlot", "LoadConstant","OperatorInstruction","GetSlot","SetSlot","GetSlot",
"GetSlot","GetSlot","OperatorInstruction","GetSlot","SetSlot", "GetSlot","GetSlot","OperatorInstruction","GetSlot","SetSlot",
"GetSlot","GetSlot","LoadConstant","OperatorInstruction","IsZeroBranch", "GetSlot","GetSlot","LoadConstant","OperatorInstruction","IsZeroBranch",
"GetSlot","GetSlot","GetSlot","SetSlot","LoadConstant",
"SetSlot","GetSlot","SetSlot","RegisterTransfer","FunctionCall",
"SaveReturn","GetSlot","LoadConstant","OperatorInstruction","GetSlot",
"SetSlot","GetSlot","GetSlot","GetSlot","OperatorInstruction",
"GetSlot","SetSlot","GetSlot","GetSlot","LoadConstant",
"OperatorInstruction","IsZeroBranch","GetSlot","GetSlot","GetSlot",
"SetSlot","LoadConstant","SetSlot","GetSlot","SetSlot",
"RegisterTransfer","FunctionCall","SaveReturn","GetSlot","LoadConstant",
"OperatorInstruction","GetSlot","SetSlot","GetSlot","GetSlot",
"GetSlot","OperatorInstruction","GetSlot","SetSlot","GetSlot",
"GetSlot","LoadConstant","OperatorInstruction","IsZeroBranch","GetSlot",
"GetSlot","GetSlot","SetSlot","LoadConstant","SetSlot", "GetSlot","GetSlot","SetSlot","LoadConstant","SetSlot",
"GetSlot","GetSlot","SetSlot","RegisterTransfer","FunctionCall", "GetSlot","SetSlot","RegisterTransfer","FunctionCall","SaveReturn",
"SaveReturn","GetSlot","LoadConstant","OperatorInstruction","IsZeroBranch", "GetSlot","LoadConstant","OperatorInstruction","GetSlot","SetSlot",
"LoadConstant","GetSlot","LoadConstant","OperatorInstruction","IsZeroBranch", "GetSlot","GetSlot","GetSlot","OperatorInstruction","GetSlot",
"LoadConstant","GetSlot","LoadConstant","OperatorInstruction","IsZeroBranch", "SetSlot","GetSlot","GetSlot","LoadConstant","OperatorInstruction",
"LoadConstant","GetSlot","LoadConstant","OperatorInstruction","IsZeroBranch", "IsZeroBranch","GetSlot","GetSlot","GetSlot"].each_with_index do |name , index|
"LoadConstant","GetSlot","LoadConstant","OperatorInstruction","IsZeroBranch",
"LoadConstant","NilClass"].each_with_index do |name , index|
got = ticks(1) got = ticks(1)
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}"
end end

View File

@ -26,7 +26,7 @@ class TestPuts < MiniTest::Test
def test_branch def test_branch
was = @interpreter.block was = @interpreter.block
assert_equal Register::Branch , ticks(1).class assert_equal Register::AlwaysBranch , ticks(1).class
assert was != @interpreter.block assert was != @interpreter.block
end end
def test_load def test_load
@ -56,7 +56,7 @@ class TestPuts < MiniTest::Test
def test_chain def test_chain
#show_ticks # get output of what is #show_ticks # get output of what is
["Branch","LoadConstant","GetSlot","SetSlot","RegisterTransfer", ["AlwaysBranch","LoadConstant","GetSlot","SetSlot","RegisterTransfer",
"FunctionCall","SaveReturn","GetSlot","LoadConstant","SetSlot", "FunctionCall","SaveReturn","GetSlot","LoadConstant","SetSlot",
"LoadConstant","SetSlot","RegisterTransfer","FunctionCall","SaveReturn", "LoadConstant","SetSlot","RegisterTransfer","FunctionCall","SaveReturn",
"GetSlot","RegisterTransfer","Syscall","RegisterTransfer","RegisterTransfer", "GetSlot","RegisterTransfer","Syscall","RegisterTransfer","RegisterTransfer",