first interpreted tests, fix branch issues
whole branch logic wobbly better syntax needed, but working(ish) for now
This commit is contained in:
parent
d767caf479
commit
bdcd0f297d
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
|
@ -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|
|
||||||
|
@ -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
|
||||||
|
@ -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",
|
||||||
|
Loading…
Reference in New Issue
Block a user