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")
60
+ tTracking[val] = true
61
+ if isArray(val) then
62
+ arrEncoding(val, "[", "]", ipairs, function(k,v)
63
+ str = str .. encodeCommon(v, pretty, tabLevel, tTracking)
64
+ end)
65
+ else
66
+ arrEncoding(val, "{", "}", pairs, function(k,v)
67
+ assert(type(k) == "string", "JSON object keys must be strings", 2)
68
+ str = str .. encodeCommon(k, pretty, tabLevel, tTracking)
69
+ str = str .. (pretty and ": " or ":") .. encodeCommon(v, pretty, tabLevel, tTracking)
70
+ end)
71
+ end
72
+ -- String encoding
73
+ elseif type(val) == "string" then
74
+ str = '"' .. val:gsub("[%c\"\\]", controls) .. '"'
75
+ -- Number encoding
76
+ elseif type(val) == "number" or type(val) == "boolean" then
77
+ str = tostring(val)
78
+ else
79
+ error("JSON only supports arrays, objects, numbers, booleans, and strings", 2)
80
+ end
81
+ return str
82
+ end
83
+
84
+ function encode(val)
85
+ return encodeCommon(val, false, 0, {})
86
+ end
87
+
88
+ function encodePretty(val)
89
+ return encodeCommon(val, true, 0, {})
90
+ end
91
+
92
+ ------------------------------------------------------------------ decoding
93
+
94
+ function parseBoolean(str)
95
+ if str:sub(1, 4) == "true" then
96
+ return true, removeWhite(str:sub(5))
97
+ else
98
+ return false, removeWhite(str:sub(6))
99
+ end
100
+ end
101
+
102
+ function parseNull(str)
103
+ return nil, removeWhite(str:sub(5))
104
+ end
105
+
106
+ local numChars = {['e']=true; ['E']=true; ['+']=true; ['-']=true; ['.']=true}
107
+ function parseNumber(str)
108
+ local i = 1
109
+ while numChars[str:sub(i, i)] or tonumber(str:sub(i, i)) do
110
+ i = i + 1
111
+ end
112
+ local val = tonumber(str:sub(1, i - 1))
113
+ str = removeWhite(str:sub(i))
114
+ return val, str
115
+ end
116
+
117
+ function parseString(str)
118
+ local i,j = str:find('[^\\]"')
119
+ local s = str:sub(2, j - 1)
120
+
121
+ for k,v in pairs(controls) do
122
+ s = s:gsub(v, k)
123
+ end
124
+ str = removeWhite(str:sub(j + 1))
125
+ return s, str
126
+ end
127
+
128
+ function parseArray(str)
129
+ str = removeWhite(str:sub(2))
130
+
131
+ local val = {}
132
+ local i = 1
133
+ while str:sub(1, 1) ~= "]" do
134
+ local v = nil
135
+ v, str = parseValue(str)
136
+ val[i] = v
137
+ i = i + 1
138
+ str = removeWhite(str)
139
+ end
140
+ str = removeWhite(str:sub(2))
141
+ return val, str
142
+ end
143
+
144
+ function parseObject(str)
145
+ str = removeWhite(str:sub(2))
146
+
147
+ local val = {}
148
+ while str:sub(1, 1) ~= "}" do
149
+ local k, v = nil, nil
150
+ k, v, str = parseMember(str)
151
+ val[k] = v
152
+ str = removeWhite(str)
153
+ end
154
+ str = removeWhite(str:sub(2))
155
+ return val, str
156
+ end
157
+
158
+ function parseMember(str)
159
+ local k = nil
160
+ k, str = parseValue(str)
161
+ local val = nil
162
+ val, str = parseValue(str)
163
+ return k, val, str
164
+ end
165
+
166
+ function parseValue(str)
167
+ local fchar = str:sub(1, 1)
168
+ if fchar == "{" then
169
+ return parseObject(str)
170
+ elseif fchar == "[" then
171
+ return parseArray(str)
172
+ elseif tonumber(fchar) ~= nil or numChars[fchar] then
173
+ return parseNumber(str)
174
+ elseif str:sub(1, 4) == "true" or str:sub(1, 5) == "false" then
175
+ return parseBoolean(str)
176
+ elseif fchar == "\"" then
177
+ return parseString(str)
178
+ elseif str:sub(1, 4) == "null" then
179
+ return parseNull(str)
180
+ end
181
+ return nil
182
+ end
183
+
184
+ function decode(str)
185
+ str = removeWhite(str)
186
+ t = parseValue(str)
187
+ return t
188
+ end
189
+
190
+ function decodeFromFile(path)
191
+ local file = assert(fs.open(path, "r"))
192
+ return decode(file.readAll())
193
+ end
0 commit comments