From ba1d3fcb6a03312290e7ca5e4a2846dd3f8bfe42 Mon Sep 17 00:00:00 2001 From: Pauli Date: Sat, 30 Jun 2018 14:32:02 +0300 Subject: [PATCH 1/2] Add dfhack.printall_recurse to quickly print df containers I often want to see multiple items quickly when trying to figure out what states actually matter to an issue that I debug. I decided to make it easier to quickly dump df structures with substructures and containers. It will generate large amount of data which can be sometimes slow to process manually. But processing can be automated using dfhack-run lua ^ and pipe to other tools (eg grep, sed, perl, sort, uniq etc) --- library/lua/dfhack.lua | 114 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 95c31c2d7..93ab48dca 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -180,6 +180,116 @@ function printall_ipairs(table) end end +local do_print_recurse + +local function print_string(printfn, v, seen, indent) + local str = tostring(v) + printfn(str) + return #str; +end + +local fill_chars = { + __index = function(table, key, value) + local rv = string.rep(' ', 23 - key) .. ' = ' + rawset(table, key, rv) + return rv + end, +} + +setmetatable(fill_chars, fill_chars) + +local function print_fields(value, seen, indent, prefix) + local ok,f,t,k = pcall(pairs,value) + if not ok then + dfhack.print(prefix) + dfhack.println('') + return 0 + end + local prev_value = "not a value" + local repeated = 0 + for k, v in f,t,k do + -- Only show set values of bitfields + if value._kind ~= "bitfield" or v then + local continue = false + if type(k) == "number" then + if prev_value == v then + repeated = repeated + 1 + continue = true + else + prev_value = v + end + else + prev_value = "not a value" + end + if not continue then + if repeated > 0 then + dfhack.println(prefix .. "") + repeated = 0 + end + dfhack.print(prefix) + local len = do_print_recurse(dfhack.print, k, seen, indent + 1) + dfhack.print(fill_chars[len <= 23 and len or 23]) + do_print_recurse(dfhack.println, v, seen, indent + 1) + end + end + end + if repeated > 0 then + dfhack.println(prefix .. "") + end + return 0 +end + +-- This should be same as print_array but userdata doesn't compare equal even if +-- they hold same pointer. +local function print_userdata(printfn, value, seen, indent) + local prefix = string.rep(' ', indent) + local strvalue = tostring(value) + dfhack.println(strvalue) + if seen[strvalue] then + dfhack.print(prefix) + dfhack.println('\n') + return 0 + end + seen[strvalue] = true + return print_fields(value, seen, indent, prefix) +end + +local function print_array(printfn, value, seen, indent) + local prefix = string.rep(' ', indent) + dfhack.println(tostring(value)) + if seen[value] then + dfhack.print(prefix) + dfhack.println('\n') + return 0 + end + seen[value] = true + return print_fields(value, seen, indent, prefix) +end + +local recurse_type_map = { + number = print_string, + string = print_string, + boolean = print_string, + ['function'] = print_string, + ['nil'] = print_string, + userdata = print_userdata, + table = print_array, +} + +do_print_recurse = function(printfn, value, seen, indent) + local t = type(value) + if not recurse_type_map[t] then + printfn("Unknown type " .. t .. " " .. tostring(value)) + return + end + return recurse_type_map[t](printfn, value, seen, indent) +end + +function printall_recurse(value) + local seen = {} + do_print_recurse(dfhack.println, value, seen, 0) +end + function copyall(table) local rv = {} for k,v in pairs(table) do rv[k] = v end @@ -334,6 +444,7 @@ function dfhack.interpreter(prompt,hfile,env) " '= foo' => '_1,_2,... = foo'\n".. " '! foo' => 'print(foo)'\n".. " '~ foo' => 'printall(foo)'\n".. + " '^ foo' => 'printall_recurse(foo)'\n".. " '@ foo' => 'printall_ipairs(foo)'\n".. "All of these save the first result as '_'.") print_banner = false @@ -358,6 +469,9 @@ function dfhack.interpreter(prompt,hfile,env) print(table.unpack(data,2,data.n)) printall_ipairs(data[2]) end, + ['^'] = function(data) + printall_recurse(data[2]) + end, ['='] = function(data) for i=2,data.n do local varname = '_'..vcnt From 03f8a04a430823a5252f7392b540e07bb58f8f22 Mon Sep 17 00:00:00 2001 From: Pauli Date: Sat, 30 Jun 2018 22:33:32 +0300 Subject: [PATCH 2/2] Document and changelog entry for printall_recurse --- docs/Lua API.rst | 4 ++++ docs/changelog.txt | 3 +++ 2 files changed, 7 insertions(+) diff --git a/docs/Lua API.rst b/docs/Lua API.rst index e9e03debf..a2120d41c 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -2404,6 +2404,10 @@ environment by the mandatory init file dfhack.lua: If the argument is a lua table or DF object reference, prints all fields. +* ``printall_recurse(obj)`` + + If the argument is a lua table or DF object reference, prints all fields recursively. + * ``copyall(obj)`` Returns a shallow copy of the table or reference as a lua table. diff --git a/docs/changelog.txt b/docs/changelog.txt index 3d8cf862c..48095cf65 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,6 +44,9 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - New functions: - ``Units::isDiplomat(unit)`` +## Lua +- Add ``printall_recurse`` to print tables and DF references recursively. It can be also used with ``^`` from lua interpreter. + ## Internals - jsoncpp: updated to version 1.8.4 and switched to using a git submodule