284 lines
6.8 KiB
Plaintext
284 lines
6.8 KiB
Plaintext
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
|
|
|