Output Lua Table To Html File |
|
|
== Take #2 == |
|
=== Take #2 === |
|
local ids = {} local current_id = 0 local count = {} local from = {} |
|
local coroutine = coroutine local next = next local pairs = pairs local string = string local tostring = tostring local type = type local _G = _G |
|
-- Escape string to make suitable for embedding in HTML. |
|
--FIX: improve? |
|
local function output(s) io.stdout:write(s) end |
|
local function analyze_tree(o) if type(o) == 'table' then if count[o] then count[o] = count[o] + 1 |
|
-- iterator function for table pairs. -- hash part, then array part. -- used for display. local function table_pairs(t) local keys = {} for k in pairs(t) do keys[#keys+1] = k end table.sort(keys, function(a,b) if type(a) == 'number' and type(b) == 'string' then return false elseif type(a) == 'string' and type(b) == 'number' then return true |
|
count[o] = 1 current_id = current_id + 1 ids[o] = current_id local this_id = current_id for k,v in pairs(o) do analyze_tree(k) analyze_tree(v) if type(k) == 'table' then from[k] = from[k] or {}; from[k][o] = this_id .. '.' .. ids[k] end if type(v) == 'table' then from[v] = from[v] or {}; from[v][o] = this_id .. '.' .. (ids[k] or tostring(k)) end end |
|
return a < b |
|
end) local i = 0 return function() i = i + 1 local k = keys[i] if k then return k, t[k] end |
|
local function output_html(o) |
|
-- Serialize object o. Writes one or more substrings to function append. local function obj_serialize(o, append) |
|
local is_empty = next(o) == nil output( format('<a name="%d"></a>', ids[o]) .. '<div class="table">' .. (is_empty and '(empty)' or ('<a href="javascript:toggle(' .. ids[o] .. ')">[toggle]</a>')) .. format([[Table ID %d]], ids[o]) .. (type(o.tag) == 'string' and ('[Tag=' .. o.tag .. ']') or '') .. format('<div style="display:none" id="%d">\n', ids[o])) if from[o] and not next(from[o]) then output('<div>Referenced from: ') for _,from_id in pairs(from[o]) do output(format([[ <a onlick="show(%s)" href="#%s">%s</a> ]], from_id, from_id, from_id)) end output('</div>') |
|
append('{') for k,v in table_pairs(o) do append('[') obj_serialize(k, append) append(']=[') obj_serialize(v, append) append('];') |
|
append('}') elseif type(o) == 'string' then append(string.format('%q', o)) else append(tostring(o)) end end |
|
for k,v in pairs(o) do local knew, vnew if count[k] then knew = format([[<a onclick="show(%s)" href="#%s">(see %s)</a>]], ids[k],ids[k],ids[k]) elseif type(k) ~= 'table' then knew = htmlize(tostring(k)) end if count[v] then vnew = format([[<a onclick="show(%s)" href="#%s">(see %s)</a>]], ids[v],ids[v],ids[v]) elseif type(v) ~= 'table' then vnew = htmlize(tostring(v)) end |
|
local kname = format([[<a name="%s"></a>]], ids[o] .. '.' .. (ids[k] or tostring(k))) |
|
-- Returns serialization of o, not exceeding maxlen characters. local function obj_tostring_short(o, maxlen) local s = '' local function append(ss) s = s .. ss if #s > maxlen then s = s:sub(1,maxlen) .. '...' coroutine.yield() end end local f = coroutine.wrap(obj_serialize) f(o, append) return s end |
|
if knew and vnew then output(kname .. '<div class="table_row">' .. knew .. '=' .. vnew .. '</div>') else output('<div class="table_row">\n') output(kname) if knew then output(knew) else output_html(k) end |
|
output('=') if vnew then output(vnew) else output_html(v) |
|
local function analyze_tree(o) local ids = {} local current_id = 0 local count = {} local from = {} local function analyze_tree_helper(o) if type(o) == 'table' then if count[o] then count[o] = count[o] + 1 else count[o] = 1 current_id = current_id + 1 ids[o] = current_id local this_id = current_id for k,v in pairs(o) do analyze_tree_helper(k) analyze_tree_helper(v) if type(k) == 'table' then from[k] = from[k] or {}; from[k][o] = this_id .. '.' .. ids[k] end if type(v) == 'table' then from[v] = from[v] or {}; from[v][o] = this_id .. '.' .. (ids[k] or tostring(k)) end |
|
output('</div>\n') |
|
output('</div></div>\n') else output('<div>' .. htmlize(tostring(o)) .. '</div>\n') |
|
end |
|
analyze_tree_helper(o) |
|
local function dump(o) analyze_tree(o) |
|
output [[ |
|
return ids, count, from end local header = [[ |
|
.table {margin:1em; border: 1px solid black} .table_row { margin-left:1em; border: 1px solid black} |
|
.table {margin-left:1em; border: 1px solid black} .table_row {border: 1px solid black} |
|
<script> |
|
<script type="text/javascript"><!-- |
|
if (ele.parentNode) show(ele.parentNode); |
|
if (ele.parentNode) show_node(ele.parentNode); |
|
</script> |
|
//--></script> |
|
]] |
|
]] local footer = [[</body></html>]] -- Writes HTML representations of object o as one or more strings to -- function output. local function object_to_html(o, output) local ids, count, from = analyze_tree(o) local output_html local function output_header_html(o) if type(o) == 'table' then output(format('<a name="id%s"></a>', ids[o])) local is_empty = next(o) == nil output(is_empty and '(empty)' or '<a href="javascript:toggle(\'id' .. ids[o] .. '\')">[+]</a>') output(format([[Table ID %s]], ids[o])) output(type(o.tag) == 'string' and ' [Tag=' .. o.tag .. ']' or '') output(' ') output(htmlize(obj_tostring_short(o, 40))) elseif type(o) == 'string' then output(htmlize(string.format('%q', o))) else output(htmlize(tostring(o))) end end local function output_body_html(o) if type(o) == 'table' then output(format('<div class="table" style="display:none" id="id%s">\n', ids[o])) -- xref if from[o] and next(from[o]) and next(from[o], next(from[o])) then output('<div>Referenced from: ') for _,from_id in pairs(from[o]) do output(format([[ <a onclick="show('id%s')" href="#id%s">%s</a> ]], from_id, from_id, from_id)) end output('</div>') end -- key/values for k,v in table_pairs(o) do local function prepare_output(oo) local f, is_long if count[oo] then f = function() output(format([[<a onclick="show('id%s')" href="#id%s">(see %s)</a>]], ids[oo],ids[oo],ids[oo])) end elseif type(oo) ~= 'table' then f = function() output_header_html(oo) end else f = function() output_header_html(oo) end is_long = true end return f, is_long end local kf, klong = prepare_output(k) local vf, vlong = prepare_output(v) local field_id = ids[o] .. '.' .. (ids[k] or tostring(k)) output(format([[<a name="id%s"></a>]], field_id)) output(format('<div class="table_row" id="id%s">\n', field_id)) kf() output('=') vf() if vlong then output_body_html(v) end output('</div>') end output('</div>\n') end end --local function output_html(o) output('<div>') output_header_html(o) output('</div>') output_body_html(o) end output(header) |
|
output_html(oo) |
|
if oo ~= o then output_html(oo) end |
|
output [[</body></html>]] |
|
output(footer) end -- example usage local function dump(obj) object_to_html(obj, function(s) io.stdout:write(s) end) |
|
--[=[ package.path = package.path .. ';/u/work/luawork/luaanalyze/lib/?.lua' |
|
--[[ package.path = package.path .. ';/luaanalyze/lib/?.lua' |
|
--]=] |
|
--]] |
|
== See Also == |
|
=== See Also === |
-- dontspamme_samlie@yahoo.com
-- Converts Lua table to HTML output in table.html file function tohtml(x) ret = tohtml_table(x,1) writefile("table.html", ret) os.execute("table.html") return(ret) end -- Saves a string to file function writefile(filename, value) if (value) then local file = io.open(filename,"w+") file:write(value) file:close() end end -- Flattens a table to html output function tohtml_table(x, table_level) local k, s, tcolor local html_colors = { "#339900","#33CC00","#669900","#666600","#FF3300", "#FFCC00","#FFFF00","#CCFFCC","#CCCCFF","#CC66FF", "#339900","#33CC00","#669900","#666600","#FF3300", "#FFCC00","#FFFF00","#CCFFCC","#CCCCFF","#CC66FF" } local lineout = {} local tablefound = false if type(x) == "table" then s = "" k = 1 local i, v = next(x) while i do if (type(v) == "table") then if (table_level<10) then lineout[k] = "<b>" .. flat(i) .. "</b>".. tohtml_table(v, table_level + 1) else lineout[k] = "<b>MAXIMUM LEVEL BREACHED</b>" end tablefound = true else lineout[k] = flat(i) .. "=" .. tohtml_table(v) end k = k + 1 i, v = next(x, i) end for k,line in ipairs(lineout) do if (tablefound) then s = s .. "<tr><td>" .. line .. "</td></tr>\n" else s = s .. "<td>" .. line .. "</td>\n" end end if not (tablefound) then s = "<table border='1' bgcolor='#FFFFCC' cellpadding='5' cellspacing='0'>" .. "<tr>" .. s .. "</tr></table>\n" else tcolor = html_colors[table_level] s = "<table border='3' bgcolor='"..tcolor.."' cellpadding='10' cellspacing='0'>" .. s .. "</table>\n" end return s end if type(x) == "function" then return "FUNC" end if type(x) == "file" then return "FILE" end return tostring(x) end -- Flattens a table to string function flat(x) return toflat(x,1) end -- Flattens a table to string function toflat(x, tlevel) local s tlevel = tlevel + 1 if type(x) == "table" then s = "{" local i, v = next(x) while i do if (tlevel < 15) then s = s .. i .. "=" .. toflat(v, tlevel) else s = s .. i .. "={#}" end i, v = next(x, i) if i then s = s .. ", " end end return s .. "}\n" end if type(x) == "function" then return "FUNC" end if type(x) == "file" then return "FILE" end return tostring(x) end
Here is a more advanced implementation, with expand/collapsing and handling cycles. (See warning in comments before use.)
-- Example dumping Lua tables to HTML for debugging. -- -- Warning: not complete or well tested. This is only intended -- as an example/starting point. Clean it up if used in production. -- -- (c) 2008 David Manura (2008-12) -- Licensed under the same terms as Lua (MIT license). local coroutine = coroutine local next = next local pairs = pairs local string = string local tostring = tostring local type = type local _G = _G local format = string.format -- Escape string to make suitable for embedding in HTML. local function htmlize(s) s = s:gsub('&', '&') s = s:gsub('<', '<') s = s:gsub('>', '>') return s end -- iterator function for table pairs. -- hash part, then array part. -- used for display. local function table_pairs(t) local keys = {} for k in pairs(t) do keys[#keys+1] = k end table.sort(keys, function(a,b) if type(a) == 'number' and type(b) == 'string' then return false elseif type(a) == 'string' and type(b) == 'number' then return true else return a < b end end) local i = 0 return function() i = i + 1 local k = keys[i] if k then return k, t[k] end end end -- Serialize object o. Writes one or more substrings to function append. local function obj_serialize(o, append) if type(o) == 'table' then append('{') for k,v in table_pairs(o) do append('[') obj_serialize(k, append) append(']=[') obj_serialize(v, append) append('];') end append('}') elseif type(o) == 'string' then append(string.format('%q', o)) else append(tostring(o)) end end -- Returns serialization of o, not exceeding maxlen characters. local function obj_tostring_short(o, maxlen) local s = '' local function append(ss) s = s .. ss if #s > maxlen then s = s:sub(1,maxlen) .. '...' coroutine.yield() end end local f = coroutine.wrap(obj_serialize) f(o, append) return s end local function analyze_tree(o) local ids = {} local current_id = 0 local count = {} local from = {} local function analyze_tree_helper(o) if type(o) == 'table' then if count[o] then count[o] = count[o] + 1 else count[o] = 1 current_id = current_id + 1 ids[o] = current_id local this_id = current_id for k,v in pairs(o) do analyze_tree_helper(k) analyze_tree_helper(v) if type(k) == 'table' then from[k] = from[k] or {}; from[k][o] = this_id .. '.' .. ids[k] end if type(v) == 'table' then from[v] = from[v] or {}; from[v][o] = this_id .. '.' .. (ids[k] or tostring(k)) end end end end end analyze_tree_helper(o) for k,v in pairs(count) do if v == 1 then count[k] = nil end end return ids, count, from end local header = [[ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head> <title>FIX</title> <style type="text/css"> .table {margin-left:1em; border: 1px solid black} .table_row {border: 1px solid black} </style> <script type="text/javascript"><!-- function toggle(id) { if (document.getElementById) { var ele = document.getElementById(id); if (ele && ele.style) { ele.style.display = ele.style.display == 'none' ? '' : 'none'; } } } function show_node(ele) { if (ele.style) { ele.style.display = ''; if (ele.parentNode) show_node(ele.parentNode); } } function show(id) { if (document.getElementById) { var ele = document.getElementById(id); if (ele) show_node(ele); } } //--></script> </head> <body> ]] local footer = [[</body></html>]] -- Writes HTML representations of object o as one or more strings to -- function output. local function object_to_html(o, output) local ids, count, from = analyze_tree(o) local output_html local function output_header_html(o) if type(o) == 'table' then output(format('<a name="id%s"></a>', ids[o])) local is_empty = next(o) == nil output(is_empty and '(empty)' or '<a href="javascript:toggle(\'id' .. ids[o] .. '\')">[+]</a>') output(format([[Table ID %s]], ids[o])) output(type(o.tag) == 'string' and ' [Tag=' .. o.tag .. ']' or '') output(' ') output(htmlize(obj_tostring_short(o, 40))) elseif type(o) == 'string' then output(htmlize(string.format('%q', o))) else output(htmlize(tostring(o))) end end local function output_body_html(o) if type(o) == 'table' then output(format('<div class="table" style="display:none" id="id%s">\n', ids[o])) -- xref if from[o] and next(from[o]) and next(from[o], next(from[o])) then output('<div>Referenced from: ') for _,from_id in pairs(from[o]) do output(format([[ <a onclick="show('id%s')" href="#id%s">%s</a> ]], from_id, from_id, from_id)) end output('</div>') end -- key/values for k,v in table_pairs(o) do local function prepare_output(oo) local f, is_long if count[oo] then f = function() output(format([[<a onclick="show('id%s')" href="#id%s">(see %s)</a>]], ids[oo],ids[oo],ids[oo])) end elseif type(oo) ~= 'table' then f = function() output_header_html(oo) end else f = function() output_header_html(oo) end is_long = true end return f, is_long end local kf, klong = prepare_output(k) local vf, vlong = prepare_output(v) local field_id = ids[o] .. '.' .. (ids[k] or tostring(k)) output(format([[<a name="id%s"></a>]], field_id)) output(format('<div class="table_row" id="id%s">\n', field_id)) kf() output('=') vf() if vlong then output_body_html(v) end output('</div>') end output('</div>\n') end end --local function output_html(o) output('<div>') output_header_html(o) output('</div>') output_body_html(o) end output(header) output_html(o) for oo in pairs(count) do if oo ~= o then output_html(oo) end end output(footer) end -- example usage local function dump(obj) object_to_html(obj, function(s) io.stdout:write(s) end) end dump(_G) -- metalua example --[[ package.path = package.path .. ';/luaanalyze/lib/?.lua' require "lexer" require "gg" require "mlp_lexer" require "mlp_misc" require "mlp_table" require "mlp_meta" require "mlp_expr" require "mlp_stat" require "mlp_ext" mlc = {} -- make gg happy local mlp = assert(_G.mlp) local function string_to_ast(src, filename) filename = filename or '(string)' local lx = mlp.lexer:newstream (src, filename) local ast = mlp.chunk(lx) return ast end local src_file = assert(io.open ('mydump.lua', 'r')) local src = src_file:read '*a'; src_file:close() local ast = string_to_ast(src, src_filename) local function remove_lineinfo(o) if type(o) == 'table' then o.lineinfo = nil for k,v in pairs(o) do remove_lineinfo(k) remove_lineinfo(v) end end end remove_lineinfo(ast) dump(ast) --]]