local ds = require("DataStructures") 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 local defineWord = helpers.defineWord local ret = helpers.ret local makeCall = helpers.makeCall -- Mathematical operations local function add(state: Environment) local a, b = popTwoOperands(state) if areNumbers(a,b) then local c = (a as number) + (b as number) getActiveDataStack(state):push(c) else error("invalid operands for add operation!") end end local function sub(state: Environment) local a, b = popTwoOperands(state) if areNumbers(a, b) then local c = (a as number) - (b as number) getActiveDataStack(state):push(c) else error("invalid operands for sub operation!") end end local function mul(state: Environment) local a, b = popTwoOperands(state) if areNumbers(a, b) then local c = (a as number) * (b as number) getActiveDataStack(state):push(c) else error("invalid operands for mul operation!") end end local function div(state: Environment) local a, b = popTwoOperands(state) if areNumbers(a, b) then local c = (a as number) / (b as number) getActiveDataStack(state):push(c) else error("invalid operands for div operation!") end end local function dup(state: Environment) local stack = getActiveDataStack(state) local top = stack:pop() stack:push(top) stack:push(top) end local function swap(state: Environment) local stack = getActiveDataStack(state) local a, b = popTwoOperands(state) stack:push(b) stack:push(a) end local function rot(state: Environment) local stack = getActiveDataStack(state) local c, b, a= stack:pop(), stack:pop(), stack:pop() stack:push(b) stack:push(c) stack:push(a) end local function drop(state: Environment) local stack = getActiveDataStack(state) stack:pop() end local function over(state: Environment) local stack = getActiveDataStack(state) local b, a = stack:pop(), stack:pop() stack:push(a) stack:push(b) stack:push(a) end -- I/O operations local function dot(state: Environment) local out = state.activeDataStack:pop() io.write(tostring(out) as string.."\n") end local function twoDup(state: Environment) over(state) over(state) end local function twoSwap(state: Environment) local stack = getActiveDataStack(state) 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 local function twoOver(state: Environment) local stack = getActiveDataStack(state) 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 local function nip(state: Environment) local stack = getActiveDataStack(state) local b, _ = stack:pop(), stack:pop() stack:push(b) end local function tuck(state: Environment) swap(state) over(state) end local function roll(state: Environment) local stack = getActiveDataStack(state) 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 local function getExecutionToken(state: Environment) local stack = getActiveDataStack(state) skipWhitespace(state) local name: string = parseToken(state) for _, dictionary in ipairs(state.dictionaries) do local wordinfo = dictionary:lookup(name) if wordinfo then stack:push((wordinfo as WordInfo).func) break end end end local function execute(state: Environment) local stack = getActiveDataStack(state) local func: function(Environment) = stack:pop() as function(Environment) func(state) end 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) 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) defineWord(CoreWords, "'", getExecutionToken, false) defineWord(CoreWords, "EXECUTE", execute, false) CoreWords:define("+", addInfo) CoreWords:define("-", subInfo) CoreWords:define("*", mulInfo) CoreWords:define("/", divInfo) CoreWords:define(".", dotInfo) 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) return CoreWords