diff --git a/.idea/misc.xml b/.idea/misc.xml
index 0b76fe5..1dc917f 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,5 +3,7 @@
-
+
+
+
\ No newline at end of file
diff --git a/.idea/tyche.iml b/.idea/tyche.iml
deleted file mode 100644
index 4c94235..0000000
--- a/.idea/tyche.iml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/lua-temp/tests.lua b/lua-temp/tests.lua
index 4cec26e..586fd4b 100644
--- a/lua-temp/tests.lua
+++ b/lua-temp/tests.lua
@@ -152,7 +152,7 @@ end
----------------------
local function arith(a, b, op)
- return VM:new():load(assemble(string.format([[
+ return VM.new():load(assemble(string.format([[
.func 0
pushi %d
pushi %d
@@ -163,7 +163,7 @@ end
do TEST "VM: basic"
- local vm = VM:new()
+ local vm = VM.new()
-- vm.debug = true
local bytecode = assemble [[
.func 0
@@ -207,7 +207,7 @@ do TEST "VM: logic/arithmetic"
end
do TEST "VM: local variables"
- local vm = VM:new():load(assemble([[
+ local vm = VM.new():load(assemble([[
.func 0
pushv 2 ; local a, b
pushi 3 ; a = 3
@@ -223,7 +223,7 @@ do TEST "VM: local variables"
end
do TEST "VM: functions"
- local vm = VM:new():load(assemble([[
+ local vm = VM.new():load(assemble([[
.func 0
pushf 1
pushi 2
@@ -241,4 +241,63 @@ do TEST "VM: functions"
assert_eq(vm:to_integer(-1), -1)
end
+do
+ TEST "VM: jumps (jmp + bnz)"
+ local vm = VM.new():load(assemble [[
+ .func 0
+ jmp @x1
+ pushi 5
+ @x1:
+ pushi 1
+ bnz @x2
+ bz @x3
+ @x2:
+ pushi 6
+ ret
+ @x3:
+ pushi 7
+ ret
+ ]]):call(0)
+
+ assert_eq(vm:to_integer(-1), 6)
+end
+
+do
+ TEST "VM: jumps (bz)"
+ pprint(assemble [[
+ .func 0
+ jmp @x1
+ pushi 5
+ @x1:
+ pushi 0
+ bnz @x2
+ pushi 0
+ bz @x3
+ @x2:
+ pushi 6
+ ret
+ @x3:
+ pushi 7
+ ret
+ ]])
+ local vm = VM.new():set_debug(true):load(assemble [[
+ .func 0
+ jmp @x1
+ pushi 5
+ @x1:
+ pushi 0
+ bnz @x2
+ bz @x3
+ @x2:
+ pushi 6
+ ret
+ @x3:
+ pushi 7
+ ret
+ ]]):call(0)
+
+ assert_eq(vm:to_integer(-1), 7)
+end
+
+
print('End.')
\ No newline at end of file
diff --git a/lua-temp/tyche-as.lua b/lua-temp/tyche-as.lua
index e481b32..b62b9c2 100644
--- a/lua-temp/tyche-as.lua
+++ b/lua-temp/tyche-as.lua
@@ -37,10 +37,10 @@ local function assemble(source)
end
elseif section == 'function' then
local regexes = {
- "^%s*(%a+)%s+(%d+)%s*$", -- instruction + parameter
- "^%s*(%a+)%s+(@[%a_]+)%s*$", -- instruction + label
- "^%s*(%a+)%s*$", -- instruction only
- "^(@[%a_]+):%s*$", -- label
+ "^%s*(%a+)%s+(%d+)%s*$", -- instruction + parameter
+ "^%s*(%a+)%s+(@[%a_][%a%d_]*)%s*$", -- instruction + label
+ "^%s*(%a+)%s*$", -- instruction only
+ "^(@[%a_][%a%d_]*):%s*$", -- label
}
local match = false
for i,regex in ipairs(regexes) do
diff --git a/lua-temp/tyche-vm.lua b/lua-temp/tyche-vm.lua
index aa7cebf..911f686 100644
--- a/lua-temp/tyche-vm.lua
+++ b/lua-temp/tyche-vm.lua
@@ -14,7 +14,7 @@ local ARITH_LOGIC_OPS = {
-- --
----------------------
-function format_value(v)
+local function format_value(v)
if v.type == 'integer' or v.type == 'real' then
return tostring(v.value)
elseif v.type == 'string' then
@@ -28,9 +28,10 @@ function format_value(v)
end
end
-function validate_value(v)
+local function validate_value(v)
assert(v, "value cannot be nil")
- assert(type(v) == 'table', "invalid value format (expected { type='...', value=... }), received: " .. pprint.pformat(value))
+ assert(type(v) == 'table',
+ "invalid value format (expected { type='...', value=... }), received: " .. pprint.pformat(v))
assert(TYPE_MAP[v.type], "missing field 'type' in value")
if v.type == 'nil' then
assert(v.value == nil)
@@ -41,6 +42,12 @@ function validate_value(v)
end
end
+function is_zero(v)
+ if v.type == 'nil' then return true end
+ if v.type == 'integer' and v.value == 0 then return true end
+ return false
+end
+
----------------------
-- --
-- STACK --
@@ -168,6 +175,18 @@ function Code:next_instruction(function_id, pc)
}
end
+function Code:find_label(function_id, label)
+ for pc, op in ipairs(self.bytecode.functions[function_id]) do
+ if op.labels then
+ for _,lbl in ipairs(op.labels) do
+ if lbl == label then
+ return pc
+ end
+ end
+ end
+ end
+end
+
----------------------
-- --
-- EXPR --
@@ -182,7 +201,7 @@ for op,_ in pairs(ARITH_LOGIC_OPS) do
for _,type1 in ipairs(TYPES) do
EXPR[op][type1] = {}
for _,type2 in ipairs(TYPES) do
- EXPR[op][type1][type2] = function(vm, a, b) error(string.format("Type mismatch for operation '%s': types '%s' and '%s'", op, type1, type2)) end
+ EXPR[op][type1][type2] = function(_, _, _) error(string.format("Type mismatch for operation '%s': types '%s' and '%s'", op, type1, type2)) end
end
end
end
@@ -310,6 +329,12 @@ function VM:_run_until_return()
end
end
+function VM:_debug_stack()
+ if self.debug then
+ print(self.stack:debug())
+ end
+end
+
function VM:_step()
local loc = self.loc[#self.loc]
local op = self.code:next_instruction(loc.f_id, loc.pc)
@@ -327,13 +352,16 @@ function VM:_step()
assert(op.operand >= 0)
self.stack:push({ type = 'function', value = op.operand })
+ elseif op.operator == 'dup' then
+ self.stack:push(self.stack:peek())
+
--
-- local variables
--
elseif op.operator == 'pushv' then
assert(op.operand >= 0)
- for i=1,op.operand do
+ for _=1,op.operand do
self:push_nil()
end
@@ -369,14 +397,43 @@ function VM:_step()
self.stack:pop_fp()
self.stack:push(v)
table.remove(self.loc)
- if self.debug then print(self.stack:debug()) end
+ self:_debug_stack()
return
- else
+ --
+ -- jumps/branching
+ --
+
+ elseif op.operator == 'jmp' then
+ loc.pc = self.code:find_label(loc.f_id, op.operand)
+ self:_debug_stack()
+ return
+
+ elseif op.operator == 'bz' then
+ local v = self.stack:pop()
+ if is_zero(v) then
+ loc.pc = self.code:find_label(loc.f_id, op.operand)
+ self:_debug_stack()
+ return
+ end
+
+ elseif op.operator == 'bnz' then
+ local v = self.stack:pop()
+ if not is_zero(v) then
+ loc.pc = self.code:find_label(loc.f_id, op.operand)
+ self:_debug_stack()
+ return
+ end
+
+ --
+ -- instruction not found
+ --
+
+ else
error("Unknown operator '" .. tostring(op.operator) .. "'")
end
- if self.debug then print(self.stack:debug()) end
+ self:_debug_stack()
loc.pc = loc.pc + op.instruction_size
end