1
0

Major restructure in how tables are formatted. Options max_depth and recursion has been deprecated.

This commit is contained in:
Jon Michael Aanes 2017-07-25 17:49:06 +02:00
parent fe1c166044
commit f332d6bf95
10 changed files with 181 additions and 168 deletions

View File

@ -105,7 +105,6 @@ option a bad value.
Tasks to be done before `pretty` can be called version 1.0.0, in order of Tasks to be done before `pretty` can be called version 1.0.0, in order of
priority: priority:
- Depricate `max_depth` and `recursion` options.
- Add a dedicated unicode submodule, to handle some minor alignment and - Add a dedicated unicode submodule, to handle some minor alignment and
character escaping issues. `pretty` should escape all malformed unicode character escaping issues. `pretty` should escape all malformed unicode
sequences. sequences.
@ -123,6 +122,10 @@ It would be nice to have the following, but these are secondary:
- Expand on the comment output in output, for `__tostring` methods, and global - Expand on the comment output in output, for `__tostring` methods, and global
namespaces like `io` or `math`. namespaces like `io` or `math`.
- Fit output within a predefined width limit. Default to 80. - Fit output within a predefined width limit. Default to 80.
- Look into tool for understanding complex structures with recursive
definitions. Whatever modes are thought up, they should be automatic modes,
not an options. Should at least include modes for self-referential tables
and Directed-Acyclic-Graphs.
## Alternative pretty printers ## Alternative pretty printers

View File

@ -228,6 +228,8 @@ local function get_table_info (t)
info.is_tabular = is_tabular(t) info.is_tabular = is_tabular(t)
info.is_uniform = has_uniform_structure(t) info.is_uniform = has_uniform_structure(t)
info.is_leaf_node = is_leaf_node(t) info.is_leaf_node = is_leaf_node(t)
info.key_types = get_key_types(t)
info.value_types = get_value_types(t)
-- Determine type of table -- Determine type of table
if not info.has_seq and not info.has_map then info.type = TABLE_TYPE.EMPTY if not info.has_seq and not info.has_map then info.type = TABLE_TYPE.EMPTY
@ -246,7 +248,6 @@ end
local function analyze_structure (root, max_depth, info) local function analyze_structure (root, max_depth, info)
-- Argument fixing -- Argument fixing
local info = info or {} local info = info or {}
local max_depth = max_depth or math.huge
-- Quick return -- Quick return
if type(root) ~= 'table' then return info end if type(root) ~= 'table' then return info end

View File

