392 lines
8.1 KiB
Lua
392 lines
8.1 KiB
Lua
local t = { }
|
|
local assert = assert
|
|
local Null
|
|
Null = function()
|
|
return Null
|
|
end
|
|
local StringBuilder = {
|
|
buffer = { }
|
|
}
|
|
StringBuilder.New = function(self)
|
|
local o = setmetatable({ }, self)
|
|
self.__index = self
|
|
o.buffer = { }
|
|
return o
|
|
end
|
|
StringBuilder.Append = function(self, s)
|
|
do
|
|
local _obj_0 = self.buffer
|
|
_obj_0[#_obj_0 + 1] = s
|
|
end
|
|
end
|
|
StringBuilder.ToString = function(self)
|
|
return table.concat(self.buffer)
|
|
end
|
|
local JsonWriter = {
|
|
backslashes = {
|
|
["\b"] = "\\b",
|
|
["\t"] = "\\t",
|
|
["\n"] = "\\n",
|
|
["\f"] = "\\f",
|
|
["\r"] = "\\r",
|
|
['"'] = '\\"',
|
|
["\\"] = "\\\\",
|
|
["/"] = "\\/"
|
|
}
|
|
}
|
|
JsonWriter.New = function(self)
|
|
local o = setmetatable({ }, self)
|
|
o.writer = StringBuilder:New()
|
|
self.__index = self
|
|
return o
|
|
end
|
|
JsonWriter.Append = function(self, s)
|
|
return self.writer:Append(s)
|
|
end
|
|
JsonWriter.ToString = function(self)
|
|
return self.writer:ToString()
|
|
end
|
|
JsonWriter.Write = function(self, o)
|
|
local _exp_0 = type(o)
|
|
if "nil" == _exp_0 then
|
|
return self:WriteNil()
|
|
elseif "boolean" == _exp_0 or "number" == _exp_0 then
|
|
return self:WriteString(o)
|
|
elseif "string" == _exp_0 then
|
|
return self:ParseString(o)
|
|
elseif "table" == _exp_0 then
|
|
return self:WriteTable(o)
|
|
elseif "function" == _exp_0 then
|
|
return self:WriteFunction(o)
|
|
elseif "thread" == _exp_0 or "userdata" == _exp_0 then
|
|
return self:WriteError(o)
|
|
end
|
|
end
|
|
JsonWriter.WriteNil = function(self)
|
|
return self:Append("null")
|
|
end
|
|
JsonWriter.WriteString = function(self, o)
|
|
return self:Append(tostring(o))
|
|
end
|
|
JsonWriter.ParseString = function(self, 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))
|
|
return self:Append('"')
|
|
end
|
|
JsonWriter.IsArray = function(self, t)
|
|
local count = 0
|
|
local isindex
|
|
isindex = function(k)
|
|
if type(k) == "number" and k > 0 and math.floor(k) == k then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
for k, _ in pairs(t) do
|
|
if not isindex(k) then
|
|
return false, "{", "}"
|
|
else
|
|
count = math.max(count, k)
|
|
end
|
|
end
|
|
return true, "[", "]", count
|
|
end
|
|
JsonWriter.WriteTable = function(self, 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
|
|
return self:Append(et)
|
|
end
|
|
JsonWriter.WriteError = function(self, o)
|
|
return error(string.format("Encoding of %s unsupported", tostring(o)))
|
|
end
|
|
JsonWriter.WriteFunction = function(self, o)
|
|
if o == Null then
|
|
return self:WriteNil()
|
|
else
|
|
return self:WriteError(o)
|
|
end
|
|
end
|
|
local StringReader = {
|
|
s = "",
|
|
i = 0
|
|
}
|
|
StringReader.New = function(self, s)
|
|
local o = setmetatable({ }, self)
|
|
self.__index = self
|
|
o.s = s or o.s
|
|
return o
|
|
end
|
|
StringReader.Peek = function(self)
|
|
local i = self.i + 1
|
|
if i <= #self.s then
|
|
return string.sub(self.s, i, i)
|
|
end
|
|
return nil
|
|
end
|
|
StringReader.Next = function(self)
|
|
self.i = self.i + 1
|
|
if self.i <= #self.s then
|
|
return string.sub(self.s, self.i, self.i)
|
|
end
|
|
return nil
|
|
end
|
|
StringReader.All = function(self)
|
|
return self.s
|
|
end
|
|
local JsonReader = {
|
|
escapes = {
|
|
["t"] = "\t",
|
|
["n"] = "\n",
|
|
["f"] = "\f",
|
|
["r"] = "\r",
|
|
["b"] = "\b"
|
|
}
|
|
}
|
|
JsonReader.New = function(self, s)
|
|
local o = setmetatable({ }, self)
|
|
o.reader = StringReader:New(s)
|
|
self.__index = self
|
|
return o
|
|
end
|
|
JsonReader.Read = function(self)
|
|
self:SkipWhiteSpace()
|
|
local peek = self:Peek()
|
|
if not (peek ~= nil) then
|
|
return 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
|
|
return nil
|
|
end
|
|
end
|
|
JsonReader.ReadTrue = function(self)
|
|
self:TestReservedWord({
|
|
"t",
|
|
"r",
|
|
"u",
|
|
"e"
|
|
})
|
|
return true
|
|
end
|
|
JsonReader.ReadFalse = function(self)
|
|
self:TestReservedWord({
|
|
"f",
|
|
"a",
|
|
"l",
|
|
"s",
|
|
"e"
|
|
})
|
|
return false
|
|
end
|
|
JsonReader.ReadNull = function(self)
|
|
self:TestReservedWord({
|
|
"n",
|
|
"u",
|
|
"l",
|
|
"l"
|
|
})
|
|
return nil
|
|
end
|
|
JsonReader.TestReservedWord = function(self, t)
|
|
for _, 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
|
|
JsonReader.ReadNumber = function(self)
|
|
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 not (result ~= nil) then
|
|
return error(string.format("Invalid number: '%s'", result))
|
|
else
|
|
return result
|
|
end
|
|
end
|
|
JsonReader.ReadString = function(self)
|
|
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
|
|
fromunicode = function(m)
|
|
return string.char(tonumber(m, 16))
|
|
end
|
|
return string.gsub(result, "u%x%x(%x%x)", fromunicode)
|
|
end
|
|
JsonReader.ReadComment = function(self)
|
|
assert(self:Next() == "/")
|
|
local second = self:Next()
|
|
if second == "/" then
|
|
return self:ReadSingleLineComment()
|
|
elseif second == "*" then
|
|
return self:ReadBlockComment()
|
|
else
|
|
return error(string.format("Invalid comment: %s", self:All()))
|
|
end
|
|
end
|
|
JsonReader.ReadBlockComment = function(self)
|
|
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
|
|
return self:Next()
|
|
end
|
|
JsonReader.ReadSingleLineComment = function(self)
|
|
local ch = self:Next()
|
|
while ch ~= "\r" and ch ~= "\n" do
|
|
ch = self:Next()
|
|
end
|
|
end
|
|
JsonReader.ReadArray = function(self)
|
|
local result = { }
|
|
assert(self:Next() == "[")
|
|
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
|
|
JsonReader.ReadObject = function(self)
|
|
local result = { }
|
|
assert(self:Next() == "{")
|
|
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
|
|
JsonReader.SkipWhiteSpace = function(self)
|
|
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
|
|
JsonReader.Peek = function(self)
|
|
return self.reader:Peek()
|
|
end
|
|
JsonReader.Next = function(self)
|
|
return self.reader:Next()
|
|
end
|
|
JsonReader.All = function(self)
|
|
return self.reader:All()
|
|
end
|
|
local Encode
|
|
Encode = function(o)
|
|
local _with_0 = JsonWriter:New()
|
|
_with_0:Write(o)
|
|
_with_0:ToString()
|
|
return _with_0
|
|
end
|
|
local Decode
|
|
Decode = function(s)
|
|
local _with_0 = JsonReader:New(s)
|
|
_with_0:Read()
|
|
return _with_0
|
|
end
|