~philho/+junk/Lua

18 by PhiLho
Improving DumpObject to handle nicely the array part of the tables
1
#!Lua5.1.exe
7 by plhoste
Much improved version.
2
--[[
3
String dump of any Lua object.
4
5
File/Project history:
18 by PhiLho
Improving DumpObject to handle nicely the array part of the tables
6
 1.03 -- 2010/03/18 (PL) -- I dumped { 'a', 'b', 'c' } to { [1] = 'a', [2] = 'b', [3] = 'c' }
7
         which wasn't very nice. Now if I find a key of 1, I walk the array part of the table
8
         first then I dump the other keys.
8 by plhoste
Special, compact handling of empty tables.
9
 1.02 -- 2007/03/15 (PL) -- Special, compact handling of empty tables.
10
         Also corrected a bug in quotes around tostring result ("foo')
7 by plhoste
Much improved version.
11
 1.01 -- 2006/11/07 (PL) -- Exclude keywords from "naked" string keys.
12
 1.00 -- 2006/07/13 (PL) -- Creation of this version.
13
 0.01 -- 2003/??/?? (PL) -- First version?
14
]]
18 by PhiLho
Improving DumpObject to handle nicely the array part of the tables
15
--[[
16
Author: Philippe Lhoste <PhiLho(a)GMX.net> http://Phi.Lho.free.fr
17
Copyright notice: For details, see the following file:
18
http://Phi.Lho.free.fr/softwares/PhiLhoSoft/PhiLhoSoftLicense.txt
19
This program is distributed under the zlib/libpng license.
20
Copyright (c) 2006-2010 Philippe Lhoste / PhiLhoSoft
7 by plhoste
Much improved version.
21
]]
22
1 by plhoste
Initial import of Lua scripts (take 2...)
23
--[[
24
Returns a textual representation of "any" Lua object,
25
including tables (and nested tables).
26
Functions are not decompiled (of course).
7 by plhoste
Much improved version.
27
It sticks strictly to the real table constructor syntax,
28
so the result can be reparsed by Lua to recreate the object,
29
at least if there are NO complex objects (functions, userdata...).
30
It handles references correctly, except for cycles (a ref. b which ref. a).
31
32
Example of use:
33
For an indented dump:
34
  print(DumpObject(tableName))
35
For an inline dump:
36
  print(DumpObject(t, '', ' '))
1 by plhoste
Initial import of Lua scripts (take 2...)
37
]]
7 by plhoste
Much improved version.
38
function DumpObject(objectToDump, indentUnit, newline)
18 by PhiLho
Improving DumpObject to handle nicely the array part of the tables
39
	local function KeywordList(list)
40
		local kl = {}
41
		for i, v in ipairs(list) do
42
			kl[v] = i
43
		end
44
		return kl
45
	end
46
47
	-- We can write a = 1 in table constructor, but not for = 1 because for is a keyword.
48
	-- So we have to list all keywords to make a special rule for them.
49
	local keywords = KeywordList
7 by plhoste
Much improved version.
50
	{
18 by PhiLho
Improving DumpObject to handle nicely the array part of the tables
51
		'and', 'break',    'do',     'else', 'elseif', 'end',   'false',
52
		'for', 'function', 'if',     'in',   'local',  'nil',   'not',
53
		'or',  'repeat',   'return', 'then', 'true',   'until', 'while'
7 by plhoste
Much improved version.
54
	}
18 by PhiLho
Improving DumpObject to handle nicely the array part of the tables
55
7 by plhoste
Much improved version.
56
	-- The string used to indent
57
	-- Give an empty string for no indentation
58
	if indentUnit == nil then
59
		indentUnit = "  " -- 2 spaces
60
	end
8 by plhoste
Special, compact handling of empty tables.
61
	-- Give an empty string or space to put all the dump in one line
7 by plhoste
Much improved version.
62
	if newline == nil then
63
		newline = "\n"
64
	end
65
66
	-- Current indent level/string
67
	local indent = ""
68
	-- Table of visited tables, to handle cross-references
69
	local visited = {}
70
	-- Number of the currently visited table
71
	local tableIndex = 0
72
	-- We put the dump string fragments here, to concatenate them later
73
	local dump = {}
74
	-- Half backed tentative to handle cyclic references...
75
	local dumpAfter, daIndex = {}, 0
76
77
	-- In the following functions, the  bOnLeft parameter
78
	-- is true if we are on the left side of an assignment (=), false on the right side
79
80
	-- If on left of assignment, add brackets around the string,
81
	-- otherwise, leave it as is.
82
	local function GetLeftOrRight(str, bOnLeft)
83
		if bOnLeft then
84
			-- Index syntax
85
			return '[' .. str .. ']'
86
		else
87
			-- Plain object
88
			return str
1 by plhoste
Initial import of Lua scripts (take 2...)
89
		end
90
	end
91
7 by plhoste
Much improved version.
92
	-- Add the right quotes around the string, depending on what we
93
	-- find in it, and if we are on left or on right of assignment.
94
	local function QuoteString(str, bOnLeft)
95
		if bOnLeft and string.find(str, '[^%w_]') == nil and not keywords[str] then
1 by plhoste
Initial import of Lua scripts (take 2...)
96
			-- String with variable syntax on the left side of '=': can be left unquoted
97
			return str
98
		end
99
		local qs
100
		if string.find(str, '[\\\r\n"]') == nil then
7 by plhoste
Much improved version.
101
			-- No special chars, can just use double quotes
1 by plhoste
Initial import of Lua scripts (take 2...)
102
			qs = '"' .. str .. '"'
7 by plhoste
Much improved version.
103
		elseif string.find(str, "[\\\r\n']") == nil then
104
			-- No special chars, can just use single quotes
105
			qs = "'" .. str .. "'"
1 by plhoste
Initial import of Lua scripts (take 2...)
106
		else
7 by plhoste
Much improved version.
107
			-- Backslash or newline or double quotes in string, must use literal string
1 by plhoste
Initial import of Lua scripts (take 2...)
108
			if bOnLeft then
18 by PhiLho
Improving DumpObject to handle nicely the array part of the tables
109
				-- We have to write [ [[...]] ] (with spaces) to avoid ambiguity
7 by plhoste
Much improved version.
110
				qs = ' [[' .. str .. ']] '
1 by plhoste
Initial import of Lua scripts (take 2...)
111
			else
7 by plhoste
Much improved version.
112
				qs = '[[' .. str .. ']]'
1 by plhoste
Initial import of Lua scripts (take 2...)
113
			end
114
		end
7 by plhoste
Much improved version.
115
		return GetLeftOrRight(qs, bOnLeft)
116
	end
117
118
	-- Return a correctly quoted (or not) string representation
119
	-- of the given object.
120
	-- Must not be called with a table, which is processed separately.
121
	local function DumpItem(object, bOnLeft)
122
		local di = "UH?"
123
		local to = type(object)
124
		if to == "string" then
125
			di = QuoteString(object, bOnLeft)
126
		elseif to == "number" or to == "boolean" then
127
			di = GetLeftOrRight(tostring(object), bOnLeft)
128
		elseif to  == "nil" then -- If the first object is nil itself...
18 by PhiLho
Improving DumpObject to handle nicely the array part of the tables
129
			return 'nil' -- Cannot happen on left, can it?
7 by plhoste
Much improved version.
130
		elseif to  == "table" then
131
			di = "TABLE!" -- Should be tested before...
1 by plhoste
Initial import of Lua scripts (take 2...)
132
		else
7 by plhoste
Much improved version.
133
			-- function, userdata, thread, ...
134
			-- Put the tostring result between double quotes
135
			-- It won't be restored correctly, but it won't give a syntax error...
8 by plhoste
Special, compact handling of empty tables.
136
			di = GetLeftOrRight('"' .. tostring(object) .. '"', bOnLeft)
7 by plhoste
Much improved version.
137
		end
138
		return di
139
	end
140
18 by PhiLho
Improving DumpObject to handle nicely the array part of the tables
141
	local function AddKeyValue(td, tdi, left, right)
142
		if tdi ~= 1 then
143
			-- Add field separator
144
			tdi = tdi + 1
145
			td[tdi] = "," .. newline
146
		end
147
		-- Add this field
148
		tdi = tdi + 1
149
		if left == nil then
150
			-- Successive numerical index, don't put a key
151
			td[tdi] = indent .. right
152
		else
153
			td[tdi] = indent .. left .. " = " .. right
154
		end
155
		return tdi
156
	end
157
7 by plhoste
Much improved version.
158
	-- Walk the object (if it is a table) and build
159
	-- a string representation of the found objects.
160
	-- bForce bypasses the reference mechanism,
161
	-- forcing to dump the table (for referenced tables).
162
	local function DumpRecursively(object, bOnLeft, bForce)
163
		if type(object) == "table" then
164
			if visited[object].visits > 1 and not bForce then
165
				-- Cross-reference
166
				if object == objectToDump then
167
					-- Don't reference the root table! (trying to handle cycles)
168
					return nil
169
				else
170
					-- We have a referenced table, return its variable name
171
					return GetLeftOrRight("T" .. visited[object].idx, bOnLeft)
172
				end
173
			end
174
175
			local openBrace, closeBrace
176
			if bOnLeft then
177
				-- Using a raw table constructor as key!
178
				-- Only accessible by iteration...
8 by plhoste
Special, compact handling of empty tables.
179
				if next(object) == nil then	-- Empty table
180
					-- Use a lighter traditional representation
181
					openBrace = "[ {"; closeBrace = "} ]"
182
				else
183
					openBrace = "[ {" .. newline
184
					closeBrace = newline .. indent .. "} ]"
185
				end
7 by plhoste
Much improved version.
186
			else
8 by plhoste
Special, compact handling of empty tables.
187
				if next(object) == nil then	-- Empty table
188
					-- Use a lighter traditional representation
189
					openBrace = "{"; closeBrace = "}"
190
				else
191
					openBrace = newline .. indent .. "{" .. newline
192
					closeBrace = newline .. indent .. "}"
193
				end
7 by plhoste
Much improved version.
194
			end
18 by PhiLho
Improving DumpObject to handle nicely the array part of the tables
195
7 by plhoste
Much improved version.
196
			-- Start of table dump
197
			local td = { openBrace }
198
			local tdi = 1
199
			-- Indent more
200
			indent = indent .. indentUnit
18 by PhiLho
Improving DumpObject to handle nicely the array part of the tables
201
202
			--Handle the array part, if any
203
			local lastNumericalIndex = 0
204
			if object[1] ~= nil then
205
				-- This table as an array section, perhaps with increasing numerical indices
206
				for i, v in ipairs(object) do
207
					lastNumericalIndex = i
208
					-- Add value without key
209
					local right = DumpRecursively(v, false, false)
210
					tdi = AddKeyValue(td, tdi, nil, string.gsub(right, '^\n%s*', '')) -- Skip initial newline if any
211
--~ 					tdi = AddKeyValue(td, tdi, nil, right)
212
				end
213
			end
214
7 by plhoste
Much improved version.
215
			-- Walk the table
216
			local k, v = nil, nil
217
			repeat
218
				k, v = next(object, k)
219
				if k ~= nil then
18 by PhiLho
Improving DumpObject to handle nicely the array part of the tables
220
					local bIsNumberKey = type(k) == "number"
221
					if not bIsNumberKey or bIsNumberKey and k > lastNumericalIndex then
222
						local left = DumpRecursively(k, true, false)
223
						local right = DumpRecursively(v, false, false)
224
						if right == nil then
225
							-- Tentative to handle cycles
226
							daIndex = daIndex + 1
227
							-- Incorrect, it must not be 'left' but the full path!
228
							dumpAfter[daIndex] = left .. " = T" .. visited[v].idx
229
						else
230
							tdi = AddKeyValue(td, tdi, left, right)
231
						end
7 by plhoste
Much improved version.
232
					end
233
				end
234
			until k == nil
18 by PhiLho
Improving DumpObject to handle nicely the array part of the tables
235
7 by plhoste
Much improved version.
236
			-- Deindent
237
			indent = string.sub(indent, string.len(indentUnit) + 1)
238
			-- End of table
239
			tdi = tdi + 1
240
			td[tdi] = closeBrace
241
			return table.concat(td)
242
		else -- Not a table
243
			-- Return a string equivalence
244
			return DumpItem(object, bOnLeft)
245
		end
246
	end
247
248
	-- List cross references of the given object (must be a table).
249
	local function ListCrossReferences(object)
250
		if type(object) == "table" then
251
			if visited[object] ~= nil then
252
				-- Already seen, cross-reference
253
				visited[object].visits = visited[object].visits + 1
254
				return
255
			end
256
			-- First seen, number it
257
			tableIndex = tableIndex + 1
258
			visited[object] = { visits = 1, idx = tableIndex }
259
260
			-- Walk this table and see if it has other tables inside
261
			local k, v = nil, nil
262
			repeat
263
				k, v = next(object, k)
264
				if k ~= nil then
265
					ListCrossReferences(k)
266
					ListCrossReferences(v)
267
				end
268
			until k == nil
269
		end
270
	end
271
272
	-- Dump the tables referenced more than once,
273
	-- so they make easy to reference variables.
274
	local function DumpCrossReferences()
275
		local k, v = nil, nil
276
		repeat
277
			k, v = next(visited, k)
278
			if k ~= nil and v.visits > 1 and k ~= objectToDump then
279
				dump[v.idx] = "\nlocal T" .. v.idx .. " = " .. DumpRecursively(k, false, true)
280
			end
281
		until k == nil
282
	end
283
284
	-- List all cross-references of the (possibly) table
285
	ListCrossReferences(objectToDump)
286
	-- Dump the tables used in cross-references
287
	DumpCrossReferences()
288
	-- Add the main table
289
	dump[tableIndex + 1] = "\nlocal T = " .. DumpRecursively(objectToDump, false, true)
290
	-- Fill in the gaps, so concat could work
291
	for i = 1, tableIndex do
292
		if dump[i] == nil then
293
			dump[i] = ''
294
		end
295
	end
296
	return table.concat(dump) .. "\n" .. table.concat(dumpAfter)
297
end
298
299
--[[
300
Write a dump of the object (likely to be a table...)
301
to the given file.
302
It can be used with:
303
  table = dofile("DumpedTable.lua")
304
]]
8 by plhoste
Special, compact handling of empty tables.
305
function DumpObjectToFile(object, fileName, indentUnit, newline)
7 by plhoste
Much improved version.
306
	local fh = io.open(fileName, "w")
8 by plhoste
Special, compact handling of empty tables.
307
	fh:write("do\n" .. DumpObject(object, indentUnit, newline) .. "\nreturn T\n\nend\n")
7 by plhoste
Much improved version.
308
	fh:close()
309
end
310
311
--[[
312
For a quick in-line dump of a simple table (no cross-references)
313
without the initialization stuff.
314
]]
315
function FormatSimpleTable(t)
316
	return (string.gsub(string.gsub(DumpObject(t, '', ' '), "^\nlocal T = ", ""), "\n$", ""))
1 by plhoste
Initial import of Lua scripts (take 2...)
317
end