Reformatted code

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2025-08-09 02:56:19 +0200
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2025-08-09 02:56:19 +0200
Commit 7e927d46915646c7c219f6a57df5569bee9d78f8 (patch)
-rw-r--r-- stdlib/json.lua 500
1 files changed, 239 insertions, 261 deletions
diff --git a/stdlib/json.lua b/stdlib/json.lua
...
31
local encode
31
local encode
32
  
32
  
33
local escape_char_map = {
33
local escape_char_map = {
34
  [ "\\" ] = "\\",
34
	[ "\\" ] = "\\",
35
  [ "\"" ] = "\"",
35
	[ "\"" ] = "\"",
36
  [ "\b" ] = "b",
36
	[ "\b" ] = "b",
37
  [ "\f" ] = "f",
37
	[ "\f" ] = "f",
38
  [ "\n" ] = "n",
38
	[ "\n" ] = "n",
39
  [ "\r" ] = "r",
39
	[ "\r" ] = "r",
40
  [ "\t" ] = "t",
40
	[ "\t" ] = "t",
41
}
41
}
42
  
42
  
43
local escape_char_map_inv = { [ "/" ] = "/" }
43
local escape_char_map_inv = { [ "/" ] = "/" }
44
for k, v in pairs(escape_char_map) do
44
for k, v in pairs(escape_char_map) do
45
  escape_char_map_inv[v] = k
45
	escape_char_map_inv[v] = k
46
end
46
end
47
  
47
  
48
  
  
49
local function escape_char(c)
48
local function escape_char(c)
50
  return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
49
	return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
51
end
50
end
52
  
  
53
  
51
  
54
local function encode_nil(val)
52
local function encode_nil(val)
55
  return "null"
53
	return "null"
56
end
54
end
57
  
55
  
58
  
  
59
local function encode_table(val, stack)
56
local function encode_table(val, stack)
60
  local res = {}
57
	local res = {}
61
  stack = stack or {}
58
	stack = stack or {}
62
  
59
  
63
  -- Circular reference?
60
	-- Circular reference?
64
  if stack[val] then error("circular reference") end
61
	if stack[val] then error("circular reference") end
65
  
62
  
66
  stack[val] = true
63
	stack[val] = true
67
  
64
  
68
  if rawget(val, 1) ~= nil or next(val) == nil then
65
	if rawget(val, 1) ~= nil or next(val) == nil then
69
    -- Treat as array -- check keys are valid and it is not sparse
66
		-- Treat as array -- check keys are valid and it is not sparse
70
    local n = 0
67
		local n = 0
71
    for k in pairs(val) do
68
		for k in pairs(val) do
72
      if type(k) ~= "number" then
69
			if type(k) ~= "number" then
73
        error("invalid table: mixed or invalid key types")
70
				error("invalid table: mixed or invalid key types")
74
      end
71
			end
75
      n = n + 1
72
			n = n + 1
76
    end
73
		end
77
    if n ~= #val then
74
		if n ~= #val then
78
      error("invalid table: sparse array")
75
			error("invalid table: sparse array")
79
    end
76
		end
80
    -- Encode
77
		-- Encode
81
    for i, v in ipairs(val) do
78
		for i, v in ipairs(val) do
82
      table.insert(res, encode(v, stack))
79
			table.insert(res, encode(v, stack))
83
    end
80
		end
84
    stack[val] = nil
81
		stack[val] = nil
85
    return "[" .. table.concat(res, ",") .. "]"
82
		return "[" .. table.concat(res, ",") .. "]"
86
  
83
  
87
  else
84
	else
88
    -- Treat as an object
85
		-- Treat as an object
89
    for k, v in pairs(val) do
86
		for k, v in pairs(val) do
90
      if type(k) ~= "string" then
87
			if type(k) ~= "string" then
91
        error("invalid table: mixed or invalid key types")
88
				error("invalid table: mixed or invalid key types")
92
      end
89
			end
93
      table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
90
			table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
94
    end
91
		end
95
    stack[val] = nil
92
		stack[val] = nil
96
    return "{" .. table.concat(res, ",") .. "}"
93
		return "{" .. table.concat(res, ",") .. "}"
