@@ -48,6 +48,11 @@ inspect.KEY = setmetatable({}, { __tostring = function() return 'inspect.KEY' en
4848inspect .METATABLE = setmetatable ({}, { __tostring = function () return ' inspect.METATABLE' end })
4949
5050local tostring = tostring
51+ local rep = string.rep
52+ local match = string.match
53+ local char = string.char
54+ local gsub = string.gsub
55+ local fmt = string.format
5156
5257local function rawpairs (t )
5358 return next , t , nil
5661
5762
5863local function smartQuote (str )
59- if str : match (' "' ) and not str : match (" '" ) then
64+ if match (str , ' "' ) and not match (str , " '" ) then
6065 return " '" .. str .. " '"
6166 end
62- return ' "' .. str : gsub (' "' , ' \\ "' ) .. ' "'
67+ return ' "' .. gsub (str , ' "' , ' \\ "' ) .. ' "'
6368end
6469
6570
@@ -69,17 +74,17 @@ local shortControlCharEscapes = {
6974}
7075local longControlCharEscapes = { [" \127 " ] = " \127 " }
7176for i = 0 , 31 do
72- local ch = string. char (i )
77+ local ch = char (i )
7378 if not shortControlCharEscapes [ch ] then
7479 shortControlCharEscapes [ch ] = " \\ " .. i
75- longControlCharEscapes [ch ] = string.format (" \\ %03d" , i )
80+ longControlCharEscapes [ch ] = fmt (" \\ %03d" , i )
7681 end
7782end
7883
7984local function escape (str )
80- return (str : gsub (" \\ " , " \\\\ " ):
81- gsub ( " (%c)%f[0-9]" , longControlCharEscapes ):
82- gsub ( " %c" , shortControlCharEscapes ))
85+ return (gsub (gsub ( gsub ( str , " \\ " , " \\\\ " ),
86+ " (%c)%f[0-9]" , longControlCharEscapes ),
87+ " %c" , shortControlCharEscapes ))
8388end
8489
8590local function isIdentifier (str )
@@ -107,59 +112,45 @@ local function sortKeys(a, b)
107112 return (a ) < (b )
108113 end
109114
110- local dta , dtb = defaultTypeOrders [ta ], defaultTypeOrders [tb ]
111-
112- if dta and dtb then return defaultTypeOrders [ta ] < defaultTypeOrders [tb ]
113- elseif dta then return true
114- elseif dtb then return false
115- end
115+ local dta = defaultTypeOrders [ta ] or 100
116+ local dtb = defaultTypeOrders [tb ] or 100
116117
117118
118- return ta < tb
119+ return dta == dtb and ta < tb or dta < dtb
119120end
120121
122+ local function getKeys (t )
121123
122-
123- local function getSequenceLength (t )
124- local len = 1
125- local v = rawget (t , len )
126- while v ~= nil do
127- len = len + 1
128- v = rawget (t , len )
124+ local seqLen = 1
125+ while rawget (t , seqLen ) ~= nil do
126+ seqLen = seqLen + 1
129127 end
130- return len - 1
131- end
128+ seqLen = seqLen - 1
132129
133- local function getNonSequentialKeys (t )
134- local keys , keysLength = {}, 0
135- local sequenceLength = getSequenceLength (t )
136- for k , _ in rawpairs (t ) do
137- if not isSequenceKey (k , sequenceLength ) then
138- keysLength = keysLength + 1
139- keys [keysLength ] = k
130+ local keys , keysLen = {}, 0
131+ for k in rawpairs (t ) do
132+ if not isSequenceKey (k , seqLen ) then
133+ keysLen = keysLen + 1
134+ keys [keysLen ] = k
140135 end
141136 end
142137 table.sort (keys , sortKeys )
143- return keys , keysLength , sequenceLength
138+ return keys , keysLen , seqLen
144139end
145140
146- local function countTableAppearances (t , tableAppearances )
147- tableAppearances = tableAppearances or {}
148-
149- if type (t ) == " table" then
150- if not tableAppearances [t ] then
151- tableAppearances [t ] = 1
152- for k , v in rawpairs (t ) do
153- countTableAppearances (k , tableAppearances )
154- countTableAppearances (v , tableAppearances )
155- end
156- countTableAppearances (getmetatable (t ), tableAppearances )
141+ local function countCycles (x , cycles )
142+ if type (x ) == " table" then
143+ if cycles [x ] then
144+ cycles [x ] = cycles [x ] + 1
157145 else
158- tableAppearances [t ] = tableAppearances [t ] + 1
146+ cycles [x ] = 1
147+ for k , v in rawpairs (x ) do
148+ countCycles (k , cycles )
149+ countCycles (v , cycles )
150+ end
151+ countCycles (getmetatable (x ), cycles )
159152 end
160153 end
161-
162- return tableAppearances
163154end
164155
165156local function makePath (path , a , b )
@@ -202,7 +193,10 @@ local function processRecursive(process,
202193 return processed
203194end
204195
205-
196+ local function puts (buf , str )
197+ buf .n = buf .n + 1
198+ buf [buf .n ] = str
199+ end
206200
207201
208202
@@ -219,118 +213,85 @@ local Inspector = {}
219213
220214local Inspector_mt = { __index = Inspector }
221215
222- function Inspector :puts (a , b , c , d , e )
223- local buffer = self .buffer
224- local len = # buffer
225- buffer [len + 1 ] = a
226- buffer [len + 2 ] = b
227- buffer [len + 3 ] = c
228- buffer [len + 4 ] = d
229- buffer [len + 5 ] = e
230- end
231-
232- function Inspector :down (f )
233- self .level = self .level + 1
234- f ()
235- self .level = self .level - 1
236- end
237-
238- function Inspector :tabify ()
239- self :puts (self .newline ,
240- string.rep (self .indent , self .level ))
241- end
242-
243- function Inspector :alreadyVisited (v )
244- return self .ids [v ] ~= nil
216+ local function tabify (inspector )
217+ puts (inspector .buf , inspector .newline .. rep (inspector .indent , inspector .level ))
245218end
246219
247220function Inspector :getId (v )
248221 local id = self .ids [v ]
222+ local ids = self .ids
249223 if not id then
250224 local tv = type (v )
251- id = (self .maxIds [tv ] or 0 ) + 1
252- self .maxIds [tv ] = id
253- self .ids [v ] = id
225+ id = (ids [tv ] or 0 ) + 1
226+ ids [v ], ids [tv ] = id , id
254227 end
255228 return tostring (id )
256229end
257230
258-
259- function Inspector :putValue (_ )
260- end
261-
262- function Inspector :putKey (k )
263- if isIdentifier (k ) then
264- self :puts (k )
265- return
266- end
267- self :puts (" [" )
268- self :putValue (k )
269- self :puts (" ]" )
270- end
271-
272- function Inspector :putTable (t )
273- if t == inspect .KEY or t == inspect .METATABLE then
274- self :puts (tostring (t ))
275- elseif self :alreadyVisited (t ) then
276- self :puts (' <table ' , self :getId (t ), ' >' )
277- elseif self .level >= self .depth then
278- self :puts (' {...}' )
279- else
280- if self .tableAppearances [t ] > 1 then self :puts (' <' , self :getId (t ), ' >' ) end
281-
282- local nonSequentialKeys , nonSequentialKeysLength , sequenceLength = getNonSequentialKeys (t )
283- local mt = getmetatable (t )
284-
285- self :puts (' {' )
286- self :down (function ()
287- local count = 0
288- for i = 1 , sequenceLength do
289- if count > 0 then self :puts (' ,' ) end
290- self :puts (' ' )
291- self :putValue (t [i ])
292- count = count + 1
293- end
294-
295- for i = 1 , nonSequentialKeysLength do
296- local k = nonSequentialKeys [i ]
297- if count > 0 then self :puts (' ,' ) end
298- self :tabify ()
299- self :putKey (k )
300- self :puts (' = ' )
301- self :putValue (t [k ])
302- count = count + 1
231+ function Inspector :putValue (v )
232+ local buf = self .buf
233+ local tv = type (v )
234+ if tv == ' string' then
235+ puts (buf , smartQuote (escape (v )))
236+ elseif tv == ' number' or tv == ' boolean' or tv == ' nil' or
237+ tv == ' cdata' or tv == ' ctype' then
238+ puts (buf , tostring (v ))
239+ elseif tv == ' table' and not self .ids [v ] then
240+ local t = v
241+
242+ if t == inspect .KEY or t == inspect .METATABLE then
243+ puts (buf , tostring (t ))
244+ elseif self .level >= self .depth then
245+ puts (buf , ' {...}' )
246+ else
247+ if self .cycles [t ] > 1 then puts (buf , fmt (' <%d>' , self :getId (t ))) end
248+
249+ local keys , keysLen , seqLen = getKeys (t )
250+
251+ puts (buf , ' {' )
252+ self .level = self .level + 1
253+
254+ for i = 1 , seqLen + keysLen do
255+ if i > 1 then puts (buf , ' ,' ) end
256+ if i <= seqLen then
257+ puts (buf , ' ' )
258+ self :putValue (t [i ])
259+ else
260+ local k = keys [i - seqLen ]
261+ tabify (self )
262+ if isIdentifier (k ) then
263+ puts (buf , k )
264+ else
265+ puts (buf , " [" )
266+ self :putValue (k )
267+ puts (buf , " ]" )
268+ end
269+ puts (buf , ' = ' )
270+ self :putValue (t [k ])
271+ end
303272 end
304273
274+ local mt = getmetatable (t )
305275 if type (mt ) == ' table' then
306- if count > 0 then self : puts (' ,' ) end
307- self : tabify ()
308- self : puts (' <metatable> = ' )
276+ if seqLen + keysLen > 0 then puts (buf , ' ,' ) end
277+ tabify (self )
278+ puts (buf , ' <metatable> = ' )
309279 self :putValue (mt )
310280 end
311- end )
312281
313- if nonSequentialKeysLength > 0 or type (mt ) == ' table' then
314- self :tabify ()
315- elseif sequenceLength > 0 then
316- self :puts (' ' )
317- end
282+ self .level = self .level - 1
318283
319- self :puts (' }' )
320- end
321- end
284+ if keysLen > 0 or type (mt ) == ' table' then
285+ tabify (self )
286+ elseif seqLen > 0 then
287+ puts (buf , ' ' )
288+ end
289+
290+ puts (buf , ' }' )
291+ end
322292
323- function Inspector :putValue (v )
324- local tv = type (v )
325- if tv == ' string' then
326- self :puts (smartQuote (escape (v )))
327- elseif tv == ' number' or tv == ' boolean' or tv == ' nil' or
328- tv == ' cdata' or tv == ' ctype' then
329- self :puts (tostring (v ))
330- elseif tv == ' table' then
331- self :putTable (v )
332293 else
333- self : puts (' < ' , tv , ' ' , self :getId (v ), ' > ' )
294+ puts (buf , fmt ( ' <%s %d> ' , tv , self :getId (v )) )
334295 end
335296end
336297
@@ -349,20 +310,22 @@ function inspect.inspect(root, options)
349310 root = processRecursive (process , root , {}, {})
350311 end
351312
313+ local cycles = {}
314+ countCycles (root , cycles )
315+
352316 local inspector = setmetatable ({
317+ buf = { n = 0 },
318+ ids = {},
319+ cycles = cycles ,
353320 depth = depth ,
354321 level = 0 ,
355- buffer = {},
356- ids = {},
357- maxIds = {},
358322 newline = newline ,
359323 indent = indent ,
360- tableAppearances = countTableAppearances (root ),
361324 }, Inspector_mt )
362325
363326 inspector :putValue (root )
364327
365- return table.concat (inspector .buffer )
328+ return table.concat (inspector .buf )
366329end
367330
368331setmetatable (inspect , {
0 commit comments