2 LuCI - Lua Configuration Interface
4 Copyright 2008 Steven Barth <steven@midlink.org>
5 Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
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
11 http://www.apache.org/licenses/LICENSE-2.0
16 local util = require "luci.util"
17 local ltn12 = require "luci.ltn12"
18 local table = require "table"
19 local coroutine = require "coroutine"
22 local tonumber = tonumber
27 --- Null replacement function
33 Decoder = util.class()
35 --- Create an LTN12 sink from the decoder object
37 function Decoder.sink(self)
38 local sink = coroutine.create(self.dispatch)
40 return coroutine.resume(sink, self, ...)
45 --- Get the decoded data packets
46 -- @return Decoded data
47 function Decoder.get(self)
52 function Decoder.dispatch(self, chunk, src_err, strict)
60 assert(not strict or chunk, "Unexpected EOS")
66 local char = chunk:sub(1, 1)
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
87 chunk, robject = parser(self, chunk)
89 if robject ~= nil then
90 assert(object == nil, "Scope violation: Too many objects")
94 if strict and object ~= nil then
98 error("Unexpected char '%s'" % char)
102 assert(not src_err, src_err)
103 assert(object ~= nil, "Unexpected EOS")
110 function Decoder.fetch(self)
111 local tself, chunk, src_err = coroutine.yield()
112 assert(chunk or not src_err, src_err)
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
128 function Decoder.fetch_until(self, chunk, pattern)
129 local start = chunk:find(pattern)
132 local nchunk = self:fetch()
133 assert(nchunk, "Unexpected EOS")
134 chunk = chunk .. nchunk
135 start = chunk:find(pattern)
142 function Decoder.parse_space(self, chunk)
143 local start = chunk:find("[^%s]")
150 start = chunk:find("[^%s]")
153 return chunk:sub(start)
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
164 function Decoder.parse_null(self, chunk)
165 return self:parse_literal(chunk, "null", null)
169 function Decoder.parse_true(self, chunk)
170 return self:parse_literal(chunk, "true", true)
174 function Decoder.parse_false(self, chunk)
175 return self:parse_literal(chunk, "false", false)
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
187 function Decoder.parse_string(self, chunk)
190 assert(chunk:sub(1, 1) == '"', 'Expected "')
194 local spos = chunk:find('[\\"]')
196 str = str .. chunk:sub(1, spos - 1)
198 local char = chunk:sub(spos, spos)
199 if char == '"' then -- String end
200 chunk = chunk:sub(spos + 1)
202 elseif char == "\\" then -- Escape sequence
203 chunk, object = self:parse_escape(chunk:sub(spos))
209 assert(chunk, "Unexpected EOS while parsing a string")
217 function Decoder.parse_escape(self, chunk)
219 chunk = self:fetch_atleast(chunk:sub(2), 1)
220 local char = chunk:sub(1, 1)
225 elseif char == "\\" then
227 elseif char == "/" then
229 elseif char == "b" then
231 elseif char == "f" then
233 elseif char == "n" then
235 elseif char == "r" then
237 elseif char == "t" then
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)
245 -- ToDo: Unicode support
246 return chunk:sub(5), s1 == 0 and s2 or ""
248 error("Unexpected escaping sequence '\\%s'" % char)
253 function Decoder.parse_array(self, chunk)
257 local chunk, object = self:parse_delimiter(chunk, "%]")
264 chunk, object = self:dispatch(chunk, nil, true)
265 table.insert(array, object)
267 chunk, object = self:parse_delimiter(chunk, ",%]")
268 assert(object, "Delimiter expected")
275 function Decoder.parse_object(self, chunk)
280 local chunk, object = self:parse_delimiter(chunk, "}")
287 chunk = self:parse_space(chunk)
288 assert(chunk, "Unexpected EOS")
290 chunk, name = self:parse_string(chunk)
292 chunk, object = self:parse_delimiter(chunk, ":")
293 assert(object, "Separator expected")
295 chunk, object = self:dispatch(chunk, nil, true)
298 chunk, object = self:parse_delimiter(chunk, ",}")
299 assert(object, "Delimiter expected")
306 function Decoder.parse_delimiter(self, chunk, delimiter)
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