mirror of
https://github.com/grumpycoders/pcsx-redux.git
synced 2025-04-02 10:41:54 -04:00
127 lines
4 KiB
Lua
127 lines
4 KiB
Lua
--lualoader, R"EOF(--
|
|
|
|
-- seems this is the only way to escape these, as lua don't know how to map char '\a' to 'a'
|
|
local ESCAPE_MAP = {
|
|
['\a'] = '\\a', ['\b'] = '\\b', ['\f'] = '\\f', ['\n'] = '\\n', ['\r'] = '\\r',
|
|
['\t'] = '\\t', ['\v'] = '\\v', ['\\'] = '\\\\',
|
|
}
|
|
|
|
-- generic utilities
|
|
pprint._internals.tokenize_string = function(s)
|
|
local t = {}
|
|
for i = 1, #s do
|
|
local c = s:sub(i, i)
|
|
local b = c:byte()
|
|
local e = ESCAPE_MAP[c]
|
|
if (b >= 0x20 and b < 0x80) or e then
|
|
local s = e or c
|
|
t[i] = { char = s, len = #s }
|
|
else
|
|
t[i] = { char = string.format('\\x%02x', b), len = 4 }
|
|
end
|
|
if c == '"' then
|
|
t.has_double_quote = true
|
|
elseif c == "'" then
|
|
t.has_single_quote = true
|
|
end
|
|
end
|
|
return t
|
|
end
|
|
pprint._internals.tokenize_utf8_string = tokenize_string
|
|
|
|
local has_lpeg, lpeg = pcall(require, 'lpeg')
|
|
|
|
if has_lpeg then
|
|
local function utf8_valid_char(c)
|
|
return { char = c, len = 1 }
|
|
end
|
|
|
|
local function utf8_invalid_char(c)
|
|
local b = c:byte()
|
|
local e = ESCAPE_MAP[c]
|
|
if (b >= 0x20 and b < 0x80) or e then
|
|
local s = e or c
|
|
return { char = s, len = #s }
|
|
else
|
|
return { char = string.format('\\x%02x', b), len = 4 }
|
|
end
|
|
end
|
|
|
|
local cont = lpeg.R('\x80\xbf')
|
|
local utf8_char =
|
|
lpeg.R('\x20\x7f') +
|
|
lpeg.R('\xc0\xdf') * cont +
|
|
lpeg.R('\xe0\xef') * cont * cont +
|
|
lpeg.R('\xf0\xf7') * cont * cont * cont
|
|
|
|
local utf8_capture = (((utf8_char / utf8_valid_char) + (lpeg.P(1) / utf8_invalid_char)) ^ 0) * -1
|
|
|
|
pprint._internals.tokenize_utf8_string = function(s)
|
|
local dq = s:find('"')
|
|
local sq = s:find("'")
|
|
local t = table.pack(utf8_capture:match(s))
|
|
t.has_double_quote = not not dq
|
|
t.has_single_quote = not not sq
|
|
return t
|
|
end
|
|
end
|
|
|
|
local CACHE_TYPES = {
|
|
['table'] = true, ['function'] = true, ['thread'] = true,
|
|
['userdata'] = true, ['cdata'] = true,
|
|
}
|
|
|
|
-- cache would be populated to be like:
|
|
-- {
|
|
-- function = { `fun1` = 1, _cnt = 1 }, -- object id
|
|
-- table = { `table1` = 1, `table2` = 2, _cnt = 2 },
|
|
-- visited_tables = { `table1` = 7, `table2` = 8 }, -- visit count
|
|
-- }
|
|
-- use weakrefs to avoid accidentall adding refcount
|
|
pprint._internals.cache_apperance = function(obj, cache, option)
|
|
if not cache.visited_tables then
|
|
cache.visited_tables = setmetatable({}, {__mode = 'k'})
|
|
end
|
|
local t = type(obj)
|
|
|
|
-- TODO can't test filter_function here as we don't have the ix and key,
|
|
-- might cause different results?
|
|
-- respect show_xxx and filter_function to be consistent with print results
|
|
if (not pprint._internals.TYPES[t] and not option.show_table)
|
|
or (pprint._internals.TYPES[t] and not option['show_'..t]) then
|
|
return
|
|
end
|
|
|
|
if CACHE_TYPES[t] or pprint._internals.TYPES[t] == nil then
|
|
if not cache[t] then
|
|
cache[t] = setmetatable({}, {__mode = 'k'})
|
|
cache[t]._cnt = 0
|
|
end
|
|
if not cache[t][obj] then
|
|
cache[t]._cnt = cache[t]._cnt + 1
|
|
cache[t][obj] = cache[t]._cnt
|
|
end
|
|
end
|
|
if t == 'table' or pprint._internals.TYPES[t] == nil then
|
|
if cache.visited_tables[obj] == false then
|
|
-- already printed, no need to mark this and its children anymore
|
|
return
|
|
elseif cache.visited_tables[obj] == nil then
|
|
cache.visited_tables[obj] = 1
|
|
else
|
|
-- visited already, increment and continue
|
|
cache.visited_tables[obj] = cache.visited_tables[obj] + 1
|
|
return
|
|
end
|
|
for k, v in pairs(obj) do
|
|
pprint._internals.cache_apperance(k, cache, option)
|
|
pprint._internals.cache_apperance(v, cache, option)
|
|
end
|
|
local mt = getmetatable(obj)
|
|
if mt and option.show_metatable then
|
|
pprint._internals.cache_apperance(mt, cache, option)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- )EOF"
|