forth-stuff/CoreWords.tl

425 lines
12 KiB
Plaintext
Raw Normal View History

2021-05-12 08:32:25 +00:00
local ds = require("DataStructures")
2021-05-14 08:29:54 +00:00
local Dictionary, Stack, WordInfo, Environment = ds.Dictionary, ds.Stack, ds.WordInfo, ds.Environment
local helpers = require("CoreHelpers")
local areNumbers, getActiveDataStack, isNumber, isWhitespace= helpers.areNumbers, helpers.getActiveDataStack, helpers.isNumber, helpers.isWhitespace
local skipWhitespace, parseToken = helpers.skipWhitespace, helpers.parseToken
local popTwoOperands = helpers.popTwoOperands
2021-05-13 10:54:06 +00:00
local defineWord = helpers.defineWord
local ret = helpers.ret
local makeCall = helpers.makeCall
2021-05-25 08:41:39 +00:00
local literal = helpers.literal
local compile = helpers.compile
2021-05-11 08:19:54 +00:00
-- Mathematical operations
2021-05-25 07:27:57 +00:00
local function add(env: Environment)
local a, b = popTwoOperands(env)
if areNumbers(a,b) then
2021-05-11 08:19:54 +00:00
local c = (a as number) + (b as number)
2021-05-25 07:27:57 +00:00
getActiveDataStack(env):push(c)
2021-05-11 06:58:09 +00:00
else
error("invalid operands for add operation!")
end
end
2021-05-25 07:27:57 +00:00
local function sub(env: Environment)
local a, b = popTwoOperands(env)
if areNumbers(a, b) then
2021-05-11 08:19:54 +00:00
local c = (a as number) - (b as number)
2021-05-25 07:27:57 +00:00
getActiveDataStack(env):push(c)
2021-05-11 08:19:54 +00:00
else
error("invalid operands for sub operation!")
end
2021-05-11 06:58:09 +00:00
end
2021-05-25 07:27:57 +00:00
local function mul(env: Environment)
local a, b = popTwoOperands(env)
if areNumbers(a, b) then
2021-05-11 08:19:54 +00:00
local c = (a as number) * (b as number)
2021-05-25 07:27:57 +00:00
getActiveDataStack(env):push(c)
2021-05-11 08:19:54 +00:00
else
error("invalid operands for mul operation!")
end
end
2021-05-25 07:27:57 +00:00
local function div(env: Environment)
local a, b = popTwoOperands(env)
if areNumbers(a, b) then
2021-05-11 08:19:54 +00:00
local c = (a as number) / (b as number)
2021-05-25 07:27:57 +00:00
getActiveDataStack(env):push(c)
2021-05-11 08:19:54 +00:00
else
error("invalid operands for div operation!")
end
end
2021-05-13 10:54:06 +00:00
2021-05-25 07:27:57 +00:00
local function dup(env: Environment)
local stack = getActiveDataStack(env)
2021-05-13 10:54:06 +00:00
local top = stack:pop()
stack:push(top)
stack:push(top)
end
2021-05-25 07:27:57 +00:00
local function swap(env: Environment)
local stack = getActiveDataStack(env)
local a, b = popTwoOperands(env)
2021-05-13 10:54:06 +00:00
stack:push(b)
stack:push(a)
end
2021-05-25 07:27:57 +00:00
local function rot(env: Environment)
local stack = getActiveDataStack(env)
2021-05-13 10:54:06 +00:00
local c, b, a= stack:pop(), stack:pop(), stack:pop()
stack:push(b)
stack:push(c)
stack:push(a)
end
2021-05-25 07:27:57 +00:00
local function drop(env: Environment)
local stack = getActiveDataStack(env)
2021-05-13 10:54:06 +00:00
stack:pop()
end
2021-05-25 07:27:57 +00:00
local function over(env: Environment)
local stack = getActiveDataStack(env)
2021-05-13 10:54:06 +00:00
local b, a = stack:pop(), stack:pop()
stack:push(a)
stack:push(b)
stack:push(a)
end
2021-05-11 08:19:54 +00:00
-- I/O operations
2021-05-25 07:27:57 +00:00
local function dot(env: Environment)
local out = env.activeDataStack:pop()
2021-05-29 07:12:29 +00:00
io.write(tostring(out) as string)
2021-05-13 10:54:06 +00:00
end
2021-05-25 07:27:57 +00:00
local twoDup = helpers.makeCall{over, over, ret}
local function twoSwap(env: Environment)
local stack = getActiveDataStack(env)
2021-05-13 10:54:06 +00:00
local d, c, b, a = stack:pop(), stack:pop(), stack:pop(), stack:pop()
stack:push(c)
stack:push(d)
stack:push(a)
stack:push(b)
end
2021-05-11 08:19:54 +00:00
2021-05-25 07:27:57 +00:00
local function twoOver(env: Environment)
local stack = getActiveDataStack(env)
2021-05-13 10:54:06 +00:00
local d, c, b, a = stack:pop(), stack:pop(), stack:pop(), stack:pop()
stack:push(a)
stack:push(b)
stack:push(c)
stack:push(d)
stack:push(a)
stack:push(b)
end
2021-05-25 07:27:57 +00:00
local function nip(env: Environment)
local stack = getActiveDataStack(env)
2021-05-13 10:54:06 +00:00
local b, _ = stack:pop(), stack:pop()
stack:push(b)
2021-05-11 08:19:54 +00:00
end
2021-05-25 07:27:57 +00:00
local tuck = helpers.makeCall{swap, over, ret}
local function roll(env: Environment)
local stack = getActiveDataStack(env)
2021-05-13 10:54:06 +00:00
local u = stack:pop()
local bufferStack = Stack:new()
if u is number then
local v = u as number
while(v > 0) do
local item = stack:pop()
bufferStack:push(item)
v = v - 1
end
local newTop = stack:pop()
local x = (u as number)
while(x > 0) do
local item = bufferStack:pop()
stack:push(item)
x = x - 1
end
stack:push(newTop)
else
error("u is not a number")
end
end
2021-05-25 07:27:57 +00:00
local function getExecutionToken(env: Environment)
local stack = getActiveDataStack(env)
skipWhitespace(env)
local name: string = parseToken(env)
for _, dictionary in ipairs(env.dictionaries) do
2021-05-13 11:09:05 +00:00
local wordinfo = dictionary:lookup(name)
if wordinfo then
stack:push((wordinfo as WordInfo).func)
break
end
end
end
2021-05-13 10:54:06 +00:00
2021-05-25 07:27:57 +00:00
local function execute(env: Environment)
local stack = getActiveDataStack(env)
2021-05-14 08:29:54 +00:00
local func: function(Environment) = stack:pop() as function(Environment)
2021-05-25 07:27:57 +00:00
func(env)
end
local function enterCompileMode(e: Environment)
e.compileState = true
end
local function exitCompileMode(e: Environment)
e.compileState = false
end
2021-05-26 04:27:18 +00:00
local function colon(e: Environment)
skipWhitespace(e)
local name: string | nil = parseToken(e)
if not name then
e.running = false
return
end
e.currentDefinitionName = name as string
enterCompileMode(e)
end
local function semicolon(e: Environment)
2021-05-29 03:50:58 +00:00
table.insert(e.currentDefinition as {function(Environment)}, helpers.ret)
2021-05-26 04:27:18 +00:00
local instrs = e.currentDefinition
2021-05-29 03:50:58 +00:00
local call = helpers.makeCall(instrs as {function(Environment)})
2021-05-26 04:27:18 +00:00
local dict = e.dictionaries[1]
defineWord(dict, e.currentDefinitionName, call, false)
2021-05-29 07:12:29 +00:00
e.mostRecentDefinition = e.currentDefinitionName
2021-05-26 04:27:18 +00:00
e.currentDefinitionName = nil
e.currentDefinition = {}
exitCompileMode(e)
end
2021-05-29 03:50:58 +00:00
local function toCompileStack(e: Environment)
local dataStack = getActiveDataStack(e)
local compileStack = e.compilerStack
local val = dataStack:pop()
compileStack:push(val)
end
local function fromCompileStack(e: Environment)
e.activeDataStack:push(e.compilerStack:pop())
end
local function getDefinitionIndex(e: Environment)
e.activeDataStack:push(#e.currentDefinition)
end
local function unresolvedZeroBranch(e: Environment)
helpers.noop(e)
end
local function unresolvedUncondBranch(e: Environment)
helpers.noop(e)
end
local function getResolvedZeroBranch(destinationIndex: integer): function(Environment)
return function(e: Environment)
local flag = e.activeDataStack:pop()
if flag == 0 or not flag then
e.instructionPointer.index = destinationIndex
end
end
end
local function resolveBranch(e: Environment, sourceIndex: integer, destinationIndex: integer): function(Environment)
if e.currentDefinition[sourceIndex] == unresolvedZeroBranch then
return function(e: Environment)
local flag = e.activeDataStack:pop()
if flag == 0 or not flag then
e.instructionPointer.index = destinationIndex
end
end
elseif e.currentDefinition[sourceIndex] == unresolvedUncondBranch then
return function(e: Environment)
e.instructionPointer.index = destinationIndex
end
end
-- TODO: abort if none of the checks succeed
end
local function forthIf(e: Environment)
table.insert(e.currentDefinition as {function(Environment)}, unresolvedZeroBranch)
getDefinitionIndex(e)
toCompileStack(e)
end
local function patchBranch(e: Environment)
local branchIndex = e.activeDataStack:pop() as integer
local destinationIndex = e.activeDataStack:pop() as integer
e.currentDefinition[branchIndex as integer] = resolveBranch(e,branchIndex,destinationIndex)
end
local function forthThen(e: Environment)
getDefinitionIndex(e)
fromCompileStack(e)
patchBranch(e)
end
local function forthElse(e: Environment)
table.insert(e.currentDefinition as {function(Environment)}, unresolvedUncondBranch)
getDefinitionIndex(e)
fromCompileStack(e)
patchBranch(e)
getDefinitionIndex(e)
toCompileStack(e)
end
local function cmplt(e: Environment)
local a, b = popTwoOperands(e)
e.activeDataStack:push((a as number) < (b as number))
end
local function cmplte(e: Environment)
local a, b = popTwoOperands(e)
e.activeDataStack:push((a as number) <= (b as number))
end
local function cmpe(e: Environment)
local a, b = popTwoOperands(e)
e.activeDataStack:push((a as number) == (b as number))
end
local function cmpgte(e: Environment)
local a, b = popTwoOperands(e)
e.activeDataStack:push((a as number) >= (b as number))
end
local function cmpgt(e: Environment)
local a, b = popTwoOperands(e)
e.activeDataStack:push((a as number) > (b as number))
end
local function cmpne(e: Environment)
local a, b = popTwoOperands(e)
e.activeDataStack:push((a as number) ~= (b as number))
end
2021-05-29 07:12:29 +00:00
local function markImmediate(e: Environment)
helpers.searchDictionaries(e, e.mostRecentDefinition).immediate = true
end
local function forthBegin(e: Environment)
getDefinitionIndex(e)
toCompileStack(e)
end
local function forthUntil(e: Environment)
fromCompileStack(e)
table.insert(e.currentDefinition as {function(Environment)}, unresolvedZeroBranch)
getDefinitionIndex(e)
patchBranch(e)
end
local function forthAgain(e: Environment)
fromCompileStack(e)
table.insert(e.currentDefinition as {function(Environment)}, unresolvedUncondBranch)
getDefinitionIndex(e)
patchBranch(e)
end
local function forthWhile(e: Environment)
fromCompileStack(e)
table.insert(e.currentDefinition as {function(Environment)}, unresolvedZeroBranch)
getDefinitionIndex(e)
toCompileStack(e)
toCompileStack(e)
end
local function forthRepeat(e: Environment)
fromCompileStack(e)
table.insert(e.currentDefinition as {function(Environment)}, unresolvedUncondBranch)
getDefinitionIndex(e)
patchBranch(e)
getDefinitionIndex(e)
fromCompileStack(e)
patchBranch(e)
end
2021-05-26 04:27:18 +00:00
local function stringLiteral(e: Environment)
local str: string | nil = ""
local chr: string | nil = ""
if not e.activeInputStream:curr() then e.activeDataStack:push("") end
while(not (chr == "\"") and chr ~= nil) do
str = str..(chr as string)
chr = e.activeInputStream:next()
end
e.activeInputStream:next()
e.activeDataStack:push(str as string)
end
2021-05-26 04:27:18 +00:00
2021-05-11 08:19:54 +00:00
local CoreWords = Dictionary:new()
local addInfo = WordInfo:new(add, false)
local subInfo = WordInfo:new(sub, false)
local mulInfo = WordInfo:new(mul, false)
local divInfo = WordInfo:new(div, false)
local dotInfo = WordInfo:new(dot, false)
2021-05-13 10:54:06 +00:00
local dupInfo = WordInfo:new(dup, false)
local swapInfo = WordInfo:new(swap, false)
local rotInfo = WordInfo:new(rot, false)
local dropInfo = WordInfo:new(drop, false)
local overInfo = WordInfo:new(over, false)
local twoDupInfo = WordInfo:new(twoDup, false)
local twoSwapInfo = WordInfo:new(twoSwap, false)
local twoOverInfo = WordInfo:new(twoOver, false)
local nipInfo = WordInfo:new(nip, false)
defineWord(CoreWords, "TUCK", tuck, false)
defineWord(CoreWords, "ROLL", roll, false)
2021-05-13 11:09:05 +00:00
defineWord(CoreWords, "'", getExecutionToken, false)
defineWord(CoreWords, "EXECUTE", execute, false)
2021-05-25 08:07:03 +00:00
defineWord(CoreWords, "[", exitCompileMode, true)
2021-05-25 07:27:57 +00:00
defineWord(CoreWords, "]", enterCompileMode, false)
2021-05-25 08:07:03 +00:00
defineWord(CoreWords, "COMPILE,", compile, true)
defineWord(CoreWords, "LITERAL", literal, true)
2021-05-26 04:27:18 +00:00
defineWord(CoreWords, ":", colon, true)
defineWord(CoreWords, ";", semicolon, true)
2021-05-29 03:50:58 +00:00
defineWord(CoreWords, ">C",toCompileStack, false)
defineWord(CoreWords, "C>", fromCompileStack, false)
defineWord(CoreWords, "DP", getDefinitionIndex, false)
defineWord(CoreWords, "IF", forthIf, true)
defineWord(CoreWords, "THEN", forthThen, true)
defineWord(CoreWords, "ELSE", forthElse, true)
2021-05-29 07:12:29 +00:00
defineWord(CoreWords, "IMMEDIATE", markImmediate, false)
defineWord(CoreWords, "BEGIN", forthBegin, true)
defineWord(CoreWords, "UNTIL", forthUntil, true)
defineWord(CoreWords, "WHILE", forthWhile, true)
defineWord(CoreWords, "REPEAT", forthRepeat, true)
defineWord(CoreWords, "AGAIN", forthAgain, true)
2021-05-29 03:50:58 +00:00
defineWord(CoreWords,"<", cmplt, false)
defineWord(CoreWords,"<=", cmplte, false)
defineWord(CoreWords,"=", cmpe, false)
defineWord(CoreWords,">=", cmpgte, false)
defineWord(CoreWords,">", cmpgt, false)
defineWord(CoreWords,"<>", cmpne, false)
defineWord(CoreWords,"S\"", stringLiteral, false)
2021-05-29 03:50:58 +00:00
2021-05-13 10:54:06 +00:00
CoreWords:define("+", addInfo)
CoreWords:define("-", subInfo)
CoreWords:define("*", mulInfo)
CoreWords:define("/", divInfo)
CoreWords:define(".", dotInfo)
2021-05-13 10:54:06 +00:00
CoreWords:define("DUP", dupInfo)
CoreWords:define("SWAP", swapInfo)
CoreWords:define("ROT", rotInfo)
CoreWords:define("DROP", dropInfo)
CoreWords:define("OVER", overInfo)
CoreWords:define("2DUP", twoDupInfo)
CoreWords:define("2SWAP", twoSwapInfo)
CoreWords:define("2OVER", twoOverInfo)
CoreWords:define("NIP", nipInfo)
local sqr = {dup, mul, ret}
local sqrf = makeCall(sqr)
defineWord(CoreWords, "SQR", sqrf, false)
2021-05-11 08:19:54 +00:00
return CoreWords
2021-05-11 06:55:13 +00:00