forth_combinator/forth.lua

1230 lines
33 KiB
Lua
Raw Permalink Normal View History

2024-11-24 23:43:23 -05:00
require('base')
-- Bullshit
sigs = {
virtual_signal = {},
item = {},
fluid = {},
}
for sig_name, sig_arr in pairs(sigs) do
for name, prototype in pairs(prototypes[sig_name]) do
if not prototype.parameter then
table.insert(sig_arr, name)
end
end
end
-- Bullshit
local F_TRUE = -1
local F_FALSE = 0
local F_IMMED = 0x80
local F_HIDDEN = 0x20
local F_LENMASK = 0x1F
local cw_index = 0
local codewords = {}
local codeword_names = {}
local labels = {}
local init_context = {
memspace = {},
xp = 0,
sp = 0x1000,
rp = 0x1100,
last_xt = 0,
keybuf = {},
outbuf = {},
yield = false,
waiting_key = false,
sleep_ticks = 0,
bs = '',
valid = false,
}
function copy_context(ctx)
local new_ctx = {
xp = ctx.xp,
sp = ctx.sp,
rp = ctx.rp,
last_xt = ctx.last_xt,
yield = ctx.yield,
waiting_key = ctx.waiting_key,
sleep_ticks = ctx.sleep_ticks,
bs = ctx.bs,
keybuf = {},
outbuf = {},
memspace = {},
}
for i, v in pairs(ctx.keybuf) do new_ctx.keybuf[i] = v end
for i, v in pairs(ctx.outbuf) do new_ctx.outbuf[i] = v end
for i, v in pairs(ctx.memspace) do new_ctx.memspace[i] = v end
return new_ctx
end
dbg_print = function(...)
--print(...)
-- local str = ''
-- for i, v in pairs({...}) do
-- str = str..tostring(v)..' '
-- end
-- log(str)
-- game.print(str)
end
dbg_interp = dbg_print
local function output_string(context, str)
for c in str:gmatch('.') do
table.insert(context.outbuf, c)
end
end
local function read(context, addr)
local res = context.memspace[addr]
if res == nil then
error('Read from invalid address')
end
return res
end
local function write(context, addr, data)
context.memspace[addr] = data
end
local function push_rsp(context, data)
dbg_print('push rsp ', data)
context.rp = context.rp - 1
write(context, context.rp, data)
end
local function pop_rsp(context)
local data = read(context, context.rp)
context.rp = context.rp + 1
dbg_print('pop rsp ', data)
return data
end
local function push(context, data)
dbg_print('push ', data)
context.sp = context.sp - 1
write(context, context.sp, data)
end
local function pop(context)
local data = read(context, context.sp)
if data == nil then
error('Pop from outside stack')
end
context.sp = context.sp + 1
dbg_print('pop ', data)
return data
end
local function peek(context, off)
return read(context, context.sp + off)
end
local function do_codeword(context, xt)
-- Save xt so we can resume from a yield
context.last_xt = xt
local codeword = read(context, xt)
dbg_print('XT', xt, labels[xt])
dbg_print('CW', codeword, codeword_names[codeword])
-- Reinit codewords when loading a save
if #codewords == 0 then
forth_initialize(context.entity)
end
codewords[codeword](context)
end
local function forth_next(context)
local xt = nil
if context.yield then
xt = context.last_xt
context.yield = false
else
local xp = context.xp
xt = read(context, xp)
dbg_print('XP', xp)
context.xp = context.xp + 1
end
do_codeword(context, xt)
end
function forth_tick(context)
if context.sleep_ticks > 0 then
context.sleep_ticks = context.sleep_ticks - 1
return true
elseif context.yield and context.waiting_key and #context.keybuf == 0 then
return true
else
forth_next(context)
return false
end
end
function feed_key(context, key)
table.insert(context.keybuf, key)
end
function feed_file(context, file)
while true do
local c = file:read(1)
if c == nil then break end
feed_key(context, string.byte(c))
end
end
function forth_initialize(entity)
-- init_context.valid = false
if init_context.valid then
local new_ctx = copy_context(init_context)
new_ctx.entity = entity
return new_ctx
end
local link = 0
local init_here = 0x10
local function init_comma(data)
write(init_context, init_here, data)
init_here = init_here + 1
end
local function init_comma_string(str)
for i=1, #str do
init_comma(string.byte(str, i, i))
end
end
local function defcode(name, flags, callback)
-- log(tostring(init_here)..' '..name)
init_comma(link)
link = init_here - 1
local lenflag = flags + #name
init_comma(lenflag)
init_comma_string(name)
local xt = init_here
codewords[cw_index] = callback
codeword_names[cw_index] = name
init_comma(cw_index) -- codeword
init_comma(0) -- doesword
cw_index = cw_index + 1
labels[xt] = name
return xt
end
local DOCOL = defcode('DOCOL', 0, function(context)
-- XP point to the XT to return to, push that
push_rsp(context, context.xp)
-- Skip past the codeword and doesword
context.xp = context.last_xt + 2
-- XP now points to first XT of the called word
end)
local __DOCOL = read(init_context, DOCOL)
local DOCREATE = defcode('DOCREATE', 0, function(context)
-- Push address of data field (after codeword and doesword)
push(context, context.last_xt + 2)
-- log('docreate '..tonumber(context.last_xt + 2)..' = '..read(context, context.last_xt + 2))
end)
local __DOCREATE = read(init_context, DOCREATE)
local DODOES = defcode('DODOES', 0, function(context)
-- XP point to the XT to return to, push that
push_rsp(context, context.xp)
-- Push address of data field (after codeword and doesword)
push(context, context.last_xt + 2)
-- Skip past the codeword and read doesword
context.xp = read(context, context.last_xt + 1)
-- XP now points to first XT of the DOES> section
end)
local __DODOES = read(init_context, DODOES)
local function defword(name, flags, xts)
-- log(tostring(init_here)..' '..name)
init_comma(link)
link = init_here - 1
local lenflag = flags + #name
init_comma(lenflag)
init_comma_string(name)
local xt = init_here
init_comma(__DOCOL) -- codeword
init_comma(0) -- doesword
for i=1, #xts do
init_comma(xts[i])
end
labels[xt] = name
return xt
end
local function defvar(name, flags, value)
-- log(tostring(init_here)..' '..name)
init_comma(link)
link = init_here - 1
local lenflag = flags + #name
init_comma(lenflag)
init_comma_string(name)
local xt = init_here
init_comma(__DOCREATE) -- codeword
init_comma(0) -- doesword
init_comma(value) -- value on param list
labels[xt] = name
return xt
end
local function readvar(context, xt)
-- Skip over codeword, variable is stored after it
-- log('readvar @ '..tonumber(xt + 2)..': '..read(context, xt + 2))
return read(context, xt + 2)
end
local function writevar(context, xt, data)
-- Skip over codeword, variable is stored after it
-- log('writevar @ '..tonumber(xt + 2)..': '..data)
return write(context, xt + 2, data)
end
local function defconst(name, flags, value)
local entry = defcode(name, flags, function(context)
push(context, value)
end)
return entry
end
local DROP = defcode('DROP', 0, function(context)
pop(context)
end)
local SWAP = defcode('SWAP', 0, function(context)
local a = pop(context)
local b = pop(context)
push(context, a)
push(context, b)
-- print(b, a, '->', a, b)
end)
local DUP = defcode('DUP', 0, function(context)
local a = peek(context, 0)
push(context, a)
end)
local OVER = defcode('OVER', 0, function(context)
local a = peek(context, 1)
push(context, a)
end)
local ROT = defcode('ROT', 0, function(context)
local c = pop(context)
local b = pop(context)
local a = pop(context)
push(context, b)
push(context, c)
push(context, a)
end)
local NROT = defcode('-ROT', 0, function(context)
local c = pop(context)
local b = pop(context)
local a = pop(context)
push(context, c)
push(context, a)
push(context, b)
end)
local TWODROP = defcode('2DROP', 0, function(context)
pop(context)
pop(context)
end)
local TWODUP = defcode('2DUP', 0, function(context)
push(context, peek(context, 1))
push(context, peek(context, 1))
end)
local TWOSWAP = defcode('2SWAP', 0, function(context)
local a1 = pop(context)
local a2 = pop(context)
local b1 = pop(context)
local b2 = pop(context)
push(context, a1)
push(context, a2)
push(context, b1)
push(context, b2)
end)
local QDUP = defcode('?DUP', 0, function(context)
local a = peek(context, 0)
if a ~= 0 then
push(context, a)
end
end)
local INCR = defcode('1+', 0, function(context)
local a = pop(context)
push(context, a + 1)
end)
local DECR = defcode('1-', 0, function(context)
local a = pop(context)
push(context, a - 1)
end)
local CELLPLUS = defcode('CELL+', 0, function(context)
local a = pop(context)
push(context, a + 1)
end)
local CELLMINUS = defcode('CELL-', 0, function(context)
local a = pop(context)
push(context, a - 1)
end)
local ADD = defcode('+', 0, function(context)
local b = pop(context)
local a = pop(context)
push(context, a + b)
end)
local SUB = defcode('-', 0, function(context)
local b = pop(context)
local a = pop(context)
-- print(a, '-', b, '=', a-b)
push(context, a - b)
end)
local MUL = defcode('*', 0, function(context)
local b = pop(context)
local a = pop(context)
push(context, a * b)
end)
local UMDIVMOD = defcode('UM/MOD', 0, function(context)
local b = pop(context)
local a = pop(context)
-- We do some cope
a = bit32.band(a, 0x7FFFFFFF)
b = bit32.band(b, 0x7FFFFFFF)
push(context, a % b)
push(context, math.floor(a / b))
end)
local DIVMOD = defcode('/MOD', 0, function(context)
local b = pop(context)
local a = pop(context)
push(context, a % b)
push(context, math.floor(a / b))
end)
local EQU = defcode('=', 0, function(context)
local b = pop(context)
local a = pop(context)
if a == b then
push(context, F_TRUE)
else
push(context, F_FALSE)
end
end)
local NEQU = defcode('<>', 0, function(context)
local b = pop(context)
local a = pop(context)
if a ~= b then
push(context, F_TRUE)
else
push(context, F_FALSE)
end
end)
local LT = defcode('<', 0, function(context)
local b = pop(context)
local a = pop(context)
if a < b then
push(context, F_TRUE)
else
push(context, F_FALSE)
end
end)
local GT = defcode('>', 0, function(context)
local b = pop(context)
local a = pop(context)
if a > b then
push(context, F_TRUE)
else
push(context, F_FALSE)
end
end)
local LE = defcode('<=', 0, function(context)
local b = pop(context)
local a = pop(context)
if a <= b then
push(context, F_TRUE)
else
push(context, F_FALSE)
end
end)
local GE = defcode('>=', 0, function(context)
local b = pop(context)
local a = pop(context)
if a >= b then
push(context, F_TRUE)
else
push(context, F_FALSE)
end
end)
local ZEQU = defcode('0=', 0, function(context)
local a = pop(context)
if a == 0 then
push(context, F_TRUE)
else
push(context, F_FALSE)
end
end)
local ZNEQU = defcode('0<>', 0, function(context)
local a = pop(context)
if a ~= 0 then
push(context, F_TRUE)
else
push(context, F_FALSE)
end
end)
local ZLT = defcode('0<', 0, function(context)
local a = pop(context)
if a < 0 then
push(context, F_TRUE)
else
push(context, F_FALSE)
end
end)
local ZGT = defcode('0>', 0, function(context)
local a = pop(context)
if a > 0 then
push(context, F_TRUE)
else
push(context, F_FALSE)
end
end)
local ZLE = defcode('0<=', 0, function(context)
local a = pop(context)
if a <= 0 then
push(context, F_TRUE)
else
push(context, F_FALSE)
end
end)
local ZGE = defcode('0>=', 0, function(context)
local a = pop(context)
if a >= 0 then
push(context, F_TRUE)
else
push(context, F_FALSE)
end
end)
local AND = defcode('AND', 0, function(context)
local b = pop(context)
local a = pop(context)
push(context, bit32.band(a, b))
end)
local OR = defcode('OR', 0, function(context)
local b = pop(context)
local a = pop(context)
push(context, bit32.bor(a, b))
end)
local XOR = defcode('XOR', 0, function(context)
local b = pop(context)
local a = pop(context)
push(context, bit32.bxor(a, b))
end)
local LSL = defcode('LSL', 0, function(context)
local b = pop(context)
local a = pop(context)
push(context, bit32.lshift(a, b))
end)
local LSR = defcode('LSR', 0, function(context)
local b = pop(context)
local a = pop(context)
push(context, bit32.rshift(a, b))
end)
local INVERT = defcode('INVERT', 0, function(context)
local a = pop(context)
push(context, bit32.bnot(a))
end)
local NEGATE = defcode('NEGATE', 0, function(context)
local a = pop(context)
push(context, -a)
end)
local EXIT = defcode('EXIT', 0, function(context)
context.xp = pop_rsp(context)
end)
local LIT = defcode('LIT', 0, function(context)
local a = read(context, context.xp)
context.xp = context.xp + 1
push(context, a)
end)
local STORE = defcode('!', 0, function(context)
local addr = pop(context)
local data = pop(context)
write(context, addr, data)
-- print(data, addr, '!', read(context, addr))
end)
local FETCH = defcode('@', 0, function(context)
local addr = pop(context)
-- print(addr, '@', read(context, addr))
push(context, read(context, addr))
end)
local ADDSTORE = defcode('+!', 0, function(context)
local addr = pop(context)
local amnt = pop(context)
write(context, addr, read(context, addr) + amnt)
end)
local SUBSTORE = defcode('-!', 0, function(context)
local addr = pop(context)
local amnt = pop(context)
write(context, addr, read(context, addr) - amnt)
end)
local CMOVE = defcode('CMOVE', 0, function(context)
local len = pop(context)
local dst = pop(context)
local src = pop(context)
for i=1,len do
write(context, dst + i - 1, read(context, src + i - 1))
end
end)
local STATE = defvar('STATE', 0, 0)
local HERE = defvar('HERE', 0, 0)
local LATEST = defvar('LATEST', 0, 0)
local S0 = defvar('S0', 0, init_context.sp)
local BASE = defvar('BASE', 0, 10)
local R0 = defconst('R0', 0, init_context.rp)
local __F_FALSE = defconst('FALSE', 0, F_FALSE)
local __F_TRUE = defconst('TRUE', 0, F_TRUE)
local __F_IMMED = defconst('F_IMMED', 0, F_IMMED)
local __F_HIDDEN = defconst('F_HIDDEN', 0, F_HIDDEN)
local __F_LENMASK = defconst('F_LENMASK', 0, F_LENMASK)
local TOR = defcode('>R', 0, function(context)
local a = pop(context)
push_rsp(context, a)
end)
local FROMR = defcode('R>', 0, function(context)
local a = pop_rsp(context)
push(context, a)
end)
local RFETCH = defcode('R@', 0, function(context)
local data = read(context, context.rp)
push(context, data)
end)
local RSTORE = defcode('R!', 0, function(context)
local data = pop(context)
write(context, context.rp, data)
end)
local RSPFETCH = defcode('RSP@', 0, function(context)
push(context, context.rp)
end)
local RSPSTORE = defcode('RSP!', 0, function(context)
context.rp = pop(context)
end)
local RDROP = defcode('RDROP', 0, function(context)
pop_rsp(context)
end)
local DSPFETCH = defcode('DSP@', 0, function(context)
push(context, context.sp)
end)
local DSPSTORE = defcode('DSP!', 0, function(context)
context.sp = pop(context)
end)
local function _key(context)
if #context.keybuf == 0 then
context.waiting_key = true
context.yield = true
return nil
end
context.waiting_key = false
return table.remove(context.keybuf, 1)
end
local KEY = defcode('KEY', 0, function(context)
local key = _key(context)
if key ~= nil then
context.bs = context.bs..string.char(key)
if key == 10 then
dbg_print('enter is here '..context.bs)
context.bs = ''
end
push(context, key)
end
end)
local EMIT = defcode('EMIT', 0, function(context)
table.insert(context.outbuf, string.char(pop(context)))
end)
local BRANCH = defcode('BRANCH', 0, function(context)
context.xp = context.xp + read(context, context.xp)
end)
local ZBRANCH = defcode('0BRANCH', 0, function(context)
local data = pop(context)
if data == 0 then
context.xp = context.xp + read(context, context.xp)
else
context.xp = context.xp + 1
end
end)
local function _comma(context, data)
local here = readvar(context, HERE)
write(context, here, data)
here = here + 1
writevar(context, HERE, here)
end
local COMMA = defcode(',', 0, function(context)
local data = pop(context)
_comma(context, data)
end)
local WORD = defword('WORD', 0, { -- ( delim -- paddr)
HERE, FETCH, TOR, -- ( delim ) ( r: start )
LIT, 0, COMMA, -- Store temp length
TOR, -- ( ) ( r: start delim )
-- Skip leading delimiters
KEY, -- ( key ) ( r: start delim )
DUP, RFETCH, EQU, -- ( key delim ) ( r: start delim )
ZBRANCH, 4, -- ( key ) ( r: start delim )
DROP, -- ( ) ( r: start delim )
BRANCH, -8, -- Branch till no more delim's
-- Check for /n or delim
DUP, RFETCH, EQU, -- ( key not_delim ) Check for delim
OVER, LIT, 10, EQU, -- ( key not_delim not_nl ) Check for \n
OR, ZBRANCH, 15, -- ( key ) ( r: start delim ) If delim or space then
FROMR, TWODROP, -- ( ) ( r: start )
RFETCH, -- ( start ) ( r: start )
HERE, FETCH, FROMR, SUB, DECR, -- ( start len ) ( r: )
OVER, STORE, -- ( start ) ( r: ) - Store length
DUP, HERE, STORE, -- Restore HERE
EXIT,
-- Store key and grab the next one
COMMA, -- Store key ( ) ( r: start delim )
KEY, -- ( key ) ( r: start delim)
BRANCH, -27, -- Branch till invalid key (space, \n, etc)
})
local function _number(context, accum, addr, len)
local str = ''
for i=1, len do
str = str..string.char(read(context, addr + i - 1))
end
local start = 1
if string.sub(str, 1, 1) == '-' then start = 2 end
local result = accum
local last = 0
for i=start, len do
local tmp = tonumber(string.sub(str, 1, i), readvar(context, BASE))
if tmp == nil then break end
result = tmp
last = i
end
return result, addr + last, len - last
end
local NUMBER = defcode('>NUMBER', 0, function(context)
local len = pop(context)
local addr = pop(context)
local accum = pop(context)
local num, rem_addr, remaining = _number(context, accum, addr, len)
push(context, num)
push(context, rem_addr)
push(context, remaining)
end)
local function _find(context, paddr)
local len = read(context, paddr)
local addr = paddr + 1
local current = readvar(context, LATEST)
while true do
if current == 0 then
return 0 end
local cur_len = bit32.band(read(context, current + 1), bit32.bor(F_HIDDEN, F_LENMASK))
-- game.print('current='..tostring(current)..' cur_len='..tostring(cur_len)..' len='..tostring(len))
local a = ''
local b = ''
if cur_len == len then
local match = true
for i=1, len do
-- +2 to skip link/flags, -1 because lua | addr is just text so only -1 because lua
a = a..string.char(read(context, current + i + 1))
b = b..string.char(read(context, addr + i - 1))
if read(context, current + i + 1) ~= read(context, addr + i - 1) then
match = false
break
end
end
if match then
return current
end
end
current = read(context, current)
end
end
local FIND = defcode('FIND', 0, function(context)
local paddr = pop(context)
push(context, _find(context, paddr))
end)
local function _tcfa(context, addr)
local len = bit32.band(read(context, addr+1), F_LENMASK)
return addr + len + 2
end
-- ( entry -- cfa)
local TCFA = defcode('>CFA', 0, function(context)
local addr = pop(context)
push(context, _tcfa(context, addr))
end)
-- ( entry -- does)
local TDOES = defcode('>DOES', 0, function(context)
local addr = pop(context)
push(context, _tcfa(context, addr) + 1)
end)
-- ( entry -- dfa)
local TDFA = defcode('>DFA', 0, function(context)
local addr = pop(context)
push(context, _tcfa(context, addr) + 2)
end)
-- ( xt -- dfa)
local TBODY = defcode('>BODY', 0, function(context)
local xt = pop(context)
push(context, xt + 2)
end)
local ALLOT = defword('ALLOT', 0, { -- ( n -- )
HERE, ADDSTORE, -- ( )
EXIT
})
-- Our cell size is 1 so do nothing /shrug
local CELLS = defword('CELLS', 0, {
EXIT
})
local COUNT = defword('COUNT', 0, {
DUP, FETCH, SWAP, INCR, SWAP,
EXIT
})
local BL = defword('BL', 0, {
LIT, string.byte(' '),
EXIT
})
local LBRAC = defcode('[', F_IMMED, function(context)
-- print('SET STATE EXEC')
writevar(context, STATE, 0)
-- print('STATE ', readvar(context, STATE))
end)
local RBRAC = defcode(']', 0, function(context)
-- print('SET STATE COMP')
writevar(context, STATE, 1)
-- print('STATE ', readvar(context, STATE))
end)
local IMMEDIATE = defcode('IMMEDIATE', F_IMMED, function(context)
local latest = readvar(context, LATEST)
local flags = read(context, latest + 1)
flags = bit32.bxor(flags, F_IMMED)
write(context, latest + 1, flags)
end)
local HIDDEN = defcode('HIDDEN', 0, function(context)
local entry = pop(context)
local flags = read(context, entry + 1)
flags = bit32.bxor(flags, F_HIDDEN)
write(context, entry + 1, flags)
end)
local HEADERCOMMA = defword('HEADER,', 0, {
HERE, FETCH, -- ( HERE ) ( c: )
LATEST, FETCH, COMMA, -- ( HERE ) ( c: LATEST )
LATEST, STORE, -- ( ) ( c: LATEST )
BL, WORD, COUNT, INCR, ALLOT, DROP, -- ( ) ( c: LATEST WORD ) Technically should align here
EXIT
})
local CREATE = defword('CREATE', 0, {
HEADERCOMMA,
LIT, __DOCREATE, COMMA,
LIT, 0, COMMA,
EXIT
})
local DOES = defword('(DOES>)', 0, {
FROMR, -- ( RP ) Steal the return address (points to target code)
LATEST, FETCH, TCFA, -- ( RP CFA ) Jump to CFA of LATEST word
DUP, LIT, __DODOES, SWAP, STORE, -- ( RP CFA ) Swap docreate for dodoes
INCR, STORE, -- ( ) Store RP in doesword
EXIT,
})
local COLON = defword(':', 0, {
HEADERCOMMA,
LIT, __DOCOL, COMMA,
LIT, 0, COMMA,
LATEST, FETCH, HIDDEN,
RBRAC,
EXIT
})
local SEMICOLON = defword(';', F_IMMED, {
LIT, EXIT, COMMA,
LATEST, FETCH, HIDDEN,
LBRAC,
EXIT,
})
-- Needed to bootstrap LITERAL/['] dependency loop
local COMPTICK = defcode('COMP\'', 0, function(context)
local xt = read(context, context.xp)
context.xp = context.xp + 1
push(context, xt)
end)
local LITSTRING = defcode('LITSTRING', 0, function(context)
local len = read(context, context.xp)
context.xp = context.xp + 1
local addr = context.xp
context.xp = context.xp + len
push(context, addr)
push(context, len)
end)
local TELL = defcode('TELL', 0, function(context)
local len = pop(context)
local addr = pop(context)
-- local str = ''
for i=1, len do
-- str = str..string.char(read(context, addr + i - 1))
table.insert(context.outbuf, string.char(read(context, addr + i - 1)))
end
-- game.print(str)
end)
local INTERPRET = defcode('INTERPRET', 0, function(context)
local paddr = pop(context)
local is_lit = false
local num = nil
local entry = _find(context, paddr)
local xt = nil
if entry == 0 then
dbg_print('Not found')
-- Literal?
is_lit = true
local remaining = nil
local len = read(context, paddr)
local addr = paddr + 1
num, _, remaining = _number(context, 0, addr, len)
if remaining ~= 0 then
-- Not a number
context.keybuf = {}
dbg_interp('ERR!')
output_string(context, 'err!\n')
return
else
dbg_interp('literal')
xt = LIT
end
else
dbg_print('entry=', entry)
xt = _tcfa(context, entry)
dbg_print('xt=', xt)
local flags = read(context, entry + 1)
if bit32.band(flags, F_IMMED) > 0 then
-- Execute XT
dbg_interp('IMMEDIATE EXEC')
local codeword = read(context, xt)
dbg_print('XT', xt, labels[xt])
dbg_print('CW', codeword, codeword_names[codeword])
do_codeword(context, xt)
return
end
end
-- Word or literal
-- print('STATE ', readvar(context, STATE))
if readvar(context, STATE) == 0 then
-- Executing
if is_lit then
dbg_interp('push '..tostring(num))
push(context, num)
else
-- Execute XT
dbg_interp('INTERPRET EXEC')
local codeword = read(context, xt)
dbg_interp('XT '..tostring(xt)..' '..tostring(labels[xt]))
dbg_interp('CW '..tostring(codeword)..' '..tostring(codeword_names[codeword]))
do_codeword(context, xt)
end
else
-- Compiling
dbg_interp('comma '..tostring(xt))
_comma(context, xt)
if is_lit then
dbg_interp('comma '..tostring(num))
_comma(context, num)
end
end
end)
local OK = defcode('OK', 0, function(context)
-- game.print('ok')
output_string(context, 'ok\n')
end)
local QUIT = defword('QUIT', 0, {
R0, RSPSTORE,
BL, WORD,
DUP,FETCH,ZGT,ZBRANCH,4,
INTERPRET,
BRANCH,2,
DROP,
OK,
BRANCH,-13,
EXIT,
})
local EXECUTE = defcode('EXECUTE', 0, function(context)
local xt = pop(context)
local cw = read(context, xt)
dbg_print('EXECUTE EXEC')
do_codeword(context, xt)
end)
local ITEMS = defcode('ITEMS', 0, function(context)
for id, name in pairs(sigs.item) do
output_string(context, id..' '..name..'\n')
end
end)
local FLUIDS = defcode('FLUIDS', 0, function(context)
for id, name in pairs(sigs.fluid) do
output_string(context, id..' '..name..'\n')
end
end)
local SIGNALS = defcode('SIGNALS', 0, function(context)
for id, name in pairs(sigs.virtual_signal) do
output_string(context, id..' '..name..'\n')
end
end)
local NSIGS = defcode('#SLOTS', 0, function(context)
local control = context.entity.get_or_create_control_behavior()
local section = control.get_section(1)
push(context, section.filters_count)
end)
local LIST_SIGS = defcode('LIST-SLOTS', 0, function(context)
local control = context.entity.get_or_create_control_behavior()
local section = control.get_section(1)
local filters = section.filters
end)
local function id_for_signal(type, name)
-- Factorio moment
if type == 'virtual' then type = 'virtual_signal' end
for i, sname in pairs(sigs[type]) do
if sname == name then return i end
end
return -1
end
local SLOTFETCH = defcode('SLOT@', 0, function(context)
local control = context.entity.get_or_create_control_behavior()
local section = control.get_section(1)
local filters = section.filters
local index = pop(context)
if index > section.filters_count then
error('Invalid slot index')
end
local filter = filters[index]
push(context, id_for_signal(filter.value.type, filter.value.name))
push(context, filter.min)
end)
function set_slot(context, type)
local control = context.entity.get_or_create_control_behavior()
control.enabled = true
local section = control.get_section(1)
section.active = true
local filters = section.filters
local value = pop(context)
local id = pop(context)
local index = pop(context)
local sig_list = sigs[type]
if id > #sig_list then
error('Invalid signal ID')
end
local name = sig_list[id]
-- More factorio cope
if type == 'virtual_signal' then type = 'virtual' end
local new_filter = { min = value, ['value'] = { comparator = '=', name = name, quality = 'normal', type = type } }
filters[index] = new_filter
section.filters = filters
end
local ITEMSTORE = defcode('ITEM!', 0, function(context)
set_slot(context, 'item')
end)
local FLUIDSTORE = defcode('FLUID!', 0, function(context)
set_slot(context, 'fluid')
end)
local SIGNALSTORE = defcode('SIGNAL!', 0, function(context)
set_slot(context, 'virtual_signal')
end)
local SLEEP = defcode('SLEEP', 0, function(context)
local ms = pop(context)
context.sleep_ticks = math.floor(ms * 60.0 / 1000.0)
end)
local DICTEND = defword('DICTEND', 0, {QUIT})
write(init_context, 0, QUIT)
init_context.xp = 0
writevar(init_context, HERE, init_here)
writevar(init_context, LATEST, link)
-- Bake the base forth code
for c in base_fth:gmatch('.') do
table.insert(init_context.keybuf, string.byte(c))
end
table.insert(init_context.keybuf, 10)
while true do
local waiting = forth_tick(init_context)
if waiting then break end
end
-- log('End bake')
-- for i, v in pairs(init_context.memspace) do
-- log(tostring(i)..' '..tostring(v))
-- end
init_context.outbuf = {}
init_context.valid = true
local new_ctx = copy_context(init_context)
new_ctx.entity = entity
return new_ctx
end
function pprint(thing)
for i, v in pairs(thing) do
print(i, v)
end
end
function pc(ctx)
print(ctx.xp, labels[read(ctx, ctx.xp)])
end
-- local f = io.open('base.fth', 'r')
-- if f ~= nil then
-- feed_file(ctx, f)
-- f:close()
-- end
function e()
while true do
local waiting = forth_tick(ctx)
if waiting then
local key = string.byte(io.read(1))
dbg_print('feed ', key, string.char(key))
feed_key(ctx, key)
end
end
end