Calame

json.lua

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