local ds = require("DataStructures") local Dictionary, Stack, WordInfo, Environment = ds.Dictionary, ds.Stack, ds.WordInfo, ds.Environment local Pointer = ds.Pointer local InputStream = require("InputStream") local CoreHelpers = {} function CoreHelpers.defineWord(dict: Dictionary, str: string, func: function(Environment): any..., imm: boolean, toFunc: function(Environment) | nil) local info = WordInfo:new(func, imm, toFunc) dict:define(str, info) end function CoreHelpers.standardInputRefill(): string io.write("\nok> ") local input = io.read().."\n" return input end function CoreHelpers._oneReadRefill(str: string): function(): string local alreadyRead: boolean = false return function(): string if not alreadyRead then alreadyRead = true return str else return "" end end end function CoreHelpers._fileRefill(fname: string): function(): string local f = assert(io.open(fname, "r")) return function(): string local chunk = f:read(2^3) if not chunk then chunk = "" end return chunk end end function CoreHelpers.fileStream(fname: string): InputStream local istream = InputStream:new(CoreHelpers._fileRefill(fname)) return istream end function CoreHelpers.oneReadInputStream(str: string): InputStream local iStream = InputStream:new(CoreHelpers._oneReadRefill(str)) return iStream end function CoreHelpers.areNumbers(a: any, b: any): boolean return a is number and b is number end function CoreHelpers.getActiveDataStack(state: Environment): Stack return state.activeDataStack end function CoreHelpers.isNumber(token: string): boolean if tonumber(token) ~= nil then return true else return false end end function CoreHelpers.isWhitespace(chr: string): boolean return (chr == " " or chr == "\t" or chr == "\r" or chr == "\n" or chr == "\v" or chr == "\f") end function CoreHelpers.skipWhitespace(state: Environment) local chr = state.activeInputStream:curr() while (CoreHelpers.isWhitespace(chr)) do state.activeInputStream:next() chr = state.activeInputStream:curr() end end function CoreHelpers.skipSpaces(e: Environment) local chr = e.activeInputStream:curr() while chr == " " do e.activeInputStream:next() chr = e.activeInputStream:curr() end end function CoreHelpers.popTwoOperands(state: Environment): any, any local stack = CoreHelpers.getActiveDataStack(state) local b: any = stack:pop() local a: any = stack:pop() return a, b end function CoreHelpers.parseToken(state: Environment): string | nil local token: string | nil = "" local chr: string | nil = "" if not state.activeInputStream:curr() then return nil end while(not CoreHelpers.isWhitespace(chr) and chr ~= nil) do token = token..(chr as string) chr = state.activeInputStream:next() end return token end function CoreHelpers.reset(env: Environment) env.instructionPointer.index = 0 -- will get incremented after this instruction end function CoreHelpers.ret(env: Environment) local retAddr = env.returnStack:pop() as Pointer env.instructionPointer = retAddr end function CoreHelpers.makeCall(body: {function(Environment)}, localCount: integer | nil): function(Environment) if localCount == nil then localCount = 0 end return function(e: Environment) e.returnStack:push(e.instructionPointer) for i = localCount as number, 1, -1 do e.returnStack:push(-1234) end e.instructionPointer = Pointer:new(body, 0) end end function CoreHelpers.compile(env: Environment) local stack = env.activeDataStack local f = stack:pop() as function(Environment) table.insert(env.currentDefinition, f) end function CoreHelpers.literal(env: Environment) local stack = env.activeDataStack local val = stack:pop() local f = function(e: Environment) local s = e.activeDataStack s:push(val) end table.insert(env.currentDefinition, f) end function CoreHelpers.noop(_: Environment) return end function CoreHelpers.postpone(e: Environment) CoreHelpers.noop(e) end function CoreHelpers.execute(env: Environment) local stack = CoreHelpers.getActiveDataStack(env) local func: function(Environment) = stack:pop() as function(Environment) func(env) end function CoreHelpers.recNumber(env: Environment) local dataStack = CoreHelpers.getActiveDataStack(env) local token = dataStack:pop() as string if CoreHelpers.isNumber(token) then -- number dataStack:push(tonumber(token)) -- interpret dataStack:push(CoreHelpers.noop) -- compile dataStack:push(CoreHelpers.literal) -- immediate dataStack:push(CoreHelpers.literal) -- return success dataStack:push(true) return end dataStack:push(token) dataStack:push(false) end function CoreHelpers.searchDictionaries(e: Environment, token: string): WordInfo, Dictionary local wordinfo = e.locals:lookup(token) if wordinfo then return wordinfo end for _, dictionary in ipairs(e.dictionaries) do wordinfo = dictionary:lookup(token) if wordinfo then return wordinfo end end end function selectRecXT(env: Environment): function(Environment) local dataStack = env.activeDataStack if env.compileState then dataStack:pop() local selected = dataStack:pop() dataStack:pop() return selected as function(Environment) else dataStack:pop() dataStack:pop() return dataStack:pop() as function(Environment) end end function CoreHelpers.interpreters_outer(env: Environment) local dataStack = env.activeDataStack if not env.toMode then CoreHelpers.skipWhitespace(env) else CoreHelpers.skipSpaces(env) end local token: string | nil = CoreHelpers.parseToken(env) if not token then env.running = false return end for _, rec in ipairs(env.recognizers) do dataStack:push(token) rec(env) local result = dataStack:pop() as boolean if not result then token = dataStack:pop() as string end if result then (selectRecXT(env))(env) return end end io.write("\""..token.."\"?\n") end function CoreHelpers.interpreters_inner(env: Environment) while(env.running) do env.instructionPointer:deref() as function(Environment)(env) env.instructionPointer:inc() end end function CoreHelpers.recWord(env: Environment) local dataStack = CoreHelpers.getActiveDataStack(env) local token = dataStack:pop() as string local wordinfo = CoreHelpers.searchDictionaries(env, token) if wordinfo then -- XT if not env.toMode then dataStack:push((wordinfo as WordInfo).func) else dataStack:push((wordinfo as WordInfo).toFunc) env.toMode = false end -- interpret dataStack:push(CoreHelpers.execute) -- compile if (wordinfo as WordInfo).immediate then dataStack:push(CoreHelpers.execute) else dataStack:push(CoreHelpers.compile) end -- immediate dataStack:push(CoreHelpers.postpone) -- return success dataStack:push(true) return end dataStack:push(token) dataStack:push(false) end --function CoreHelpers.ack(e: Environment) -- io.write("ok\n") --end return CoreHelpers