summaryrefslogtreecommitdiff
path: root/library/lua
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-05-08 12:55:06 +0400
committerAlexander Gavrilov2012-05-08 12:55:06 +0400
commit191071beb6730f9575b737fdae39db6572ebd3f4 (patch)
treed19723ff80a38264db9ed76b0f1258665a9c7e2e /library/lua
parentfca618ff1baa48ad839317ed6d7479d34f110248 (diff)
downloaddfhack-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.lua234
-rw-r--r--library/lua/utils.lua173
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