This commit is contained in:
2026-05-09 09:24:46 -05:00
parent 19b51fcaa0
commit a6adb9b723
2 changed files with 82 additions and 5 deletions

View File

@@ -242,8 +242,7 @@ do TEST "VM: functions"
assert_eq(vm:to_integer(-1), -1) assert_eq(vm:to_integer(-1), -1)
end end
do do TEST "VM: jumps (jmp + bnz)"
TEST "VM: jumps (jmp + bnz)"
local vm = VM.new():load(assemble [[ local vm = VM.new():load(assemble [[
.func 0 .func 0
jmp @x1 jmp @x1
@@ -264,9 +263,8 @@ do
assert_eq(vm:to_integer(-1), 6) assert_eq(vm:to_integer(-1), 6)
end end
do do TEST "VM: jumps (bz)"
TEST "VM: jumps (bz)" local vm = VM.new():load(assemble [[
local vm = VM.new():set_debug(true):load(assemble [[
.func 0 .func 0
jmp @x1 jmp @x1
pushi 5 pushi 5
@@ -286,5 +284,25 @@ do
assert_eq(vm:to_integer(-1), 7) assert_eq(vm:to_integer(-1), 7)
end end
do TEST "VM: string from const"
local vm = VM.new():load(assemble [[
.const
0: "Hello"
.func 0
pushc 0
ret
]]):call(0)
assert_eq(vm:to_string(-1), "Hello")
end
do TEST "VM: managed strings"
local vm = VM.new():push_string("Hello")
assert_eq(vm:to_string(-1), "Hello")
end
-- TODO - concatenate strings
-- TODO - collect string (GC)
print('End.') print('End.')

View File

@@ -8,6 +8,8 @@ local ARITH_LOGIC_OPS = {
['and']=true, ['or']=true, xor=true, pow=true, shl=true, shr=true, mod=true ['and']=true, ['or']=true, xor=true, pow=true, shl=true, shr=true, mod=true
} }
math.randomseed(os.time())
---------------------- ----------------------
-- -- -- --
-- UTIL -- -- UTIL --
@@ -25,6 +27,8 @@ local function validate_value(v)
assert(type(v.value) == 'number') assert(type(v.value) == 'number')
elseif v.type == 'function' then elseif v.type == 'function' then
assert(type(v.value) == 'number' and v.value >= 0, "function must be a positive number") assert(type(v.value) == 'number' and v.value >= 0, "function must be a positive number")
elseif v.type == 'string' then
assert(type(v.ref) == 'number' or type(v.const_ref) == 'number')
end end
end end
@@ -200,6 +204,32 @@ 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.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.shr.integer.integer = function(vm, b, a) vm:push_integer(a >> b) end
----------------------
-- --
-- HEAP --
-- --
----------------------
local Heap = {}
Heap.__index = Heap
function Heap.new()
return setmetatable({
items = {}
}, Heap)
end
function Heap:add_value(value)
local key = math.random(math.mininteger, math.maxinteger)
while self.items[key] do key = math.random(math.mininteger, math.maxinteger) end
self.items[key] = value
return key
end
function Heap:get_value(key)
return self.items[key]
end
---------------------- ----------------------
-- -- -- --
-- VM -- -- VM --
@@ -212,6 +242,7 @@ VM.__index = VM
function VM.new() function VM.new()
return setmetatable({ return setmetatable({
stack = Stack.new(), stack = Stack.new(),
heap = Heap.new(),
code = Code.new(), code = Code.new(),
loc = {}, loc = {},
debug = false, debug = false,
@@ -239,10 +270,17 @@ end
function VM:push_integer(n) function VM:push_integer(n)
self.stack:push({ type = 'integer', value = n }) self.stack:push({ type = 'integer', value = n })
return self
end
function VM:push_string(str)
self.stack:push({ type = 'string', ref = self.heap:add_value(str) })
return self
end end
function VM:push_nil() function VM:push_nil()
self.stack:push({ type = 'nil' }) self.stack:push({ type = 'nil' })
return self
end end
-- --
@@ -263,6 +301,18 @@ function VM:to_integer(idx)
return value.value return value.value
end end
function VM:to_string(idx)
local value = self.stack[idx]
if value.type ~= 'string' then error("Type error: not a string") end
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()
end
end
function VM:format_value(v) function VM:format_value(v)
if v.type == 'integer' or v.type == 'real' then if v.type == 'integer' or v.type == 'real' then
return tostring(v.value) return tostring(v.value)
@@ -273,6 +323,7 @@ function VM:format_value(v)
elseif v.type == 'nil' then elseif v.type == 'nil' then
return 'nil' return 'nil'
else else
print('warning: cannot convert from type ' .. v.type)
return pprint.pformat(v) return pprint.pformat(v)
end end
end end
@@ -354,6 +405,14 @@ function VM:_step()
assert(op.operand >= 0) assert(op.operand >= 0)
self.stack:push({ type = 'function', value = op.operand }) self.stack:push({ type = 'function', value = op.operand })
elseif op.operator == 'pushc' then
local c = self.code.bytecode.constants[op.operand]
if type(c) == 'string' then
self.stack:push({ type = 'string', const_ref = op.operand })
elseif type(c) == 'number' then
error('REAL consts not supported for now.')
end
elseif op.operator == 'dup' then elseif op.operator == 'dup' then
self.stack:push(self.stack:peek()) self.stack:push(self.stack:peek())