97
  end
94
	end
98
end
95
end
99
  
96
  
100
  
  
101
local function encode_string(val)
97
local function encode_string(val)
102
  return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
98
	return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
103
end
99
end
104
  
100
  
105
  
  
106
local function encode_number(val)
101
local function encode_number(val)
107
  -- Check for NaN, -inf and inf
102
	-- Check for NaN, -inf and inf
108
  if val ~= val or val <= -math.huge or val >= math.huge then
103
	if val ~= val or val <= -math.huge or val >= math.huge then
109
    error("unexpected number value '" .. tostring(val) .. "'")
104
		error("unexpected number value '" .. tostring(val) .. "'")
110
  end
105
	end
111
  return string.format("%.14g", val)
106
	return string.format("%.14g", val)
112
end
107
end
113
  
  
114
  
108
  
115
local type_func_map = {
109
local type_func_map = {
116
  [ "nil"     ] = encode_nil,
110
	[ "nil"     ] = encode_nil,
117
  [ "table"   ] = encode_table,
111
	[ "table"   ] = encode_table,
118
  [ "string"  ] = encode_string,
112
	[ "string"  ] = encode_string,
119
  [ "number"  ] = encode_number,
113
	[ "number"  ] = encode_number,
120
  [ "boolean" ] = tostring,
114
	[ "boolean" ] = tostring,
121
}
115
}
122
  
116
  
123
  
  
124
encode = function(val, stack)
117
encode = function(val, stack)
125
  local t = type(val)
118
	local t = type(val)
126
  local f = type_func_map[t]
119
	local f = type_func_map[t]
127
  if f then
120
	if f then
128
    return f(val, stack)
121
		return f(val, stack)
129
  end
122
	end
130
  error("unexpected type '" .. t .. "'")
123
	error("unexpected type '" .. t .. "'")
131
end
124
end
132
  
125
  
133
  
  
134
function json.encode(val)
126
function json.encode(val)
135
  return ( encode(val) )
127
	return ( encode(val) )
136
end
128
end
137
  
  
138
  
129
  
139
-------------------------------------------------------------------------------
130
-------------------------------------------------------------------------------
140
-- Decode
131
-- Decode
...
143
local parse
134
local parse
144
  
135
  
145
local function create_set(...)
136
local function create_set(...)
146
  local res = {}
137
	local res = {}
147
  for i = 1, select("#", ...) do
138
	for i = 1, select("#", ...) do
148
    res[ select(i, ...) ] = true
139
		res[ select(i, ...) ] = true
149
  end
140
	end
150
  return res
141
	return res
151
end
142
end
152
  
143
  
153
local space_chars   = create_set(" ", "\t", "\r", "\n")
144
local space_chars   = create_set(" ", "\t", "\r", "\n")
...
156
local literals      = create_set("true", "false", "null")
147
local literals      = create_set("true", "false", "null")
157
  
148
  
158
local literal_map = {
149
local literal_map = {
159
  [ "true"  ] = true,
150
	[ "true"  ] = true,
160
  [ "false" ] = false,
151
	[ "false" ] = false,
161
  [ "null"  ] = nil,
152
	[ "null"  ] = nil,
162
}
153
}
163
  
154
  
164
  
  
165
local function next_char(str, idx, set, negate)
155
local function next_char(str, idx, set, negate)
166
  for i = idx, #str do
156
	for i = idx, #str do
167
    if set[str:sub(i, i)] ~= negate then
157
		if set[str:sub(i, i)] ~= negate then
168
      return i
158
			return i
169
    end
159
		end
170
  end
160
	end
171
  return #str + 1
161
	return #str + 1
172
end
162
end
173
  
  
174
  
163
  
175
local function decode_error(str, idx, msg)
164
local function decode_error(str, idx, msg)
176
  local line_count = 1
165
	local line_count = 1
177
  local col_count = 1
166
	local col_count = 1
178
  for i = 1, idx - 1 do
167
	for i = 1, idx - 1 do
179
    col_count = col_count + 1
168
		col_count = col_count + 1
180
    if str:sub(i, i) == "\n" then
169
		if str:sub(i, i) == "\n" then
181
      line_count = line_count + 1
