diff options
author | Steven Barth <steven@midlink.org> | 2008-08-22 20:04:49 +0000 |
---|---|---|
committer | Steven Barth <steven@midlink.org> | 2008-08-22 20:04:49 +0000 |
commit | 03c0b0ad120c83f80d94e1e4233a6b4bd6eaf3e9 (patch) | |
tree | 5942fe3525fdf26a36f14a847c58f4032445bfa8 /libs/json | |
parent | 4eead17501584a09679841d75d451b19a7c234d0 (diff) |
RPC part #3
Diffstat (limited to 'libs/json')
-rw-r--r-- | libs/json/Makefile | 2 | ||||
-rw-r--r-- | libs/json/luasrc/json.lua | 519 |
2 files changed, 521 insertions, 0 deletions
diff --git a/libs/json/Makefile b/libs/json/Makefile new file mode 100644 index 000000000..f7fac7740 --- /dev/null +++ b/libs/json/Makefile @@ -0,0 +1,2 @@ +include ../../build/config.mk +include ../../build/module.mk diff --git a/libs/json/luasrc/json.lua b/libs/json/luasrc/json.lua new file mode 100644 index 000000000..85b85e1e8 --- /dev/null +++ b/libs/json/luasrc/json.lua @@ -0,0 +1,519 @@ +--[[ + + JSON Encoder and Parser for Lua 5.1 + + Copyright � 2007 Shaun Brown (http://www.chipmunkav.com). + All Rights Reserved. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + If you find this software useful please give www.chipmunkav.com a mention. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Usage: + + -- Lua script: + local t = { + ["name1"] = "value1", + ["name2"] = {1, false, true, 23.54, "a \021 string"}, + name3 = Json.Null() + } + + local json = Json.Encode (t) + print (json) + --> {"name1":"value1","name3":null,"name2":[1,false,true,23.54,"a \u0015 string"]} + + local t = Json.Decode(json) + print(t.name2[4]) + --> 23.54 + + Notes: + 1) Encodable Lua types: string, number, boolean, table, nil + 2) Use Json.Null() to insert a null value into a Json object + 3) All control chars are encoded to \uXXXX format eg "\021" encodes to "\u0015" + 4) All Json \uXXXX chars are decoded to chars (0-255 byte range only) + 5) Json single line // and /* */ block comments are discarded during decoding + 6) Numerically indexed Lua arrays are encoded to Json Lists eg [1,2,3] + 7) Lua dictionary tables are converted to Json objects eg {"one":1,"two":2} + 8) Json nulls are decoded to Lua nil and treated by Lua in the normal way + +--]] + +local string = string +local math = math +local table = table +local error = error +local tonumber = tonumber +local tostring = tostring +local type = type +local setmetatable = setmetatable +local pairs = pairs +local ipairs = ipairs +local assert = assert +local Chipmunk = Chipmunk + +module("luci.json") + +local StringBuilder = { + buffer = {} +} + +function StringBuilder:New() + local o = {} + setmetatable(o, self) + self.__index = self + o.buffer = {} + return o +end + +function StringBuilder:Append(s) + self.buffer[#self.buffer+1] = s +end + +function StringBuilder:ToString() + return table.concat(self.buffer) +end + +local JsonWriter = { + backslashes = { + ['\b'] = "\\b", + ['\t'] = "\\t", + ['\n'] = "\\n", + ['\f'] = "\\f", + ['\r'] = "\\r", + ['"'] = "\\\"", + ['\\'] = "\\\\", + ['/'] = "\\/" + } +} + +function JsonWriter:New() + local o = {} + o.writer = StringBuilder:New() + setmetatable(o, self) + self.__index = self + return o +end + +function JsonWriter:Append(s) + self.writer:Append(s) +end + +function JsonWriter:ToString() + return self.writer:ToString() +end + +function JsonWriter:Write(o) + local t = type(o) + if t == "nil" then + self:WriteNil() + elseif t == "boolean" then + self:WriteString(o) + elseif t == "number" then + self:WriteString(o) + elseif t == "string" then + self:ParseString(o) + elseif t == "table" then + self:WriteTable(o) + elseif t == "function" then + self:WriteFunction(o) + elseif t == "thread" then + self:WriteError(o) + elseif t == "userdata" then + self:WriteError(o) + end +end + +function JsonWriter:WriteNil() + self:Append("null") +end + +function JsonWriter:WriteString(o) + self:Append(tostring(o)) +end + +function JsonWriter:ParseString(s) + self:Append('"') + self:Append(string.gsub(s, "[%z%c\\\"/]", function(n) + local c = self.backslashes[n] + if c then return c end + return string.format("\\u%.4X", string.byte(n)) + end)) + self:Append('"') +end + +function JsonWriter:IsArray(t) + local count = 0 + local isindex = function(k) + if type(k) == "number" and k > 0 then + if math.floor(k) == k then + return true + end + end + return false + end + for k,v in pairs(t) do + if not isindex(k) then + return false, '{', '}' + else + count = math.max(count, k) + end + end + return true, '[', ']', count +end + +function JsonWriter:WriteTable(t) + local ba, st, et, n = self:IsArray(t) + self:Append(st) + if ba then + for i = 1, n do + self:Write(t[i]) + if i < n then + self:Append(',') + end + end + else + local first = true; + for k, v in pairs(t) do + if not first then + self:Append(',') + end + first = false; + self:ParseString(k) + self:Append(':') + self:Write(v) + end + end + self:Append(et) +end + +function JsonWriter:WriteError(o) + error(string.format( + "Encoding of %s unsupported", + tostring(o))) +end + +function JsonWriter:WriteFunction(o) + if o == Null then + self:WriteNil() + else + self:WriteError(o) + end +end + +local StringReader = { + s = "", + i = 0 +} + +function StringReader:New(s) + local o = {} + setmetatable(o, self) + self.__index = self + o.s = s or o.s + return o +end + +function StringReader:Peek() + local i = self.i + 1 + if i <= #self.s then + return string.sub(self.s, i, i) + end + return nil +end + +function StringReader:Next() + self.i = self.i+1 + if self.i <= #self.s then + return string.sub(self.s, self.i, self.i) + end + return nil +end + +function StringReader:All() + return self.s +end + +local JsonReader = { + escapes = { + ['t'] = '\t', + ['n'] = '\n', + ['f'] = '\f', + ['r'] = '\r', + ['b'] = '\b', + } +} + +function JsonReader:New(s) + local o = {} + o.reader = StringReader:New(s) + setmetatable(o, self) + self.__index = self + return o; +end + +function JsonReader:Read() + self:SkipWhiteSpace() + local peek = self:Peek() + if peek == nil then + error(string.format( + "Nil string: '%s'", + self:All())) + elseif peek == '{' then + return self:ReadObject() + elseif peek == '[' then + return self:ReadArray() + elseif peek == '"' then + return self:ReadString() + elseif string.find(peek, "[%+%-%d]") then + return self:ReadNumber() + elseif peek == 't' then + return self:ReadTrue() + elseif peek == 'f' then + return self:ReadFalse() + elseif peek == 'n' then + return self:ReadNull() + elseif peek == '/' then + self:ReadComment() + return self:Read() + else + error(string.format( + "Invalid input: '%s'", + self:All())) + end +end + +function JsonReader:ReadTrue() + self:TestReservedWord{'t','r','u','e'} + return true +end + +function JsonReader:ReadFalse() + self:TestReservedWord{'f','a','l','s','e'} + return false +end + +function JsonReader:ReadNull() + self:TestReservedWord{'n','u','l','l'} + return nil +end + +function JsonReader:TestReservedWord(t) + for i, v in ipairs(t) do + if self:Next() ~= v then + error(string.format( + "Error reading '%s': %s", + table.concat(t), + self:All())) + end + end +end + +function JsonReader:ReadNumber() + local result = self:Next() + local peek = self:Peek() + while peek ~= nil and string.find( + peek, + "[%+%-%d%.eE]") do + result = result .. self:Next() + peek = self:Peek() + end + result = tonumber(result) + if result == nil then + error(string.format( + "Invalid number: '%s'", + result)) + else + return result + end +end + +function JsonReader:ReadString() + local result = "" + assert(self:Next() == '"') + while self:Peek() ~= '"' do + local ch = self:Next() + if ch == '\\' then + ch = self:Next() + if self.escapes[ch] then + ch = self.escapes[ch] + end + end + result = result .. ch + end + assert(self:Next() == '"') + local fromunicode = function(m) + return string.char(tonumber(m, 16)) + end + return string.gsub( + result, + "u%x%x(%x%x)", + fromunicode) +end + +function JsonReader:ReadComment() + assert(self:Next() == '/') + local second = self:Next() + if second == '/' then + self:ReadSingleLineComment() + elseif second == '*' then + self:ReadBlockComment() + else + error(string.format( + "Invalid comment: %s", + self:All())) + end +end + +function JsonReader:ReadBlockComment() + local done = false + while not done do + local ch = self:Next() + if ch == '*' and self:Peek() == '/' then + done = true + end + if not done and + ch == '/' and + self:Peek() == "*" then + error(string.format( + "Invalid comment: %s, '/*' illegal.", + self:All())) + end + end + self:Next() +end + +function JsonReader:ReadSingleLineComment() + local ch = self:Next() + while ch ~= '\r' and ch ~= '\n' do + ch = self:Next() + end +end + +function JsonReader:ReadArray() + local result = {} + assert(self:Next() == '[') + self:SkipWhiteSpace() + local done = false + if self:Peek() == ']' then + done = true; + end + while not done do + local item = self:Read() + result[#result+1] = item + self:SkipWhiteSpace() + if self:Peek() == ']' then + done = true + end + if not done then + local ch = self:Next() + if ch ~= ',' then + error(string.format( + "Invalid array: '%s' due to: '%s'", + self:All(), ch)) + end + end + end + assert(']' == self:Next()) + return result +end + +function JsonReader:ReadObject() + local result = {} + assert(self:Next() == '{') + self:SkipWhiteSpace() + local done = false + if self:Peek() == '}' then + done = true + end + while not done do + local key = self:Read() + if type(key) ~= "string" then + error(string.format( + "Invalid non-string object key: %s", + key)) + end + self:SkipWhiteSpace() + local ch = self:Next() + if ch ~= ':' then + error(string.format( + "Invalid object: '%s' due to: '%s'", + self:All(), + ch)) + end + self:SkipWhiteSpace() + local val = self:Read() + result[key] = val + self:SkipWhiteSpace() + if self:Peek() == '}' then + done = true + end + if not done then + ch = self:Next() + if ch ~= ',' then + error(string.format( + "Invalid array: '%s' near: '%s'", + self:All(), + ch)) + end + end + end + assert(self:Next() == "}") + return result +end + +function JsonReader:SkipWhiteSpace() + local p = self:Peek() + while p ~= nil and string.find(p, "[%s/]") do + if p == '/' then + self:ReadComment() + else + self:Next() + end + p = self:Peek() + end +end + +function JsonReader:Peek() + return self.reader:Peek() +end + +function JsonReader:Next() + return self.reader:Next() +end + +function JsonReader:All() + return self.reader:All() +end + +function Encode(o) + local writer = JsonWriter:New() + writer:Write(o) + return writer:ToString() +end + +function Decode(s) + local reader = JsonReader:New(s) + local object = reader:Read() + reader:SkipWhiteSpace() + assert(reader:Peek() == nil, "Invalid characters after JSON body") + return object +end + +function Null() + return Null +end |