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