local chupacabra = {} -- Parse a string into a list of tokens function chupacabra.tokenize(s) local tokens = {} local array = nil for token in s:gmatch("%S+") do if array then array = array .. " " .. token if token:find("]$") then table.insert(tokens, array) array = nil end elseif token:find("^%[") then array = token else table.insert(tokens, token) end end return tokens end function chupacabra.evaluate(tokens, context) local stack = {} for _, token in ipairs(tokens) do if tonumber(token) then table.insert(stack, tonumber(token)) elseif token:match("^%b[]$") then -- If the token is an array local array = {} for number in token:sub(2, -2):gmatch("%S+") do table.insert(array, tonumber(number)) end table.insert(stack, array) elseif token == "pop" then table.remove(stack) -- testing is fun elseif token == "+" then local b = table.remove(stack) local a = table.remove(stack) table.insert(stack, a + b) elseif token == "-" then local b = table.remove(stack) local a = table.remove(stack) table.insert(stack, a - b) elseif token == "*" then local b = table.remove(stack) local a = table.remove(stack) table.insert(stack, a * b) elseif token == "/" then local b = table.remove(stack) local a = table.remove(stack) table.insert(stack, a / b) elseif token == "map+" then -- FIXME: fix all map functions to either add 2 arrays or spread local a = table.remove(stack) for i, v in ipairs(a) do a[i] = v + 1 end table.insert(stack, a) elseif token == "map*" then local a = table.remove(stack) for i, v in ipairs(a) do a[i] = v * 2 end table.insert(stack, a) elseif token == "map-" then local a = table.remove(stack) for i, v in ipairs(a) do a[i] = v - 1 end table.insert(stack, a) elseif token == "map/" then local a = table.remove(stack) for i, v in ipairs(a) do a[i] = v / 2 end table.insert(stack, a) else error("invalid token: " .. token) end end return table.remove(stack) end function chupacabra.run(str, context) local tokens = chupacabra.tokenize(str) return chupacabra.evaluate(tokens, context) end return chupacabra