diff --git a/lua-temp/doc/OPCODES b/lua-temp/doc/OPCODES index 18338b3..c568e34 100644 --- a/lua-temp/doc/OPCODES +++ b/lua-temp/doc/OPCODES @@ -22,12 +22,13 @@ Stack operations: a0 c0 e0 pushi [int] Push int a1 c1 e1 pushc [index] Push constant a2 c2 e2 pushf [function] Push function id -00 pushz Push zero (or false) -01 pusht Push true -02 newa Push (create) empty array -03 newt Push (create) empty table -04 pop -05 dup +00 pushn Push nil +01 pushz Push zero (or false) +02 pusht Push true +03 newa Push (create) empty array +04 newt Push (create) empty table +05 pop +06 dup Local variables: a3 c3 e3 pushv [int] Push n nil values into the stack (used to init local vars) @@ -88,6 +89,8 @@ Control flow (the destination is always a 16-bit field): ca jmp [pc] Unconditional jump * Jumps can only happen within the same function. +Memory management: +4b gc Call garbage collector Error handling: (0xa0~0xaf) ??? diff --git a/lua-temp/tests.lua b/lua-temp/tests.lua index 4d51114..7c6ff6d 100755 --- a/lua-temp/tests.lua +++ b/lua-temp/tests.lua @@ -298,10 +298,47 @@ end do TEST "VM: managed strings" local vm = VM.new():push_string("Hello") + -- print(vm:debug_heap()) assert_eq(vm:to_string(-1), "Hello") end --- TODO - concatenate strings +do TEST "VM: concatenate strings" + local vm = VM.new():load(assemble [[ + .const + 0: "Hello " + 1: "world" + .func 0 + pushc 0 + pushc 1 + sum + ret + ]]):call(0) + + -- print(vm:debug_heap()) + assert_eq(vm:to_string(-1), "Hello world") + assert_eq(vm.heap:size(), 1) +end + +do TEST "VM: collect strings" + local vm = VM.new():load(assemble [[ + .const + 0: "Hello " + 1: "world" + .func 0 + pushn + pushc 0 + pushc 1 + sum + pop + gc + ret + ]]):call(0) + + print(vm:debug_heap()) + assert_eq(vm:is(-1, 'nil'), true) + assert_eq(vm.heap:size(), 0) +end + -- TODO - collect string (GC) diff --git a/lua-temp/tyche-vm.lua b/lua-temp/tyche-vm.lua index 1536e3b..86380cd 100644 --- a/lua-temp/tyche-vm.lua +++ b/lua-temp/tyche-vm.lua @@ -185,24 +185,25 @@ for op,_ in pairs(ARITH_LOGIC_OPS) do end end -EXPR.sum.integer.integer = function(vm, b, a) vm:push_integer(a + b) end -EXPR.sub.integer.integer = function(vm, b, a) vm:push_integer(a - b) end -EXPR.mul.integer.integer = function(vm, b, a) vm:push_integer(a * b) end +EXPR.sum.integer.integer = function(vm, b, a) vm:push_integer(a.value + b.value) end +EXPR.sum.string.string = function(vm, b, a) vm:push_string(vm:_extract_string(a) ..vm:_extract_string(b)) end +EXPR.sub.integer.integer = function(vm, b, a) vm:push_integer(a.value - b.value) end +EXPR.mul.integer.integer = function(vm, b, a) vm:push_integer(a.value * b.value) end -- TODO - div -EXPR.idiv.integer.integer = function(vm, b, a) vm:push_integer(math.floor(a / b)) end -EXPR.mod.integer.integer = function(vm, b, a) vm:push_integer(a % b) end -EXPR.eq.integer.integer = function(vm, b, a) vm:push_integer((a == b) and 1 or 0) end -EXPR.neq.integer.integer = function(vm, b, a) vm:push_integer((a ~= b) and 1 or 0) end -EXPR.lt.integer.integer = function(vm, b, a) vm:push_integer((a < b) and 1 or 0) end -EXPR.lte.integer.integer = function(vm, b, a) vm:push_integer((a <= b) and 1 or 0) end -EXPR.gt.integer.integer = function(vm, b, a) vm:push_integer((a > b) and 1 or 0) end -EXPR.gte.integer.integer = function(vm, b, a) vm:push_integer((a >= b) and 1 or 0) end -EXPR['and'].integer.integer = function(vm, b, a) vm:push_integer(a & b) end -EXPR['or'].integer.integer = function(vm, b, a) vm:push_integer(a | b) end -EXPR.xor.integer.integer = function(vm, b, a) vm:push_integer(a ~ b) end -EXPR.pow.integer.integer = function(vm, b, a) vm:push_integer(a ^ b) end -EXPR.shl.integer.integer = function(vm, b, a) vm:push_integer(a << b) end -EXPR.shr.integer.integer = function(vm, b, a) vm:push_integer(a >> b) end +EXPR.idiv.integer.integer = function(vm, b, a) vm:push_integer(math.floor(a.value / b.value)) end +EXPR.mod.integer.integer = function(vm, b, a) vm:push_integer(a.value % b.value) end +EXPR.eq.integer.integer = function(vm, b, a) vm:push_integer((a.value == b.value) and 1 or 0) end +EXPR.neq.integer.integer = function(vm, b, a) vm:push_integer((a.value ~= b.value) and 1 or 0) end +EXPR.lt.integer.integer = function(vm, b, a) vm:push_integer((a.value < b.value) and 1 or 0) end +EXPR.lte.integer.integer = function(vm, b, a) vm:push_integer((a.value <= b.value) and 1 or 0) end +EXPR.gt.integer.integer = function(vm, b, a) vm:push_integer((a.value > b.value) and 1 or 0) end +EXPR.gte.integer.integer = function(vm, b, a) vm:push_integer((a.value >= b.value) and 1 or 0) end +EXPR['and'].integer.integer = function(vm, b, a) vm:push_integer(a.value & b.value) end +EXPR['or'].integer.integer = function(vm, b, a) vm:push_integer(a.value | b.value) end +EXPR.xor.integer.integer = function(vm, b, a) vm:push_integer(a.value ~ b.value) end +EXPR.pow.integer.integer = function(vm, b, a) vm:push_integer(a.value ^ b.value) end +EXPR.shl.integer.integer = function(vm, b, a) vm:push_integer(a.value << b.value) end +EXPR.shr.integer.integer = function(vm, b, a) vm:push_integer(a.value >> b.value) end ---------------------- -- -- @@ -230,6 +231,16 @@ function Heap:get_value(key) return self.items[key] end +function Heap:size() + local n = 0 + for _ in pairs(self.items) do n = n + 1 end + return n +end + +function Heap:call_gc() + -- TODO +end + ---------------------- -- -- -- VM -- @@ -301,23 +312,29 @@ function VM:to_integer(idx) return value.value end -function VM:to_string(idx) - local value = self.stack[idx] - if value.type ~= 'string' then error("Type error: not a string") end +function VM:_extract_string(value) + assert(value) + assert(value.type == 'string') if value.const_ref then return self.code.bytecode.constants[value.const_ref] elseif value.ref then return self.heap:get_value(value.ref) else - error() + error("Incorrect string value (nor 'const_ref' or 'ref')") end end +function VM:to_string(idx) + local value = self.stack[idx] + if value.type ~= 'string' then error("Type error: not a string") end + return self:_extract_string(value) +end + function VM:format_value(v) if v.type == 'integer' or v.type == 'real' then return tostring(v.value) elseif v.type == 'string' then - return '"' .. v.value .. '"' + return self_:extract_string(v) elseif v.type == 'function' then return '@' .. tostring(v.value) elseif v.type == 'nil' then @@ -340,6 +357,19 @@ function VM:debug_stack() return table.concat(ss) end +function VM:debug_heap() + local ss = { "Heap:\n" } + for k,v in pairs(self.heap.items) do + table.insert(ss, string.format(" [%X] = ", k)) + if type(v) == 'string' then + table.insert(ss, '"' .. v .. '"') + else + error('Unsupported value in heap') + end + table.insert(ss, "\n") + end + return table.concat(ss) +end -- -- code execution @@ -397,8 +427,11 @@ function VM:_step() -- -- stack operations -- + + if op.operator == 'pushn' then + self:push_nil() - if op.operator == 'pushi' then + elseif op.operator == 'pushi' then self:push_integer(op.operand) elseif op.operator == 'pushf' then @@ -413,6 +446,9 @@ function VM:_step() error('REAL consts not supported for now.') end + elseif op.operator == 'pop' then + self.stack:pop() + elseif op.operator == 'dup' then self.stack:push(self.stack:peek()) @@ -443,7 +479,7 @@ function VM:_step() elseif ARITH_LOGIC_OPS[op.operator] then local a = self.stack:pop() local b = self.stack:pop() - EXPR[op.operator][a.type][b.type](self, a.value, b.value) + EXPR[op.operator][a.type][b.type](self, a, b) -- -- function management @@ -486,6 +522,13 @@ function VM:_step() return end + -- + -- memory management + -- + + elseif op.operator == 'gc' then + self.heap:call_gc() + -- -- instruction not found --