170
			line_count = line_count + 1
182
      col_count = 1
171
			col_count = 1
183
    end
172
		end
184
  end
173
	end
185
  error( string.format("%s at line %d col %d", msg, line_count, col_count) )
174
	error( string.format("%s at line %d col %d", msg, line_count, col_count) )
186
end
175
end
187
  
176
  
188
  
  
189
local function codepoint_to_utf8(n)
177
local function codepoint_to_utf8(n)
190
  -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
178
	-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
191
  local f = math.floor
179
	local f = math.floor
192
  if n <= 0x7f then
180
	if n <= 0x7f then
193
    return string.char(n)
181
		return string.char(n)
194
  elseif n <= 0x7ff then
182
	elseif n <= 0x7ff then
195
    return string.char(f(n / 64) + 192, n % 64 + 128)
183
		return string.char(f(n / 64) + 192, n % 64 + 128)
196
  elseif n <= 0xffff then
184
	elseif n <= 0xffff then
197
    return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
185
		return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
198
  elseif n <= 0x10ffff then
186
	elseif n <= 0x10ffff then
199
    return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
187
		return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
200
                       f(n % 4096 / 64) + 128, n % 64 + 128)
188
		f(n % 4096 / 64) + 128, n % 64 + 128)
201
  end
189
	end
202
  error( string.format("invalid unicode codepoint '%x'", n) )
190
	error( string.format("invalid unicode codepoint '%x'", n) )
203
end
191
end
204
  
192
  
205
  
  
206
local function parse_unicode_escape(s)
193
local function parse_unicode_escape(s)
207
  local n1 = tonumber( s:sub(1, 4),  16 )
194
	local n1 = tonumber( s:sub(1, 4),  16 )
208
  local n2 = tonumber( s:sub(7, 10), 16 )
195
	local n2 = tonumber( s:sub(7, 10), 16 )
209
   -- Surrogate pair?
196
	-- Surrogate pair?
210
  if n2 then
197
	if n2 then
211
    return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
198
		return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
212
  else
199
	else
213
    return codepoint_to_utf8(n1)
200
		return codepoint_to_utf8(n1)
214
  end
201
	end
215
end
202
end
216
  
203
  
217
  
  
218
local function parse_string(str, i)
204
local function parse_string(str, i)
219
  local res = ""
205
	local res = ""
220
  local j = i + 1
206
	local j = i + 1
221
  local k = j
207
	local k = j
222
  
208
  
223
  while j <= #str do
209
	while j <= #str do
224
    local x = str:byte(j)
210
		local x = str:byte(j)
225
  
211
  
226
    if x < 32 then
212
		if x < 32 then
227
      decode_error(str, j, "control character in string")
213
			decode_error(str, j, "control character in string")
228
  
214
  
229
    elseif x == 92 then -- `\`: Escape
215
		elseif x == 92 then -- `\`: Escape
230
      res = res .. str:sub(k, j - 1)
216
			res = res .. str:sub(k, j - 1)
231
      j = j + 1
217
			j = j + 1
232
      local c = str:sub(j, j)
218
			local c = str:sub(j, j)
233
      if c == "u" then
219
			if c == "u" then
234
        local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
220
				local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
235
                 or str:match("^%x%x%x%x", j + 1)
221
				or str:match("^%x%x%x%x", j + 1)
236
                 or decode_error(str, j - 1, "invalid unicode escape in string")
222
				or decode_error(str, j - 1, "invalid unicode escape in string")
237
        res = res .. parse_unicode_escape(hex)
223
				res = res .. parse_unicode_escape(hex)
238
        j = j + #hex
224
				j = j + #hex
239
      else
225
			else
240
        if not escape_chars[c] then
226
				if not escape_chars[c] then
241
          decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
227
					decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
242
        end
228
				end
243
        res = res .. escape_char_map_inv[c]
229
				res = res .. escape_char_map_inv[c]
244
      end
230
			end
245
      k = j + 1
231
			k = j + 1
246
  
232
  
247
    elseif x == 34 then -- `"`: End of string
233
		elseif x == 34 then -- `"`: End of string
248
      res = res .. str:sub(k, j - 1)
