Advertisement
Belzebub

lib/json.lua

Mar 25th, 2021
689
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.83 KB | None | 0 0
  1. -- Source: https://github.com/rxi/json.lua
  2.  
  3. --
  4. -- json.lua
  5. --
  6. -- Copyright (c) 2020 rxi
  7. --
  8. -- Permission is hereby granted, free of charge, to any person obtaining a copy of
  9. -- this software and associated documentation files (the "Software"), to deal in
  10. -- the Software without restriction, including without limitation the rights to
  11. -- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  12. -- of the Software, and to permit persons to whom the Software is furnished to do
  13. -- so, subject to the following conditions:
  14. --
  15. -- The above copyright notice and this permission notice shall be included in all
  16. -- copies or substantial portions of the Software.
  17. --
  18. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  24. -- SOFTWARE.
  25. --
  26.  
  27. local json = { _version = "0.1.2" }
  28.  
  29. -------------------------------------------------------------------------------
  30. -- Encode
  31. -------------------------------------------------------------------------------
  32.  
  33. local encode
  34.  
  35. local escape_char_map = {
  36.   [ "\\" ] = "\\",
  37.   [ "\"" ] = "\"",
  38.   [ "\b" ] = "b",
  39.   [ "\f" ] = "f",
  40.   [ "\n" ] = "n",
  41.   [ "\r" ] = "r",
  42.   [ "\t" ] = "t",
  43. }
  44.  
  45. local escape_char_map_inv = { [ "/" ] = "/" }
  46. for k, v in pairs(escape_char_map) do
  47.   escape_char_map_inv[v] = k
  48. end
  49.  
  50.  
  51. local function escape_char(c)
  52.   return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
  53. end
  54.  
  55.  
  56. local function encode_nil(val)
  57.   return "null"
  58. end
  59.  
  60.  
  61. local function encode_table(val, stack)
  62.   local res = {}
  63.   stack = stack or {}
  64.  
  65.   -- Circular reference?
  66.   if stack[val] then error("circular reference") end
  67.  
  68.   stack[val] = true
  69.  
  70.   if rawget(val, 1) ~= nil or next(val) == nil then
  71.     -- Treat as array -- check keys are valid and it is not sparse
  72.     local n = 0
  73.     for k in pairs(val) do
  74.       if type(k) ~= "number" then
  75.         error("invalid table: mixed or invalid key types")
  76.       end
  77.       n = n + 1
  78.     end
  79.     if n ~= #val then
  80.       error("invalid table: sparse array")
  81.     end
  82.     -- Encode
  83.     for i, v in ipairs(val) do
  84.       table.insert(res, encode(v, stack))
  85.     end
  86.     stack[val] = nil
  87.     return "[" .. table.concat(res, ",") .. "]"
  88.  
  89.   else
  90.     -- Treat as an object
  91.     for k, v in pairs(val) do
  92.       if type(k) ~= "string" then
  93.         error("invalid table: mixed or invalid key types")
  94.       end
  95.       table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
  96.     end
  97.     stack[val] = nil
  98.     return "{" .. table.concat(res, ",") .. "}"
  99.   end
  100. end
  101.  
  102.  
  103. local function encode_string(val)
  104.   return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
  105. end
  106.  
  107.  
  108. local function encode_number(val)
  109.   -- Check for NaN, -inf and inf
  110.   if val ~= val or val <= -math.huge or val >= math.huge then
  111.     error("unexpected number value '" .. tostring(val) .. "'")
  112.   end
  113.   return string.format("%.14g", val)
  114. end
  115.  
  116.  
  117. local type_func_map = {
  118.   [ "nil"     ] = encode_nil,
  119.   [ "table"   ] = encode_table,
  120.   [ "string"  ] = encode_string,
  121.   [ "number"  ] = encode_number,
  122.   [ "boolean" ] = tostring,
  123. }
  124.  
  125.  
  126. encode = function(val, stack)
  127.   local t = type(val)
  128.   local f = type_func_map[t]
  129.   if f then
  130.     return f(val, stack)
  131.   end
  132.   error("unexpected type '" .. t .. "'")
  133. end
  134.  
  135.  
  136. function json.encode(val)
  137.   return ( encode(val) )
  138. end
  139.  
  140.  
  141. -------------------------------------------------------------------------------
  142. -- Decode
  143. -------------------------------------------------------------------------------
  144.  
  145. local parse
  146.  
  147. local function create_set(...)
  148.   local res = {}
  149.   for i = 1, select("#", ...) do
  150.     res[ select(i, ...) ] = true
  151.   end
  152.   return res
  153. end
  154.  
  155. local space_chars   = create_set(" ", "\t", "\r", "\n")
  156. local delim_chars   = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
  157. local escape_chars  = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
  158. local literals      = create_set("true", "false", "null")
  159.  
  160. local literal_map = {
  161.   [ "true"  ] = true,
  162.   [ "false" ] = false,
  163.   [ "null"  ] = nil,
  164. }
  165.  
  166.  
  167. local function next_char(str, idx, set, negate)
  168.   for i = idx, #str do
  169.     if set[str:sub(i, i)] ~= negate then
  170.       return i
  171.     end
  172.   end
  173.   return #str + 1
  174. end
  175.  
  176.  
  177. local function decode_error(str, idx, msg)
  178.   local line_count = 1
  179.   local col_count = 1
  180.   for i = 1, idx - 1 do
  181.     col_count = col_count + 1
  182.     if str:sub(i, i) == "\n" then
  183.       line_count = line_count + 1
  184.       col_count = 1
  185.     end
  186.   end
  187.   error( string.format("%s at line %d col %d", msg, line_count, col_count) )
  188. end
  189.  
  190.  
  191. local function codepoint_to_utf8(n)
  192.   -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
  193.   local f = math.floor
  194.   if n <= 0x7f then
  195.     return string.char(n)
  196.   elseif n <= 0x7ff then
  197.     return string.char(f(n / 64) + 192, n % 64 + 128)
  198.   elseif n <= 0xffff then
  199.     return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
  200.   elseif n <= 0x10ffff then
  201.     return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
  202.                        f(n % 4096 / 64) + 128, n % 64 + 128)
  203.   end
  204.   error( string.format("invalid unicode codepoint '%x'", n) )
  205. end
  206.  
  207.  
  208. local function parse_unicode_escape(s)
  209.   local n1 = tonumber( s:sub(1, 4),  16 )
  210.   local n2 = tonumber( s:sub(7, 10), 16 )
  211.    -- Surrogate pair?
  212.   if n2 then
  213.     return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
  214.   else
  215.     return codepoint_to_utf8(n1)
  216.   end
  217. end
  218.  
  219.  
  220. local function parse_string(str, i)
  221.   local res = ""
  222.   local j = i + 1
  223.   local k = j
  224.  
  225.   while j <= #str do
  226.     local x = str:byte(j)
  227.  
  228.     if x < 32 then
  229.       decode_error(str, j, "control character in string")
  230.  
  231.     elseif x == 92 then -- `\`: Escape
  232.       res = res .. str:sub(k, j - 1)
  233.       j = j + 1
  234.       local c = str:sub(j, j)
  235.       if c == "u" then
  236.         local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
  237.                  or str:match("^%x%x%x%x", j + 1)
  238.                  or decode_error(str, j - 1, "invalid unicode escape in string")
  239.         res = res .. parse_unicode_escape(hex)
  240.         j = j + #hex
  241.       else
  242.         if not escape_chars[c] then
  243.           decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
  244.         end
  245.         res = res .. escape_char_map_inv[c]
  246.       end
  247.       k = j + 1
  248.  
  249.     elseif x == 34 then -- `"`: End of string
  250.       res = res .. str:sub(k, j - 1)
  251.       return res, j + 1
  252.     end
  253.  
  254.     j = j + 1
  255.   end
  256.  
  257.   decode_error(str, i, "expected closing quote for string")
  258. end
  259.  
  260.  
  261. local function parse_number(str, i)
  262.   local x = next_char(str, i, delim_chars)
  263.   local s = str:sub(i, x - 1)
  264.   local n = tonumber(s)
  265.   if not n then
  266.     decode_error(str, i, "invalid number '" .. s .. "'")
  267.   end
  268.   return n, x
  269. end
  270.  
  271.  
  272. local function parse_literal(str, i)
  273.   local x = next_char(str, i, delim_chars)
  274.   local word = str:sub(i, x - 1)
  275.   if not literals[word] then
  276.     decode_error(str, i, "invalid literal '" .. word .. "'")
  277.   end
  278.   return literal_map[word], x
  279. end
  280.  
  281.  
  282. local function parse_array(str, i)
  283.   local res = {}
  284.   local n = 1
  285.   i = i + 1
  286.   while 1 do
  287.     local x
  288.     i = next_char(str, i, space_chars, true)
  289.     -- Empty / end of array?
  290.     if str:sub(i, i) == "]" then
  291.       i = i + 1
  292.       break
  293.     end
  294.     -- Read token
  295.     x, i = parse(str, i)
  296.     res[n] = x
  297.     n = n + 1
  298.     -- Next token
  299.     i = next_char(str, i, space_chars, true)
  300.     local chr = str:sub(i, i)
  301.     i = i + 1
  302.     if chr == "]" then break end
  303.     if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
  304.   end
  305.   return res, i
  306. end
  307.  
  308.  
  309. local function parse_object(str, i)
  310.   local res = {}
  311.   i = i + 1
  312.   while 1 do
  313.     local key, val
  314.     i = next_char(str, i, space_chars, true)
  315.     -- Empty / end of object?
  316.     if str:sub(i, i) == "}" then
  317.       i = i + 1
  318.       break
  319.     end
  320.     -- Read key
  321.     if str:sub(i, i) ~= '"' then
  322.       decode_error(str, i, "expected string for key")
  323.     end
  324.     key, i = parse(str, i)
  325.     -- Read ':' delimiter
  326.     i = next_char(str, i, space_chars, true)
  327.     if str:sub(i, i) ~= ":" then
  328.       decode_error(str, i, "expected ':' after key")
  329.     end
  330.     i = next_char(str, i + 1, space_chars, true)
  331.     -- Read value
  332.     val, i = parse(str, i)
  333.     -- Set
  334.     res[key] = val
  335.     -- Next token
  336.     i = next_char(str, i, space_chars, true)
  337.     local chr = str:sub(i, i)
  338.     i = i + 1
  339.     if chr == "}" then break end
  340.     if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
  341.   end
  342.   return res, i
  343. end
  344.  
  345.  
  346. local char_func_map = {
  347.   [ '"' ] = parse_string,
  348.   [ "0" ] = parse_number,
  349.   [ "1" ] = parse_number,
  350.   [ "2" ] = parse_number,
  351.   [ "3" ] = parse_number,
  352.   [ "4" ] = parse_number,
  353.   [ "5" ] = parse_number,
  354.   [ "6" ] = parse_number,
  355.   [ "7" ] = parse_number,
  356.   [ "8" ] = parse_number,
  357.   [ "9" ] = parse_number,
  358.   [ "-" ] = parse_number,
  359.   [ "t" ] = parse_literal,
  360.   [ "f" ] = parse_literal,
  361.   [ "n" ] = parse_literal,
  362.   [ "[" ] = parse_array,
  363.   [ "{" ] = parse_object,
  364. }
  365.  
  366.  
  367. parse = function(str, idx)
  368.   local chr = str:sub(idx, idx)
  369.   local f = char_func_map[chr]
  370.   if f then
  371.     return f(str, idx)
  372.   end
  373.   decode_error(str, idx, "unexpected character '" .. chr .. "'")
  374. end
  375.  
  376.  
  377. function json.decode(str)
  378.   if type(str) ~= "string" then
  379.     error("expected argument of type string, got " .. type(str))
  380.   end
  381.   local res, idx = parse(str, next_char(str, 1, space_chars, true))
  382.   idx = next_char(str, idx, space_chars, true)
  383.   if idx <= #str then
  384.     decode_error(str, idx, "trailing garbage")
  385.   end
  386.   return res
  387. end
  388.  
  389.  
  390. return json
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement