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