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