234
			res = res .. str:sub(k, j - 1)
249
      return res, j + 1
235
			return res, j + 1
250
    end
236
		end
251
  
237
  
252
    j = j + 1
238
		j = j + 1
253
  end
239
	end
254
  
240
  
255
  decode_error(str, i, "expected closing quote for string")
241
	decode_error(str, i, "expected closing quote for string")
256
end
242
end
257
  
243
  
258
  
  
259
local function parse_number(str, i)
244
local function parse_number(str, i)
260
  local x = next_char(str, i, delim_chars)
245
	local x = next_char(str, i, delim_chars)
261
  local s = str:sub(i, x - 1)
246
	local s = str:sub(i, x - 1)
262
  local n = tonumber(s)
247
	local n = tonumber(s)
263
  if not n then
248
	if not n then
264
    decode_error(str, i, "invalid number '" .. s .. "'")
249
		decode_error(str, i, "invalid number '" .. s .. "'")
265
  end
250
	end
266
  return n, x
251
	return n, x
267
end
252
end
268
  
  
269
  
253
  
270
local function parse_literal(str, i)
254
local function parse_literal(str, i)
271
  local x = next_char(str, i, delim_chars)
255
	local x = next_char(str, i, delim_chars)
272
  local word = str:sub(i, x - 1)
256
	local word = str:sub(i, x - 1)
273
  if not literals[word] then
257
	if not literals[word] then
274
    decode_error(str, i, "invalid literal '" .. word .. "'")
258
		decode_error(str, i, "invalid literal '" .. word .. "'")
275
  end
259
	end
276
  return literal_map[word], x
260
	return literal_map[word], x
277
end
261
end
278
  
  
279
  
262
  
280
local function parse_array(str, i)
263
local function parse_array(str, i)
281
  local res = {}
264
	local res = {}
282
  local n = 1
265
	local n = 1
283
  i = i + 1
266
	i = i + 1
284
  while 1 do
267
	while 1 do
285
    local x
268
		local x
286
    i = next_char(str, i, space_chars, true)
269
		i = next_char(str, i, space_chars, true)
287
    -- Empty / end of array?
270
		-- Empty / end of array?
288
    if str:sub(i, i) == "]" then
271
		if str:sub(i, i) == "]" then
289
      i = i + 1
272
			i = i + 1
290
      break
273
			break
291
    end
274
		end
292
    -- Read token
275
		-- Read token
293
    x, i = parse(str, i)
276
		x, i = parse(str, i)
294
    res[n] = x
277
		res[n] = x
295
    n = n + 1
278
		n = n + 1
296
    -- Next token
279
		-- Next token
297
    i = next_char(str, i, space_chars, true)
280
		i = next_char(str, i, space_chars, true)
298
    local chr = str:sub(i, i)
281
		local chr = str:sub(i, i)
299
    i = i + 1
282
		i = i + 1
300
    if chr == "]" then break end
283
		if chr == "]" then break end
301
    if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
284
		if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
302
  end
285
	end
303
  return res, i
286
	return res, i
304
end
287
end
305
  
  
306
  
288
  
307
local function parse_object(str, i)
289
local function parse_object(str, i)
308
  local res = {}
290
	local res = {}
309
  i = i + 1
291
	i = i + 1
310
  while 1 do
292
	while 1 do
311
    local key, val
293
		local key, val
312
    i = next_char(str, i, space_chars, true)
294
		i = next_char(str, i, space_chars, true)
313
    -- Empty / end of object?
295
		-- Empty / end of object?
314
    if str:sub(i, i) == "}" then
296
		if str:sub(i, i) == "}" then
315
      i = i + 1
297
			i = i + 1
316
      break
298
			break
317
    end
299
		end
318
    -- Read key
300
		-- Read key
319
    if str:sub(i, i) ~= '"' then
301
		if str:sub(i, i) ~= '"' then
320
      decode_error(str, i, "expected string for key")
302
			decode_error(str, i, "expected string for key")
321
    end
303
		end
322
    key, i = parse(str, i)
304
		key, i = parse(str, i)
323
    -- Read ':' delimiter
305
		-- Read ':' delimiter
324
    i = next_char(str, i, space_chars, true)
