diff --git a/_G.yml b/_G.yml new file mode 100644 index 0000000..52da894 --- /dev/null +++ b/_G.yml @@ -0,0 +1,246 @@ +__root: &__root_t0x4004f960 + _G: *__root_t0x4004f960 + _VERSION: Lua 5.1 + arg: &arg_t0x40055cc8 + "-1": luajit + 0: run.lua + assert: fbuiltin#2 + bit: &bit_t0x40054d58 + arshift: fbuiltin#70 + band: fbuiltin#73 + bnot: fbuiltin#66 + bor: fbuiltin#74 + bswap: fbuiltin#67 + bxor: fbuiltin#75 + lshift: fbuiltin#68 + rol: fbuiltin#71 + ror: fbuiltin#72 + rshift: fbuiltin#69 + tobit: fbuiltin#65 + tohex: fbuiltin#76 + collectgarbage: fbuiltin#27 + coroutine: &coroutine_t0x40051730 + create: fbuiltin#32 + resume: fbuiltin#34 + running: fbuiltin#31 + status: fbuiltin#30 + wrap: fbuiltin#36 + yield: fbuiltin#33 + debug: &debug_t0x40054740 + debug: fbuiltin#145 + getfenv: fbuiltin#134 + gethook: fbuiltin#144 + getinfo: fbuiltin#136 + getlocal: fbuiltin#137 + getmetatable: fbuiltin#132 + getregistry: fbuiltin#131 + getupvalue: fbuiltin#139 + setfenv: fbuiltin#135 + sethook: fbuiltin#143 + setlocal: fbuiltin#138 + setmetatable: fbuiltin#133 + setupvalue: fbuiltin#140 + traceback: fbuiltin#146 + upvalueid: fbuiltin#141 + upvaluejoin: fbuiltin#142 + dofile: fbuiltin#25 + dump: f0x4005dd40 + error: fbuiltin#19 + gcinfo: fbuiltin#26 + getfenv: fbuiltin#10 + getmetatable: fbuiltin#8 + io: &io_t0x40052a90 + close: fbuiltin#112 + flush: fbuiltin#115 + input: fbuiltin#116 + lines: fbuiltin#118 + open: fbuiltin#109 + output: fbuiltin#117 + popen: fbuiltin#110 + read: fbuiltin#113 + stderr: file (0x7ff9dd21f500) + stdin: file (0x7ff9dd21e8a0) + stdout: file (0x7ff9dd21f5e0) + tmpfile: fbuiltin#111 + type: fbuiltin#119 + write: fbuiltin#114 + ipairs: fbuiltin#7 + jit: &jit_t0x40055318 + arch: x64 + attach: fbuiltin#151 + flush: fbuiltin#149 + off: fbuiltin#148 + on: fbuiltin#147 + opt: &opt_t0x40055bd8 + start: fbuiltin#163 + os: Linux + status: fbuiltin#150 + util: &util_t0x400556a0 + funcbc: fbuiltin#153 + funcinfo: fbuiltin#152 + funck: fbuiltin#154 + funcuvname: fbuiltin#155 + ircalladdr: fbuiltin#162 + traceexitstub: fbuiltin#161 + traceinfo: fbuiltin#156 + traceir: fbuiltin#157 + tracek: fbuiltin#158 + tracemc: fbuiltin#160 + tracesnap: fbuiltin#159 + version: LuaJIT 2.0.4 + version_num: 20004 + load: fbuiltin#23 + loadfile: fbuiltin#22 + loadstring: fbuiltin#24 + math: &math_t0x40053b60 + abs: fbuiltin#37 + acos: fbuiltin#47 + asin: fbuiltin#46 + atan: fbuiltin#48 + atan2: fbuiltin#57 + ceil: fbuiltin#39 + cos: fbuiltin#44 + cosh: fbuiltin#50 + deg: fbuiltin#54 + exp: fbuiltin#42 + floor: fbuiltin#38 + fmod: fbuiltin#59 + frexp: fbuiltin#52 + huge: inf + ldexp: fbuiltin#60 + log: fbuiltin#56 + log10: fbuiltin#41 + max: fbuiltin#62 + min: fbuiltin#61 + mod: fbuiltin#59 + modf: fbuiltin#53 + pi: 3.1415926535898 + pow: fbuiltin#58 + rad: fbuiltin#55 + random: fbuiltin#63 + randomseed: fbuiltin#64 + sin: fbuiltin#43 + sinh: fbuiltin#49 + sqrt: fbuiltin#40 + tan: fbuiltin#45 + tanh: fbuiltin#51 + module: f0x40051ee0 + newproxy: fbuiltin#28 + next: fbuiltin#4 + os: &os_t0x40052fd0 + clock: fbuiltin#126 + date: fbuiltin#127 + difftime: fbuiltin#129 + execute: fbuiltin#120 + exit: fbuiltin#125 + getenv: fbuiltin#124 + remove: fbuiltin#121 + rename: fbuiltin#122 + setlocale: fbuiltin#130 + time: fbuiltin#128 + tmpname: fbuiltin#123 + package: &package_t0x40051ac8 + config: + / + ; + ? + ! + - + cpath: /home/notwa/opt/local/lib/?.so;/home/notwa/.luarocks/lib/lua/5.1/?.so;/home/notwa/opt/local/lib/lua/5.1/?.so;./?.so;/usr/local/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so + loaded: &loaded_t0x40050b38 + _G: *__root_t0x4004f960 + bit: *bit_t0x40054d58 + coroutine: *coroutine_t0x40051730 + debug: *debug_t0x40054740 + extra: &extra_t0x4005cd30 + add_zeros: f0x4005db58 + mixed_sorter: f0x4005db98 + opairs: f0x4005cca8 + order_keys: f0x4005b2f8 + strpad: f0x4004f8b8 + traverse: f0x4005cce8 + io: *io_t0x40052a90 + jit: *jit_t0x40055318 + "jit.opt": *opt_t0x40055bd8 + "jit.util": *util_t0x400556a0 + math: *math_t0x40053b60 + os: *os_t0x40052fd0 + package: *package_t0x40051ac8 + pt: &pt_t0x4005ce20 + __metatable: *pt_t0x4005ce20 + __call: f0x4005dc98 + __index: *pt_t0x4005ce20 + inner: f0x4005dde0 + outer: f0x4005dd18 + outer_old: f0x4005dc00 + safecanon: f0x4005dd60 + safekey: f0x4005dd80 + safeval: f0x4005ddc0 + write: f0x4005dcd8 + string: *string_t0x400534b0 + table: *table_t0x400522f8 + loaders: &loaders_t0x40051c38 + 1: f0x40051c88 + 2: f0x40051cb0 + 3: f0x40051cd8 + 4: f0x40051d00 + loadlib: f0x40051b58 + path: /home/notwa/.luarocks/share/lua/5.1/?.lua;/home/notwa/.luarocks/share/lua/5.1/?/init.lua;/home/notwa/opt/local/share/lua/5.1/?.lua;/home/notwa/opt/local/share/lua/5.1/?/init.lua;./?.lua;/home/notwa/opt/local/share/luajit-2.1.0-beta1/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua + preload: &preload_t0x400520c0 + ffi: f0x40055c80 + searchpath: f0x40051ba0 + seeall: f0x40051bf0 + pairs: fbuiltin#5 + pcall: fbuiltin#20 + print: fbuiltin#29 + rawequal: fbuiltin#14 + rawget: fbuiltin#12 + rawset: fbuiltin#13 + require: f0x40051f28 + select: fbuiltin#16 + setfenv: fbuiltin#11 + setmetatable: fbuiltin#9 + string: &string_t0x400534b0 + byte: fbuiltin#78 + char: fbuiltin#79 + dump: fbuiltin#85 + find: fbuiltin#86 + format: fbuiltin#91 + gfind: fbuiltin#89 + gmatch: fbuiltin#89 + gsub: fbuiltin#90 + len: fbuiltin#77 + lower: fbuiltin#83 + match: fbuiltin#87 + rep: fbuiltin#81 + reverse: fbuiltin#82 + sub: fbuiltin#80 + upper: fbuiltin#84 + t: &t_t0x4005e298 + A: hello + B: &B_t0x4005e328 + a: beep + b: boop + c: burp + d: *d_t0x4005e550 + C: &d_t0x4005e550 + a: nude + b: dude + c: lewd + d: *B_t0x4005e328 + D: goodbye + E: *t_t0x4005e298 + table: &table_t0x400522f8 + concat: fbuiltin#98 + foreach: fbuiltin#93 + foreachi: fbuiltin#92 + getn: fbuiltin#94 + insert: fbuiltin#96 + maxn: fbuiltin#95 + remove: fbuiltin#97 + sort: fbuiltin#99 + tonumber: fbuiltin#17 + tostring: fbuiltin#18 + type: fbuiltin#3 + unpack: fbuiltin#15 + xpcall: fbuiltin#21 diff --git a/extra.lua b/extra.lua new file mode 100755 index 0000000..fb5982b --- /dev/null +++ b/extra.lua @@ -0,0 +1,69 @@ +local insert = table.insert +local pairs = pairs +local rawget = rawget +local sort = table.sort +local tostring = tostring +local type = type + +local function strpad(num, count, pad) + num = tostring(num) + return (pad:rep(count)..num):sub(#num) +end + +local function add_zeros(num, count) + return strpad(num, count - 1, '0') +end + +local function mixed_sorter(a, b) + a = type(a) == 'number' and add_zeros(a, 16) or tostring(a) + b = type(b) == 'number' and add_zeros(b, 16) or tostring(b) + return a < b +end + +-- loosely based on http://lua-users.org/wiki/SortedIteration +-- the original didn't make use of closures for who knows why +local function order_keys(t) + local oi = {} + for key in pairs(t) do + insert(oi, key) + end + sort(oi, mixed_sorter) + return oi +end + +local function opairs(t, cache) + local oi = cache and cache[t] or order_keys(t) + if cache then + cache[t] = oi + end + local i = 0 + return function() + i = i + 1 + local key = oi[i] + if key ~= nil then return key, t[key] end + end +end + +local function traverse(path) + if not path then return end + local parent = _G + local key + for w in path:gfind("[%w_]+") do + if key then + parent = rawget(parent, key) + if type(parent) ~= 'table' then return end + end + key = w + end + if not key then return end + return {parent=parent, key=key} +end + +return { + strpad = strpad, + add_zeros = add_zeros, + mixed_sorter = mixed_sorter, + order_keys = order_keys, + opairs = opairs, + traverse = traverse, +} diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..e60915d --- /dev/null +++ b/init.lua @@ -0,0 +1,2 @@ +local path = string.gsub(..., "[^.]+$", "") +return require(path.."pt") diff --git a/pt.lua b/pt.lua new file mode 100755 index 0000000..821634f --- /dev/null +++ b/pt.lua @@ -0,0 +1,203 @@ +local path = string.gsub(..., "[^.]+$", "") +local extra = require(path.."extra") +local opairs = extra.opairs + +local pt = {} +pt.__index = pt +setmetatable(pt, pt) + +local function rawstr(v) + if v == nil then return 'nil' end + local mt = getmetatable(v) + local ts = mt and rawget(mt, '__tostring') + if not ts then return tostring(v) end + mt.__tostring = nil + local s = tostring(v) + mt.__tostring = ts + return s +end + +local function getaddr(t) + return rawstr(t):sub(#type(t) + 3) +end + +local function copy(t) + -- shallow copy + if type(t) ~= 'table' then return end + local new = {} + for k,v in pairs(t) do + new[k] = v + end + return new +end + +function pt.__call(pt, args) + -- print a table as semi-valid YAML + -- with references to prevent recursion/duplication + local t = args.table or args[1] + local self = {} + setmetatable(self, pt) + self.seen = copy(args.seen) or {} + self.skipped = copy(args.skipped) or {} + self.seen_elsewhere = args.seen or {} + self.depth = args.depth or 16 + self.writer = args.writer or io.write + self.skeleton = args.skeleton or false + self.outer = args.alt_order and self.outer_old or self.outer + self.noncanon = args.noncanon or false + self.indent = args.indent or ' ' + self.queued = {} + self.cache = {} + self.canonicalized = {} + self:inner('__root', t, '') + return self.seen +end + +function pt:write(...) + self.writer(...) +end + +function pt:safecanon(k) + local s = tostring(k) + return s:gsub('[^%w_]', '_') +end + +function pt:safekey(k) + if type(k) == 'table' then + return 't'..getaddr(k) + end + local s = tostring(k) + s = s:gsub('[\r\n]', '') + return s:find('[^%w_]') and ('%q'):format(s) or s +end + +function pt:safeval(v, indentation) + if type(v) == 'function' then + return 'f'..getaddr(v) + end + local s = tostring(v) + if type(v) == 'number' then + return s + end + -- TODO: move newline/indentation handling to another function? + if s:find('[\r\n]') then + s = ('\n'..s):gsub('[\r\n]', '\n'..indentation..self.indent) + end + --local safe = ('%q'):format(s) + --return s == safe:sub(2, -2) and s or safe + -- TODO: finish matching valid characters + return s:find('[^%w_()[]{}.]') and ('%q'):format(s) or s +end + +function pt:inner(k, v, indentation) + if type(v) ~= 'table' then + if self.skeleton then return end + self:write(indentation, self:safekey(k), ': ') + self:write(self:safeval(v, indentation), '\n') + return + end + + local addr = getaddr(v) + self:write(indentation, self:safekey(k)) + + local canon + if not self.noncanon and type(k) ~= 'table' then + canon = self.canonicalized[addr] + if canon == nil then + canon = self:safecanon(k)..'_t'..addr + self.canonicalized[addr] = canon + end + else + canon = 't'..addr + end + + if #indentation > self.depth or self.skipped[addr] then + --self.skipped[addr] = true -- TODO: extra logics + self:write(': #', canon, '\n') + return + end + + if self.seen[addr] or self.queued[addr] then + self:write(': *', canon, self.seen_elsewhere[addr] and ' #\n' or '\n') + return + end + + self.seen[addr] = true + + self:write(': &', canon, '\n') + self:outer(v, indentation..self.indent) +end + +function pt:outer_old(t, indentation) + if type(t) ~= "table" then + local s = self:safeval(t, indentation) + self:write(indentation, s, '\n') + return + end + + local ours = {} + local not_ours = {} + + for k,v in opairs(t) do + if type(v) == 'table' then + local addr = getaddr(v) + if not (self.queued[addr] or self.seen[addr] or self.skipped[addr]) then + self.queued[addr] = true + ours[k] = v + else + not_ours[k] = v + end + else + self:inner(k, v, indentation) + end + end + + for k,v in opairs(not_ours) do + self:inner(k, v, indentation) + end + + for k,v in opairs(ours) do + self.queued[getaddr(v)] = nil + self:inner(k, v, indentation) + end + + local mt = getmetatable(t) + if mt ~= nil then + self:inner('__metatable', mt, indentation) + end +end + +function pt:outer(t, indentation) + if type(t) ~= "table" then + local s = self:safeval(t, indentation) + self:write(indentation, s, '\n') + return + end + + local ours = {} + + for k,v in opairs(t, self.cache) do + if type(v) == 'table' then + local addr = getaddr(v) + if not (self.queued[addr] or self.seen[addr] or self.skipped[addr]) then + self.queued[addr] = true + ours[k] = addr + end + end + end + + local mt = getmetatable(t) + if mt ~= nil then + self:inner('__metatable', mt, indentation) + end + + for k,v in opairs(t, self.cache) do + local addr = ours[k] + if addr then + self.queued[addr] = nil + end + self:inner(k, v, indentation) + end +end + +return pt diff --git a/run.lua b/run.lua new file mode 100755 index 0000000..63f3c00 --- /dev/null +++ b/run.lua @@ -0,0 +1,41 @@ +#!/usr/bin/lua +local pt = require('pt') + +t = { + A = 'hello', + B = { + a = 'beep', + b = 'boop', + c = 'burp', + }, + C = { + a = 'nude', + b = 'dude', + c = 'lewd', + }, + D = 'goodbye', +} +t.B.d = t.C +t.C.d = t.B +t.E = t + +function dump(t, fn, seen) + if t == nil then return end + + local file = io.open(fn, "w") + if not file then + io.write("Failed opening ", fn, "\n") + return + end + + local writer = function(...) + file:write(...) + end + seen = pt{t, writer=writer, seen=seen} + + file:close() + return seen +end + +pt{t} +dump(_G, '_G.yml')