Rework LuCI build system
[project/luci.git] / libs / luci-lib-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 Decoder:
16         Info:
17                 null will be decoded to luci.json.null if first parameter of Decoder() is true
18
19         Example:
20                 decoder = luci.json.Decoder()
21                 luci.ltn12.pump.all(luci.ltn12.source.string("decodableJSON"), decoder:sink())
22                 luci.util.dumptable(decoder:get())
23
24         Known issues:
25                 does not support unicode conversion \uXXYY with XX != 00 will be ignored
26
27
28 Encoder:
29         Info:
30                 Accepts numbers, strings, nil, booleans as they are
31                 Accepts luci.json.null as replacement for nil
32                 Accepts full associative and full numerically indexed tables
33                 Mixed tables will loose their associative values during conversion
34                 Iterator functions will be encoded as an array of their return values
35                 Non-iterator functions will probably corrupt the encoder
36
37         Example:
38                 encoder = luci.json.Encoder(encodableData)
39                 luci.ltn12.pump.all(encoder:source(), luci.ltn12.sink.file(io.open("someFile", w)))
40 ]]--
41
42 local nixio     = require "nixio"
43 local util      = require "luci.util"
44 local table     = require "table"
45 local string    = require "string"
46 local coroutine = require "coroutine"
47
48 local assert    = assert
49 local tonumber  = tonumber
50 local tostring  = tostring
51 local error     = error
52 local type          = type
53 local pairs         = pairs
54 local ipairs    = ipairs
55 local next      = next
56 local pcall             = pcall
57
58 local band      = nixio.bit.band
59 local bor       = nixio.bit.bor
60 local rshift    = nixio.bit.rshift
61 local char      = string.char
62
63 local getmetatable = getmetatable
64
65 --- LuCI JSON-Library
66 -- @cstyle      instance
67 module "luci.json"
68
69
70 --- Directly decode a JSON string
71 -- @param json JSON-String
72 -- @return Lua object
73 function decode(json, ...)
74         local a = ActiveDecoder(function() return nil end, ...)
75         a.chunk = json
76         local s, obj = pcall(a.get, a)
77         return s and obj or nil
78 end
79
80
81 --- Direcly encode a Lua object into a JSON string.
82 -- @param obj Lua Object
83 -- @return JSON string
84 function encode(obj, ...)
85         local out = {}
86         local e = Encoder(obj, 1, ...):source()
87         local chnk, err
88         repeat
89                 chnk, err = e()
90                 out[#out+1] = chnk
91         until not chnk
92         return not err and table.concat(out) or nil
93 end
94
95
96 --- Null replacement function
97 -- @return null
98 function null()
99         return null
100 end
101
102 --- Create a new JSON-Encoder.
103 -- @class       function
104 -- @name        Encoder
105 -- @param data                  Lua-Object to be encoded.
106 -- @param buffersize    Blocksize of returned data source.
107 -- @param fastescape    Use non-standard escaping (don't escape control chars)
108 -- @return JSON-Encoder
109 Encoder = util.class()
110
111 function Encoder.__init__(self, data, buffersize, fastescape)
112         self.data = data
113         self.buffersize = buffersize or 512
114         self.buffer = ""
115         self.fastescape = fastescape
116
117         getmetatable(self).__call = Encoder.source
118 end
119
120 --- Create an LTN12 source providing the encoded JSON-Data.
121 -- @return LTN12 source
122 function Encoder.source(self)
123         local source = coroutine.create(self.dispatch)
124         return function()
125                 local res, data = coroutine.resume(source, self, self.data, true)
126                 if res then
127                         return data
128                 else
129                         return nil, data
130                 end
131         end
132 end
133
134 function Encoder.dispatch(self, data, start)
135         local parser = self.parsers[type(data)]
136
137         parser(self, data)
138
139         if start then
140                 if #self.buffer > 0 then
141                         coroutine.yield(self.buffer)
142                 end
143
144                 coroutine.yield()
145         end
146 end
147
148 function Encoder.put(self, chunk)
149         if self.buffersize < 2 then
150                 coroutine.yield(chunk)
151         else
152                 if #self.buffer + #chunk > self.buffersize then
153                         local written = 0
154                         local fbuffer = self.buffersize - #self.buffer
155
156                         coroutine.yield(self.buffer .. chunk:sub(written + 1, fbuffer))
157                         written = fbuffer
158
159                         while #chunk - written > self.buffersize do
160                                 fbuffer = written + self.buffersize
161                                 coroutine.yield(chunk:sub(written + 1, fbuffer))
162                                 written = fbuffer
163                         end
164
165                         self.buffer = chunk:sub(written + 1)
166                 else
167                         self.buffer = self.buffer .. chunk
168                 end
169         end
170 end
171
172 function Encoder.parse_nil(self)
173         self:put("null")
174 end
175
176 function Encoder.parse_bool(self, obj)
177         self:put(obj and "true" or "false")
178 end
179
180 function Encoder.parse_number(self, obj)
181         self:put(tostring(obj))
182 end
183
184 function Encoder.parse_string(self, obj)
185         if self.fastescape then
186                 self:put('"' .. obj:gsub('\\', '\\\\'):gsub('"', '\\"') .. '"')
187         else
188                 self:put('"' ..
189                         obj:gsub('[%c\\"]',
190                                 function(char)
191                                         return '\\u00%02x' % char:byte()
192                                 end
193                         )
194                 .. '"')
195         end
196 end
197
198 function Encoder.parse_iter(self, obj)
199         if obj == null then
200                 return self:put("null")
201         end
202
203         if type(obj) == "table" and (#obj == 0 and next(obj)) then
204                 self:put("{")
205                 local first = true
206
207                 for key, entry in pairs(obj) do
208                         first = first or self:put(",")
209                         first = first and false
210                         self:parse_string(tostring(key))
211                         self:put(":")
212                         self:dispatch(entry)
213                 end
214
215                 self:put("}")
216         else
217                 self:put("[")
218                 local first = true
219
220                 if type(obj) == "table" then
221                         for i=1, #obj do
222                                 first = first or self:put(",")
223                                 first = first and nil
224                                 self:dispatch(obj[i])
225                         end
226                 else
227                         for entry in obj do
228                                 first = first or self:put(",")
229                                 first = first and nil
230                                 self:dispatch(entry)
231                         end
232                 end
233
234                 self:put("]")
235         end
236 end
237
238 Encoder.parsers = {
239         ['nil']      = Encoder.parse_nil,
240         ['table']    = Encoder.parse_iter,
241         ['number']   = Encoder.parse_number,
242         ['string']   = Encoder.parse_string,
243         ['boolean']  = Encoder.parse_bool,
244         ['function'] = Encoder.parse_iter
245 }
246
247
248 --- Create a new JSON-Decoder.
249 -- @class       function
250 -- @name        Decoder
251 -- @param customnull Use luci.json.null instead of nil for decoding null
252 -- @return JSON-Decoder
253 Decoder = util.class()
254
255 function Decoder.__init__(self, customnull)
256         self.cnull = customnull
257         getmetatable(self).__call = Decoder.sink
258 end
259
260 --- Create an LTN12 sink from the decoder object which accepts the JSON-Data.
261 -- @return LTN12 sink
262 function Decoder.sink(self)
263         local sink = coroutine.create(self.dispatch)
264         return function(...)
265                 return coroutine.resume(sink, self, ...)
266         end
267 end
268
269
270 --- Get the decoded data packets after the rawdata has been sent to the sink.
271 -- @return Decoded data
272 function Decoder.get(self)
273         return self.data
274 end
275
276 function Decoder.dispatch(self, chunk, src_err, strict)
277         local robject, object
278         local oset = false
279
280         while chunk do
281                 while chunk and #chunk < 1 do
282                         chunk = self:fetch()
283                 end
284
285                 assert(not strict or chunk, "Unexpected EOS")
286                 if not chunk then break end
287
288                 local char   = chunk:sub(1, 1)
289                 local parser = self.parsers[char]
290                  or (char:match("%s")     and self.parse_space)
291                  or (char:match("[0-9-]") and self.parse_number)
292                  or error("Unexpected char '%s'" % char)
293
294                 chunk, robject = parser(self, chunk)
295
296                 if parser ~= self.parse_space then
297                         assert(not oset, "Scope violation: Too many objects")
298                         object = robject
299                         oset = true
300
301                         if strict then
302                                 return chunk, object
303                         end
304                 end
305         end
306
307         assert(not src_err, src_err)
308         assert(oset, "Unexpected EOS")
309
310         self.data = object
311 end
312
313
314 function Decoder.fetch(self)
315         local tself, chunk, src_err = coroutine.yield()
316         assert(chunk or not src_err, src_err)
317         return chunk
318 end
319
320
321 function Decoder.fetch_atleast(self, chunk, bytes)
322         while #chunk < bytes do
323                 local nchunk = self:fetch()
324                 assert(nchunk, "Unexpected EOS")
325                 chunk = chunk .. nchunk
326         end
327
328         return chunk
329 end
330
331
332 function Decoder.fetch_until(self, chunk, pattern)
333         local start = chunk:find(pattern)
334
335         while not start do
336                 local nchunk = self:fetch()
337                 assert(nchunk, "Unexpected EOS")
338                 chunk = chunk .. nchunk
339                 start = chunk:find(pattern)
340         end
341
342         return chunk, start
343 end
344
345
346 function Decoder.parse_space(self, chunk)
347         local start = chunk:find("[^%s]")
348
349         while not start do
350                 chunk = self:fetch()
351                 if not chunk then
352                         return nil
353                 end
354                 start = chunk:find("[^%s]")
355         end
356
357         return chunk:sub(start)
358 end
359
360
361 function Decoder.parse_literal(self, chunk, literal, value)
362         chunk = self:fetch_atleast(chunk, #literal)
363         assert(chunk:sub(1, #literal) == literal, "Invalid character sequence")
364         return chunk:sub(#literal + 1), value
365 end
366
367
368 function Decoder.parse_null(self, chunk)
369         return self:parse_literal(chunk, "null", self.cnull and null)
370 end
371
372
373 function Decoder.parse_true(self, chunk)
374         return self:parse_literal(chunk, "true", true)
375 end
376
377
378 function Decoder.parse_false(self, chunk)
379         return self:parse_literal(chunk, "false", false)
380 end
381
382
383 function Decoder.parse_number(self, chunk)
384         local chunk, start = self:fetch_until(chunk, "[^0-9eE.+-]")
385         local number = tonumber(chunk:sub(1, start - 1))
386         assert(number, "Invalid number specification")
387         return chunk:sub(start), number
388 end
389
390
391 function Decoder.parse_string(self, chunk)
392         local str = ""
393         local object = nil
394         assert(chunk:sub(1, 1) == '"', 'Expected "')
395         chunk = chunk:sub(2)
396
397         while true do
398                 local spos = chunk:find('[\\"]')
399                 if spos then
400                         str = str .. chunk:sub(1, spos - 1)
401
402                         local char = chunk:sub(spos, spos)
403                         if char == '"' then                             -- String end
404                                 chunk = chunk:sub(spos + 1)
405                                 break
406                         elseif char == "\\" then                -- Escape sequence
407                                 chunk, object = self:parse_escape(chunk:sub(spos))
408                                 str = str .. object
409                         end
410                 else
411                         str = str .. chunk
412                         chunk = self:fetch()
413                         assert(chunk, "Unexpected EOS while parsing a string")
414                 end
415         end
416
417         return chunk, str
418 end
419
420
421 function Decoder.utf8_encode(self, s1, s2)
422         local n = s1 * 256 + s2
423
424         if n >= 0 and n <= 0x7F then
425                 return char(n)
426         elseif n >= 0 and n <= 0x7FF then
427                 return char(
428                         bor(band(rshift(n,  6), 0x1F), 0xC0),
429                         bor(band(n,             0x3F), 0x80)
430                 )
431         elseif n >= 0 and n <= 0xFFFF then
432                 return char(
433                         bor(band(rshift(n, 12), 0x0F), 0xE0),
434                         bor(band(rshift(n,  6), 0x3F), 0x80),
435                         bor(band(n,             0x3F), 0x80)
436                 )
437         elseif n >= 0 and n <= 0x10FFFF then
438                 return char(
439                         bor(band(rshift(n, 18), 0x07), 0xF0),
440                         bor(band(rshift(n, 12), 0x3F), 0x80),
441                         bor(band(rshift(n,  6), 0x3F), 0x80),
442                         bor(band(n,             0x3F), 0x80)
443                 )
444         else
445                 return "?"
446         end
447 end
448
449
450 function Decoder.parse_escape(self, chunk)
451         local str = ""
452         chunk = self:fetch_atleast(chunk:sub(2), 1)
453         local char = chunk:sub(1, 1)
454         chunk = chunk:sub(2)
455
456         if char == '"' then
457                 return chunk, '"'
458         elseif char == "\\" then
459                 return chunk, "\\"
460         elseif char == "u" then
461                 chunk = self:fetch_atleast(chunk, 4)
462                 local s1, s2 = chunk:sub(1, 2), chunk:sub(3, 4)
463                 s1, s2 = tonumber(s1, 16), tonumber(s2, 16)
464                 assert(s1 and s2, "Invalid Unicode character")
465
466                 return chunk:sub(5), self:utf8_encode(s1, s2)
467         elseif char == "/" then
468                 return chunk, "/"
469         elseif char == "b" then
470                 return chunk, "\b"
471         elseif char == "f" then
472                 return chunk, "\f"
473         elseif char == "n" then
474                 return chunk, "\n"
475         elseif char == "r" then
476                 return chunk, "\r"
477         elseif char == "t" then
478                 return chunk, "\t"
479         else
480                 error("Unexpected escaping sequence '\\%s'" % char)
481         end
482 end
483
484
485 function Decoder.parse_array(self, chunk)
486         chunk = chunk:sub(2)
487         local array = {}
488         local nextp = 1
489
490         local chunk, object = self:parse_delimiter(chunk, "%]")
491
492         if object then
493                 return chunk, array
494         end
495
496         repeat
497                 chunk, object = self:dispatch(chunk, nil, true)
498                 table.insert(array, nextp, object)
499                 nextp = nextp + 1
500
501                 chunk, object = self:parse_delimiter(chunk, ",%]")
502                 assert(object, "Delimiter expected")
503         until object == "]"
504
505         return chunk, array
506 end
507
508
509 function Decoder.parse_object(self, chunk)
510         chunk = chunk:sub(2)
511         local array = {}
512         local name
513
514         local chunk, object = self:parse_delimiter(chunk, "}")
515
516         if object then
517                 return chunk, array
518         end
519
520         repeat
521                 chunk = self:parse_space(chunk)
522                 assert(chunk, "Unexpected EOS")
523
524                 chunk, name   = self:parse_string(chunk)
525
526                 chunk, object = self:parse_delimiter(chunk, ":")
527                 assert(object, "Separator expected")
528
529                 chunk, object = self:dispatch(chunk, nil, true)
530                 array[name] = object
531
532                 chunk, object = self:parse_delimiter(chunk, ",}")
533                 assert(object, "Delimiter expected")
534         until object == "}"
535
536         return chunk, array
537 end
538
539
540 function Decoder.parse_delimiter(self, chunk, delimiter)
541         while true do
542                 chunk = self:fetch_atleast(chunk, 1)
543                 local char = chunk:sub(1, 1)
544                 if char:match("%s") then
545                         chunk = self:parse_space(chunk)
546                         assert(chunk, "Unexpected EOS")
547                 elseif char:match("[%s]" % delimiter) then
548                         return chunk:sub(2), char
549                 else
550                         return chunk, nil
551                 end
552         end
553 end
554
555
556 Decoder.parsers = {
557         ['"'] = Decoder.parse_string,
558         ['t'] = Decoder.parse_true,
559         ['f'] = Decoder.parse_false,
560         ['n'] = Decoder.parse_null,
561         ['['] = Decoder.parse_array,
562         ['{'] = Decoder.parse_object
563 }
564
565
566 --- Create a new Active JSON-Decoder.
567 -- @class       function
568 -- @name        ActiveDecoder
569 -- @param   customnull  Use luci.json.null instead of nil for decoding null
570 -- @return  Active JSON-Decoder
571 ActiveDecoder = util.class(Decoder)
572
573 function ActiveDecoder.__init__(self, source, customnull)
574         Decoder.__init__(self, customnull)
575         self.source = source
576         self.chunk = nil
577         getmetatable(self).__call = self.get
578 end
579
580
581 --- Fetches one JSON-object from given source
582 -- @return Decoded object
583 function ActiveDecoder.get(self)
584         local chunk, src_err, object
585         if not self.chunk then
586                 chunk, src_err = self.source()
587         else
588                 chunk = self.chunk
589         end
590
591         self.chunk, object = self:dispatch(chunk, src_err, true)
592         return object
593 end
594
595
596 function ActiveDecoder.fetch(self)
597         local chunk, src_err = self.source()
598         assert(chunk or not src_err, src_err)
599         return chunk
600 end