diff options
| author | Alexander Gavrilov | 2012-05-08 12:55:06 +0400 |
|---|---|---|
| committer | Alexander Gavrilov | 2012-05-08 12:55:06 +0400 |
| commit | 191071beb6730f9575b737fdae39db6572ebd3f4 (patch) | |
| tree | d19723ff80a38264db9ed76b0f1258665a9c7e2e /library/lua | |
| parent | fca618ff1baa48ad839317ed6d7479d34f110248 (diff) | |
| download | dfhack-191071beb6730f9575b737fdae39db6572ebd3f4.tar.gz dfhack-191071beb6730f9575b737fdae39db6572ebd3f4.tar.bz2 dfhack-191071beb6730f9575b737fdae39db6572ebd3f4.tar.xz | |
Add more lua scripts.
Diffstat (limited to 'library/lua')
| -rw-r--r-- | library/lua/dumper.lua | 234 | ||||
| -rw-r--r-- | library/lua/utils.lua | 173 |
2 files changed, 402 insertions, 5 deletions
diff --git a/library/lua/dumper.lua b/library/lua/dumper.lua new file mode 100644 index 00000000..44e787db --- /dev/null +++ b/library/lua/dumper.lua @@ -0,0 +1,234 @@ +--[[ DataDumper.lua +Copyright (c) 2007 Olivetti-Engineering SA + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +]] + +local _ENV = mkmodule('dumper') + +local dumplua_closure = [[ +local closures = {} +local function closure(t) + closures[#closures+1] = t + t[1] = assert(loadstring(t[1])) + return t[1] +end + +for _,t in pairs(closures) do + for i = 2,#t do + debug.setupvalue(t[1], i-1, t[i]) + end +end +]] + +local lua_reserved_keywords = { + 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', + 'function', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', + 'return', 'then', 'true', 'until', 'while' } + +local function keys(t) + local res = {} + local oktypes = { stringstring = true, numbernumber = true } + local function cmpfct(a,b) + if oktypes[type(a)..type(b)] then + return a < b + else + return type(a) < type(b) + end + end + for k in pairs(t) do + res[#res+1] = k + end + table.sort(res, cmpfct) + return res +end + +local c_functions = {} +for _,lib in pairs{'_G', 'string', 'table', 'math', + 'io', 'os', 'coroutine', 'package', 'debug'} do + local t = _G[lib] or {} + lib = lib .. "." + if lib == "_G." then lib = "" end + for k,v in pairs(t) do + if type(v) == 'function' and not pcall(string.dump, v) then + c_functions[v] = lib..k + end + end +end + +function DataDumper(value, varname, fastmode, ident, indent_step) + indent_step = indent_step or 2 + local defined, dumplua = {} + -- Local variables for speed optimization + local string_format, type, string_dump, string_rep = + string.format, type, string.dump, string.rep + local tostring, pairs, table_concat = + tostring, pairs, table.concat + local keycache, strvalcache, out, closure_cnt = {}, {}, {}, 0 + setmetatable(strvalcache, {__index = function(t,value) + local res = string_format('%q', value) + t[value] = res + return res + end}) + local fcts = { + string = function(value) return strvalcache[value] end, + number = function(value) return value end, + boolean = function(value) return tostring(value) end, + ['nil'] = function(value) return 'nil' end, + ['function'] = function(value) + return string_format("loadstring(%q)", string_dump(value)) + end, + userdata = function() error("Cannot dump userdata") end, + thread = function() error("Cannot dump threads") end, + } + local function test_defined(value, path) + if defined[value] then + if path:match("^getmetatable.*%)$") then + out[#out+1] = string_format("s%s, %s)\n", path:sub(2,-2), defined[value]) + else + out[#out+1] = path .. " = " .. defined[value] .. "\n" + end + return true + end + defined[value] = path + end + local function make_key(t, key) + local s + if type(key) == 'string' and key:match('^[_%a][_%w]*$') then + s = key .. "=" + else + s = "[" .. dumplua(key, 0) .. "]=" + end + t[key] = s + return s + end + for _,k in ipairs(lua_reserved_keywords) do + keycache[k] = '["'..k..'"] = ' + end + if fastmode then + fcts.table = function (value) + -- Table value + local numidx = 1 + out[#out+1] = "{" + for key,val in pairs(value) do + if key == numidx then + numidx = numidx + 1 + else + out[#out+1] = keycache[key] + end + local str = dumplua(val) + out[#out+1] = str.."," + end + if string.sub(out[#out], -1) == "," then + out[#out] = string.sub(out[#out], 1, -2); + end + out[#out+1] = "}" + return "" + end + else + fcts.table = function (value, ident, path) + if test_defined(value, path) then return "nil" end + -- Table value + local sep, str, numidx, totallen = " ", {}, 1, 0 + local meta, metastr = (debug or getfenv()).getmetatable(value) + if meta then + ident = ident + 1 + metastr = dumplua(meta, ident, "getmetatable("..path..")") + totallen = totallen + #metastr + 16 + end + for _,key in pairs(keys(value)) do + local val = value[key] + local s = "" + local subpath = path + if key == numidx then + subpath = subpath .. "[" .. numidx .. "]" + numidx = numidx + 1 + else + s = keycache[key] + if not s:match "^%[" then subpath = subpath .. "." end + subpath = subpath .. s:gsub("%s*=%s*$","") + end + s = s .. dumplua(val, ident+1, subpath) + str[#str+1] = s + totallen = totallen + #s + 2 + end + if totallen > 80 then + sep = "\n" .. string_rep(' ', indent_step*(ident+1)) + end + str = "{"..sep..table_concat(str, ","..sep).." "..sep:sub(1,-1-indent_step).."}" + if meta then + sep = sep:sub(1,-3) + return "setmetatable("..sep..str..","..sep..metastr..sep:sub(1,-3)..")" + end + return str + end + fcts['function'] = function (value, ident, path) + if test_defined(value, path) then return "nil" end + if c_functions[value] then + return c_functions[value] + elseif debug == nil or debug.getupvalue(value, 1) == nil then + return string_format("loadstring(%q)", string_dump(value)) + end + closure_cnt = closure_cnt + 1 + local res = {string.dump(value)} + for i = 1,math.huge do + local name, v = debug.getupvalue(value,i) + if name == nil then break end + res[i+1] = v + end + return "closure " .. dumplua(res, ident, "closures["..closure_cnt.."]") + end + end + function dumplua(value, ident, path) + return fcts[type(value)](value, ident, path) + end + if varname == nil then + varname = "return " + elseif varname:match("^[%a_][%w_]*$") then + varname = varname .. " = " + end + if fastmode then + setmetatable(keycache, {__index = make_key }) + out[1] = varname + table.insert(out,dumplua(value, 0)) + return table.concat(out) + else + setmetatable(keycache, {__index = make_key }) + local items = {} + for i=1,10 do items[i] = '' end + items[3] = dumplua(value, ident or 0, "t") + if closure_cnt > 0 then + items[1], items[6] = dumplua_closure:match("(.*\n)\n(.*)") + out[#out+1] = "" + end + if #out > 0 then + items[2], items[4] = "local t = ", "\n" + items[5] = table.concat(out) + items[7] = varname .. "t" + else + items[2] = varname + end + return table.concat(items) + end +end + +return _ENV diff --git a/library/lua/utils.lua b/library/lua/utils.lua index 3eeb0cc6..68f5731a 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -1,5 +1,7 @@ local _ENV = mkmodule('utils') +local df = df + -- Comparator function function compare(a,b) if a < b then @@ -26,6 +28,43 @@ function compare_name(a,b) end end +-- Make a field comparator +function compare_field(field,cmp) + cmp = cmp or compare + if field then + return function (a,b) + return cmp(a[field],b[field]) + end + else + return cmp + end +end + +-- Make a comparator of field vs key +function compare_field_key(field,cmp) + cmp = cmp or compare + if field then + return function (a,b) + return cmp(a[field],b) + end + else + return cmp + end +end + +function is_container(obj) + return df.isvalid(obj) == 'ref' and obj._kind == 'container' +end + +-- Make a sequence of numbers in 1..size +function make_index_sequence(size) + local index = {} + for i=1,size do + index[i] = i + end + return index +end + --[[ Sort items in data according to ordering. @@ -75,10 +114,7 @@ function make_sort_order(data,ordering) end -- Make an order table - local index = {} - for i=1,size do - index[i] = i - end + local index = make_index_sequence(size) -- Sort the ordering table table.sort(index, function(ia,ib) @@ -119,7 +155,7 @@ function assign(tgt,src) df.assign(tgt, src) elseif type(tgt) == 'table' then for k,v in pairs(src) do - if type(v) == 'table' or df.isvalid(v) == 'ref' then + if type(v) == 'table' then local cv = tgt[k] if cv == nil then cv = {} @@ -136,4 +172,131 @@ function assign(tgt,src) return tgt end +local function copy_field(obj,k,v,deep) + if v == nil then + return NULL + end + if deep then + local field = obj:_field(k) + if field == v then + return clone(v,deep) + end + end + return v +end + +-- Copy the object as lua data structures. +function clone(obj,deep) + if type(obj) == 'table' then + if deep then + return assign({},obj) + else + return copyall(obj) + end + elseif df.isvalid(obj) == 'ref' then + local kind = obj._kind + if kind == 'primitive' then + return obj.value + elseif kind == 'bitfield' then + local rv = {} + for k,v in pairs(obj) do + rv[k] = v + end + return rv + elseif kind == 'container' then + local rv = {} + for k,v in ipairs(obj) do + rv[k+1] = copy_field(obj,k,v,deep) + end + return rv + else -- struct + local rv = {} + for k,v in pairs(obj) do + rv[k] = copy_field(obj,k,v,deep) + end + return rv + end + else + return obj + end +end + +-- Sort a vector or lua table +function sort_vector(vector,field,cmp) + local fcmp = compare_field(field,cmp) + local scmp = function(a,b) + return fcmp(a,b) < 0 + end + if df.isvalid(vector) then + if vector._kind ~= 'container' then + error('Container expected: '..tostring(vector)) + end + local items = clone(vector, true) + table.sort(items, scmp) + vector:assign(items) + else + table.sort(vector, scmp) + end + return vector +end + +-- Binary search in a vector or lua table +function binsearch(vector,key,field,cmp,min,max) + if not(min and max) then + if df.isvalid(vector) then + min = -1 + max = #vector + else + min = 0 + max = #vector+1 + end + end + local mf = math.floor + local fcmp = compare_field_key(field,cmp) + while true do + local mid = mf((min+max)/2) + if mid <= min then + return nil, false, max + end + local item = vector[mid] + local cv = fcmp(item, key) + if cv == 0 then + return item, true, mid + elseif cv < 0 then + min = mid + else + max = mid + end + end +end + +-- Binary search and insert +function insert_sorted(vector,item,field,cmp) + local key = item + if field and item then + key = item[field] + end + local cur,found,pos = binsearch(vector,key,field,cmp) + if found then + return false,cur,pos + else + if df.isvalid(vector) then + vector:insert(pos, item) + else + table.insert(vector, pos, item) + end + return true,vector[pos],pos + end +end + +-- Binary search, then insert or overwrite +function insert_or_replace(vector,item,field,cmp) + local added,cur,pos = insert_sorted(vector,item,field,cmp) + if not added then + vector[pos] = item + cur = vector[pos] + end + return added,cur,pos +end + return _ENV
\ No newline at end of file |
