Skip to content

Instantly share code, notes, and snippets.

@notwa
Last active December 11, 2016 13:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save notwa/13fbddf05f654ba48321 to your computer and use it in GitHub Desktop.
Save notwa/13fbddf05f654ba48321 to your computer and use it in GitHub Desktop.
lua table dumping as pseudo-yaml
__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
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,
}
local path = string.gsub(..., "[^.]+$", "")
return require(path.."pt")
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
#!/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')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment