libs/rpc: Created new library
authorSteven Barth <steven@midlink.org>
Thu, 21 Aug 2008 19:13:45 +0000 (19:13 +0000)
committerSteven Barth <steven@midlink.org>
Thu, 21 Aug 2008 19:13:45 +0000 (19:13 +0000)
libs/rpc/Makefile [new file with mode: 0644]
libs/rpc/luasrc/Json.lua [new file with mode: 0644]

diff --git a/libs/rpc/Makefile b/libs/rpc/Makefile
new file mode 100644 (file)
index 0000000..f7fac77
--- /dev/null
@@ -0,0 +1,2 @@
+include ../../build/config.mk
+include ../../build/module.mk
diff --git a/libs/rpc/luasrc/Json.lua b/libs/rpc/luasrc/Json.lua
new file mode 100644 (file)
index 0000000..1dbc65c
--- /dev/null
@@ -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("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