View difference between Paste ID: djyFBXR3 and 4nRg9CHU
SHOW: | | - or go back to the newest paste.
1
------------------------------------------------------------------ utils
2-
local controls = {["\n"]="\\n", ["\r"]="\\r", ["\t"]="\\t", ["\b"]="\\b", ["\f"]="\\f", ["\""]="\\\"", ["\\"]="\\\\"}
2+
local controls = {["\n"]="\\n", ["\r"]="\\r", ["\t"]="\\t", ["\b"]="\\b", ["\f"]="\\f", ["\""]="\\\"", ["\\"]="\\\\", ["/"] = "\\/"}
3
-- Encode stuff with fancy Unicode sequences. Kind of wrong. Oh well.
4
for i = 0, 31 do
5
	controls[string.char(i)] = string.format("\\u%04x", i)
6
end
7
8
local function isArray(t)
9
	local max = 0
10
	for k,v in pairs(t) do
11
		if type(k) ~= "number" then
12
			return false
13
		elseif k > max then
14
			max = k
15
		end
16
	end
17
	return max == #t
18
end
19
20
local whites = {['\n']=true; ['\r']=true; ['\t']=true; [' ']=true; [',']=true; [':']=true}
21
function removeWhite(str)
22
	while whites[str:sub(1, 1)] do
23
		str = str:sub(2)
24
	end
25
	return str
26
end
27
28
------------------------------------------------------------------ encoding
29
30
local function encodeCommon(val, pretty, tabLevel, tTracking)
31
	local str = ""
32
33
	-- Tabbing util
34
	local function tab(s)
35
		str = str .. ("\t"):rep(tabLevel) .. s
36
	end
37
38
	local function arrEncoding(val, bracket, closeBracket, iterator, loopFunc)
39
		str = str .. bracket
40
		if pretty then
41
			str = str .. "\n"
42
			tabLevel = tabLevel + 1
43
		end
44
		for k,v in iterator(val) do
45
			tab("")
46
			loopFunc(k,v)
47
			str = str .. ","
48
			if pretty then str = str .. "\n" end
49
		end
50
		if pretty then
51
			tabLevel = tabLevel - 1
52
		end
53
		if str:sub(-2) == ",\n" then
54
			str = str:sub(1, -3) .. "\n"
55
		elseif str:sub(-1) == "," then
56
			str = str:sub(1, -2)
57
		end
58
		tab(closeBracket)
59
	end
60
61
	-- Table encoding
62
	if type(val) == "table" then
63
		assert(not tTracking[val], "Cannot encode a table holding itself recursively")
64
		tTracking[val] = true
65
		if isArray(val) then
66
			arrEncoding(val, "[", "]", ipairs, function(k,v)
67
				str = str .. encodeCommon(v, pretty, tabLevel, tTracking)
68
			end)
69
		else
70
			arrEncoding(val, "{", "}", pairs, function(k,v)
71
				assert(type(k) == "string", "JSON object keys must be strings", 2)
72
				str = str .. encodeCommon(k, pretty, tabLevel, tTracking)
73
				str = str .. (pretty and ": " or ":") .. encodeCommon(v, pretty, tabLevel, tTracking)
74
			end)
75
		end
76
	-- String encoding
77
	elseif type(val) == "string" then
78
		str = '"' .. val:gsub("[%c\"\\]", controls) .. '"'
79
	-- Number encoding
80
	elseif type(val) == "number" or type(val) == "boolean" then
81
		str = tostring(val)
82
	else
83
		error("JSON only supports arrays, objects, numbers, booleans, and strings", 2)
84
	end
85
	return str
86
end
87
88
function encode(val)
89
	return encodeCommon(val, false, 0, {})
90
end
91
92
function encodePretty(val)
93
	return encodeCommon(val, true, 0, {})
94
end
95
96
------------------------------------------------------------------ decoding
97
98
local decodeControls = {}
99
for k,v in pairs(controls) do
100
	decodeControls[v] = k
101
end
102
103
function parseBoolean(str)
104
	if str:sub(1, 4) == "true" then
105
		return true, removeWhite(str:sub(5))
106
	else
107
		return false, removeWhite(str:sub(6))
108
	end
109
end
110
111
function parseNull(str)
112
	return nil, removeWhite(str:sub(5))
113
end
114
115
local numChars = {['e']=true; ['E']=true; ['+']=true; ['-']=true; ['.']=true}
116
function parseNumber(str)
117
	local i = 1
118
	while numChars[str:sub(i, i)] or tonumber(str:sub(i, i)) do
119
		i = i + 1
120
	end
121
	local val = tonumber(str:sub(1, i - 1))
122
	str = removeWhite(str:sub(i))
123
	return val, str
124
end
125
126
function parseString(str)
127
	str = str:sub(2)
128
	local s = ""
129
	while str:sub(1,1) ~= "\"" do
130
		local next = str:sub(1,1)
131
		str = str:sub(2)
132
		assert(next ~= "\n", "Unclosed string")
133
134-
			next = assert(decodeControls[next..escape], "Invalid escape character")
134+
135
			local escape = str:sub(1,1)
136
			str = str:sub(2)
137
138
			-- Handle \uXXXX escapes. Probably wrongly, too!
139
			if escape == "u" then
140
				local escapeValue = str:sub(1, 4)
141
				str = str:sub(5)
142
				next = string.char(math.min(255, tonumber(escapeValue, 16)))
143
			else
144
				next = assert(decodeControls[next..escape], "Invalid escape " .. next .. escape)
145
			end
146
		end
147
148
		s = s .. next
149
	end
150
	return s, removeWhite(str:sub(2))
151
end
152
153
function parseArray(str)
154
	str = removeWhite(str:sub(2))
155
156
	local val = {}
157
	local i = 1
158
	while str:sub(1, 1) ~= "]" do
159
		local v = nil
160
		v, str = parseValue(str)
161
		val[i] = v
162
		i = i + 1
163
		str = removeWhite(str)
164
	end
165
	str = removeWhite(str:sub(2))
166
	return val, str
167
end
168
169
function parseObject(str)
170
	str = removeWhite(str:sub(2))
171
172
	local val = {}
173
	while str:sub(1, 1) ~= "}" do
174
		local k, v = nil, nil
175
		k, v, str = parseMember(str)
176
		val[k] = v
177
		str = removeWhite(str)
178
	end
179
	str = removeWhite(str:sub(2))
180
	return val, str
181
end
182
183
function parseMember(str)
184
	local k = nil
185
	k, str = parseValue(str)
186
	local val = nil
187
	val, str = parseValue(str)
188
	return k, val, str
189
end
190
191
function parseValue(str)
192
	local fchar = str:sub(1, 1)
193
	if fchar == "{" then
194
		return parseObject(str)
195
	elseif fchar == "[" then
196
		return parseArray(str)
197
	elseif tonumber(fchar) ~= nil or numChars[fchar] then
198
		return parseNumber(str)
199
	elseif str:sub(1, 4) == "true" or str:sub(1, 5) == "false" then
200
		return parseBoolean(str)
201
	elseif fchar == "\"" then
202
		return parseString(str)
203
	elseif str:sub(1, 4) == "null" then
204
		return parseNull(str)
205
	end
206
	return nil
207
end
208
209
function decode(str)
210
	str = removeWhite(str)
211
	t = parseValue(str)
212
	return t
213
end
214
215
function decodeFromFile(path)
216
	local file = assert(fs.open(path, "r"))
217
	local decoded = decode(file.readAll())
218
	file.close()
219
	return decoded
220
end