conky-config/core/func/json.lua

246 lines
6.6 KiB
Lua

local c = {}
local _STRING_GSUB = string.gsub
local _STRING_CHAR = string.char
local _STRING_FIND = string.find
local _STRING_SUB = string.sub
local _TABLE_CONCAT = table.concat
local _MATH_FLOOR = math.floor
local _PAIRS = pairs
local _TONUMBER = tonumber
local decode -- to ref this before definition
local decode_scanWhitespace = function(s, startPos)
local whitespace = " \n\r\t"
local stringLen = #s
while (_STRING_FIND(whitespace, _STRING_SUB(s, startPos, startPos), 1, true) and
startPos <= stringLen) do
startPos = startPos + 1
end
return startPos
end
local decode_scanArray = function(s, startPos)
local array = {}
local stringLen = #s
--~ assert(_STRING_SUB(s, startPos, startPos) == '[',
--~ 'decode_scanArray called but array does not start at position ' .. startPos ..
--~ ' in string:\n'..s )
startPos = startPos + 1
repeat
startPos = decode_scanWhitespace(s, startPos)
--~ assert(startPos <= stringLen, 'JSON String ended unexpectedly scanning array.')
local curChar = _STRING_SUB(s,startPos,startPos)
if (curChar == ']') then
return array, startPos + 1
end
if (curChar == ',') then
startPos = decode_scanWhitespace(s, startPos + 1)
end
--~ assert(startPos <= stringLen, 'JSON String ended unexpectedly scanning array.')
object, startPos = decode(s, startPos)
array[#array + 1] = object
until false
end
local decode_scanComment = function(s, startPos)
--~ assert(_STRING_SUB(s, startPos, startPos + 1) == '/*',
--~ "decode_scanComment called but comment does not start at position " .. startPos)
local endPos = _STRING_FIND(s, '*/', startPos + 2)
--~ assert(endPos ~= nil, "Unterminated comment in string at " .. startPos)
return endPos + 2
end
local decode_scanConstant = function(s, startPos)
local consts = {["true"] = true, ["false"] = false, ["null"] = nil}
local constNames = {"true", "false", "null"}
for _, k in _PAIRS(constNames) do
if _STRING_SUB(s, startPos, startPos + #k - 1 ) == k then
return consts[k], startPos + #k
end
end
--~ assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' ..
--~ startPos)
end
local decode_scanNumber = function(s, startPos)
local endPos = startPos + 1
local stringLen = #s
local acceptableChars = "+-0123456789.e"
while (_STRING_FIND(acceptableChars, _STRING_SUB(s, endPos, endPos), 1, true)
and endPos <= stringLen) do
endPos = endPos + 1
end
local numberString = _STRING_GSUB(_STRING_SUB(s, startPos, endPos - 1), '+', '')
return _TONUMBER(numberString), endPos
end
local decode_scanObject = function(s, startPos)
local object = {}
local stringLen = #s
local key, value
--~ assert(_STRING_SUB(s, startPos, startPos) == '{',
--~ 'decode_scanObject called but object does not start at position ' .. startPos ..
--~ ' in string:\n' .. s)
startPos = startPos + 1
repeat
startPos = decode_scanWhitespace(s, startPos)
--~ assert(startPos <= stringLen, 'JSON string ended unexpectedly while scanning object.')
local curChar = _STRING_SUB(s, startPos, startPos)
if (curChar == '}') then
return object, startPos + 1
end
if (curChar == ',') then
startPos = decode_scanWhitespace(s, startPos + 1)
end
--~ assert(startPos <= stringLen, 'JSON string ended unexpectedly scanning object.')
-- Scan the key
key, startPos = decode(s, startPos)
--~ assert(startPos <= stringLen,
--~ 'JSON string ended unexpectedly searching for value of key ' .. key)
startPos = decode_scanWhitespace(s, startPos)
--~ assert(startPos <= stringLen,
--~ 'JSON string ended unexpectedly searching for value of key ' .. key)
--~ assert(_STRING_SUB(s, startPos, startPos) == ':',
--~ 'JSON object key-value assignment mal-formed at ' .. startPos)
startPos = decode_scanWhitespace(s, startPos + 1)
--~ assert(startPos <= stringLen,
--~ 'JSON string ended unexpectedly searching for value of key ' .. key)
value, startPos = decode(s, startPos)
object[key] = value
until false
end
local escapeSequences = {
["\\t"] = "\t",
["\\f"] = "\f",
["\\r"] = "\r",
["\\n"] = "\n",
["\\b"] = "\b"
}
setmetatable(escapeSequences, {__index = function(t, k) return _STRING_SUB(k, 2) end})--skip "\"
local decode_scanString = function (s, startPos)
--~ assert(startPos, 'decode_scanString(..) called without start position')
local startChar = _STRING_SUB(s, startPos, startPos)
--~ assert(startChar == [["]] or startChar == [[']],
--~ 'decode_scanString called for a non-string')
local t = {}
local i, j = startPos, startPos
while _STRING_FIND(s, startChar, j + 1) ~= j + 1 do
local oldj = j
local x, y = _STRING_FIND(s, startChar, oldj + 1)
i, j = _STRING_FIND(s, "\\.", j + 1)
if not i or x < i then i, j = x, y - 1 end
--~ table.insert(t, _STRING_SUB(s, oldj + 1, i - 1))
t[#t + 1] = _STRING_SUB(s, oldj + 1, i - 1)
if _STRING_SUB(s, i, j) == "\\u" then
local a = _STRING_SUB(s, j + 1, j + 4)
local n = _TONUMBER(a, 16)
local x
j = j + 4
--~ assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " ..
--~ i .. " : " .. j)
if n < 0x80 then
x = _STRING_CHAR(n % 0x80)
elseif n < 0x800 then
x = _STRING_CHAR(0xC0 + (_MATH_FLOOR(n / 64) % 0x20), 0x80 + (n % 0x40))
else
x = _STRING_CHAR(0xE0 + (_MATH_FLOOR(n / 4096) % 0x10), 0x80 +
(_MATH_FLOOR(n / 64) % 0x40), 0x80 + (n % 0x40))
end
--~ table.insert(t, x)
t[#t + 1] = x
else
--~ table.insert(t, escapeSequences[_STRING_SUB(s, i, j)])
t[#t + 1] = escapeSequences[_STRING_SUB(s, i, j)]
end
end
--~ table.insert(t, _STRING_SUB(j, j + 1))
t[#t + 1] = _STRING_SUB(j, j + 1)
--~ assert(_STRING_FIND(s, startChar, j + 1), "String decoding failed: missing closing " ..
--~ startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")")
return _TABLE_CONCAT(t, ""), j + 2
end
decode = function(s, startPos)
startPos = startPos or 1
startPos = decode_scanWhitespace(s, startPos)
--~ assert(startPos <= #s,
--~ 'Unterminated JSON encoded object found at position in [' .. s .. ']')
local curChar = _STRING_SUB(s, startPos, startPos)
if curChar == '{' then
return decode_scanObject(s, startPos)
end
if curChar == '[' then
return decode_scanArray(s, startPos)
end
if _STRING_FIND("+-0123456789.e", curChar, 1, true) then
return decode_scanNumber(s, startPos)
end
if curChar == [["]] or curChar == [[']] then
return decode_scanString(s, startPos)
end
if _STRING_SUB(s, startPos, startPos + 1) == '/*' then
return decode(s, decode_scanComment(s, startPos))
end
return decode_scanConstant(s, startPos)
end
c.decode = decode
return c