local chupacabra = {}
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 equal length 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.table_to_string(t)
local str = "["
for i, v in ipairs(t) do
if i > 1 then
str = str .. " "
end
str = str .. tostring(v)
end
str = str .. "]"
return str
end
function chupacabra.run(str, context)
local tokens = chupacabra.tokenize(str)
return chupacabra.evaluate(tokens, context)
end
return chupacabra