TableDumper.lua

Aus Romwiki

Wechseln zu: Navigation, Suche

Mit diesem lua-Script habe ich die Liste aller Widgets erstellt.

Ja, es fehlen noch Kommentare. Ist auch noch nicht fertig.

Elrac 08:02, 30. Jan 2009 (CET)

local mod = b9.StartModule("TableDumper")

local handle,gripe = io.open("TableStats.txt", "w")
if not handle then 
    b9.perr(gripe)
    error(gripe)
end

local function addTo(list, item)
    if not list then
        return { item } 
    else
        table.insert(list, item)
        return list
    end
end

local tables = { } -- key -> entry

-- Create an entry from a table. Record its noteworthy properties.
local function discover(name, tab, newRank)
    local key = tostring(tab)
    local entry = tables[key] 
    if not entry then
        entry = { table=tab, rank=newRank }
        tables[key] = entry
    else
        if newRank > entry.rank then entry.rank = newRank end
    end
    if name then
        entry.names = addTo(entry.names, name)
    end
    local idx = tab.__index
    if type(idx) == "table" and idx ~= tab then
        entry.index = discover(nil, idx, entry.rank + 1)
        entry.index.iChildren = addTo(entry.index.iChildren, tab)
    end
    local mt = getmetatable(tab)
    if type(mt) == "table" and mt ~= tab then
        entry.meta = discover(nil, mt, entry.rank + 1)
        entry.meta.mChildren = addTo(entry.meta.mChildren, tab)
    end
    return entry            
end

-- Loop over all variables to gather all tables
for name, tab in pairs(_G) do
    if type(tab) == "table" then
        discover(name, tab, 1)
    end
end

local function tableSize(tab)
    local size = 0
    for k,v in pairs(tab) do size = size + 1 end
    return size
end

local function max(n1,n2)
    if n1 > n2 then return n1 else return n2 end
end    
    
-- Gather all indices and metatables
local ancestors = { } -- key,... 
local iCount, mCount, imCount = 0, 0, 0
for key, entry in pairs(tables) do
    if entry.iChildren and entry.mChildren then
        if not entry.name then
            imCount = imCount + 1
            entry.names = addTo(entry.names, "meta-index#" .. tostring(imCount))
        end
        entry.childCount = max(tableSize(entry.iChildren), tableSize(entry.mChildren))
        table.insert(ancestors, key)
    elseif entry.iChildren then
        if not entry.name then
            iCount = iCount + 1
            entry.names = addTo(entry.names, "index#" .. tostring(iCount))
        end
        entry.childCount = tableSize(entry.iChildren)
        table.insert(ancestors, key)
    elseif entry.mChildren then
        if not entry.name then
            mCount = mCount + 1
            entry.names = addTo(entry.names, "meta#" .. tostring(mCount))
        end
        entry.childCount = tableSize(entry.mChildren)
        table.insert(ancestors, key)
    end
end

-- Sort ancestors on
--- descending rank
--- ascending child count

local function compareEntries(key1,key2)
    local e1 = tables[key1]
    local e2 = tables[key2]
    if e1.rank > e2.rank then return true end
    if e1.rank < e2.rank then return false end
    if e1.childCount < e2.childCount then return true end
    return false
end

table.sort(ancestors, compareEntries)

