libs/core: Reworked some basic libraries to not use package.seeall
[project/luci.git] / libs / json / luasrc / json.lua
1 --[[
2 LuCI - Lua Configuration Interface
3
4 Copyright 2008 Steven Barth <steven@midlink.org>
5 Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14 ]]--
15
16 local util = require "luci.util"
17 local ltn12 = require "luci.ltn12"
18 local table = require "table"
19 local coroutine = require "coroutine"
20
21 local assert = assert
22 local tonumber = tonumber
23 local error = error
24
25 module "luci.json"
26
27 --- Null replacement function
28 -- @return null
29 function null()
30 return null
31 end
32
33 Decoder = util.class()
34
35 --- Create an LTN12 sink from the decoder object
36 -- @return LTN12 sink
37 function Decoder.sink(self)
38 local sink = coroutine.create(self.dispatch)
39 return function(...)
40 return coroutine.resume(sink, self, ...)
41 end
42 end
43
44
45 --- Get the decoded data packets
46 -- @return Decoded data
47 function Decoder.get(self)
48 return self.data
49 end
50
51
52 function Decoder.dispatch(self, chunk, src_err, strict)
53 local robject, object
54
55 while chunk do
56 if #chunk < 1 then
57 chunk = self:fetch()
58 end
59
60 assert(not strict or chunk, "Unexpected EOS")
61 if not chunk then
62 break
63 end
64
65 local parser = nil
66 local char = chunk:sub(1, 1)
67
68 if char == '"' then
69 parser = self.parse_string
70 elseif char == 't' then
71 parser = self.parse_true
72 elseif char == 'f' then
73 parser = self.parse_false
74 elseif char == 'n' then
75 parser = self.parse_null
76 elseif char == '[' then
77 parser = self.parse_array
78 elseif char == '{' then
79 parser = self.parse_object
80 elseif char:match("%s") then
81 parser = self.parse_space
82 elseif char:match("[0-9-]") then
83 parser = self.parse_number
84 end
85
86 if parser then
87 chunk, robject = parser(self, chunk)
88
89 if robject ~= nil then
90 assert(object == nil, "Scope violation: Too many objects")
91 object = robject
92 end
93
94 if strict and object ~= nil then
95 return chunk, object
96 end
97 else
98 error("Unexpected char '%s'" % char)
99 end
100 end
101
102 assert(not src_err, src_err)
103 assert(object ~= nil, "Unexpected EOS")
104
105 self.data = object
106 return chunk, object
107 end
108
109
110 function Decoder.fetch(self)
111 local tself, chunk, src_err = coroutine.yield()
112 assert(chunk or not src_err, src_err)
113 return chunk
114 end
115
116
117 function Decoder.fetch_atleast(self, chunk, bytes)
118 while #chunk < bytes do
119 local nchunk = self:fetch()
120 assert(nchunk, "Unexpected EOS")
121 chunk = chunk .. nchunk
122 end
123
124 return chunk
125 end
126
127
128 function Decoder.fetch_until(self, chunk, pattern)
129 local start = chunk:find(pattern)
130
131 while not start do
132 local nchunk = self:fetch()
133 assert(nchunk, "Unexpected EOS")
134 chunk = chunk .. nchunk
135 start = chunk:find(pattern)
136 end
137
138 return chunk, start
139 end
140
141
142 function Decoder.parse_space(self, chunk)
143 local start = chunk:find("[^%s]")
144
145 while not start do
146 chunk = self:fetch()
147 if not chunk then
148 return nil
149 end
150 start = chunk:find("[^%s]")
151 end
152
153 return chunk:sub(start)
154 end
155
156
157 function Decoder.parse_literal(self, chunk, literal, value)
158 chunk = self:fetch_atleast(chunk, #literal)
159 assert(chunk:sub(1, #literal) == literal, "Invalid character sequence")
160 return chunk:sub(#literal + 1), value
161 end
162
163
164 function Decoder.parse_null(self, chunk)
165 return self:parse_literal(chunk, "null", null)
166 end
167
168
169 function Decoder.parse_true(self, chunk)
170 return self:parse_literal(chunk, "true", true)
171 end
172
173
174 function Decoder.parse_false(self, chunk)
175 return self:parse_literal(chunk, "false", false)
176 end
177
178
179 function Decoder.parse_number(self, chunk)
180 local chunk, start = self:fetch_until(chunk, "[^0-9eE.+-]")
181 local number = tonumber(chunk:sub(1, start - 1))
182 assert(number, "Invalid number specification")
183 return chunk:sub(start), number
184 end
185
186
187 function Decoder.parse_string(self, chunk)
188 local str = ""
189 local object = nil
190 assert(chunk:sub(1, 1) == '"', 'Expected "')
191 chunk = chunk:sub(2)
192
193 while true do
194 local spos = chunk:find('[\\"]')
195 if spos then
196 str = str .. chunk:sub(1, spos - 1)
197
198 local char = chunk:sub(spos, spos)
199 if char == '"' then -- String end
200 chunk = chunk:sub(spos + 1)
201 break
202 elseif char == "\\" then -- Escape sequence
203 chunk, object = self:parse_escape(chunk:sub(spos))
204 str = str .. object
205 end
206 else
207 str = str .. chunk
208 chunk = self:fetch()
209 assert(chunk, "Unexpected EOS while parsing a string")
210 end
211 end
212
213 return chunk, str
214 end
215
216
217 function Decoder.parse_escape(self, chunk)
218 local str = ""
219 chunk = self:fetch_atleast(chunk:sub(2), 1)
220 local char = chunk:sub(1, 1)
221 chunk = chunk:sub(2)
222
223 if char == '"' then
224 return chunk, '"'
225 elseif char == "\\" then
226 return chunk, "\\"
227 elseif char == "/" then
228 return chunk, "/"
229 elseif char == "b" then
230 return chunk, "\b"
231 elseif char == "f" then
232 return chunk, "\f"
233 elseif char == "n" then
234 return chunk, "\n"
235 elseif char == "r" then
236 return chunk, "\r"
237 elseif char == "t" then
238 return chunk, "\t"
239 elseif char == "u" then
240 chunk = self:fetch_atleast(chunk, 4)
241 local s1, s2 = chunk:sub(1, 4):match("^([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])$")
242 assert(s1 and s2, "Invalid Unicode character 'U+%s%s'" % {s1, s2})
243 s1, s2 = tonumber(s1, 16), tonumber(s2, 16)
244
245 -- ToDo: Unicode support
246 return chunk:sub(5), s1 == 0 and s2 or ""
247 else
248 error("Unexpected escaping sequence '\\%s'" % char)
249 end
250 end
251
252
253 function Decoder.parse_array(self, chunk)
254 chunk = chunk:sub(2)
255 local array = {}
256
257 local chunk, object = self:parse_delimiter(chunk, "%]")
258
259 if object then
260 return chunk, array
261 end
262
263 repeat
264 chunk, object = self:dispatch(chunk, nil, true)
265 table.insert(array, object)
266
267 chunk, object = self:parse_delimiter(chunk, ",%]")
268 assert(object, "Delimiter expected")
269 until object == "]"
270
271 return chunk, array
272 end
273
274
275 function Decoder.parse_object(self, chunk)
276 chunk = chunk:sub(2)
277 local array = {}
278 local name
279
280 local chunk, object = self:parse_delimiter(chunk, "}")
281
282 if object then
283 return chunk, array
284 end
285
286 repeat
287 chunk = self:parse_space(chunk)
288 assert(chunk, "Unexpected EOS")
289
290 chunk, name = self:parse_string(chunk)
291
292 chunk, object = self:parse_delimiter(chunk, ":")
293 assert(object, "Separator expected")
294
295 chunk, object = self:dispatch(chunk, nil, true)
296 array[name] = object
297
298 chunk, object = self:parse_delimiter(chunk, ",}")
299 assert(object, "Delimiter expected")
300 until object == "}"
301
302 return chunk, array
303 end
304
305
306 function Decoder.parse_delimiter(self, chunk, delimiter)
307 while true do
308 chunk = self:fetch_atleast(chunk, 1)
309 local char = chunk:sub(1, 1)
310 if char:match("%s") then
311 chunk = self:parse_space(chunk)
312 assert(chunk, "Unexpected EOS")
313 elseif char:match("[%s]" % delimiter) then
314 return chunk:sub(2), char
315 else
316 return chunk, nil
317 end
318 end
319 end