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 local array = {} for number in token:sub(2, -2):gmatch("%S+") do table.insert(array, tonumber(number)) end table.insert(stack, array) elseif token == ".." then local array = {} for i = 1, #stack do table.insert(array, stack[i]) end table.insert(stack, array) elseif token == "@.." then local result = {} for i = 1, #stack do local value = stack[i] if type(value) == "table" then for j = 1, #value do table.insert(result, value[j]) end else table.insert(result, value) end end table.insert(stack, result) elseif token == "." then table.remove(stack) elseif token == ":" then table.insert(stack, stack[#stack]) elseif token == "?" then local a = table.remove(stack) local b = table.remove(stack) table.insert(stack, a) table.insert(stack, b) elseif token == "=" then local b = table.remove(stack) local a = table.remove(stack) table.insert(stack, a == b) elseif token == "@=" then -- checks if the values of 2 equally lenghted arrays are equal, and returns a bit mask of the differences local b = table.remove(stack) local a = table.remove(stack) if #a ~= #b then error("Arrays must have equal length") end local result = {} for i = 1, #a do table.insert(result, a[i] == b[i]) end table.insert(stack, result) elseif token == "@" then local index = table.remove(stack) local array = table.remove(stack) table.insert(stack, array[index]) 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 == "@+" then local a = table.remove(stack) local b = table.remove(stack) if type(a) == "table" and type(b) == "table" then if #a ~= #b then error("Arrays must have equal length") end local result = {} for i = 1, #a do table.insert(result, a[i] + b[i]) end table.insert(stack, result) elseif type(a) == "table" and type(b) == "number" then local result = {} for i = 1, #a do table.insert(result, a[i] + b) end table.insert(stack, result) elseif type(a) == "number" and type(b) == "table" then local result = {} for i = 1, #b do table.insert(result, a + b[i]) end table.insert(stack, result) else error("Invalid operands for addition") end elseif token == "@*" then local a = table.remove(stack) local b = table.remove(stack) if type(a) == "table" and type(b) == "table" then if #a ~= #b then error("Arrays must have equal length") end local result = {} for i = 1, #a do table.insert(result, a[i] * b[i]) end table.insert(stack, result) elseif type(a) == "table" and type(b) == "number" then local result = {} for i = 1, #a do table.insert(result, a[i] * b) end table.insert(stack, result) elseif type(a) == "number" and type(b) == "table" then local result = {} for i = 1, #b do table.insert(result, a * b[i]) end table.insert(stack, result) else error("Invalid operands for multiplication") end elseif token == "@-" then local a = table.remove(stack) local b = table.remove(stack) if type(a) == "table" and type(b) == "table" then if #a ~= #b then error("Arrays must have equal length") end local result = {} for i = 1, #a do table.insert(result, a[i] - b[i]) end table.insert(stack, result) elseif type(a) == "table" and type(b) == "number" then local result = {} for i = 1, #a do table.insert(result, a[i] - b) end table.insert(stack, result) elseif type(a) == "number" and type(b) == "table" then local result = {} for i = 1, #b do table.insert(result, a - b[i]) end table.insert(stack, result) else error("Invalid operands for subtraction") end elseif token == "@/" then local a = table.remove(stack) local b = table.remove(stack) if type(a) == "table" and type(b) == "table" then if #a ~= #b then error("Arrays must have equal length") end local result = {} for i = 1, #a do table.insert(result, a[i] / b[i]) end table.insert(stack, result) elseif type(a) == "table" and type(b) == "number" then local result = {} for i = 1, #a do table.insert(result, a[i] / b) end table.insert(stack, result) elseif type(a) == "number" and type(b) == "table" then local result = {} for i = 1, #b do table.insert(result, a / b[i]) end table.insert(stack, result) else error("Invalid operands for division") end 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