Compare commits
13 Commits
04b9821662
...
lua-temp
| Author | SHA1 | Date | |
|---|---|---|---|
| 9561d5cacd | |||
| 0116a214f6 | |||
| a70abe76ad | |||
| e2f930641e | |||
| 85443ded9d | |||
| 6762c49ed3 | |||
| 610491c1d7 | |||
| 554a7b55c5 | |||
| 8a26ba5351 | |||
| 83b80f6e7d | |||
| a6adb9b723 | |||
| 19b51fcaa0 | |||
| 27164aaac3 |
2
.idea/tyche.iml
generated
Normal file
2
.idea/tyche.iml
generated
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module classpath="CIDR" type="CPP_MODULE" version="4" />
|
||||||
@@ -11,11 +11,11 @@ Progress of the Lua port:
|
|||||||
- [x] Control flow
|
- [x] Control flow
|
||||||
- [x] Labels in Assembly
|
- [x] Labels in Assembly
|
||||||
- [x] Recursion
|
- [x] Recursion
|
||||||
- [ ] Strings
|
- [x] Strings
|
||||||
- [ ] From constants
|
- [x] From constants
|
||||||
- [ ] Garbage collection
|
- [x] Garbage collection
|
||||||
- [ ] Arrays
|
- [x] Arrays
|
||||||
- [ ] Garbage collection
|
- [x] Garbage collection
|
||||||
- [ ] Tables
|
- [ ] Tables
|
||||||
- [ ] Garbage collection
|
- [ ] Garbage collection
|
||||||
- [ ] Metatables
|
- [ ] Metatables
|
||||||
@@ -28,3 +28,10 @@ Progress of the Lua port:
|
|||||||
|
|
||||||
- [ ] Assembler generate bytecode
|
- [ ] Assembler generate bytecode
|
||||||
- [ ] VM interpret it
|
- [ ] VM interpret it
|
||||||
|
|
||||||
|
|
||||||
|
## C interface
|
||||||
|
|
||||||
|
- [ ] Error management (decision)
|
||||||
|
- [ ] Format for value and heap value
|
||||||
|
- [ ] Transparency and log levels
|
||||||
|
|||||||
@@ -22,12 +22,13 @@ Stack operations:
|
|||||||
a0 c0 e0 pushi [int] Push int
|
a0 c0 e0 pushi [int] Push int
|
||||||
a1 c1 e1 pushc [index] Push constant
|
a1 c1 e1 pushc [index] Push constant
|
||||||
a2 c2 e2 pushf [function] Push function id
|
a2 c2 e2 pushf [function] Push function id
|
||||||
00 pushz Push zero (or false)
|
00 pushn Push nil
|
||||||
01 pusht Push true
|
01 pushz Push zero (or false)
|
||||||
02 newa Push (create) empty array
|
02 pusht Push true
|
||||||
03 newt Push (create) empty table
|
03 newa Push (create) empty array
|
||||||
04 pop
|
04 newt Push (create) empty table
|
||||||
05 dup
|
05 pop
|
||||||
|
06 dup
|
||||||
|
|
||||||
Local variables:
|
Local variables:
|
||||||
a3 c3 e3 pushv [int] Push n nil values into the stack (used to init local vars)
|
a3 c3 e3 pushv [int] Push n nil values into the stack (used to init local vars)
|
||||||
@@ -44,12 +45,12 @@ Function operations:
|
|||||||
Table and array operations:
|
Table and array operations:
|
||||||
16 getkv Get table's value based on key (pull 1 value, push 1 value)
|
16 getkv Get table's value based on key (pull 1 value, push 1 value)
|
||||||
17 setkv Set table's key and value (pull 2 values from stack)
|
17 setkv Set table's key and value (pull 2 values from stack)
|
||||||
18 geta Get array's position value
|
a8 c8 e8 geti Get array's position value
|
||||||
19 seta Set array's position value (pull 2 values from stack)
|
a9 c9 e9 seti Set array's position value
|
||||||
1a appnd Add value to the end of array
|
18 appnd Add value to the end of array
|
||||||
1b next Push the next pair into the stack (for loops)
|
19 next Push the next pair into the stack (for loops)
|
||||||
1c smt Set value metatable
|
1a smt Set value metatable
|
||||||
1d mt Get value metatable
|
1b mt Get value metatable
|
||||||
|
|
||||||
Logical/arithmetic:
|
Logical/arithmetic:
|
||||||
20 sum Sum top 2 values in stack
|
20 sum Sum top 2 values in stack
|
||||||
@@ -74,7 +75,7 @@ Logical/arithmetic:
|
|||||||
Other value operations:
|
Other value operations:
|
||||||
40 len Get table, array or string size
|
40 len Get table, array or string size
|
||||||
41 type Get type from value at the top of the stack
|
41 type Get type from value at the top of the stack
|
||||||
b0 cast [type] Cast type to another type
|
aa cast [type] Cast type to another type
|
||||||
42 ver Return VM version
|
42 ver Return VM version
|
||||||
|
|
||||||
External code:
|
External code:
|
||||||
@@ -83,11 +84,13 @@ External code:
|
|||||||
4a load Load bytecode as function (will place function on stack)
|
4a load Load bytecode as function (will place function on stack)
|
||||||
|
|
||||||
Control flow (the destination is always a 16-bit field):
|
Control flow (the destination is always a 16-bit field):
|
||||||
c8 bz [pc] Branch if zero
|
ca bz [pc] Branch if zero
|
||||||
c9 bnz [pc] Branch if not zero
|
cb bnz [pc] Branch if not zero
|
||||||
ca jmp [pc] Unconditional jump
|
cc jmp [pc] Unconditional jump
|
||||||
* Jumps can only happen within the same function.
|
* Jumps can only happen within the same function.
|
||||||
|
|
||||||
|
Memory management:
|
||||||
|
4b gc Call garbage collector
|
||||||
|
|
||||||
Error handling: (0xa0~0xaf)
|
Error handling: (0xa0~0xaf)
|
||||||
???
|
???
|
||||||
|
|||||||
205
lua-temp/tests.lua
Normal file → Executable file
205
lua-temp/tests.lua
Normal file → Executable file
@@ -1,3 +1,5 @@
|
|||||||
|
#!/usr/bin/env lua
|
||||||
|
|
||||||
local pprint = require('pprint')
|
local pprint = require('pprint')
|
||||||
local assemble = require('tyche-as')
|
local assemble = require('tyche-as')
|
||||||
local VM = require('tyche-vm')
|
local VM = require('tyche-vm')
|
||||||
@@ -146,7 +148,7 @@ end
|
|||||||
|
|
||||||
----------------------
|
----------------------
|
||||||
-- --
|
-- --
|
||||||
-- VM ARITH --
|
-- VM --
|
||||||
-- --
|
-- --
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
@@ -240,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
|
||||||
@@ -249,6 +250,7 @@ do
|
|||||||
@x1:
|
@x1:
|
||||||
pushi 1
|
pushi 1
|
||||||
bnz @x2
|
bnz @x2
|
||||||
|
pushi 1
|
||||||
bz @x3
|
bz @x3
|
||||||
@x2:
|
@x2:
|
||||||
pushi 6
|
pushi 6
|
||||||
@@ -261,31 +263,15 @@ 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 [[
|
||||||
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
|
.func 0
|
||||||
jmp @x1
|
jmp @x1
|
||||||
pushi 5
|
pushi 5
|
||||||
@x1:
|
@x1:
|
||||||
pushi 0
|
pushi 0
|
||||||
bnz @x2
|
bnz @x2
|
||||||
|
pushi 0
|
||||||
bz @x3
|
bz @x3
|
||||||
@x2:
|
@x2:
|
||||||
pushi 6
|
pushi 6
|
||||||
@@ -298,5 +284,178 @@ 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)
|
||||||
|
|
||||||
print('End.')
|
assert_eq(vm:to_string(-1), "Hello")
|
||||||
|
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
|
||||||
|
|
||||||
|
do TEST "VM: concatenate strings (GC won't delete)"
|
||||||
|
local vm = VM.new():load(assemble [[
|
||||||
|
.const
|
||||||
|
0: "Hello "
|
||||||
|
1: "world"
|
||||||
|
.func 0
|
||||||
|
pushc 0
|
||||||
|
pushc 1
|
||||||
|
sum
|
||||||
|
gc
|
||||||
|
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: GC 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
|
||||||
|
|
||||||
|
do TEST "VM: arrays"
|
||||||
|
local vm = VM.new():load(assemble [[
|
||||||
|
.func 0
|
||||||
|
newa
|
||||||
|
pushi 10
|
||||||
|
seti 0
|
||||||
|
pushi 20
|
||||||
|
seti 1
|
||||||
|
pushi 30
|
||||||
|
seti 2
|
||||||
|
geti 1
|
||||||
|
ret
|
||||||
|
]]):call(0)
|
||||||
|
|
||||||
|
-- print(vm:debug_heap())
|
||||||
|
assert_eq(vm:to_integer(-1), 20)
|
||||||
|
assert_eq(vm.heap:size(), 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
do TEST "VM: arrays GC"
|
||||||
|
local vm = VM.new():load(assemble [[
|
||||||
|
.func 0
|
||||||
|
pushn
|
||||||
|
newa
|
||||||
|
pushi 10
|
||||||
|
seti 0
|
||||||
|
pop
|
||||||
|
gc
|
||||||
|
ret
|
||||||
|
]]):call(0)
|
||||||
|
|
||||||
|
assert_eq(vm.heap:size(), 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
do TEST "VM: GC items (1st level) - no items removed"
|
||||||
|
local vm = VM.new():load(assemble [[
|
||||||
|
.const
|
||||||
|
0: "Hello "
|
||||||
|
1: "world"
|
||||||
|
.func 0
|
||||||
|
pushn
|
||||||
|
newa
|
||||||
|
pushc 0
|
||||||
|
pushc 1
|
||||||
|
sum
|
||||||
|
seti 0
|
||||||
|
gc
|
||||||
|
pop
|
||||||
|
ret
|
||||||
|
]]):call(0)
|
||||||
|
|
||||||
|
assert_eq(vm.heap:size(), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
do TEST "VM: GC items (1st level) - all items removed"
|
||||||
|
local vm = VM.new():load(assemble [[
|
||||||
|
.const
|
||||||
|
0: "Hello "
|
||||||
|
1: "world"
|
||||||
|
.func 0
|
||||||
|
pushn
|
||||||
|
newa
|
||||||
|
pushc 0
|
||||||
|
pushc 1
|
||||||
|
sum
|
||||||
|
seti 0
|
||||||
|
pop
|
||||||
|
gc
|
||||||
|
ret
|
||||||
|
]]):call(0)
|
||||||
|
|
||||||
|
assert_eq(vm.heap:size(), 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
do TEST "VM: GC items (2nd level) - no items removed"
|
||||||
|
local vm = VM.new():load(assemble [[
|
||||||
|
.const
|
||||||
|
0: "Hello "
|
||||||
|
1: "world"
|
||||||
|
.func 0
|
||||||
|
pushn
|
||||||
|
newa
|
||||||
|
newa
|
||||||
|
pushc 0
|
||||||
|
pushc 1
|
||||||
|
sum
|
||||||
|
seti 0
|
||||||
|
seti 0
|
||||||
|
gc
|
||||||
|
pop
|
||||||
|
ret
|
||||||
|
]]):call(0)
|
||||||
|
|
||||||
|
assert_eq(vm.heap:size(), 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
do TEST "VM: GC items (1st level) - all items removed"
|
||||||
|
local vm = VM.new():load(assemble [[
|
||||||
|
.const
|
||||||
|
0: "Hello "
|
||||||
|
1: "world"
|
||||||
|
.func 0
|
||||||
|
pushn
|
||||||
|
newa
|
||||||
|
newa
|
||||||
|
pushc 0
|
||||||
|
pushc 1
|
||||||
|
sum
|
||||||
|
seti 0
|
||||||
|
seti 0
|
||||||
|
pop
|
||||||
|
gc
|
||||||
|
ret
|
||||||
|
]]):call(0)
|
||||||
|
|
||||||
|
assert_eq(vm.heap:size(), 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
print('End.')
|
||||||
|
|||||||
@@ -8,26 +8,14 @@ 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 --
|
||||||
-- --
|
-- --
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
local function format_value(v)
|
|
||||||
if v.type == 'integer' or v.type == 'real' then
|
|
||||||
return tostring(v.value)
|
|
||||||
elseif v.type == 'string' then
|
|
||||||
return '"' .. v.value .. '"'
|
|
||||||
elseif v.type == 'function' then
|
|
||||||
return '@' .. tostring(v.value)
|
|
||||||
elseif v.type == 'nil' then
|
|
||||||
return 'nil'
|
|
||||||
else
|
|
||||||
return pprint.pformat(v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function validate_value(v)
|
local function validate_value(v)
|
||||||
assert(v, "value cannot be nil")
|
assert(v, "value cannot be nil")
|
||||||
assert(type(v) == 'table',
|
assert(type(v) == 'table',
|
||||||
@@ -39,6 +27,10 @@ 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')
|
||||||
|
elseif v.type == 'array' then
|
||||||
|
assert(type(v.ref) == 'number')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -134,17 +126,6 @@ function Stack:fp_level()
|
|||||||
return #self.fps
|
return #self.fps
|
||||||
end
|
end
|
||||||
|
|
||||||
function Stack:debug()
|
|
||||||
if #self.stack == 0 then return "empty" end
|
|
||||||
local ss = {}
|
|
||||||
for i,v in ipairs(self.stack) do
|
|
||||||
for _,fp in pairs(self.fps) do
|
|
||||||
if i == fp then table.insert(ss, '^ ') end
|
|
||||||
end
|
|
||||||
table.insert(ss, '[' .. format_value(v) .. '] ')
|
|
||||||
end
|
|
||||||
return table.concat(ss)
|
|
||||||
end
|
|
||||||
|
|
||||||
----------------------
|
----------------------
|
||||||
-- --
|
-- --
|
||||||
@@ -206,24 +187,86 @@ for op,_ in pairs(ARITH_LOGIC_OPS) do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
EXPR.sum.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.sub.integer.integer = function(vm, b, a) vm:push_integer(a - b) end
|
EXPR.sum.string.string = function(vm, b, a) vm:push_string(vm:_extract_string(a) ..vm:_extract_string(b)) end
|
||||||
EXPR.mul.integer.integer = function(vm, b, a) vm:push_integer(a * 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
|
-- TODO - div
|
||||||
EXPR.idiv.integer.integer = function(vm, b, a) vm:push_integer(math.floor(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 % b) 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 == b) and 1 or 0) 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 ~= b) 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 < b) 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 <= b) 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 > b) 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 >= b) 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 & b) 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 | b) 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 ~ b) 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 ^ b) 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 << b) 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 >> b) end
|
EXPR.shr.integer.integer = function(vm, b, a) vm:push_integer(a.value >> b.value) end
|
||||||
|
|
||||||
|
----------------------
|
||||||
|
-- --
|
||||||
|
-- HEAP --
|
||||||
|
-- --
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
local Heap = {}
|
||||||
|
Heap.__index = Heap
|
||||||
|
|
||||||
|
function Heap.new()
|
||||||
|
return setmetatable({
|
||||||
|
items = {}
|
||||||
|
}, Heap)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Heap:add_value(value)
|
||||||
|
assert(value.type and (value.type == 'string' or value.type == 'array' or value.type == 'table'))
|
||||||
|
assert(value.value)
|
||||||
|
|
||||||
|
local key = math.random(1, math.maxinteger)
|
||||||
|
while self.items[key] do key = math.random(1, math.maxinteger) end
|
||||||
|
self.items[key] = value
|
||||||
|
return key
|
||||||
|
end
|
||||||
|
|
||||||
|
function Heap:get_value(key)
|
||||||
|
assert(type(key) == 'number')
|
||||||
|
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(roots)
|
||||||
|
-- mark
|
||||||
|
local marked = {}
|
||||||
|
|
||||||
|
local function mark(v)
|
||||||
|
if v.type == 'string' then
|
||||||
|
if v.ref then marked[v.ref] = true end
|
||||||
|
elseif v.type == 'array' then
|
||||||
|
marked[v.ref] = true
|
||||||
|
for _,vv in ipairs(self.items[v.ref].value) do mark(vv) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _,v in ipairs(roots) do -- TODO - recursive, add support to array
|
||||||
|
mark(v)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- sweep
|
||||||
|
for key,_ in pairs(self.items) do
|
||||||
|
if not marked[key] then
|
||||||
|
self.items[key] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
----------------------
|
----------------------
|
||||||
-- --
|
-- --
|
||||||
@@ -237,6 +280,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,
|
||||||
@@ -264,10 +308,22 @@ 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({ type='string', 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
|
||||||
|
|
||||||
|
function VM:new_array()
|
||||||
|
self.stack:push({ type = 'array', ref = self.heap:add_value({ type='array', value={} }) })
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -279,6 +335,8 @@ function VM:stack_sz()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function VM:is(idx, type_)
|
function VM:is(idx, type_)
|
||||||
|
assert(type(idx) == "number")
|
||||||
|
assert(TYPE_MAP[type_])
|
||||||
return self.stack[idx].type == type_
|
return self.stack[idx].type == type_
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -288,6 +346,81 @@ function VM:to_integer(idx)
|
|||||||
return value.value
|
return value.value
|
||||||
end
|
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).value
|
||||||
|
else
|
||||||
|
error("Incorrect string value (nor 'const_ref' or 'ref')")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function VM:_extract_array(value)
|
||||||
|
assert(value)
|
||||||
|
assert(value.type == 'array')
|
||||||
|
local array = self.heap:get_value(value.ref)
|
||||||
|
if type(array) ~= 'table' then error('Expected array') end
|
||||||
|
return self.heap:get_value(value.ref).value
|
||||||
|
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 '"' .. self:_extract_string(v) .. '"'
|
||||||
|
elseif v.type == 'array' then
|
||||||
|
local array = self:_extract_array(v)
|
||||||
|
local tbl = {}
|
||||||
|
for _,vv in ipairs(array) do table.insert(tbl, self:format_value(vv)) end
|
||||||
|
return "[" .. table.concat(tbl, ', ') .. "]"
|
||||||
|
elseif v.type == 'function' then
|
||||||
|
return '@' .. tostring(v.value)
|
||||||
|
elseif v.type == 'nil' then
|
||||||
|
return 'nil'
|
||||||
|
else
|
||||||
|
print('warning: cannot convert from type ' .. tostring(v.type))
|
||||||
|
return pprint.pformat(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function VM:debug_stack()
|
||||||
|
if #self.stack.stack == 0 then return "empty" end
|
||||||
|
local ss = {}
|
||||||
|
for i,v in ipairs(self.stack.stack) do
|
||||||
|
for _,fp in pairs(self.stack.fps) do
|
||||||
|
if i == fp then table.insert(ss, '^ ') end
|
||||||
|
end
|
||||||
|
table.insert(ss, self:format_value(v) .. ' ')
|
||||||
|
end
|
||||||
|
return table.concat(ss)
|
||||||
|
end
|
||||||
|
|
||||||
|
function VM:debug_heap()
|
||||||
|
local ss = { "Heap:\n" }
|
||||||
|
for k,v in pairs(self.heap.items) do
|
||||||
|
if v.type == 'string' then
|
||||||
|
table.insert(ss, string.format(' [%X] = "%s"', k, v.value))
|
||||||
|
elseif v.type == 'array' then
|
||||||
|
table.insert(ss, string.format(' [%X] = [', k))
|
||||||
|
local t = {}; for _,vv in ipairs(v.value) do t[#t+1] = self:format_value(vv) end
|
||||||
|
table.insert(ss, table.concat(t, ", ") .. ']')
|
||||||
|
else
|
||||||
|
error('Unsupported type in heap')
|
||||||
|
end
|
||||||
|
table.insert(ss, "\n")
|
||||||
|
end
|
||||||
|
return table.concat(ss)
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- code execution
|
-- code execution
|
||||||
--
|
--
|
||||||
@@ -329,9 +462,9 @@ function VM:_run_until_return()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function VM:_debug_stack()
|
function VM:_print_stack()
|
||||||
if self.debug then
|
if self.debug then
|
||||||
print(self.stack:debug())
|
print(self:debug_stack())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -344,14 +477,31 @@ function VM:_step()
|
|||||||
--
|
--
|
||||||
-- stack operations
|
-- 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)
|
self:push_integer(op.operand)
|
||||||
|
|
||||||
elseif op.operator == 'pushf' then
|
elseif op.operator == 'pushf' then
|
||||||
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 == 'newa' then
|
||||||
|
self:new_array()
|
||||||
|
|
||||||
|
elseif op.operator == 'pop' then
|
||||||
|
self.stack:pop()
|
||||||
|
|
||||||
elseif op.operator == 'dup' then
|
elseif op.operator == 'dup' then
|
||||||
self.stack:push(self.stack:peek())
|
self.stack:push(self.stack:peek())
|
||||||
|
|
||||||
@@ -375,6 +525,20 @@ function VM:_step()
|
|||||||
local a = self.stack[op.operand]
|
local a = self.stack[op.operand]
|
||||||
self.stack:push(a)
|
self.stack:push(a)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- table and array operations
|
||||||
|
--
|
||||||
|
|
||||||
|
elseif op.operator == 'seti' then
|
||||||
|
local array_ref = self.stack[-2]
|
||||||
|
local array = self:_extract_array(array_ref)
|
||||||
|
array[op.operand+1] = self.stack:pop()
|
||||||
|
|
||||||
|
elseif op.operator == 'geti' then
|
||||||
|
local array_ref = self.stack[-1]
|
||||||
|
local array = self:_extract_array(array_ref)
|
||||||
|
self.stack:push(array[op.operand+1])
|
||||||
|
|
||||||
--
|
--
|
||||||
-- logic/arithmetic operations
|
-- logic/arithmetic operations
|
||||||
--
|
--
|
||||||
@@ -382,7 +546,7 @@ function VM:_step()
|
|||||||
elseif ARITH_LOGIC_OPS[op.operator] then
|
elseif ARITH_LOGIC_OPS[op.operator] then
|
||||||
local a = self.stack:pop()
|
local a = self.stack:pop()
|
||||||
local b = 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
|
-- function management
|
||||||
@@ -397,7 +561,7 @@ function VM:_step()
|
|||||||
self.stack:pop_fp()
|
self.stack:pop_fp()
|
||||||
self.stack:push(v)
|
self.stack:push(v)
|
||||||
table.remove(self.loc)
|
table.remove(self.loc)
|
||||||
self:_debug_stack()
|
self:_print_stack()
|
||||||
return
|
return
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -406,14 +570,14 @@ function VM:_step()
|
|||||||
|
|
||||||
elseif op.operator == 'jmp' then
|
elseif op.operator == 'jmp' then
|
||||||
loc.pc = self.code:find_label(loc.f_id, op.operand)
|
loc.pc = self.code:find_label(loc.f_id, op.operand)
|
||||||
self:_debug_stack()
|
self:_print_stack()
|
||||||
return
|
return
|
||||||
|
|
||||||
elseif op.operator == 'bz' then
|
elseif op.operator == 'bz' then
|
||||||
local v = self.stack:pop()
|
local v = self.stack:pop()
|
||||||
if is_zero(v) then
|
if is_zero(v) then
|
||||||
loc.pc = self.code:find_label(loc.f_id, op.operand)
|
loc.pc = self.code:find_label(loc.f_id, op.operand)
|
||||||
self:_debug_stack()
|
self:_print_stack()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -421,10 +585,25 @@ function VM:_step()
|
|||||||
local v = self.stack:pop()
|
local v = self.stack:pop()
|
||||||
if not is_zero(v) then
|
if not is_zero(v) then
|
||||||
loc.pc = self.code:find_label(loc.f_id, op.operand)
|
loc.pc = self.code:find_label(loc.f_id, op.operand)
|
||||||
self:_debug_stack()
|
self:_print_stack()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- memory management
|
||||||
|
--
|
||||||
|
|
||||||
|
elseif op.operator == 'gc' then
|
||||||
|
-- if self.debug then
|
||||||
|
-- print('About to run GC, current heap:')
|
||||||
|
-- print(self:debug_heap())
|
||||||
|
-- end
|
||||||
|
self.heap:call_gc(self.stack.stack)
|
||||||
|
-- if self.debug then
|
||||||
|
-- print('GC executed, this is the heap:')
|
||||||
|
-- print(self:debug_heap())
|
||||||
|
-- end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- instruction not found
|
-- instruction not found
|
||||||
--
|
--
|
||||||
@@ -433,9 +612,9 @@ function VM:_step()
|
|||||||
error("Unknown operator '" .. tostring(op.operator) .. "'")
|
error("Unknown operator '" .. tostring(op.operator) .. "'")
|
||||||
end
|
end
|
||||||
|
|
||||||
self:_debug_stack()
|
self:_print_stack()
|
||||||
|
|
||||||
loc.pc = loc.pc + op.instruction_size
|
loc.pc = loc.pc + op.instruction_size
|
||||||
end
|
end
|
||||||
|
|
||||||
return VM
|
return VM
|
||||||
|
|||||||
Reference in New Issue
Block a user