From 35b177081b9207910348c9492b4b30f11e1e7934 Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Tue, 6 Feb 2018 12:13:45 +0100 Subject: [PATCH] update pt --- Lua/lib/pt.lua | 112 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 26 deletions(-) diff --git a/Lua/lib/pt.lua b/Lua/lib/pt.lua index e13c77c..46edf19 100644 --- a/Lua/lib/pt.lua +++ b/Lua/lib/pt.lua @@ -1,11 +1,12 @@ -local extra = require 'extra' +local path = string.gsub(..., "[^.]+$", "") +local extra = require(path.."extra") local opairs = extra.opairs local pt = {} pt.__index = pt setmetatable(pt, pt) -function rawstr(v) +local function rawstr(v) if v == nil then return 'nil' end local mt = getmetatable(v) local ts = mt and rawget(mt, '__tostring') @@ -16,16 +17,16 @@ function rawstr(v) return s end -function getaddr(t) +local function getaddr(t) return rawstr(t):sub(#type(t) + 3) end -function copy(t) +local function copy(t) -- shallow copy if type(t) ~= 'table' then return end local new = {} - for key, value in pairs(t) do - new[key] = value + for k,v in pairs(t) do + new[k] = v end return new end @@ -42,8 +43,13 @@ function pt.__call(pt, args) 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:inner('__root__', t, '') + self.cache = {} + self.canonicalized = {} + self:inner('__root', t, '') return self.seen end @@ -51,7 +57,12 @@ function pt:write(...) self.writer(...) end -function pt.safekey(k) +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 @@ -60,7 +71,7 @@ function pt.safekey(k) return s:find('[^%w_]') and ('%q'):format(s) or s end -function pt.safeval(v, indent) +function pt:safeval(v, indentation) if type(v) == 'function' then return 'f'..getaddr(v) end @@ -68,44 +79,60 @@ function pt.safeval(v, indent) if type(v) == 'number' then return s end - s = s:find('[\r\n]') and ('\n'..s):gsub('[\r\n]', '\n'..indent..' ') or s + -- 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, indent) +function pt:inner(k, v, indentation) if type(v) ~= 'table' then if self.skeleton then return end - self:write(indent, pt.safekey(k), ': ') - self:write(pt.safeval(v, indent), '\n') + self:write(indentation, self:safekey(k), ': ') + self:write(self:safeval(v, indentation), '\n') return end local addr = getaddr(v) - self:write(indent, pt.safekey(k)) + self:write(indentation, self:safekey(k)) - if #indent > self.depth or self.skipped[addr] then + local canon + if not self.noncanon and type(k) ~= 'table' then + -- TODO: canonicalize in advance (during ownage of :outer) + 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(': #t', addr, '\n') + self:write(': #', canon, '\n') return end if self.seen[addr] or self.queued[addr] then - self:write(': *t', addr, self.seen_elsewhere[addr] and ' #\n' or '\n') + self:write(': *', canon, self.seen_elsewhere[addr] and ' #\n' or '\n') return end self.seen[addr] = true - self:write(': &t', addr, '\n') - self:outer(v, indent..' ') + self:write(': &', canon, '\n') + self:outer(v, indentation..self.indent) end -function pt:outer(t, indent) +function pt:outer_old(t, indentation) if type(t) ~= "table" then - self:write(indent, pt.safeval(t, indent), '\n') + local s = self:safeval(t, indentation) + self:write(indentation, s, '\n') return end @@ -115,29 +142,62 @@ function pt:outer(t, indent) for k,v in opairs(t) do if type(v) == 'table' then local addr = getaddr(v) - if not self.queued[addr] and not self.seen[addr] and not self.skipped[addr] then + 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, indent) + self:inner(k, v, indentation) end end for k,v in opairs(not_ours) do - self:inner(k, v, indent) + self:inner(k, v, indentation) end for k,v in opairs(ours) do self.queued[getaddr(v)] = nil - self:inner(k, v, indent) + self:inner(k, v, indentation) end local mt = getmetatable(t) if mt ~= nil then - self:inner('__metatable', mt, indent) + 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