1+ local find , sub , gsub , tonumber , error = string.find , string.sub , string.gsub , tonumber , error
2+ local function decode (json --[[ @param json string]] ) --- @return table
3+ local ptr = 0
4+ local function consume (pattern --[[ @param pattern string]] ) --- @return string ?
5+ local _ , finish , match = find (json , pattern , ptr )
6+ if finish then
7+ ptr = finish + 1
8+ return match or true
9+ end
10+ return nil
11+ end
12+
13+ local repl = { -- escape characters
14+ [" b" ] = " \b " , [" f" ] = " \f " , [" n" ] = " \n " , [" r" ] = " \r " ,
15+ [" t" ] = " \t " , [" \\ " ] = " \\ " , [" \" " ] = " \" "
16+ }
17+
18+ local function slowstring () -- string parser that works with escapes. fallback at very far end of parsing order, since rare case.
19+ local _ , start = find (json , " ^%s*\" " , ptr )
20+ if not start then return end
21+ ptr = start + 1
22+
23+ while true do
24+ local _ , finish , escapes = find (json , " (\\ *)\" " , ptr )
25+ if finish then
26+ ptr = finish + 1
27+ if # escapes % 2 == 0 then
28+ return ( gsub ( sub (json , start + 1 , finish - 1 ), " \\ ([bfnrt\\\" ])" , repl ) )
29+ end
30+ else
31+ error (" Missing end quote for string at char " .. ptr )
32+ end
33+ end
34+ end
35+
36+ local object , array
37+ local function faststring () -- Parses a string without any escapes. Fastest and most common case.
38+ return consume (" ^%s*\" ([^\"\\ ]*)\" " )
39+ end
40+
41+ --- Parses a JSON value.
42+ --- Order is important for performance with common cases.
43+ local function value ()
44+ return faststring ()
45+ or object ()
46+ or tonumber (consume (" ^%s*(%-?%d+%.%d+)" ) or consume (" ^%s*(%-?%d+)" ))
47+ or consume (" ^%s*(true)" ) or consume (" ^%s*(false)" ) or consume (" ^%s*(null)" )
48+ or array ()
49+ or tonumber (consume (" ^%s*(%-?%d+[eE][%+%-]?%d+)" )) -- Exponential number.
50+ or slowstring ()
51+ end
52+
53+ function object ()
54+ if consume (" ^%s*{" ) then
55+ local fields = {}
56+ if consume (" ^%s*}" ) then return fields end
57+
58+ repeat
59+ local key = faststring ()
60+ if not key then
61+ error (" Expected field for object at char " .. ptr )
62+ end
63+
64+ if not consume (" ^%s*:" ) then
65+ error (" Expected : to follow key for object at char " .. ptr )
66+ end
67+
68+ local val = value ()
69+ if val then
70+ fields [key ] = val
71+ else
72+ error (" Expected value for field " .. key .. " at char " .. ptr )
73+ end
74+
75+ consume (" ^%s*," )
76+ until consume (" ^%s*}" )
77+
78+ return fields
79+ end
80+ end
81+
82+ function array ()
83+ if consume (" ^%s*%[" ) then
84+ local values , nvalues = {}, 0
85+ if consume (" ^%s*%]" ) then return values end
86+
87+ repeat
88+ nvalues = nvalues + 1
89+ local value = value ()
90+ if value then
91+ values [nvalues ] = value
92+ else
93+ error (" Expected value for field #" .. nvalues + 1 .. " at char " .. ptr )
94+ end
95+ consume (" ^%s*," )
96+ until consume (" ^%s*%]" )
97+
98+ return values
99+ end
100+ end
101+
102+ return object ()
103+ end
104+
105+ local concat , tostring , pairs = table.concat , tostring , pairs
106+ local function isarray (t )
107+ local i = 1
108+ for k in pairs (t ) do
109+ if type (k ) ~= " number" or k ~= i then
110+ return false
111+ end
112+ i = i + 1
113+ end
114+ return true
115+ end
116+
117+ local encode
118+ local function value (v )
119+ local t = type (v )
120+ if t == " table" then
121+ return encode (v )
122+ elseif t == " string" then
123+ return " \" " .. v .. " \" "
124+ else
125+ return tostring (v )
126+ end
127+ end
128+
129+ function encode (tbl --[[ @param tbl table]] ) --- @return string
130+ if isarray (tbl ) then
131+ local strs , len = {}, # tbl
132+ for i = 1 , len do
133+ strs [i ] = value (tbl [i ])
134+ end
135+ return " [" .. concat (strs , " ," , 1 , len ) .. " ]"
136+ else
137+ local kvs , nkvs = {}, 0
138+ for k , v in pairs (tbl ) do
139+ nkvs = nkvs + 1
140+ kvs [nkvs ] = " \" " .. tostring (k ) .. " \" " .. " : " .. value (v )
141+ end
142+ return " {" .. concat (kvs , " ," , 1 , nkvs ) .. " }" ;
143+ end
144+ end
145+
146+ return {
147+ encode = encode ,
148+ decode = decode
149+ }
0 commit comments