View difference between Paste ID: tXpZCQ2z 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", ["\""]="\\\"", ["\\"]="\\\\"}
3
4
local function isArray(t)
5
	local max = 0
6
	for k,v in pairs(t) do
7
		if type(k) ~= "number" then
8
			return false
9
		elseif k > max then
10
			max = k
11
		end
12
	end
13
	return max == #t
14
end
15
16
local whites = {['\n']=true; ['\r']=true; ['\t']=true; [' ']=true; [',']=true; [':']=true}
17
function removeWhite(str)
18
	while whites[str:sub(1, 1)] do
19
		str = str:sub(2)
20
	end
21
	return str
22
end
23
24
------------------------------------------------------------------ encoding
25
26
local function encodeCommon(val, pretty, tabLevel, tTracking)
27
	local str = ""
28
29
	-- Tabbing util
30
	local function tab(s)
31
		str = str .. ("\t"):rep(tabLevel) .. s
32
	end
33
34
	local function arrEncoding(val, bracket, closeBracket, iterator, loopFunc)
35
		str = str .. bracket
36
		if pretty then
37
			str = str .. "\n"
38
			tabLevel = tabLevel + 1
39
		end
40
		for k,v in iterator(val) do
41
			tab("")
42
			loopFunc(k,v)
43
			str = str .. ","
44
			if pretty then str = str .. "\n" end
45
		end
46
		if pretty then
47
			tabLevel = tabLevel - 1
48
		end
49
		if str:sub(-2) == ",\n" then
50
			str = str:sub(1, -3) .. "\n"
51
		elseif str:sub(-1) == "," then
52
			str = str:sub(1, -2)
53
		end
54
		tab(closeBracket)
55
	end
56
57
	-- Table encoding
58
	if type(val) == "table" then
59-
		assert(not tTracking[val], "Cannot encode a table holding itself recursively")
59+
		if not tTracking[val] then
60-
		tTracking[val] = true
60+
			tTracking[val] = true
61-
		if isArray(val) then
61+
			if isArray(val) then
62-
			arrEncoding(val, "[", "]", ipairs, function(k,v)
62+
				arrEncoding(val, "[", "]", ipairs, function(k,v)
63-
				str = str .. encodeCommon(v, pretty, tabLevel, tTracking)
63+
					str = str .. encodeCommon(v, pretty, tabLevel, tTracking)
64-
			end)
64+
				end)
65
			else
66-
			arrEncoding(val, "{", "}", pairs, function(k,v)
66+
				arrEncoding(val, "{", "}", pairs, function(k,v)
67-
				assert(type(k) == "string", "JSON object keys must be strings", 2)
67+
					assert(type(k) == "string", "JSON object keys must be strings", 2)
68-
				str = str .. encodeCommon(k, pretty, tabLevel, tTracking)
68+
					str = str .. encodeCommon(k, pretty, tabLevel, tTracking)
69-
				str = str .. (pretty and ": " or ":") .. encodeCommon(v, pretty, tabLevel, tTracking)
69+
					str = str .. (pretty and ": " or ":") .. encodeCommon(v, pretty, tabLevel, tTracking)
70-
			end)
70+
				end)
71
			end
72
		else
73
			str = 'null'
74
		end
75
	-- String encoding
76
	elseif type(val) == "string" then
77
		str = '"' .. val:gsub("[%c\"\\]", controls) .. '"'
78
	-- Number encoding
79-
		error("JSON only supports arrays, objects, numbers, booleans, and strings", 2)
79+
80
		str = tostring(val)
81
	else
82
        str = 'null'
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
		if next == "\\" then
135
			local escape = str:sub(1,1)
136
			str = str:sub(2)
137
138
			next = assert(decodeControls[next..escape], "Invalid escape character")
139
		end
140
141
		s = s .. next
142
	end
143
	return s, removeWhite(str:sub(2))
144
end
145
146
function parseArray(str)
147
	str = removeWhite(str:sub(2))
148
149
	local val = {}
150
	local i = 1
151
	while str:sub(1, 1) ~= "]" do
152
		local v = nil
153
		v, str = parseValue(str)
154
		val[i] = v
155
		i = i + 1
156
		str = removeWhite(str)
157
	end
158
	str = removeWhite(str:sub(2))
159
	return val, str
160
end
161
162
function parseObject(str)
163
	str = removeWhite(str:sub(2))
164
165
	local val = {}
166
	while str:sub(1, 1) ~= "}" do
167
		local k, v = nil, nil
168
		k, v, str = parseMember(str)
169
		val[k] = v
170
		str = removeWhite(str)
171
	end
172
	str = removeWhite(str:sub(2))
173
	return val, str
174
end
175
176
function parseMember(str)
177
	local k = nil
178
	k, str = parseValue(str)
179
	local val = nil
180
	val, str = parseValue(str)
181
	return k, val, str
182
end
183
184
function parseValue(str)
185
	local fchar = str:sub(1, 1)
186
	if fchar == "{" then
187
		return parseObject(str)
188
	elseif fchar == "[" then
189
		return parseArray(str)
190
	elseif tonumber(fchar) ~= nil or numChars[fchar] then
191
		return parseNumber(str)
192
	elseif str:sub(1, 4) == "true" or str:sub(1, 5) == "false" then
193
		return parseBoolean(str)
194
	elseif fchar == "\"" then
195
		return parseString(str)
196
	elseif str:sub(1, 4) == "null" then
197
		return parseNull(str)
198
	end
199
	return nil
200
end
201
202
function decode(str)
203
	str = removeWhite(str)
204
	t = parseValue(str)
205
	return t
206
end
207
208
function decodeFromFile(path)
209
	local file = assert(fs.open(path, "r"))
210
	local decoded = decode(file.readAll())
211
	file.close()
212
	return decoded
213
end