Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/resources/pandoc/datadir/_json.lua
12922 views
1
--
2
-- json.lua
3
--
4
-- Copyright (c) 2020 rxi
5
-- https://github.com/rxi/json.lua
6
--
7
-- includes unreleased upstream changes: https://github.com/rxi/json.lua/blob/dbf4b2dd2eb7c23be2773c89eb059dadd6436f94/json.lua
8
-- includes unmerged upstream pull request: https://github.com/rxi/json.lua/pull/51
9
-- includes unmerged upstream pull request: https://github.com/rxi/json.lua/pull/52
10
--
11
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
12
-- this software and associated documentation files (the "Software"), to deal in
13
-- the Software without restriction, including without limitation the rights to
14
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
15
-- of the Software, and to permit persons to whom the Software is furnished to do
16
-- so, subject to the following conditions:
17
--
18
-- The above copyright notice and this permission notice shall be included in all
19
-- copies or substantial portions of the Software.
20
--
21
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
-- SOFTWARE.
28
--
29
30
local json = { _version = "0.1.2-quarto" }
31
32
-- taken from https://www.lua.org/pil/19.3.html
33
function pairsByKeys (t, f)
34
local a = {}
35
for n in pairs(t) do table.insert(a, n) end
36
table.sort(a, f)
37
local i = 0 -- iterator variable
38
local iter = function () -- iterator function
39
i = i + 1
40
if a[i] == nil then return nil
41
else return a[i], t[a[i]]
42
end
43
end
44
return iter
45
end
46
47
-------------------------------------------------------------------------------
48
-- Encode
49
-------------------------------------------------------------------------------
50
51
local encode
52
53
local escape_char_map = {
54
[ "\\" ] = "\\",
55
[ "\"" ] = "\"",
56
[ "\b" ] = "b",
57
[ "\f" ] = "f",
58
[ "\n" ] = "n",
59
[ "\r" ] = "r",
60
[ "\t" ] = "t",
61
}
62
63
local escape_char_map_inv = { [ "/" ] = "/" }
64
for k, v in pairs(escape_char_map) do
65
escape_char_map_inv[v] = k
66
end
67
68
69
local function escape_char(c)
70
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
71
end
72
73
74
local function encode_nil(val)
75
return "null"
76
end
77
78
local function encode_string(val)
79
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
80
end
81
82
local function encode_table(val, stack)
83
local res = {}
84
stack = stack or {}
85
86
-- Circular reference?
87
if stack[val] then error("circular reference") end
88
89
stack[val] = true
90
91
if next(val) == nil then
92
return '[]'
93
end
94
95
local types = {}
96
97
for k in pairs(val) do
98
types[type(k)] = true
99
end
100
101
if #types > 1 then
102
error("invalid table: mixed or invalid key types")
103
elseif types["number"] then
104
-- Treat as array
105
local max_key = 0
106
for k in pairs(val) do
107
if k > max_key then
108
max_key = k
109
end
110
end
111
for i = 1, max_key do
112
if val[i] == nil then
113
table.insert(res, "null")
114
else
115
local v = encode(val[i], stack)
116
table.insert(res, v)
117
end
118
end
119
stack[val] = nil
120
return "[" .. table.concat(res, ",") .. "]"
121
elseif types["string"] then
122
-- Treat as object
123
for k, v in pairsByKeys(val) do
124
table.insert(res, encode_string(k) .. ":" .. encode(v, stack))
125
end
126
stack[val] = nil
127
return "{" .. table.concat(res, ",") .. "}"
128
else
129
error( string.format("invalid table: unsupported key type %s", types[1]) )
130
end
131
end
132
133
local function encode_number(val)
134
-- Check for NaN, -inf and inf
135
if val ~= val or val <= -math.huge or val >= math.huge then
136
error("unexpected number value '" .. tostring(val) .. "'")
137
end
138
return string.format("%.14g", val)
139
end
140
141
142
local type_func_map = {
143
[ "nil" ] = encode_nil,
144
[ "table" ] = encode_table,
145
[ "string" ] = encode_string,
146
[ "number" ] = encode_number,
147
[ "boolean" ] = tostring,
148
}
149
150
151
encode = function(val, stack)
152
local t = type(val)
153
local f = type_func_map[t]
154
if f then
155
return f(val, stack)
156
end
157
error("unexpected type '" .. t .. "'")
158
end
159
160
161
function json.encode(val)
162
return ( encode(val) )
163
end
164
165
166
-------------------------------------------------------------------------------
167
-- Decode
168
-------------------------------------------------------------------------------
169
170
local parse
171
172
local function create_set(...)
173
local res = {}
174
for i = 1, select("#", ...) do
175
res[ select(i, ...) ] = true
176
end
177
return res
178
end
179
180
local space_chars = create_set(" ", "\t", "\r", "\n")
181
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
182
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
183
local literals = create_set("true", "false", "null")
184
185
local literal_map = {
186
[ "true" ] = true,
187
[ "false" ] = false,
188
[ "null" ] = nil,
189
}
190
191
192
local function next_char(str, idx, set, negate)
193
for i = idx, #str do
194
if set[str:sub(i, i)] ~= negate then
195
return i
196
end
197
end
198
return #str + 1
199
end
200
201
202
local function decode_error(str, idx, msg)
203
local line_count = 1
204
local col_count = 1
205
for i = 1, idx - 1 do
206
col_count = col_count + 1
207
if str:sub(i, i) == "\n" then
208
line_count = line_count + 1
209
col_count = 1
210
end
211
end
212
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
213
end
214
215
216
local function codepoint_to_utf8(n)
217
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
218
local f = math.floor
219
if n <= 0x7f then
220
return string.char(n)
221
elseif n <= 0x7ff then
222
return string.char(f(n / 64) + 192, n % 64 + 128)
223
elseif n <= 0xffff then
224
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
225
elseif n <= 0x10ffff then
226
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
227
f(n % 4096 / 64) + 128, n % 64 + 128)
228
end
229
error( string.format("invalid unicode codepoint '%x'", n) )
230
end
231
232
233
local function parse_unicode_escape(s)
234
local n1 = tonumber( s:sub(1, 4), 16 )
235
local n2 = tonumber( s:sub(7, 10), 16 )
236
-- Surrogate pair?
237
if n2 then
238
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
239
else
240
return codepoint_to_utf8(n1)
241
end
242
end
243
244
245
local function parse_string(str, i)
246
local res = ""
247
local j = i + 1
248
local k = j
249
250
while j <= #str do
251
local x = str:byte(j)
252
253
if x < 32 then
254
decode_error(str, j, "control character in string")
255
256
elseif x == 92 then -- `\`: Escape
257
res = res .. str:sub(k, j - 1)
258
j = j + 1
259
local c = str:sub(j, j)
260
if c == "u" then
261
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
262
or str:match("^%x%x%x%x", j + 1)
263
or decode_error(str, j - 1, "invalid unicode escape in string")
264
res = res .. parse_unicode_escape(hex)
265
j = j + #hex
266
else
267
if not escape_chars[c] then
268
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
269
end
270
res = res .. escape_char_map_inv[c]
271
end
272
k = j + 1
273
274
elseif x == 34 then -- `"`: End of string
275
res = res .. str:sub(k, j - 1)
276
return res, j + 1
277
end
278
279
j = j + 1
280
end
281
282
decode_error(str, i, "expected closing quote for string")
283
end
284
285
286
local function parse_number(str, i)
287
local x = next_char(str, i, delim_chars)
288
local s = str:sub(i, x - 1)
289
local n = tonumber(s)
290
if not n then
291
decode_error(str, i, "invalid number '" .. s .. "'")
292
end
293
return n, x
294
end
295
296
297
local function parse_literal(str, i)
298
local x = next_char(str, i, delim_chars)
299
local word = str:sub(i, x - 1)
300
if not literals[word] then
301
decode_error(str, i, "invalid literal '" .. word .. "'")
302
end
303
return literal_map[word], x
304
end
305
306
307
local function parse_array(str, i)
308
local res = {}
309
local n = 1
310
i = i + 1
311
while 1 do
312
local x
313
i = next_char(str, i, space_chars, true)
314
-- Empty / end of array?
315
if str:sub(i, i) == "]" then
316
i = i + 1
317
break
318
end
319
-- Read token
320
x, i = parse(str, i)
321
res[n] = x
322
n = n + 1
323
-- Next token
324
i = next_char(str, i, space_chars, true)
325
local chr = str:sub(i, i)
326
i = i + 1
327
if chr == "]" then break end
328
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
329
end
330
return res, i
331
end
332
333
334
local function parse_object(str, i)
335
local res = {}
336
i = i + 1
337
while 1 do
338
local key, val
339
i = next_char(str, i, space_chars, true)
340
-- Empty / end of object?
341
if str:sub(i, i) == "}" then
342
i = i + 1
343
break
344
end
345
-- Read key
346
if str:sub(i, i) ~= '"' then
347
decode_error(str, i, "expected string for key")
348
end
349
key, i = parse(str, i)
350
-- Read ':' delimiter
351
i = next_char(str, i, space_chars, true)
352
if str:sub(i, i) ~= ":" then
353
decode_error(str, i, "expected ':' after key")
354
end
355
i = next_char(str, i + 1, space_chars, true)
356
-- Read value
357
val, i = parse(str, i)
358
-- Set
359
res[key] = val
360
-- Next token
361
i = next_char(str, i, space_chars, true)
362
local chr = str:sub(i, i)
363
i = i + 1
364
if chr == "}" then break end
365
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
366
end
367
return res, i
368
end
369
370
371
local char_func_map = {
372
[ '"' ] = parse_string,
373
[ "0" ] = parse_number,
374
[ "1" ] = parse_number,
375
[ "2" ] = parse_number,
376
[ "3" ] = parse_number,
377
[ "4" ] = parse_number,
378
[ "5" ] = parse_number,
379
[ "6" ] = parse_number,
380
[ "7" ] = parse_number,
381
[ "8" ] = parse_number,
382
[ "9" ] = parse_number,
383
[ "-" ] = parse_number,
384
[ "t" ] = parse_literal,
385
[ "f" ] = parse_literal,
386
[ "n" ] = parse_literal,
387
[ "[" ] = parse_array,
388
[ "{" ] = parse_object,
389
}
390
391
392
parse = function(str, idx)
393
local chr = str:sub(idx, idx)
394
local f = char_func_map[chr]
395
if f then
396
return f(str, idx)
397
end
398
decode_error(str, idx, "unexpected character '" .. chr .. "'")
399
end
400
401
402
function json.decode(str)
403
if type(str) ~= "string" then
404
error("expected argument of type string, got " .. type(str))
405
end
406
local res, idx = parse(str, next_char(str, 1, space_chars, true))
407
idx = next_char(str, idx, space_chars, true)
408
if idx <= #str then
409
decode_error(str, idx, "trailing garbage")
410
end
411
return res
412
end
413
414
415
return json
416
417