306
		i = next_char(str, i, space_chars, true)
325
    if str:sub(i, i) ~= ":" then
307
		if str:sub(i, i) ~= ":" then
326
      decode_error(str, i, "expected ':' after key")
308
			decode_error(str, i, "expected ':' after key")
327
    end
309
		end
328
    i = next_char(str, i + 1, space_chars, true)
310
		i = next_char(str, i + 1, space_chars, true)
329
    -- Read value
311
		-- Read value
330
    val, i = parse(str, i)
312
		val, i = parse(str, i)
331
    -- Set
313
		-- Set
332
    res[key] = val
314
		res[key] = val
333
    -- Next token
315
		-- Next token
334
    i = next_char(str, i, space_chars, true)
316
		i = next_char(str, i, space_chars, true)
335
    local chr = str:sub(i, i)
317
		local chr = str:sub(i, i)
336
    i = i + 1
318
		i = i + 1
337
    if chr == "}" then break end
319
		if chr == "}" then break end
338
    if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
320
		if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
339
  end
321
	end
340
  return res, i
322
	return res, i
341
end
323
end
342
  
  
343
  
324
  
344
local char_func_map = {
325
local char_func_map = {
345
  [ '"' ] = parse_string,
326
	[ '"' ] = parse_string,
346
  [ "0" ] = parse_number,
327
	[ "0" ] = parse_number,
347
  [ "1" ] = parse_number,
328
	[ "1" ] = parse_number,
348
  [ "2" ] = parse_number,
329
	[ "2" ] = parse_number,
349
  [ "3" ] = parse_number,
330
	[ "3" ] = parse_number,
350
  [ "4" ] = parse_number,
331
	[ "4" ] = parse_number,
351
  [ "5" ] = parse_number,
332
	[ "5" ] = parse_number,
352
  [ "6" ] = parse_number,
333
	[ "6" ] = parse_number,
353
  [ "7" ] = parse_number,
334
	[ "7" ] = parse_number,
354
  [ "8" ] = parse_number,
335
	[ "8" ] = parse_number,
355
  [ "9" ] = parse_number,
336
	[ "9" ] = parse_number,
356
  [ "-" ] = parse_number,
337
	[ "-" ] = parse_number,
357
  [ "t" ] = parse_literal,
338
	[ "t" ] = parse_literal,
358
  [ "f" ] = parse_literal,
339
	[ "f" ] = parse_literal,
359
  [ "n" ] = parse_literal,
340
	[ "n" ] = parse_literal,
360
  [ "[" ] = parse_array,
341
	[ "[" ] = parse_array,
361
  [ "{" ] = parse_object,
342
	[ "{" ] = parse_object,
362
}
343
}
363
  
  
364
  
344
  
365
parse = function(str, idx)
345
parse = function(str, idx)
366
  local chr = str:sub(idx, idx)
346
	local chr = str:sub(idx, idx)
367
  local f = char_func_map[chr]
347
	local f = char_func_map[chr]
368
  if f then
348
	if f then
369
    return f(str, idx)
349
		return f(str, idx)
370
  end
350
	end
371
  decode_error(str, idx, "unexpected character '" .. chr .. "'")
351
	decode_error(str, idx, "unexpected character '" .. chr .. "'")
372
end
352
end
373
  
  
374
  
353
  
375
function json.decode(str)
354
function json.decode(str)
376
  if type(str) ~= "string" then
355
	if type(str) ~= "string" then
377
    error("expected argument of type string, got " .. type(str))
356
		error("expected argument of type string, got " .. type(str))
378
  end
357
	end
379
  local res, idx = parse(str, next_char(str, 1, space_chars, true))
358
	local res, idx = parse(str, next_char(str, 1, space_chars, true))
380
  idx = next_char(str, idx, space_chars, true)
359
	idx = next_char(str, idx, space_chars, true)
381
  if idx <= #str then
360
	if idx <= #str then
382
    decode_error(str, idx, "trailing garbage")
361
		decode_error(str, idx, "trailing garbage")
383
  end
362
	end
384
  return res
363
	return res
385
end
364
end
386
  
  
387
  
365
  
388
return json
366
return json