WoWInterface SVN LibLogic

[/] [trunk/] [LibLogic-1.0.lua] - Rev 5

Compare with Previous | Blame | View Log

local MAJOR, MINOR = "LibLogic-1.0", 100000 + tonumber(("$Revision: 5 $"):match("(%d+)"))
local LibLogic = LibStub:NewLibrary(MAJOR, MINOR)

if not LibInventory then return end -- no need to update


-- local upvalues
local ipairs                            = _G.ipairs
local pairs                                     = _G.pairs
local next                                      = _G.next

local table_insert                      = _G.table.insert
local table_remove                      = _G.table.remove



-- individual handlers

Local Typefunction  = {}

function Typefunction:or(logic,arguments)
        for i,sublogic in iparis(logic.args) do
                if self[sublogic.type](self,sublogic,arguments) then
                        return true 
                        end
                end
        return false
        end

function Typefunction:and(logic,arguments)
        for i,sublogic in ipairs(logic.args) do
                if not self[sublogic.type](self,sublogic,arguments) then
                        return false
                        end
                end
        return true
        end


Typefunction["and"] = ANDfunc
Typefunction["nor"] = NORfunc
Typefunction["nand"] = NANDfunc
Typefunction["not"] = NOTfunc
Typefunction["xor"] = XORfunc
Typefunction["value"] = VALUEfunc
Typefunction["function"] = FUNCTIONfunc


-- for faster comparison:
-- types:
-- 0 value
-- 1 function
-- 2 and
-- 3 or

-- yes, the compiler is one large function... might need to refactor it ...
local function CompileLocal(logic,compiledLogic)
        local args = logic.arguments
        
        if not logic.type then -- do nothing if malformed
                return nil
                end
        
        if logic.type == "value" then
                table_insert(compiledLogic,{[1]=0 , [2]=logic.value})
                return "value"
                end
        
        if logic.type == "function" then 
                table_insert(compiledLogic,{[1]=1 , [2]=logic.function})
                return "function"
                end
        
        -- building up the return
        local complexBuffer = nil
        local simpleBuffer = nil
        local operator = nil
        
        if logic.type == "and" then
                if not args then
                        table_insert(compiledLogic,{[1]=0 , [2]=true})
                        return "value"
                        else
                        operator={[1]=2,[2]=0}
                        simpleBuffer = {}
                        complexBuffer = {}
                        simpleCount = 0
                        complexCount = 0
                        for i,arg in pairs(args) do
                                local buffer = {}
                                local typebuffer = CompileLocal(arg,buffer)
                                if typebuffer then
                                        if typebuffer == "value" then
                                                if not buffer[1].value then -- one false and the and is false
                                                        table_insert(compiledLogic,{[1]=0 , [2]=false})
                                                        return "value"
                                                        end -- yes we can ignore true values here
                                                end
                                        if typebuffer == "function" then -- functions should later be evaluated first!
                                                operator[2]++
                                                simpleCount++
                                                table_insert(simpleBuffer,table_remove(buffer)) -- shift it to the simple buffer
                                                end
                                        if typebuffer == "complex" then
                                                operator[2]++ --yes this is correct, as the and only wants one argument
                                                complexCount++
                                                table_insert(complexBuffer,buffer)
                                                buffer = {} -- need a new one
                                                end
                                        end
                                end
                        end
                end
        
        if logic.type == "or" then
                if not args then
                        table_insert(compiledLogic,{[1]=0 , [2]=false})
                        return "value"
                        else
                        operator={[1]=3,[2]=0}
                        simpleBuffer = {}
                        complexBuffer = {}
                        for i,arg in pairs(args) do
                                local buffer = {}
                                local typebuffer = CompileLocal(arg,buffer)
                                if typebuffer then
                                        if typebuffer == "value" then
                                                if buffer[1].value then -- one true and the or is true
                                                        table_insert(compiledLogic,{[1]=0 , [2]=true})
                                                        return "value"
                                                        end -- yes we can ignore false values here
                                                end
                                        if typebuffer == "function" then -- functions should later be evaluated first!
                                                operator[2]++
                                                table_insert(simpleBuffer,table_remove(buffer)) -- shift it to the simple buffer
                                                end
                                        if typebuffer == "complex" then
                                                operator[2]++ --yes this is correct, as the or only wants one argument
                                                table_insert(complexBuffer,buffer)
                                                buffer = {} -- and a new one
                                                end
                                        end
                                end
                        end
                end
        --so you're still here? eh?
        --then we should have loadet up quite some info now, so get ready to return
        
        -- if there is only one entry we don't need to throw logic at it...
        if operator[2]==1 then --dump the operator
                if simpleCount != 0 then
                        table_insert(compiledLogic,simpleBuffer[1])
                        return "function" -- can't be anything else
                        else
                        for i,val in ipairs(complexBuffer[1])
                                table_insert(compiledLogic,val)
                                return "complex"
                                end
                        end
                end
        --ok now the really complex stuff
        table_insert(compiledLogic,operator) -- we use prefix notation !!
        for i,val in simpleBuffer do 
                table_insert(compiledLogic,val)
                end
        for i,val in ipairs(complexBuffer) do 
                for i2,cval in ipairs(val) do
                        table_insert(compiledLogic,cval)
                        end
                end
        operator[3] = # compiledLogic
        return "complex"
        end


-- API
function LibLogic:Compile(logic)
        local compiledLogic = {
                ["type"] = "compiled",
                ["version"] = "1.0"
                ["logic"] = {}
                ["Test"]=LibLogic.Test
        }
        Compile_Local(logic,compiledLogic.logic)
        for i,val in ipairs(compiledlogic.logic) do -- just update the jump addresses
                if val[1] == 2 or val[1] == 3 then
                        val[3] = val[3] + i
                        end
                end
        return compiledLogic
        end

-- for faster comparison:
-- types:
-- 0 value
-- 1 function
-- 2 and
-- 3 or

function LibLogic:Test(compiledLogic,arguments)
        local stack = {}
        local head = nil
        local operator = nil
        local running = true
        local pointer = 1
        local last = # compiledLogic.logic
        local logic = compiledLogic.logic
        local current = nil
        local currtype = nil
        while running do
                current = logic[pointer]
                currtype = current[1]
                
                if currtype == 1 then -- function case, considered to be the most frequent
                        if operator == 2 then -- doing and operations
                                if not current[2](arguments) then -- if we are false
                                        while head and head[1]==2 do -- now we drop all and's until the first or or the end of the stack
                                                pointer = operator[3] -- jump to address
                                                head = table_remove(stack)
                                                end
                                        end
                                else
                                if operator == 3 then -- doing or operations
                                        if current[2](arguments) then -- if true, or is true
                                                while head and head[1]==3 do
                                                        pointer = operator[3]
                                                        head = table_remove(stack)
                                                        end
                                                end
                                        else
                                        if not head then -- no operator loaded, just a single function
                                                return current[2](arguments) --RETURN ends function
                                                end
                                        end
                                end     
                        else -- doing and
                        if currtype == 2 or currtype == 3 then
                                table_insert(stack,head)
                                head = current
                                operator = currtype
                                else
                                if currtype == 0 then
                                        return current[2] -- no need to do more for now
                                end
                        
                        end
                end
        end



Compare with Previous | Blame