-- Given a list of names, convert any group of names with common
-- alpha parts but varying numeric parts into a single name with the
-- maximum numeric value at that position in square brackets.
local function sanitizeNames(names)

	-- Build an index from the given name:
	-- if name is pure alpha, insert it into "plain". otherwise:
	-- * construct a label from the name
	-- * create a table entry from "label" and { min, max, ... } of the numeric values in the name.
	-- * a min, max pair is stored for each numeric in the name, so the value will have 2*n entries
	-- *    for n numeric substrings in "name".
	local function indexName(names, plain, name)
	
	    -- Split a name (e.g. "aa5bb66cc") into a table of alpha strings
	    -- (e.g. {"aa", "bb", "cc"}) and a table of numbers (e.g. {5, 66}).
	    -- "alpha" in this case includes the underscore ( "_" )
	    local function scanName(name)
	        local texts = { }
	        local numbers = { }
	        local pos = 1
	        while true do
	            local start, stop, part = string.find(name, "([a-zA-Z_#]+)", pos)
	            if not start then break end
	            table.insert(texts, part)
	            pos = stop + 1
	            local start, stop, part = string.find(name, "(%d+)", pos)
	            if not start then break end
	            table.insert(numbers, tonumber(part))
	            pos = stop + 1
	        end
	        if numbers[1] then
	           return texts, numbers
	        else
	           return texts
	        end
	    end
	    
	    -- Make a label (e.g. "aa[]bb[]cc") from an array of alpha strings.
	    local function makeLabel(texts)
	        local first = true
	        local label
	        for _,str in pairs(texts) do
	            if first then
	                label = str
	                first = false
	            else
	                label = label .. "[]" .. str
	            end
	        end
	        return label
	    end
	    
	    if string.find(name, "#") or not string.find(name, "%d+") then
	        table.insert(plain, name)
	    else
	        local texts, numbers = scanName(name)
	        local label = makeLabel(texts)
	        local entry = names[label]
	        if not entry then
	            entry = { }
	            for _,n in ipairs(numbers) do
	                table.insert(entry, n)
	                table.insert(entry, n)
	            end
	            names[label] = entry
	        else
	            local idx = 1
	            for _,n in ipairs(numbers) do
	                if entry[idx] == nil or n < entry[idx] then entry[idx] = n end
	                idx = idx + 1
	                if entry[idx] == nil or n > entry[idx] then entry[idx] = n end
	                idx = idx + 1
	            end
	        end
	    end
	end    

    local pool = { }
    local plain = { }
    for _,name in ipairs(names) do
        indexName(pool, plain, name)
    end
    for xname,stats in pairs(pool) do
        local name = xname  
        local idx = 0
        while true do
            local p,q = string.find(name, "%[%]")
            if not p then break end
            idx = idx + 1
            if stats[idx] == stats[idx+1] then
                name = string.sub(name, 1, p-1) .. tostring(stats[idx]) .. string.sub(name, q+1)
            else
                name = string.sub(name, 1, p) .. tostring(stats[idx+1]) .. string.sub(name, q)
            end
            idx = idx + 1
        end
        table.insert(plain, name)
    end
    return plain
end

-- output each ancestor

local function tablesByName(children, childTables)
    for _,tab in pairs(childTables) do
        local key = tostring(tab)
        local name = tables[key].names[1]
        children[name] = tab
    end
end

local function compareNames(name1, name2)
    local p1 = string.find(name1, "#")
    local p2 = string.find(name2, "#")
    if not p1 and not p2 or p1 and p2 then return name1 < name2 end
    return p1 ~= nil 
end

for _,aKey in ipairs(ancestors) do
    local entry = tables[aKey]
    handle:write(string.format(
        '=== <span id="%s">%s</span> ===\n\n',
        entry.names[1], entry.names[1])) 
    local children = { } -- name -> tab
    if entry.iChildren then
        tablesByName(children, entry.iChildren)
    end
    if entry.mChildren then
        tablesByName(children, entry.mChildren)
    end
    local names = { } -- name,...
    for name,_ in pairs(children) do
        table.insert(names, name)
    end
    local sanitized = sanitizeNames(names)
    -- local sanitized = names
    table.sort(sanitized, compareNames)
    local nth = false
    local pos = 0
    for n,name in ipairs(sanitized) do
        if pos > 80 then
            handle:write("\n")
            pos = 0
        end
        if nth then
            if pos > 0 then handle:write(" ") end
            handle:write("• ")
            pos = pos + 1
        else
            nth = true
        end
        if string.find(name, "#") then
            handle:write("[[#", name, "]]")
            pos = pos + string.len(name) + 5
        else
	        handle:write(name)
	        pos = pos + string.len(name)
	    end
    end
    handle:write("\n\n\n")
end    

handle:close()
b9.ModuleLoaded(mod)