/
json.p8
101 lines (89 loc) · 3.24 KB
/
json.p8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
pico-8 cartridge // http://www.pico-8.com
version 18
__lua__
-- pico8lib json library
-- by sparr
-- #include strings.p8
-- based on https://gist.github.com/tylerneylon/59f4bcf316be525b30ab
-- "tylerneylon commented on sep 1, 2015 [...] yes, please use it. i put this in the public domain."
-- error checking is available but commented out, useful for debugging bad hand-crafted json
local json
json = setmetatable({
null = {}, -- to uniquely identify json null values
table_delims = { ['{']="}", ['[']="]" },
-- populate json.vals with keys that should be used to replace literal strings in the json
-- json.vals[foo=bar] will result in "foo" in the json being replaced with the value bar
vals = {},
skip_delim = function(str, pos, delim) -- , err_if_missing)
pos = json.skip_whitespace(str, pos) -- only needed for unminified json
if sub(str, pos, pos) != delim then
-- if(err_if_missing) assert('delimiter missing')
return pos,false
end
return pos + 1,true
end,
skip_whitespace = function(str, pos) -- only needed for unminified json
while char_in_string(sub(str, pos, pos), " \t\n\r") do
pos += 1
end
return pos
end,
},{
__call = function(t, str, pos, end_delim)
pos = pos or 1
pos = json.skip_whitespace(str, pos) -- only needed for unminified json
-- if(pos>#str) assert('reached unexpected end of input.')
local char = sub(str, pos, pos)
if json.table_delims[char] then
local tbl, key = {}, true
-- local delim_found = true
pos += 1
while true do
key, pos = t(str, pos, json.table_delims[char])
if (key == nil) return tbl, pos
-- if not delim_found then assert('comma missing between table items.') end
if char == "{" then
pos = json.skip_delim(str, pos, ':') -- , true) -- -> error if missing.
tbl[key], pos = t(str, pos)
else
-- tbl[#tbl + 1] = key -- faster, more tokens
add(tbl, key)
end
pos, delim_found = json.skip_delim(str, pos, ',')
end
elseif char == '"' then
-- parse a string
-- lacks support for escape codes, see gist link above for escape support
local ipos = pos + 1
-- if(pos>#str) assert('end of input found while parsing string.')
repeat
pos +=1
until sub(str, pos, pos)=='"'
local val = sub(str, ipos, pos - 1)
return json.vals[val] or val, pos + 1
elseif tonum(char .. "0") then
-- parse a number.
local ipos = pos
repeat
pos += 1
-- if(pos>#str) assert('end of input found while parsing string.')
until not char_in_string(sub(str, pos, pos), "-xb0123456789abcdef.") -- support base 10, 16, and 2 numbers
-- until not tonum(sub(str, pos, pos) .. "0") -- base 10 only
return tonum(sub(str, ipos, pos - 1)), pos
elseif char == end_delim then
-- end of an object or array.
return nil, pos+1
elseif char_in_string(char," \t\n\r") then -- only needed for unminified json
-- skip whitespace
pos = json.skip_whitespace(str, pos)
return t(str, pos, end_delim)
else -- parse true, false, null
for lit_str, lit_val in pairs(json.literals) do
local lit_end = pos + #lit_str - 1
if (sub(str, pos, lit_end) == lit_str) return lit_val, lit_end + 1
end
-- assert('invalid json token')
end
end
})
json.literals = { ['true']=true, ['false']=false, ['null']=json.null }