@ -278,16 +278,24 @@ end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
return function (value, depth, l, format_value) local DISPLAY = { -- TODO: Move To common file
HIDE = 1,
SMALL = 2,
INLINE = 3,
EXPAND = 4,
}
return function (value, display, l, format_value)
assert(type(value) == 'function') assert(type(value) == 'function')
assert(type(depth) == 'number' and type(l) == 'table' and type(format_value) == 'function') assert(type(display) == 'number' and type(l) == 'table' and type(format_value) == 'function')
local info = get_function_info(value) local info = get_function_info(value)
local function_params, function_body = nil, '...' local function_params, function_body = nil, '...'
if not info.docs and info.defined_how ~= 'C' and (depth == 0 or info.defined_how == 'string') then if not info.docs and info.defined_how ~= 'C' and (display == DISPLAY.EXPAND or info.defined_how == 'string') then
-- Only look for documentation, when at depth 0, or when defined in -- Only look for documentation, when at display = DISPLAY.EXPAND, or when defined in
-- string. We don't want to open a ton of files constantly when -- string. We don't want to open a ton of files constantly when
-- formatting a table. -- formatting a table.
info = get_function_body_info(info) info = get_function_body_info(info)
@ -297,7 +305,7 @@ return function (value, depth, l, format_value)
end end
end end
if info.builtin and depth == math.huge then if info.builtin and display == DISPLAY.HIDE then
assert(info.name) assert(info.name)
return l(info.name); return l(info.name);
end end
@ -320,7 +328,7 @@ return function (value, depth, l, format_value)
l[#l+1] = { 'align', 'func_def', width_of_strings_in_l(l, top_before) } l[#l+1] = { 'align', 'func_def', width_of_strings_in_l(l, top_before) }
-- Cleanup and finish -- Cleanup and finish
if depth ~= 0 then if display ~= DISPLAY.EXPAND then
l[#l+1] = (function_body:sub(1,1) == '\n') and '' or ' ' l[#l+1] = (function_body:sub(1,1) == '\n') and '' or ' '
l[#l+1] = function_body l[#l+1] = function_body
l[#l+1] = { 'align', 'func_end', #function_body } l[#l+1] = { 'align', 'func_end', #function_body }
@ -366,7 +374,7 @@ return function (value, depth, l, format_value)
if info.nups > 0 and (not info.builtin and not info.docs) then if info.nups > 0 and (not info.builtin and not info.docs) then
l[#l+1] = indent l[#l+1] = indent
l[#l+1] = '-- Up values: ' l[#l+1] = '-- Up values: '
format_value(info.ups, depth + 1, l) format_value(info.ups, DISPLAY.INLINE, l)
end end
-- Ignore spacing and function body if it's a Λ string. -- Ignore spacing and function body if it's a Λ string.

View File

@ -208,12 +208,19 @@ local function format_hard_num (n)
assert(false) assert(false)
end end
return function (value, depth, l) local DISPLAY = { -- TODO: Move To common file
-- Formats the number nicely. If depth is 0 and we have some space for extra HIDE = 1,
-- info, we give some tidbits, to help investigation. SMALL = 2,
INLINE = 3,
EXPAND = 4,
}
return function (value, display, l)
-- Formats the number nicely. If display is DISPLAY.EXPAND, we have some
-- space for extra info, we give some tidbits, to help investigation.
assert(type(value) == 'number') assert(type(value) == 'number')
assert(type(depth) == 'number' and type(l) == 'table') assert(type(display) == 'number' and type(l) == 'table')
-- First format a "soft" version. This number is not guarenteed to accurate. -- First format a "soft" version. This number is not guarenteed to accurate.
-- It's purpose is to give a general idea of the value. -- It's purpose is to give a general idea of the value.
@ -221,7 +228,7 @@ return function (value, depth, l)
-- If we have space for it, format a "hard" value, also. This number is as -- If we have space for it, format a "hard" value, also. This number is as
-- short as possible, while evaluating precisely to the value of the number. -- short as possible, while evaluating precisely to the value of the number.
if depth == 0 then if display == DISPLAY.EXPAND then
local hard_repr = format_hard_num(value) local hard_repr = format_hard_num(value)
if l[#l] ~= hard_repr then if l[#l] ~= hard_repr then
l[#l+1] = ' -- Approx: ' l[#l+1] = ' -- Approx: '

View File

@ -78,6 +78,13 @@ local VALUE_TYPE_SORT_ORDER = {
['function'] = 7, ['function'] = 7,
} }
local DISPLAY = { -- TODO: Move To common file
HIDE = 1,
SMALL = 2,
INLINE = 3,
EXPAND = 4,
}
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Key-value-pair Util -- Key-value-pair Util
@ -355,27 +362,26 @@ local function align_into_tabular_style (l, start_i, stop_i)
return fix_alignment(l, start_i) return fix_alignment(l, start_i)
end end
local function fix_seperator_info (l, indent_char, max_depth) local function fix_seperator_info (l, indent_char)
-- Error Checking -- Error Checking
assert(type(l) == 'table') assert(type(l) == 'table')
assert(type(indent_char) == 'string') assert(type(indent_char) == 'string')
assert(type(max_depth) == 'number')
-- Do stuff -- Do stuff
local depth, inline_depth = 0, nil local display, inline_depth = 0, nil
for i = 1, #l do for i = 1, #l do
if type(l[i]) ~= 'table' then if type(l[i]) ~= 'table' then
-- Do nothing -- Do nothing
elseif l[i][1] == 'seperator' then elseif l[i][1] == 'seperator' then
assert(l[i][2] == nil or type(l[i][2]) == 'string') assert(l[i][2] == nil or type(l[i][2]) == 'string')
l[i] = (l[i][2] or '') .. (inline_depth and ' ' or ('\n' .. indent_char:rep(depth))) l[i] = (l[i][2] or '') .. (inline_depth and ' ' or ('\n' .. indent_char:rep(display)))
elseif l[i][1] == 'indent' then elseif l[i][1] == 'indent' then
depth, inline_depth = depth + 1, inline_depth or l[i][3] == 'inline' and depth + 1 or nil display, inline_depth = display + 1, inline_depth or l[i][3] == 'inline' and display + 1 or nil
l[i] = l[i][2] .. (inline_depth and ' ' or ('\n' .. indent_char:rep(depth))) l[i] = l[i][2] .. (inline_depth and ' ' or ('\n' .. indent_char:rep(display)))
elseif l[i][1] == 'unindent' then elseif l[i][1] == 'unindent' then
l[i] = (inline_depth and ' ' or ('\n' .. indent_char:rep(depth-1))) .. l[i][2] l[i] = (inline_depth and ' ' or ('\n' .. indent_char:rep(display-1))) .. l[i][2]
depth, inline_depth = depth - 1, (depth ~= inline_depth) and inline_depth or nil display, inline_depth = display - 1, (display ~= inline_depth) and inline_depth or nil
end end
end end
end end
@ -388,25 +394,25 @@ local TABLE_TYPE = import 'table_type'
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Key-value pair formatting. -- Key-value pair formatting.
local function format_key_and_value_string_map (key, value, depth, l, format_value) local function format_key_and_value_string_map (key, value, display, l, format_value)
l[#l+1] = key l[#l+1] = key
l[#l+1] = { 'align', 'key', #key } l[#l+1] = { 'align', 'key', #key }
l[#l+1] = ' = ' l[#l+1] = ' = '
return format_value(value, depth, l) return format_value(value, display, l)
end end
local function format_key_and_value_arbitr_map (key, value, depth, l, format_value) local function format_key_and_value_arbitr_map (key, value, display, l, format_value)
local index_before_key = #l+1 local index_before_key = #l+1
l[#l+1] = '[' l[#l+1] = '['
format_value(key, math.huge, l) format_value(key, DISPLAY.HIDE, l)
l[#l+1] = ']' l[#l+1] = ']'
l[#l+1] = { 'align', 'key', width_of_strings_in_l(l, index_before_key) } l[#l+1] = { 'align', 'key', width_of_strings_in_l(l, index_before_key) }
l[#l+1] = ' = ' l[#l+1] = ' = '
return format_value(value, depth, l) return format_value(value, display, l)
end end
local function format_key_and_value_sequence (key, value, depth, l, format_value) local function format_key_and_value_sequence (key, value, display, l, format_value)
return format_value(value, depth, l) return format_value(value, display, l)
end end
local TABLE_TYPE_TO_PAIR_FORMAT = { local TABLE_TYPE_TO_PAIR_FORMAT = {
@ -421,30 +427,24 @@ local TABLE_TYPE_TO_PAIR_FORMAT = {
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Table formatting -- Table formatting
local function format_table (t, depth, l, format_value) local function format_table (t, display, l, format_value)
-- Error Checking -- Error Checking
assert(type(t) == 'table') assert(type(t) == 'table')
assert(type(depth) == 'number' and type(l) == 'table') assert(type(display) == 'number' and type(l) == 'table')
-- Do stuff -- Find table info
if not l.info[t] then analyze_structure(t, l.options.max_depth-depth, l.info) end if not l.info[t] then analyze_structure(t, display, l.info) end
local table_info = l.info[t] local table_info = l.info[t]
assert(table_info) assert(table_info)
if l.options.recursion == 'marked' and table_info.marker then -- If empty or not a lot of space, give a small represetation: `{...}`
l[#l+1], l[#l+2], l[#l+3] = '<', table_info.marker, '>' if table_info.type == TABLE_TYPE.EMPTY or display <= DISPLAY.SMALL then
end
local already_visited = l.visited[t]
l.visited[t] = true
-- If empty, visited or above max-depth, give a small represetation: `{...}`
if table_info.type == TABLE_TYPE.EMPTY or depth >= l.options.max_depth or (already_visited and l.options.recursion ~= 'revisit') then
l '{' l '{'
if l.options._table_addr_comment then l[#l+1] = ' --[[' .. table_info.address .. ']] ' end if l.options._table_addr_comment then l[#l+1] = ' --[[' .. table_info.address .. ']] ' end
if table_info.type ~= TABLE_TYPE.EMPTY then l[#l+1] = '...' end if table_info.type ~= TABLE_TYPE.EMPTY then l[#l+1] = '...' end
return l '}' return l '}'
end end
-- Get key-value pairs, and possibly fill holes. -- Get key-value pairs, and possibly fill holes.
local key_value_pairs = get_key_value_pairs_in_proper_order(t) local key_value_pairs = get_key_value_pairs_in_proper_order(t)
if table_info.type == TABLE_TYPE.SEQUENCE and l.info[t].has_holes then if table_info.type == TABLE_TYPE.SEQUENCE and l.info[t].has_holes then
@ -457,10 +457,13 @@ local function format_table (t, depth, l, format_value)
assert(pair_format_func) assert(pair_format_func)
-- Begin formatting table. -- Begin formatting table.
local next_display = display - 1
if (l.info[t].value_types.nr_types >= 2) then next_display = DISPLAY.SMALL end
l[#l+1] = {'indent', '{'} l[#l+1] = {'indent', '{'}
if l.options._table_addr_comment then l[#l+1], l[#l+2] = '--[['..table_info.address..']]', {'seperator'} end if l.options._table_addr_comment then l[#l+1], l[#l+2] = '--[['..table_info.address..']]', {'seperator'} end
for _, pair in ipairs(key_value_pairs) do for _, pair in ipairs(key_value_pairs) do
pair_format_func(pair[1], pair[2], depth + 1, l, format_value) pair_format_func(pair[1], pair[2], next_display, l, format_value)
l[#l+1] = {'seperator', ','} l[#l+1] = {'seperator', ','}
end end
if l[#l][1] == 'seperator' then l[#l] = nil end if l[#l][1] == 'seperator' then l[#l] = nil end
@ -488,13 +491,13 @@ end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Coroutine formatting -- Coroutine formatting
local function format_coroutine (value, depth, l) local function format_coroutine (value, _, l)
-- Formats a coroutine. Unfortunantly we cannot gather a lot of information -- Formats a coroutine. Unfortunantly we cannot gather a lot of information
-- about coroutines. -- about coroutines.
-- Error check -- Error check
assert(type(value) == 'thread') assert(type(value) == 'thread')
assert(type(depth) == 'number' and type(l) == 'table') assert(type(l) == 'table')
-- Do stuff -- Do stuff
l[#l+1] = coroutine.status(value) l[#l+1] = coroutine.status(value)
@ -505,9 +508,9 @@ end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Primitive formatting -- Primitive formatting
local function format_primitive (value, depth, l) local function format_primitive (value, _, l)
-- Error check -- Error check
assert(type(depth) == 'number' and type(l) == 'table') assert(type(l) == 'table')
-- Do stuff -- Do stuff
l[#l+1] = tostring(value) l[#l+1] = tostring(value)
end end
@ -525,12 +528,12 @@ local TYPE_TO_FORMAT_FUNC = {
['cdata'] = format_primitive, -- TODO & Luajit only ['cdata'] = format_primitive, -- TODO & Luajit only
} }
local function format_value (value, depth, l) local function format_value (value, display, l)
assert(type(depth) == 'number' and type(l) == 'table') assert(type(display) == 'number' and type(l) == 'table')
local formatting = TYPE_TO_FORMAT_FUNC[type(value)] local formatting = TYPE_TO_FORMAT_FUNC[type(value)]
--print(value, formatting) --print(value, formatting)
if formatting then if formatting then
formatting(value, depth, l, format_value) formatting(value, display, l, format_value)
else else
error(ERROR_UNKNOWN_TYPE:format(type(value), tostring(value)), 2) error(ERROR_UNKNOWN_TYPE:format(type(value), tostring(value)), 2)
end end
@ -551,12 +554,9 @@ setmetatable(StringBuilder, {
local DEBUG_OPTION_USED = { } local DEBUG_OPTION_USED = { }
local KNOWN_OPTIONS = { local KNOWN_OPTIONS = {
_table_addr_comment = { type = 'boolean', default = false, debug = 'debug' }, -- TODO: Maybe automatically display table address when depth = 0? _table_addr_comment = { type = 'boolean', default = false, debug = 'debug' }, -- TODO: Maybe automatically display table address when display = 0?
indent = { type = 'string', default = ' ' }, indent = { type = 'string', default = ' ' },
max_depth = { type = 'number', default = math.huge },
recursion = { type = 'string', default = 'ignore', accepted = {['ignore'] = true, ['marked'] = true, ['revisit'] = true} }, -- TODO: Completely depricate this option. I do not like it.
} }
local function ensure_that_all_options_are_known (input_options) local function ensure_that_all_options_are_known (input_options)
@ -601,15 +601,14 @@ local function pretty_format (value, options)
-- Setup StringBuilder -- Setup StringBuilder
local l = StringBuilder() local l = StringBuilder()
l.visited = { next_mark = 1 }
l.options = options l.options = options
l.info = analyze_structure(value, options.max_depth) l.info = analyze_structure(value, 3)
-- Format value. -- Format value.
format_value(value, 0, l) format_value(value, DISPLAY.EXPAND, l)
-- If any alignment info still exists, ignore it -- If any alignment info still exists, ignore it
fix_seperator_info(l, l.options.indent, l.options.max_depth) fix_seperator_info(l, l.options.indent)
ignore_alignment_info(l) ignore_alignment_info(l)
return table.concat(l, '') return table.concat(l, '')

View File

@ -91,8 +91,7 @@ end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
local function format_shortform_string (str, _, l)
local function format_shortform_string (str, depth, l)
l[#l+1] = SHORT_STR_DELIMITER l[#l+1] = SHORT_STR_DELIMITER
l[#l+1] = escape_string(str) l[#l+1] = escape_string(str)
l[#l+1] = SHORT_STR_DELIMITER l[#l+1] = SHORT_STR_DELIMITER
@ -119,7 +118,7 @@ local function safe_cut (str, si, ei)
end end
local function format_cut_string (str, depth, l) local function format_cut_string (str, _, l)
-- Calculate string -- Calculate string
local str = escape_string(str) local str = escape_string(str)
str = safe_cut(str, 1, NR_CHARS_IN_LONG_STRING - #STRING_CONT_INDICATOR) str = safe_cut(str, 1, NR_CHARS_IN_LONG_STRING - #STRING_CONT_INDICATOR)
@ -131,7 +130,7 @@ local function format_cut_string (str, depth, l)
l[#l+1] = STRING_CONT_INDICATOR l[#l+1] = STRING_CONT_INDICATOR
end end
local function format_concatted_string (str, depth, l) local function format_concatted_string (str, _, l)
-- Cuts the string up into smaller individual substrings, each Concatted -- Cuts the string up into smaller individual substrings, each Concatted
-- together. Is uglier compared to longform, but is at least idempotent. -- together. Is uglier compared to longform, but is at least idempotent.
@ -139,7 +138,7 @@ local function format_concatted_string (str, depth, l)
-- Error checking -- Error checking
assert( type(str) == 'string' ) assert( type(str) == 'string' )
assert(type(depth) == 'number' and type(l) == 'table') assert( type(l) == 'table' )
-- Calculate -- Calculate
local width_without_overhead = MAX_HORIZONAL_CHARACTER - 2*#SHORT_STR_DELIMITER - #' ..' local width_without_overhead = MAX_HORIZONAL_CHARACTER - 2*#SHORT_STR_DELIMITER - #' ..'
@ -163,11 +162,11 @@ local function format_concatted_string (str, depth, l)
l[#l] = '' l[#l] = ''
end end
local function format_longform_string (str, depth, l) local function format_longform_string (str, _, l)
-- Error checking -- Error checking
assert(type(str) == 'string') assert(type(str) == 'string')
assert(type(depth) == 'number' and type(l) == 'table') assert(type(l) == 'table')
-- Calculate -- Calculate
local level_required = smallest_secure_longform_string_level(str) local level_required = smallest_secure_longform_string_level(str)
@ -179,7 +178,14 @@ local function format_longform_string (str, depth, l)
l[#l+1] = ']'..string.rep('=', level_required)..']' l[#l+1] = ']'..string.rep('=', level_required)..']'
end end
return function (str, depth, l) local DISPLAY = { -- TODO: Move To common file
HIDE = 1,
SMALL = 2,
INLINE = 3,
EXPAND = 4,
}
return function (str, display, l)
-- pretty.format_string -- pretty.format_string
-- TODO: Prefer \ddd style escaping to shorter (\n, \t), when many of the -- TODO: Prefer \ddd style escaping to shorter (\n, \t), when many of the
@ -187,17 +193,17 @@ return function (str, depth, l)
-- Error checking -- Error checking
assert(type(str) == 'string') assert(type(str) == 'string')
assert(type(depth) == 'number' and type(l) == 'table') assert(type(display) == 'number' and type(l) == 'table')
-- Do work -- Do work
if #str < NR_CHARS_IN_LONG_STRING then if #str < NR_CHARS_IN_LONG_STRING then
return format_shortform_string(str, depth, l) return format_shortform_string(str, nil, l)
elseif depth > 0 then elseif display < DISPLAY.EXPAND then
return format_cut_string (str, depth, l) return format_cut_string (str, nil, l)
elseif does_string_require_escaping (str) then elseif does_string_require_escaping (str) then
return format_concatted_string(str, depth, l) return format_concatted_string(str, nil, l)
else else
return format_longform_string(str, depth, l) return format_longform_string(str, nil, l)
end end
end end

View File

@ -10,7 +10,7 @@ SUITE:setEnviroment {
SUITE:addTest('Empty Table', function () SUITE:addTest('Empty Table', function ()
local input = {} -- Empty! local input = {} -- Empty!
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.type, TABLE_TYPE.EMPTY, 'Returned bad type: '..table_info.type) assert_equal(table_info.type, TABLE_TYPE.EMPTY, 'Returned bad type: '..table_info.type)
assert_equal(table_info.has_seq, false) assert_equal(table_info.has_seq, false)
@ -19,7 +19,7 @@ end)
SUITE:addTest('Sequence', function () SUITE:addTest('Sequence', function ()
local input = { 1, 2, 3 } local input = { 1, 2, 3 }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.type, TABLE_TYPE.SEQUENCE, 'Returned bad type: '..table_info.type) assert_equal(table_info.type, TABLE_TYPE.SEQUENCE, 'Returned bad type: '..table_info.type)
assert_equal(table_info.has_seq, true) assert_equal(table_info.has_seq, true)
@ -28,7 +28,7 @@ end)
SUITE:addTest('Sequence with holes', function () SUITE:addTest('Sequence with holes', function ()
local input = { 1, nil, 3 } local input = { 1, nil, 3 }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.type, TABLE_TYPE.SEQUENCE, 'Returned bad type: '..table_info.type) assert_equal(table_info.type, TABLE_TYPE.SEQUENCE, 'Returned bad type: '..table_info.type)
assert_equal(table_info.has_seq, true) assert_equal(table_info.has_seq, true)
@ -38,7 +38,7 @@ end)
SUITE:addTest('Sequence with hole on start', function () SUITE:addTest('Sequence with hole on start', function ()
local input = { nil, 2, 3 } local input = { nil, 2, 3 }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.type, TABLE_TYPE.SEQUENCE, 'Returned bad type: '..table_info.type) assert_equal(table_info.type, TABLE_TYPE.SEQUENCE, 'Returned bad type: '..table_info.type)
assert_equal(table_info.has_seq, true) assert_equal(table_info.has_seq, true)
@ -48,7 +48,7 @@ end)
SUITE:addTest('Pure Map', function () SUITE:addTest('Pure Map', function ()
local input = { a = 1, [true] = 2, c = 3 } local input = { a = 1, [true] = 2, c = 3 }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.type, TABLE_TYPE.PURE_MAP, 'Returned bad type: '..table_info.type) assert_equal(table_info.type, TABLE_TYPE.PURE_MAP, 'Returned bad type: '..table_info.type)
assert_equal(table_info.has_seq, false) assert_equal(table_info.has_seq, false)
@ -57,7 +57,7 @@ end)
SUITE:addTest('Boolean set', function () SUITE:addTest('Boolean set', function ()
local input = { [true] = true, [false] = false } local input = { [true] = true, [false] = false }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.type, TABLE_TYPE.SET, 'Returned bad type: '..table_info.type) assert_equal(table_info.type, TABLE_TYPE.SET, 'Returned bad type: '..table_info.type)
assert_equal(table_info.has_seq, false) assert_equal(table_info.has_seq, false)
@ -66,7 +66,7 @@ end)
SUITE:addTest('A Mixed table', function () SUITE:addTest('A Mixed table', function ()
local input = { 300, [300] = 1 } local input = { 300, [300] = 1 }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.has_seq, true) assert_equal(table_info.has_seq, true)
assert_equal(table_info.has_map, true) assert_equal(table_info.has_map, true)
@ -75,7 +75,7 @@ end)
SUITE:addTest('String Map', function () SUITE:addTest('String Map', function ()
local input = { a = 1, b = 2, c = 3 } local input = { a = 1, b = 2, c = 3 }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.type, TABLE_TYPE.STRING_MAP, 'Returned bad type: '..table_info.type) assert_equal(table_info.type, TABLE_TYPE.STRING_MAP, 'Returned bad type: '..table_info.type)
assert_equal(table_info.has_seq, false) assert_equal(table_info.has_seq, false)
@ -84,7 +84,7 @@ end)
SUITE:addTest('Set', function () SUITE:addTest('Set', function ()
local input = { a = true, b = true, c = true } local input = { a = true, b = true, c = true }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.type, TABLE_TYPE.SET, 'Returned bad type: '..table_info.type) assert_equal(table_info.type, TABLE_TYPE.SET, 'Returned bad type: '..table_info.type)
assert_equal(table_info.is_set, true) assert_equal(table_info.is_set, true)
@ -92,35 +92,35 @@ end)
SUITE:addTest('Tabular of sequences', function () SUITE:addTest('Tabular of sequences', function ()
local input = { a = {1, 2, 3}, b = {4, 5, 6}, c = {7, 8, 9} } local input = { a = {1, 2, 3}, b = {4, 5, 6}, c = {7, 8, 9} }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.is_tabular, {true, true, true}) assert_equal(table_info.is_tabular, {true, true, true})
end) end)
SUITE:addTest('Tabular of maps', function () SUITE:addTest('Tabular of maps', function ()
local input = { a = {a = 1, b = 2}, b = {a = 3, b = 4}, c = {a = 2, b = 7} } local input = { a = {a = 1, b = 2}, b = {a = 3, b = 4}, c = {a = 2, b = 7} }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.is_tabular, {a = true, b = true}) assert_equal(table_info.is_tabular, {a = true, b = true})
end) end)
SUITE:addTest('Not Tabular, due to no-sub-tables', function () SUITE:addTest('Not Tabular, due to no-sub-tables', function ()
local input = { a = 1, b = {4}, c = 7 } local input = { a = 1, b = {4}, c = 7 }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.is_tabular, false) assert_equal(table_info.is_tabular, false)
end) end)
SUITE:addTest('Not Tabular, due to not being identical sub-tables', function () SUITE:addTest('Not Tabular, due to not being identical sub-tables', function ()
local input = { a = { a = 1 }, b = { b = 2 }, c = { c = 3 } } local input = { a = { a = 1 }, b = { b = 2 }, c = { c = 3 } }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.is_tabular, false) assert_equal(table_info.is_tabular, false)
end) end)
SUITE:addTest('Not Tabular, due to varying lengths', function () SUITE:addTest('Not Tabular, due to varying lengths', function ()
local input = { { 1 }, { 2, 3 }, { 4, 5, 6 } } local input = { { 1 }, { 2, 3 }, { 4, 5, 6 } }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.is_tabular, false) assert_equal(table_info.is_tabular, false)
end) end)
@ -153,7 +153,7 @@ end)
SUITE:addTest('goto is special', function () SUITE:addTest('goto is special', function ()
local input = { ['goto'] = 'hi' } local input = { ['goto'] = 'hi' }
local table_info = analyze_structure(input)[input] local table_info = analyze_structure(input, math.huge)[input]
assert_equal(table_info.type, TABLE_TYPE.PURE_MAP, 'Returned bad type: '..table_info.type) assert_equal(table_info.type, TABLE_TYPE.PURE_MAP, 'Returned bad type: '..table_info.type)
end) end)
@ -164,7 +164,7 @@ end)
SUITE:addTest('Recursive Numeration, Simple', function () SUITE:addTest('Recursive Numeration, Simple', function ()
local input = {} local input = {}
input[1] = input input[1] = input
local info = analyze_structure(input) local info = analyze_structure(input, math.huge)
assert_equal(1, info[input].marker) assert_equal(1, info[input].marker)
end) end)
@ -172,7 +172,7 @@ end)
SUITE:addTest('Recursive Numeration, Multiple-appear', function () SUITE:addTest('Recursive Numeration, Multiple-appear', function ()
local input = { {}, { {} } } local input = { {}, { {} } }
input[1][1] = input[2][1] input[1][1] = input[2][1]
local info = analyze_structure(input) local info = analyze_structure(input, math.huge)
assert_equal(1, info[input[1][1]].marker) assert_equal(1, info[input[1][1]].marker)
end) end)
@ -181,7 +181,7 @@ SUITE:addTest('Recursive Numeration, Multiple at once', function ()
local input = { {}, { {} } } local input = { {}, { {} } }
input[1][1] = input[2][1] input[1][1] = input[2][1]
input[1][2] = input input[1][2] = input
local info = analyze_structure(input) local info = analyze_structure(input, math.huge)
assert_equal('number', type(info[input[1][1]].marker)) assert_equal('number', type(info[input[1][1]].marker))
assert_equal('number', type(info[input].marker)) assert_equal('number', type(info[input].marker))
@ -190,7 +190,7 @@ end)
SUITE:addTest('Recursive Numeration, Through Keys', function () SUITE:addTest('Recursive Numeration, Through Keys', function ()
local input = { } local input = { }
input[input] = input input[input] = input
local info = analyze_structure(input) local info = analyze_structure(input, math.huge)
assert_equal(1, info[input].marker) assert_equal(1, info[input].marker)
end) end)
@ -200,14 +200,14 @@ end)
SUITE:addTest('Wont crash, even though metatable.__index throws errors', function () SUITE:addTest('Wont crash, even though metatable.__index throws errors', function ()
local input = setmetatable({ 'hi', 'hello' }, {__index = function (_, k) error('Undefined access on key: '..k) end}) local input = setmetatable({ 'hi', 'hello' }, {__index = function (_, k) error('Undefined access on key: '..k) end})
local info = analyze_structure(input) local info = analyze_structure(input, math.huge)
assert(true) assert(true)
end) end)
SUITE:addTest('Can count elements, even though metatable.__index throws errors', function () SUITE:addTest('Can count elements, even though metatable.__index throws errors', function ()
local input = setmetatable({ 'hi', 'hello' }, {__index = function (_, k) error('Undefined access on key: '..k) end}) local input = setmetatable({ 'hi', 'hello' }, {__index = function (_, k) error('Undefined access on key: '..k) end})
local info = analyze_structure(input) local info = analyze_structure(input, math.huge)
assert_equal(2, info[input].seq_elems) assert_equal(2, info[input].seq_elems)
end) end)
@ -217,21 +217,21 @@ end)
SUITE:addTest('Can detect uniform structure', function () SUITE:addTest('Can detect uniform structure', function ()
local input = { a = 'b', b = 'a' } local input = { a = 'b', b = 'a' }
local info = analyze_structure(input) local info = analyze_structure(input, math.huge)
assert_equal(true, info[input].is_uniform) assert_equal(true, info[input].is_uniform)
end) end)
SUITE:addTest('Can detect uniform structure with different key value types', function () SUITE:addTest('Can detect uniform structure with different key value types', function ()
local input = { a = 1, b = 4 } local input = { a = 1, b = 4 }
local info = analyze_structure(input) local info = analyze_structure(input, math.huge)
assert_equal(true, info[input].is_uniform) assert_equal(true, info[input].is_uniform)
end) end)
SUITE:addTest('Can detect basic non-uniform structure', function () SUITE:addTest('Can detect basic non-uniform structure', function ()
local input = { a = 'b', b = 5 } local input = { a = 'b', b = 5 }
local info = analyze_structure(input) local info = analyze_structure(input, math.huge)
assert_equal(false, info[input].is_uniform) assert_equal(false, info[input].is_uniform)
end) end)
@ -240,14 +240,14 @@ SUITE:addTest('Can detect pseudo-uniform structure with nested tables', function
-- It's pseudo-uniform because of the child having uniform structure with -- It's pseudo-uniform because of the child having uniform structure with
-- equal number of elements. -- equal number of elements.
local input = { a = { d = 7 }, b = { p = 3 } } local input = { a = { d = 7 }, b = { p = 3 } }
local info = analyze_structure(input) local info = analyze_structure(input, math.huge)
assert_equal(true, info[input].is_uniform) assert_equal(true, info[input].is_uniform)
end) end)
SUITE:addTest('Can detect non-uniform structure with nested tables', function () SUITE:addTest('Can detect non-uniform structure with nested tables', function ()
local input = { a = { 'a', 'b', 'c' }, b = { p = 3, 'hi' } } local input = { a = { 'a', 'b', 'c' }, b = { p = 3, 'hi' } }
local info = analyze_structure(input) local info = analyze_structure(input, math.huge)
assert_equal(false, info[input].is_uniform) assert_equal(false, info[input].is_uniform)
end) end)
@ -258,13 +258,13 @@ end)
-- API stuff -- API stuff
SUITE:addTest('next_mark does not escape', function () SUITE:addTest('next_mark does not escape', function ()
local info = analyze_structure( {} ) local info = analyze_structure( {}, math.huge )
assert(not info.next_mark) assert(not info.next_mark)
end) end)
SUITE:addTest('We can extend an already existing info table', function () SUITE:addTest('We can extend an already existing info table', function ()
local tab1, tab2 = {}, {} local tab1, tab2 = {}, {}
local info = analyze_structure( tab1 ) local info = analyze_structure( tab1, math.huge )
analyze_structure( tab2, math.huge, info ) analyze_structure( tab2, math.huge, info )
assert(info[tab1]) assert(info[tab1])
assert(info[tab2]) assert(info[tab2])
@ -272,7 +272,7 @@ end)
SUITE:addTest('When we extend an info table, we retain the previous root', function () SUITE:addTest('When we extend an info table, we retain the previous root', function ()
local tab1, tab2 = {}, {} local tab1, tab2 = {}, {}
local info = analyze_structure( tab1 ) local info = analyze_structure( tab1, math.huge )
analyze_structure( tab2, math.huge, info ) analyze_structure( tab2, math.huge, info )
assert(tab1 == info.root) assert(tab1 == info.root)
end) end)

View File

@ -89,6 +89,8 @@ format_test {
expect = 'function () ... end', expect = 'function () ... end',
} }
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Basic elaborate functions -- Basic elaborate functions
@ -112,7 +114,7 @@ format_test {
name = 'Elaborate function with upvalue included 1', name = 'Elaborate function with upvalue included 1',
input = function () l = TEST_UPVALUE end, input = function () l = TEST_UPVALUE end,
adv_getlocal = true, adv_getlocal = true,
expect = 'function ()\n -- Source file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- Up values: { TEST_UPVALUE = 42 }\n\n ...\nend' expect = 'function ()\n -- Source file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- Up values:\n -- TEST_UPVALUE = 42\n\n ...\nend'
} }
local func_line = curline(3) -- Must be exactly 3 lines above function local func_line = curline(3) -- Must be exactly 3 lines above function
@ -120,9 +122,36 @@ format_test {
name = 'Elaborate function with upvalue included 2', name = 'Elaborate function with upvalue included 2',
input = function () TEST_UPVALUE = true end, input = function () TEST_UPVALUE = true end,
adv_getlocal = true, adv_getlocal = true,
expect = 'function ()\n -- Source file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- Up values: { TEST_UPVALUE = 42 }\n\n ...\nend' expect = 'function ()\n -- Source file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- Up values:\n -- TEST_UPVALUE = 42\n\n ...\nend'
} }
do
local a, b, c = 'hi', {1, 2, 3}, function() end
local function test (x) return x + a + b + c end
local func_line = curline(3) -- Must be exactly 3 lines above function
format_test {
name = 'Elaborate function with multiple upvalues',
input = test,
adv_getlocal = true,
expect = 'function ()\n -- Source file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- Up values:\n -- a = \'hi\'\n -- b = {...}\n -- c = function() ... end\n\n ...\nend'
}
end
do
local a, b, cool = 'hi', {1, 2, 3}, function() end
local function test (x) return x + a + b + cool end
local func_line = curline(3) -- Must be exactly 3 lines above function
format_test {
name = 'Elaborate function with multiple upvalues, nicely aligned',
input = test,
adv_getlocal = true,
expect = 'function ()\n -- Source file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- Up values:\n -- a = \'hi\'\n -- b = {...}\n -- cool = function() ... end\n\n ...\nend'
}
end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Elaborate functions with documentation -- Elaborate functions with documentation

View File

@ -182,30 +182,6 @@ format_test {
expect = '{ { { 1, 2 }, { 3, 4 } }, { 5, 6 } }', expect = '{ { { 1, 2 }, { 3, 4 } }, { 5, 6 } }',
} }
format_test {
input = { { {1, 2}, {3, 4} }, {5, 6} },
options = { max_depth = 0 },
expect = '{...}',
}
format_test {
input = { { {1, 2}, {3, 4} }, {5, 6} },
options = { max_depth = 1 },
expect = '{ {...}, {...} }',
}
format_test {
input = { { {1, 2}, {3, 4} }, {5, 6} },
options = { max_depth = 2 },
expect = '{ { {...}, {...} }, { 5, 6 } }',
}
format_test {
input = { { {1, 2}, {3, 4} }, {5, 6} },
options = { max_depth = 3 },
expect = '{ { { 1, 2 }, { 3, 4 } }, { 5, 6 } }',
}
format_test { format_test {
input = { [{ {1,2}, {3,4} }] = 'Hello World' }, input = { [{ {1,2}, {3,4} }] = 'Hello World' },
expect = '{ [{...}] = \'Hello World\' }', expect = '{ [{...}] = \'Hello World\' }',
@ -247,6 +223,19 @@ format_test {
expect = '{\n hello_world_1 = \'dyr?\',\n hello_world_2 = \'dyr!\',\n hello\204\133_wo\204\133rld_3 = \'dyr.\'\n}', expect = '{\n hello_world_1 = \'dyr?\',\n hello_world_2 = \'dyr!\',\n hello\204\133_wo\204\133rld_3 = \'dyr.\'\n}',
} }
-- Depth Tests
format_test {
name = 'Tables with a mix of values don\'t expand subtables',
input = {
a = 'hello',
b = {1, 2, 3},
c = {1, 2, 3},
d = {1, 2, 3},
},
expect = '{\n a = \'hello\',\n b = {...},\n c = {...},\n d = {...}\n}',
}
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Pattern specific table display. -- Pattern specific table display.
@ -331,38 +320,14 @@ format_test {
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Table recursion -- Table recursion
do SUITE:addTest('Avoid infinite loops in recursion', function ()
local recursive = {} local rec = {}
recursive[1] = recursive rec[1] = rec
format_test { format(rec)
input = recursive, assert(true) -- We don't care about the output.
options = { max_depth = 5 }, end)
expect = '{ {...} }',
}
format_test {
input = recursive,
options = { max_depth = 5, recursion = 'ignore' },
expect = '{ {...} }',
}
format_test {
input = recursive,
options = { max_depth = 5, recursion = 'marked' },
expect = '<1>{ <1>{...} }',
}
end
do -- TODO: This is a very complex topic, and will expanded upon after 1.0.0.
local a = {}
local b = { a }
a[1] = b
local rec = { a = a, b = b }
format_test {
name = 'Top layers should be expanded, rather than lower layers.',
input = rec,
options = { max_depth = 5 },
expect = '{\n a = { {...} },\n b = { {...} }\n}',
}
end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- CDATA -- CDATA
@ -392,8 +357,8 @@ end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- General -- General
SUITE:addTest('UseCase: Can print _G with max_depth = 1', function () SUITE:addTest('UseCase: Can print global enviroment', function ()
format(_G, {max_depth = 1}) format(_G)
assert(true) assert(true)
end) end)

View File

@ -37,12 +37,7 @@ SUITE:addTest('Dont allow unknown options', function ()
end) end)
SUITE:addTest('Dont allow bad types in options', function () SUITE:addTest('Dont allow bad types in options', function ()
local error_msg = bad_call(pretty, 'Hello World', { max_depth = "hello world" }) local error_msg = bad_call(pretty, 'Hello World', { indent = 51 })
assert(error_msg)
end)
SUITE:addTest('Dont allow bad values in recursion', function ()
local error_msg = bad_call(pretty, 'Hello World', { recursion = "hello world" })
assert(error_msg) assert(error_msg)
end) end)