445 lines
9.5 KiB
Lua
Raw Normal View History

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 find_var_pos(state, name)
local slen = #state.locals
for i = 1, slen do
local v = state.locals[slen + 1 - i]
if in_keys("v" .. name, v) then
return slen + 1 - i
end
end
end
local function access(state, name)
local n = find_var_pos(state, name)
if n then
return state.locals[n]["v" .. name]
end
end
local function gassign(state, name, value)
state.locals[0]["v" .. name] = value
end
local function lassign(state, name, value)
state.locals[#state.locals]["v" .. name] = value
end
local function assign(state, name, value)
local n = find_var_pos(state, name)
if n then
state.locals[n]["v" .. name] = value
else
state.locals[#state.locals]["v" .. name] = value
end
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
local function unary(func)
return function(state)
local tos = statepop(state)
if tos.type == "number" then
statepush_num(state, func(tos.value))
elseif tos.type == "quote" then
local n = access(state, tos.value)
assign(state, tos.value, {type = "number", value = func(n.value)})
end
end
end
local function binary(func)
return function(state)
local tos = statepop_num(state)
local tos1 = statepop_num(state)
statepush_num(state, func(tos.value, tos1.value))
end
end
local function boolnum(b)
if b then
return 1
else
return 0
end
end
local function numbool(n)
if n ~= 0 then
return true
else
return false
end
end
builtins["--"] = unary(function(v)
return v - 1
end)
builtins["++"] = unary(function(v)
return v + 1
end)
builtins["!"] = unary(function(v)
return boolnum(not numbool(v))
end)
builtins["+"] = binary(function(v1, v2)
return v1 + v2
end)
builtins["-"] = binary(function(v1, v2)
return v1 - v2
end)
builtins["*"] = binary(function(v1, v2)
return v1 * v2
end)
builtins["/"] = binary(function(v1, v2)
return v1 / v2
end)
builtins["%"] = binary(function(v1, v2)
return v1 % v2
end)
builtins["=="] = binary(function(v1, v2)
return boolnum(v1 == v2)
end)
builtins["!="] = binary(function(v1, v2)
return boolnum(v1 ~= v2)
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
2020-11-04 19:19:36 +00:00
builtins["while"] = function(state)
local slen = #state.locals
if state.locals[slen].broke == true then
state.locals[slen].broke = nil
state.locals[slen].loop_code = nil
state.locals[slen].test_code = nil
state.locals[slen].loop_stage = nil
return
end
if state.locals[slen].loop_code == nil then
local while_block = statepop_type(state, "code")
local test_block = statepop_type(state, "code")
state.locals[slen].test_code = test_block
state.locals[slen].loop_code = while_block
state.locals[slen].loop_stage = 0
end
-- stage 0, run test
if state.locals[slen].loop_stage == 0 then
statepush(state, state.locals[slen].test_code)
state.locals[slen].pc = state.current_pc
call(state, {sg = 0, pos = #state.stack, elem = 1})
state.locals[slen].loop_stage = 1
-- stage 1, run while
elseif state.locals[slen].loop_stage == 1 then
local tos = statepop(state)
if tos and tos.value ~= 0 then
statepush(state, state.locals[slen].loop_code)
state.locals[slen].pc = state.current_pc
call(state, {sg = 0, pos = #state.stack, elem = 1})
else
state.locals[slen].pc = state.current_pc
state.locals[slen].broke = true
end
state.locals[slen].loop_stage = 0
end
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