Stops relying on rawlen/# to calculate the length of the sequence part of a table

Fixes #24
This commit is contained in:
kikito 2016-03-06 17:06:14 +01:00
parent 74643aea09
commit 5673f2364d
2 changed files with 33 additions and 13 deletions

View File

@ -31,9 +31,6 @@ local inspect ={
inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end}) inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end})
inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end}) inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end})
-- returns the length of a table, ignoring __len (if it exists)
local rawlen = _G.rawlen or function(t) return #t end
-- Apostrophizes the string if it has quotes, but not aphostrophes -- Apostrophizes the string if it has quotes, but not aphostrophes
-- Otherwise, it returns a regular quoted string -- Otherwise, it returns a regular quoted string
local function smartQuote(str) local function smartQuote(str)
@ -57,10 +54,10 @@ local function isIdentifier(str)
return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" ) return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
end end
local function isSequenceKey(k, length) local function isSequenceKey(k, sequenceLength)
return type(k) == 'number' return type(k) == 'number'
and 1 <= k and 1 <= k
and k <= length and k <= sequenceLength
and math.floor(k) == k and math.floor(k) == k
end end
@ -86,13 +83,26 @@ local function sortKeys(a, b)
return ta < tb return ta < tb
end end
-- For implementation reasons, the behavior of rawlen & # is "undefined" when
-- tables aren't pure sequences. So we implement our own # operator.
local function getSequenceLength(t)
local len = 1
local v = rawget(t,len)
while v ~= nil do
len = len + 1
v = rawget(t,len)
end
return len - 1
end
local function getNonSequentialKeys(t) local function getNonSequentialKeys(t)
local keys, length = {}, rawlen(t) local keys = {}
local sequenceLength = getSequenceLength(t)
for k,_ in pairs(t) do for k,_ in pairs(t) do
if not isSequenceKey(k, length) then table.insert(keys, k) end if not isSequenceKey(k, sequenceLength) then table.insert(keys, k) end
end end
table.sort(keys, sortKeys) table.sort(keys, sortKeys)
return keys return keys, sequenceLength
end end
local function getToStringResultSafely(t, mt) local function getToStringResultSafely(t, mt)
@ -234,8 +244,7 @@ function Inspector:putTable(t)
else else
if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end
local nonSequentialKeys = getNonSequentialKeys(t) local nonSequentialKeys, sequenceLength = getNonSequentialKeys(t)
local length = rawlen(t)
local mt = getmetatable(t) local mt = getmetatable(t)
local toStringResult = getToStringResultSafely(t, mt) local toStringResult = getToStringResultSafely(t, mt)
@ -243,11 +252,11 @@ function Inspector:putTable(t)
self:down(function() self:down(function()
if toStringResult then if toStringResult then
self:puts(' -- ', escape(toStringResult)) self:puts(' -- ', escape(toStringResult))
if length >= 1 then self:tabify() end if sequenceLength >= 1 then self:tabify() end
end end
local count = 0 local count = 0
for i=1, length do for i=1, sequenceLength do
if count > 0 then self:puts(',') end if count > 0 then self:puts(',') end
self:puts(' ') self:puts(' ')
self:putValue(t[i]) self:putValue(t[i])
@ -273,7 +282,7 @@ function Inspector:putTable(t)
if #nonSequentialKeys > 0 or mt then -- result is multi-lined. Justify closing } if #nonSequentialKeys > 0 or mt then -- result is multi-lined. Justify closing }
self:tabify() self:tabify()
elseif length > 0 then -- array tables have one extra space before closing } elseif sequenceLength > 0 then -- array tables have one extra space before closing }
self:puts(' ') self:puts(' ')
end end

View File

@ -86,6 +86,15 @@ describe( 'inspect', function()
assert.equals("{\n a = 1,\n b = 2\n}", inspect({a = 1, b = 2})) assert.equals("{\n a = 1,\n b = 2\n}", inspect({a = 1, b = 2}))
end) end)
it('identifies tables with no number 1 as struct-like', function()
assert.equals(unindent([[{
[2] = 1,
[25] = 1,
id = 1
}
]]), inspect({[2]=1,[25]=1,id=1}))
end)
it('identifies numeric non-array keys as dictionary keys', function() it('identifies numeric non-array keys as dictionary keys', function()
assert.equals("{ 1, 2,\n [-1] = true\n}", inspect({1, 2, [-1] = true})) assert.equals("{ 1, 2,\n [-1] = true\n}", inspect({1, 2, [-1] = true}))
assert.equals("{ 1, 2,\n [1.5] = true\n}", inspect({1, 2, [1.5] = true})) assert.equals("{ 1, 2,\n [1.5] = true\n}", inspect({1, 2, [1.5] = true}))
@ -131,6 +140,8 @@ describe( 'inspect', function()
c = 3 c = 3
} }
]]), inspect({ 'a', {b = 1}, 2, c = 3, ['ahoy you'] = 4 })) ]]), inspect({ 'a', {b = 1}, 2, c = 3, ['ahoy you'] = 4 }))
end) end)
it('displays <table x> instead of repeating an already existing table', function() it('displays <table x> instead of repeating an already existing table', function()