348 lines
7.2 KiB
Lua
348 lines
7.2 KiB
Lua
|
|
local function in_list(value, list)
|
|
for k, v in ipairs(list) do
|
|
if v == value then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function in_keys(value, list)
|
|
for k, v in pairs(list) do
|
|
if k == value then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- state
|
|
--[[
|
|
{
|
|
locals = {},
|
|
stack = {},
|
|
builtins = {},
|
|
code_stack = {},
|
|
wait_target = int,
|
|
nextpop = f/t
|
|
}
|
|
--]]
|
|
|
|
-- program counter
|
|
--[[
|
|
sg = 0/1,
|
|
pos = int/string,
|
|
elem = int,
|
|
wait_target = float
|
|
--]]
|
|
|
|
local literals = {
|
|
"quote",
|
|
"code",
|
|
"map",
|
|
"string",
|
|
"number"
|
|
}
|
|
|
|
|
|
local function call(state, target)
|
|
if target.sg == 0 then
|
|
state.code_stack[#state.code_stack + 1] = state.stack[target.pos]
|
|
table.remove(state.stack, target.pos)
|
|
target.pos = #state.code_stack
|
|
end
|
|
|
|
state.locals[#state.locals + 1] = {pc = target}
|
|
end
|
|
|
|
local function access(state, name)
|
|
name = "v" .. name
|
|
local slen = #state.locals
|
|
|
|
for i = 1, slen do
|
|
local v = state.locals[slen + 1 - i]
|
|
if in_keys(name, v) then
|
|
return v[name]
|
|
end
|
|
end
|
|
end
|
|
|
|
local function gassign(state, name, value)
|
|
state.locals[0]["v" .. name] = value
|
|
end
|
|
|
|
local function assign(state, name, value)
|
|
state.locals[#state.locals]["v" .. name] = value
|
|
end
|
|
|
|
local function getpc(state)
|
|
return state.locals[#state.locals].pc
|
|
end
|
|
|
|
local function accesspc(state, pc)
|
|
local code
|
|
if pc.sg == 0 then -- stack
|
|
code = state.code_stack[pc.pos]
|
|
elseif pc.sg == 1 then -- global
|
|
code = access(state, pc.pos)
|
|
end
|
|
|
|
if code then
|
|
return code.value[pc.elem]
|
|
end
|
|
end
|
|
|
|
local function incpc(state, pc)
|
|
local next_pc = {sg = pc.sg, pos = pc.pos, elem = pc.elem + 1}
|
|
|
|
if accesspc(state, next_pc) then
|
|
return next_pc
|
|
end
|
|
end
|
|
|
|
local function getnext(state)
|
|
if state.locals[#state.locals].nextpop then
|
|
state.locals[#state.locals] = nil
|
|
if #state.locals == 0 then
|
|
return nil
|
|
end
|
|
|
|
-- pop code stack
|
|
local pc = getpc(state)
|
|
if pc.sg == 0 then
|
|
state.code_stack[pc.pos] = nil
|
|
end
|
|
end
|
|
|
|
state.current_pc = getpc(state)
|
|
local current = accesspc(state, state.current_pc)
|
|
|
|
local incd = incpc(state, getpc(state))
|
|
state.locals[#state.locals].pc = incd
|
|
if not incd then
|
|
state.locals[#state.locals].nextpop = true
|
|
end
|
|
|
|
return current
|
|
end
|
|
|
|
|
|
local function statepeek(state)
|
|
return state.stack[#state.stack]
|
|
end
|
|
|
|
local function statepop(state)
|
|
local tos = statepeek(state)
|
|
state.stack[#state.stack] = nil
|
|
return tos
|
|
end
|
|
|
|
local function statepeek_type(state, t)
|
|
local tos = statepeek(state)
|
|
|
|
if tos.type == t then
|
|
return tos
|
|
else
|
|
return nil -- ERROR
|
|
end
|
|
end
|
|
|
|
local function statepop_type(state, t)
|
|
local tos = statepeek(state)
|
|
|
|
if tos.type == t then
|
|
return statepop(state)
|
|
else
|
|
return nil -- ERROR
|
|
end
|
|
end
|
|
|
|
local function statepop_num(state)
|
|
return statepop_type(state, "number")
|
|
end
|
|
|
|
local function statepush(state, value)
|
|
state.stack[#state.stack + 1] = value
|
|
end
|
|
|
|
local function statepush_num(state, number)
|
|
statepush(state, {type = "number", value = number})
|
|
end
|
|
|
|
|
|
|
|
local builtins = {}
|
|
|
|
function builtins.run(state)
|
|
call(state, {sg = 0, pos = #state.stack, elem = 1})
|
|
end
|
|
|
|
builtins["="] = function(state)
|
|
local name = statepop_type(state, "quote")
|
|
local value = statepop(state)
|
|
|
|
assign(state, name.value, value)
|
|
end
|
|
|
|
builtins["--"] = function(state)
|
|
local tos = statepop_num(state)
|
|
statepush_num(state, tos.value - 1)
|
|
end
|
|
|
|
builtins["++"] = function(state)
|
|
local tos = statepop_num(state)
|
|
statepush_num(state, tos.value + 1)
|
|
end
|
|
|
|
builtins["*"] = function(state)
|
|
local tos = statepop_num(state)
|
|
local tos1 = statepop_num(state)
|
|
|
|
statepush_num(state, tos.value * tos1.value)
|
|
end
|
|
|
|
local function boolnum(b)
|
|
if b then
|
|
return 1
|
|
else
|
|
return 0
|
|
end
|
|
end
|
|
|
|
builtins["=="] = function(state)
|
|
local tos = statepop_num(state)
|
|
local tos1 = statepop_num(state)
|
|
|
|
statepush_num(state, boolnum(tos.value == tos1.value))
|
|
end
|
|
|
|
builtins["!="] = function(state)
|
|
local tos = statepop_num(state)
|
|
local tos1 = statepop_num(state)
|
|
|
|
statepush_num(state, boolnum(tos.value ~= tos1.value))
|
|
end
|
|
|
|
builtins["if"] = function(state)
|
|
local tos = statepop_type(state, "code")
|
|
local tos1 = statepop(state)
|
|
|
|
if tos1.type == "number" then
|
|
if tos1.value ~= 0 then
|
|
statepush(state, tos)
|
|
call(state, {sg = 0, pos = #state.stack, elem = 1})
|
|
end
|
|
end
|
|
end
|
|
|
|
function builtins.print(state)
|
|
local value = statepop(state)
|
|
|
|
print(value.value)
|
|
end
|
|
|
|
function builtins.dup(state)
|
|
statepush(state, statepeek(state))
|
|
end
|
|
|
|
function builtins.popoff(state)
|
|
state.stack[#state.stack] = nil
|
|
end
|
|
|
|
function builtins.wait(state)
|
|
local tos = statepop_type(state, "number")
|
|
state.wait_target = os.clock() + tos.value
|
|
end
|
|
|
|
builtins["forever"] = function(state)
|
|
local slen = #state.locals
|
|
|
|
if state.locals[slen].broke == true then
|
|
state.locals[slen].broke = nil
|
|
state.locals[slen].loop_code = nil
|
|
|
|
return
|
|
end
|
|
|
|
if state.locals[slen].loop_code == nil then
|
|
local tos = statepop_type(state, "code")
|
|
|
|
state.locals[slen].loop_code = tos
|
|
end
|
|
|
|
statepush(state, state.locals[slen].loop_code)
|
|
|
|
state.locals[slen].pc = state.current_pc
|
|
|
|
call(state, {sg = 0, pos = #state.stack, elem = 1})
|
|
end
|
|
|
|
builtins["break"] = function(state)
|
|
local slen = #state.locals
|
|
local pos = 0
|
|
local found = false
|
|
|
|
-- find highest loop_code
|
|
-- slen - i to perform basically bitwise inverse
|
|
-- it allows it to count down the list effectively
|
|
for i = 1, slen do
|
|
if state.locals[slen + 1 - i].loop_code then
|
|
pos = slen + 1 - i
|
|
found = true
|
|
end
|
|
end
|
|
|
|
if found then
|
|
-- pop the top layers
|
|
for i = pos + 1, #state.locals do
|
|
state.locals[i] = nil
|
|
end
|
|
|
|
-- break in the lower layer
|
|
state.locals[#state.locals].broke = true
|
|
end
|
|
end
|
|
|
|
builtins["return"] = function(state)
|
|
state.locals[#state.locals] = nil
|
|
end
|
|
|
|
|
|
-- returns:
|
|
-- true - more to do
|
|
-- nil - more to do but waiting
|
|
-- false - finished
|
|
-- string - error
|
|
local step = function(state)
|
|
if state.wait_target and os.clock() < state.wait_target then
|
|
return nil
|
|
end
|
|
|
|
local cur = getnext(state)
|
|
|
|
if cur == nil then
|
|
return false
|
|
elseif in_list(cur.type, literals) then
|
|
state.stack[#state.stack + 1] = cur
|
|
elseif cur.type == "identifier" or cur.type == "symbol" then
|
|
if in_keys(cur.value, state.builtins) then
|
|
local f = state.builtins[cur.value]
|
|
f(state)
|
|
else
|
|
local var = access(state, cur.value)
|
|
if var == nil then
|
|
return "Undefined identifier: " .. cur.value
|
|
elseif var.type == "code" then
|
|
call(state, {sg = 1, pos = cur.value, elem = 1})
|
|
else
|
|
state.stack[#state.stack + 1] = var
|
|
end
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
return builtins, gassign, step
|