数据转储器 |
|
DataDumper
包含一个单独的 Lua 函数,可以轻松地将其放入单独的模块或集成到更大的模块中。该函数有四个参数,但只有第一个参数是必需的。它始终返回一个字符串值,该值是有效的 Lua 代码。只需执行此代码块,就可以将原始变量的完整结构导入回一个变量。对于简单的结构,只有一个 Lua 指令,例如表格构造函数,但一些更复杂的功能将输出具有更多指令的脚本。
以下所有语言功能都受支持
loadstring
转储简单的 Lua 函数(没有上值)
debug
库也支持带有上值的 Lua 闭包!
由于似乎不可能,以下类型不受支持
function DataDumper(value[, varname, fastmode, indent])
value
可以是任何支持的类型
varname
:可选变量名。根据其形式,输出将如下所示
nil
:"return
value"
varname =
value"
varname
".."value"
fastmode
是一个布尔值
true
:优化速度。不支持元表、闭包、C 函数和引用。返回一个没有任何空格或换行的代码块!
false
:支持所有高级功能,并支持具有良好缩进的可读代码。
indent
:额外的缩进级别数。默认值为 0。
-- Define a shortcut function for testing function dump(...) print(DataDumper(...), "\n---") end -- Simple types: dump(8) --> return 8 dump(true) --> return true dump() --> return nil dump('Hello') --> return "Hello" -- Functions: f1 = print function f2() return "Hello" end do local val = 9 function f3() return val end end dump(f1) --> return print dump(f2) --> return loadstring("?LuaQ\000... dump(f3) --[[ the following 16 lines script: local closures = {} local function closure(t) closures[#closures+1] = t t[1] = assert(loadstring(t[1])) return t[1] end local t = closure { "?LuaQ\000...", 9 } for _,t in pairs(closures) do for i = 2,#t do debug.setupvalue(t[1], i-1, t[i]) end end]] -- Tables: dump({}) --> return { } dump({1,2,3}) --> return { 1, 2, 3 } dump({a=9,b=2}) --> return { a=9, b=2 } t1 = setmetatable({1},{2}) t2 = {}; t2.next = t2 t3 = {[false] = true, 'Hello', ["key1"] = 10, ["function"] = "keyword", {1,2}} setmetatable(t3, {__index = t2}) t3[3] = t2 dump(t1) --> return setmetatable({ 1 },{ 2 }) dump(t2) --[[ the following 3 lines script: local t = { next=nil } t.next = t return t ]] dump(t3) --[[ the following 14 lines script: local t = setmetatable( { [false]=true, "Hello", { 1, 2 }, { next=nil }, ["function"] = "keyword", key1=10 }, { __index={ next=nil } } ) getmetatable(t).__index.next = getmetatable(t).__index t[3].next = t[3] return t ]] -- Parameters dump(t1, 'a') --> a = setmetatable({ 1 },{ 2 }) dump({}, '') --> { } dump({ { {} } },a,true) --> return {{{},},}
--[[ 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 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) 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 or "" 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(" ", ident+1) end str = "{"..sep..table_concat(str, ","..sep).." "..sep:sub(1,-3).."}" 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