about summary refs log blame commit diff stats
path: root/lua/chupacabra/chupacabra.lua
blob: 107a832b388f5f695c45613316cc60919d05df5f (plain) (tree)
1
2
3
4
5

                     
                                       
                               
                     












                                           






                                             
                                                                                                                      


                                                





                                                                     





















                                                                                                             















                                         
                                 
                                         























                                                                  
               
                                 
                                         























                                                                  
               
                                 
                                         























                                                                  
               
                                 
                                         













                                                                  
               






                                                                  
            

                                                  











                                               
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 = {}

    -- FIXME: explore if there is a better way to approach this, so that you don't need to special case the array math
    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 == ".." 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)
            --TODO: add the ability for this keywor, or a paired @. keyword to remove an array from the stack
        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