Merge branch 'master' of git://cams.pavlovian.net/questhelper
[QuestHelper.git] / Development / dump.lua
blob944dfdd28ce956414c54eb66b9c704ae390f649b
1 local function BufferAdd(self, text)
2 table.insert(self, tostring(text))
3 for i=#self-1, 1, -1 do
4 if string.len(self[i]) > string.len(self[i+1]) then break end
5 self[i] = self[i]..table.remove(self,i+1)
6 end
7 end
9 local function BufferDump(self)
10 for i=#self-1, 1, -1 do
11 self[i] = self[i]..table.remove(self)
12 end
13 return self[1] or ""
14 end
16 local function BufferAppend(self, buffer)
17 for i=1,#buffer do
18 BufferAdd(self, buffer[i])
19 end
20 while table.remove(buffer) do end
21 end
23 function CreateBuffer()
24 return {add=BufferAdd, dump=BufferDump, append=BufferAppend}
25 end
27 local function TableCompare(tbl_a, tbl_b)
28 local ak, av = next(tbl_a)
29 local bk, bv = next(tbl_b)
31 while ak and bk do
32 if type(ak) < type(bk) then return -1 end
33 if type(ak) > type(bk) then return 1 end
34 if ak < bk then return -1 end
35 if ak > bk then return 1 end
37 if type(av) < type(bv) then return -1 end
38 if type(av) > type(bv) then return 1 end
39 if type(av) == "table" then
40 local cmp = TableCompare(av, bv)
41 if cmp ~= 0 then return cmp end
42 elseif type(av) == "boolean" then
43 if av == bv then return 0
44 elseif av then return 1
45 else return -1 end
46 else
47 if av < bv then return -1 end
48 if av > bv then return 1 end
49 end
51 ak, av = next(tbl_a, ak)
52 bk, bv = next(tbl_b, bk)
53 end
55 if type(ak) < type(bk) then return -1 end
56 if type(ak) > type(bk) then return 1 end
57 return 0
58 end
60 local table_list = {}
61 local table_dat = {}
63 local function FindSameTable(tbl)
64 local sz = 0
65 local key = nil
66 while true do
67 key = next(tbl, key)
68 if not key then break end
69 sz = sz + 1
70 end
72 local list = table_list[sz]
73 if not list then
74 list = {}
75 table_list[sz] = list
76 end
78 local mn, mx = 1, #list+1
79 while mn ~= mx do
80 local m = math.floor((mn+mx)*0.5)
81 local ltbl = list[m]
82 local cmp = TableCompare(ltbl, tbl)
83 if cmp == -1 then
84 mx = m
85 elseif cmp == 1 then
86 mn = m+1
87 else
88 return ltbl, table_dat[ltbl]
89 end
90 end
92 table.insert(list, mn, tbl)
93 local dat = {}
94 table_dat[tbl] = dat
95 return tbl, dat
96 end
98 function ScanVariable(tbl)
99 if type(tbl) == "table" then
100 local tbl2, dat = FindSameTable(tbl)
102 if not dat.ref then
103 dat.ref = 1
105 for i, j in pairs(tbl2) do
106 tbl2[i] = ScanVariable(j)
108 else
109 dat.ref = dat.ref + 1
112 return tbl2, dat
115 return tbl, nil
118 local DumpRecurse
120 local last_id = 0
122 local function WriteDupVariables(prebuf, var, dup)
123 if not dup.id then
124 local buf = CreateBuffer()
125 local ref = dup.ref
126 dup.ref = 0 -- Do that we don't try to write DAT[???] = DAT[???] over and over again.
128 if last_id == 0 then
129 last_id = 1
130 prebuf:add("local DAT={}\n")
133 DumpRecurse(buf, prebuf, var, 1)
134 dup.ref = ref
136 dup.id = last_id
137 last_id = last_id + 1
139 prebuf:add("DAT[")
140 prebuf:add(tostring(dup.id))
141 prebuf:add("]=")
142 prebuf:append(buf)
143 prebuf:add("\n")
147 local function isArray(obj)
148 if type(obj) == "table" then
149 local c = 0
150 for i, j in pairs(obj) do c = c + 1 end
151 return c == #obj
153 return false
156 local reserved_words =
158 ["and"] = true,
159 ["break"] = true,
160 ["do"] = true,
161 ["else"] = true,
162 ["elseif"] = true,
163 ["end"] = true,
164 ["false"] = true,
165 ["for"] = true,
166 ["function"] = true,
167 ["if"] = true,
168 ["in"] = true,
169 ["local"] = true,
170 ["nil"] = true,
171 ["not"] = true,
172 ["or"] = true,
173 ["repeat"] = true,
174 ["return"] = true,
175 ["then"] = true,
176 ["true"] = true,
177 ["until"] = true,
178 ["while"] = true
181 local function isSafeString(obj)
182 return type(obj) == "string" and not reserved_words[obj] and obj:find("^[%a_][%w_]*$")
185 DumpRecurse = function(buffer, prebuf, variable, depth)
186 if type(variable) == "string" then
187 return buffer:add(("%q"):format(variable))
188 elseif type(variable) == "number" then
189 return buffer:add(tostring(variable+0))
190 elseif type(variable) == "nil" then
191 return buffer:add("nil")
192 elseif type(variable) == "boolean" then
193 return buffer:add(variable and "true" or "false")
194 elseif type(variable) == "table" then
195 local dup = table_dat[variable]
197 if dup and dup.ref > 1 then
198 WriteDupVariables(prebuf, variable, dup)
199 buffer:add("DAT["..dup.id.."]")
200 return
203 buffer:add("{")
205 if isArray(variable) then
206 for i, j in ipairs(variable) do
207 if isArray(j) then
208 buffer:add("\n"..(" "):rep(depth))
211 DumpRecurse(buffer, prebuf, j, depth+1)
212 if i ~= #variable then
213 buffer:add(", ")
216 else
217 buffer:add("\n"..(" "):rep(depth))
219 local sort_table = {}
221 for key in pairs(variable) do
222 table.insert(sort_table, key)
225 table.sort(sort_table, function (a, b)
226 if type(a) < type(b) then return true end
227 return type(a) == type(b) and (tostring(a) or "") < (tostring(b) or "")
228 end)
230 for index, i in ipairs(sort_table) do
231 local j = variable[i]
233 if isSafeString(i) then
234 buffer:add(i.."=")
235 else
236 buffer:add("[")
237 DumpRecurse(buffer, prebuf, i, depth+1)
238 buffer:add("]=")
241 --buffer:add((type(j)=="table"and"\n"..(" "):rep(depth+1) or ""))
243 DumpRecurse(buffer, prebuf, j, depth+1)
245 if index~=#sort_table then
246 buffer:add(",\n"..(" "):rep(depth))
251 buffer:add("}")
252 else
253 return buffer:add("nil --[[ UNHANDLED TYPE: '"..type(variable).."' ]]")
257 function DumpVariable(buffer, prebuf, variable, name)
258 buffer:add(name)
259 buffer:add("=")
260 DumpRecurse(buffer, prebuf, variable, 1)
261 buffer:add("\n")
264 function DumpingComplete(buffer, prebuf)
265 if last_id ~= 0 then
266 buffer:add("DAT=nil\n")
267 last_id = 0
268 prebuf:add("\n")
271 prebuf:append(buffer)
273 local result = prebuf:dump()
275 table_list = {}
276 table_dat = {}
277 return result
280 function ScanAndDumpVariable(variable, name, no_scan)
281 local buffer, prebuf = CreateBuffer(), CreateBuffer()
283 if name then
284 DumpVariable(buffer, prebuf, no_scan and variable or ScanVariable(variable), name)
285 else
286 -- If no name is specified, dump each variable in sequence.
287 local sort_table = {}
289 for key, var in pairs(variable) do
290 table.insert(sort_table, key)
292 if not no_scan then
293 ScanVariable(var)
297 table.sort(sort_table, function (a, b)
298 if type(a) < type(b) then return true end
299 return type(a) == type(b) and (tostring(a) or "") < (tostring(b) or "")
300 end)
302 for index, i in ipairs(sort_table) do
303 if isSafeString(i) then
304 buffer:add(i)
305 buffer:add("=")
306 else
307 -- A variable that doesn't have a normal name. Why this would be is a mystery.
308 buffer:add("_G[")
309 DumpRecurse(buffer, prebuf, i, 0)
310 buffer:add("]=")
313 DumpRecurse(buffer, prebuf, variable[i], 1)
314 buffer:add("\n")
318 return DumpingComplete(buffer, prebuf)