MrKey2b

json

Mar 28th, 2018
123
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 22.58 KB | None | 0 0
  1. -- Module options:
  2. local always_try_using_lpeg = true
  3. local register_global_module_table = false
  4. local global_module_name = 'json'
  5.  
  6. --[==[
  7.  
  8. David Kolf's JSON module for Lua 5.1/5.2
  9.  
  10. Version 2.5
  11.  
  12.  
  13. For the documentation see the corresponding readme.txt or visit
  14. <http://dkolf.de/src/dkjson-lua.fsl/>.
  15.  
  16. You can contact the author by sending an e-mail to 'david' at the
  17. domain 'dkolf.de'.
  18.  
  19.  
  20. Copyright (C) 2010-2013 David Heiko Kolf
  21.  
  22. Permission is hereby granted, free of charge, to any person obtaining
  23. a copy of this software and associated documentation files (the
  24. "Software"), to deal in the Software without restriction, including
  25. without limitation the rights to use, copy, modify, merge, publish,
  26. distribute, sublicense, and/or sell copies of the Software, and to
  27. permit persons to whom the Software is furnished to do so, subject to
  28. the following conditions:
  29.  
  30. The above copyright notice and this permission notice shall be
  31. included in all copies or substantial portions of the Software.
  32.  
  33. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  34. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  35. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  36. NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  37. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  38. ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  39. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  40. SOFTWARE.
  41.  
  42. --]==]
  43.  
  44. -- global dependencies:
  45. local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset =
  46.       pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset
  47. local error, require, pcall, select = error, require, pcall, select
  48. local floor, huge = math.floor, math.huge
  49. local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat =
  50.       string.rep, string.gsub, string.sub, string.byte, string.char,
  51.       string.find, string.len, string.format
  52. local strmatch = string.match
  53. local concat = table.concat
  54.  
  55. local json = { version = "dkjson 2.5" }
  56.  
  57. if register_global_module_table then
  58.   _G[global_module_name] = json
  59. end
  60.  
  61. local _ENV = nil -- blocking globals in Lua 5.2
  62.  
  63. pcall (function()
  64.   -- Enable access to blocked metatables.
  65.   -- Don't worry, this module doesn't change anything in them.
  66.   local debmeta = require "debug".getmetatable
  67.   if debmeta then getmetatable = debmeta end
  68. end)
  69.  
  70. json.null = setmetatable ({}, {
  71.   __tojson = function () return "null" end
  72. })
  73.  
  74. local function isarray (tbl)
  75.   local max, n, arraylen = 0, 0, 0
  76.   for k,v in pairs (tbl) do
  77.     if k == 'n' and type(v) == 'number' then
  78.       arraylen = v
  79.       if v > max then
  80.         max = v
  81.       end
  82.     else
  83.       if type(k) ~= 'number' or k < 1 or floor(k) ~= k then
  84.         return false
  85.       end
  86.       if k > max then
  87.         max = k
  88.       end
  89.       n = n + 1
  90.     end
  91.   end
  92.   if max > 10 and max > arraylen and max > n * 2 then
  93.     return false -- don't create an array with too many holes
  94.   end
  95.   return true, max
  96. end
  97.  
  98. local escapecodes = {
  99.   ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f",
  100.   ["\n"] = "\\n",  ["\r"] = "\\r",  ["\t"] = "\\t"
  101. }
  102.  
  103. local function escapeutf8 (uchar)
  104.   local value = escapecodes[uchar]
  105.   if value then
  106.     return value
  107.   end
  108.   local a, b, c, d = strbyte (uchar, 1, 4)
  109.   a, b, c, d = a or 0, b or 0, c or 0, d or 0
  110.   if a <= 0x7f then
  111.     value = a
  112.   elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then
  113.     value = (a - 0xc0) * 0x40 + b - 0x80
  114.   elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then
  115.     value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80
  116.   elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then
  117.     value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80
  118.   else
  119.     return ""
  120.   end
  121.   if value <= 0xffff then
  122.     return strformat ("\\u%.4x", value)
  123.   elseif value <= 0x10ffff then
  124.     -- encode as UTF-16 surrogate pair
  125.     value = value - 0x10000
  126.     local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400)
  127.     return strformat ("\\u%.4x\\u%.4x", highsur, lowsur)
  128.   else
  129.     return ""
  130.   end
  131. end
  132.  
  133. local function fsub (str, pattern, repl)
  134.   -- gsub always builds a new string in a buffer, even when no match
  135.   -- exists. First using find should be more efficient when most strings
  136.   -- don't contain the pattern.
  137.   if strfind (str, pattern) then
  138.     return gsub (str, pattern, repl)
  139.   else
  140.     return str
  141.   end
  142. end
  143.  
  144. local function quotestring (value)
  145.   -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js
  146.   value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8)
  147.   if strfind (value, "[\194\216\220\225\226\239]") then
  148.     value = fsub (value, "\194[\128-\159\173]", escapeutf8)
  149.     value = fsub (value, "\216[\128-\132]", escapeutf8)
  150.     value = fsub (value, "\220\143", escapeutf8)
  151.     value = fsub (value, "\225\158[\180\181]", escapeutf8)
  152.     value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8)
  153.     value = fsub (value, "\226\129[\160-\175]", escapeutf8)
  154.     value = fsub (value, "\239\187\191", escapeutf8)
  155.     value = fsub (value, "\239\191[\176-\191]", escapeutf8)
  156.   end
  157.   return "\"" .. value .. "\""
  158. end
  159. json.quotestring = quotestring
  160.  
  161. local function replace(str, o, n)
  162.   local i, j = strfind (str, o, 1, true)
  163.   if i then
  164.     return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1)
  165.   else
  166.     return str
  167.   end
  168. end
  169.  
  170. -- locale independent num2str and str2num functions
  171. local decpoint, numfilter
  172.  
  173. local function updatedecpoint ()
  174.   decpoint = strmatch(tostring(0.5), "([^05+])")
  175.   -- build a filter that can be used to remove group separators
  176.   numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+"
  177. end
  178.  
  179. updatedecpoint()
  180.  
  181. local function num2str (num)
  182.   return replace(fsub(tostring(num), numfilter, ""), decpoint, ".")
  183. end
  184.  
  185. local function str2num (str)
  186.   local num = tonumber(replace(str, ".", decpoint))
  187.   if not num then
  188.     updatedecpoint()
  189.     num = tonumber(replace(str, ".", decpoint))
  190.   end
  191.   return num
  192. end
  193.  
  194. local function addnewline2 (level, buffer, buflen)
  195.   buffer[buflen+1] = "\n"
  196.   buffer[buflen+2] = strrep ("  ", level)
  197.   buflen = buflen + 2
  198.   return buflen
  199. end
  200.  
  201. function json.addnewline (state)
  202.   if state.indent then
  203.     state.bufferlen = addnewline2 (state.level or 0,
  204.                            state.buffer, state.bufferlen or #(state.buffer))
  205.   end
  206. end
  207.  
  208. local encode2 -- forward declaration
  209.  
  210. local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state)
  211.   local kt = type (key)
  212.   if kt ~= 'string' and kt ~= 'number' then
  213.     return nil, "type '" .. kt .. "' is not supported as a key by JSON."
  214.   end
  215.   if prev then
  216.     buflen = buflen + 1
  217.     buffer[buflen] = ","
  218.   end
  219.   if indent then
  220.     buflen = addnewline2 (level, buffer, buflen)
  221.   end
  222.   buffer[buflen+1] = quotestring (key)
  223.   buffer[buflen+2] = ":"
  224.   return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state)
  225. end
  226.  
  227. local function appendcustom(res, buffer, state)
  228.   local buflen = state.bufferlen
  229.   if type (res) == 'string' then
  230.     buflen = buflen + 1
  231.     buffer[buflen] = res
  232.   end
  233.   return buflen
  234. end
  235.  
  236. local function exception(reason, value, state, buffer, buflen, defaultmessage)
  237.   defaultmessage = defaultmessage or reason
  238.   local handler = state.exception
  239.   if not handler then
  240.     return nil, defaultmessage
  241.   else
  242.     state.bufferlen = buflen
  243.     local ret, msg = handler (reason, value, state, defaultmessage)
  244.     if not ret then return nil, msg or defaultmessage end
  245.     return appendcustom(ret, buffer, state)
  246.   end
  247. end
  248.  
  249. function json.encodeexception(reason, value, state, defaultmessage)
  250.   return quotestring("<" .. defaultmessage .. ">")
  251. end
  252.  
  253. encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state)
  254.   local valtype = type (value)
  255.   local valmeta = getmetatable (value)
  256.   valmeta = type (valmeta) == 'table' and valmeta -- only tables
  257.   local valtojson = valmeta and valmeta.__tojson
  258.   if valtojson then
  259.     if tables[value] then
  260.       return exception('reference cycle', value, state, buffer, buflen)
  261.     end
  262.     tables[value] = true
  263.     state.bufferlen = buflen
  264.     local ret, msg = valtojson (value, state)
  265.     if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end
  266.     tables[value] = nil
  267.     buflen = appendcustom(ret, buffer, state)
  268.   elseif value == nil then
  269.     buflen = buflen + 1
  270.     buffer[buflen] = "null"
  271.   elseif valtype == 'number' then
  272.     local s
  273.     if value ~= value or value >= huge or -value >= huge then
  274.       -- This is the behaviour of the original JSON implementation.
  275.       s = "null"
  276.     else
  277.       s = num2str (value)
  278.     end
  279.     buflen = buflen + 1
  280.     buffer[buflen] = s
  281.   elseif valtype == 'boolean' then
  282.     buflen = buflen + 1
  283.     buffer[buflen] = value and "true" or "false"
  284.   elseif valtype == 'string' then
  285.     buflen = buflen + 1
  286.     buffer[buflen] = quotestring (value)
  287.   elseif valtype == 'table' then
  288.     if tables[value] then
  289.       return exception('reference cycle', value, state, buffer, buflen)
  290.     end
  291.     tables[value] = true
  292.     level = level + 1
  293.     local isa, n = isarray (value)
  294.     if n == 0 and valmeta and valmeta.__jsontype == 'object' then
  295.       isa = false
  296.     end
  297.     local msg
  298.     if isa then -- JSON array
  299.       buflen = buflen + 1
  300.       buffer[buflen] = "["
  301.       for i = 1, n do
  302.         buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state)
  303.         if not buflen then return nil, msg end
  304.         if i < n then
  305.           buflen = buflen + 1
  306.           buffer[buflen] = ","
  307.         end
  308.       end
  309.       buflen = buflen + 1
  310.       buffer[buflen] = "]"
  311.     else -- JSON object
  312.       local prev = false
  313.       buflen = buflen + 1
  314.       buffer[buflen] = "{"
  315.       local order = valmeta and valmeta.__jsonorder or globalorder
  316.       if order then
  317.         local used = {}
  318.         n = #order
  319.         for i = 1, n do
  320.           local k = order[i]
  321.           local v = value[k]
  322.           if v then
  323.             used[k] = true
  324.             buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
  325.             prev = true -- add a seperator before the next element
  326.           end
  327.         end
  328.         for k,v in pairs (value) do
  329.           if not used[k] then
  330.             buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
  331.             if not buflen then return nil, msg end
  332.             prev = true -- add a seperator before the next element
  333.           end
  334.         end
  335.       else -- unordered
  336.         for k,v in pairs (value) do
  337.           buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
  338.           if not buflen then return nil, msg end
  339.           prev = true -- add a seperator before the next element
  340.         end
  341.       end
  342.       if indent then
  343.         buflen = addnewline2 (level - 1, buffer, buflen)
  344.       end
  345.       buflen = buflen + 1
  346.       buffer[buflen] = "}"
  347.     end
  348.     tables[value] = nil
  349.   else
  350.     return exception ('unsupported type', value, state, buffer, buflen,
  351.       "type '" .. valtype .. "' is not supported by JSON.")
  352.   end
  353.   return buflen
  354. end
  355.  
  356. function json.encode (value, state)
  357.   state = state or {}
  358.   local oldbuffer = state.buffer
  359.   local buffer = oldbuffer or {}
  360.   state.buffer = buffer
  361.   updatedecpoint()
  362.   local ret, msg = encode2 (value, state.indent, state.level or 0,
  363.                    buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state)
  364.   if not ret then
  365.     error (msg, 2)
  366.   elseif oldbuffer == buffer then
  367.     state.bufferlen = ret
  368.     return true
  369.   else
  370.     state.bufferlen = nil
  371.     state.buffer = nil
  372.     return concat (buffer)
  373.   end
  374. end
  375.  
  376. local function loc (str, where)
  377.   local line, pos, linepos = 1, 1, 0
  378.   while true do
  379.     pos = strfind (str, "\n", pos, true)
  380.     if pos and pos < where then
  381.       line = line + 1
  382.       linepos = pos
  383.       pos = pos + 1
  384.     else
  385.       break
  386.     end
  387.   end
  388.   return "line " .. line .. ", column " .. (where - linepos)
  389. end
  390.  
  391. local function unterminated (str, what, where)
  392.   return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where)
  393. end
  394.  
  395. local function scanwhite (str, pos)
  396.   while true do
  397.     pos = strfind (str, "%S", pos)
  398.     if not pos then return nil end
  399.     local sub2 = strsub (str, pos, pos + 1)
  400.     if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then
  401.       -- UTF-8 Byte Order Mark
  402.       pos = pos + 3
  403.     elseif sub2 == "//" then
  404.       pos = strfind (str, "[\n\r]", pos + 2)
  405.       if not pos then return nil end
  406.     elseif sub2 == "/*" then
  407.       pos = strfind (str, "*/", pos + 2)
  408.       if not pos then return nil end
  409.       pos = pos + 2
  410.     else
  411.       return pos
  412.     end
  413.   end
  414. end
  415.  
  416. local escapechars = {
  417.   ["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f",
  418.   ["n"] = "\n", ["r"] = "\r", ["t"] = "\t"
  419. }
  420.  
  421. local function unichar (value)
  422.   if value < 0 then
  423.     return nil
  424.   elseif value <= 0x007f then
  425.     return strchar (value)
  426.   elseif value <= 0x07ff then
  427.     return strchar (0xc0 + floor(value/0x40),
  428.                     0x80 + (floor(value) % 0x40))
  429.   elseif value <= 0xffff then
  430.     return strchar (0xe0 + floor(value/0x1000),
  431.                     0x80 + (floor(value/0x40) % 0x40),
  432.                     0x80 + (floor(value) % 0x40))
  433.   elseif value <= 0x10ffff then
  434.     return strchar (0xf0 + floor(value/0x40000),
  435.                     0x80 + (floor(value/0x1000) % 0x40),
  436.                     0x80 + (floor(value/0x40) % 0x40),
  437.                     0x80 + (floor(value) % 0x40))
  438.   else
  439.     return nil
  440.   end
  441. end
  442.  
  443. local function scanstring (str, pos)
  444.   local lastpos = pos + 1
  445.   local buffer, n = {}, 0
  446.   while true do
  447.     local nextpos = strfind (str, "[\"\\]", lastpos)
  448.     if not nextpos then
  449.       return unterminated (str, "string", pos)
  450.     end
  451.     if nextpos > lastpos then
  452.       n = n + 1
  453.       buffer[n] = strsub (str, lastpos, nextpos - 1)
  454.     end
  455.     if strsub (str, nextpos, nextpos) == "\"" then
  456.       lastpos = nextpos + 1
  457.       break
  458.     else
  459.       local escchar = strsub (str, nextpos + 1, nextpos + 1)
  460.       local value
  461.       if escchar == "u" then
  462.         value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16)
  463.         if value then
  464.           local value2
  465.           if 0xD800 <= value and value <= 0xDBff then
  466.             -- we have the high surrogate of UTF-16. Check if there is a
  467.             -- low surrogate escaped nearby to combine them.
  468.             if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then
  469.               value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16)
  470.               if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then
  471.                 value = (value - 0xD800)  * 0x400 + (value2 - 0xDC00) + 0x10000
  472.               else
  473.                 value2 = nil -- in case it was out of range for a low surrogate
  474.               end
  475.             end
  476.           end
  477.           value = value and unichar (value)
  478.           if value then
  479.             if value2 then
  480.               lastpos = nextpos + 12
  481.             else
  482.               lastpos = nextpos + 6
  483.             end
  484.           end
  485.         end
  486.       end
  487.       if not value then
  488.         value = escapechars[escchar] or escchar
  489.         lastpos = nextpos + 2
  490.       end
  491.       n = n + 1
  492.       buffer[n] = value
  493.     end
  494.   end
  495.   if n == 1 then
  496.     return buffer[1], lastpos
  497.   elseif n > 1 then
  498.     return concat (buffer), lastpos
  499.   else
  500.     return "", lastpos
  501.   end
  502. end
  503.  
  504. local scanvalue -- forward declaration
  505.  
  506. local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta)
  507.   local len = strlen (str)
  508.   local tbl, n = {}, 0
  509.   local pos = startpos + 1
  510.   if what == 'object' then
  511.     setmetatable (tbl, objectmeta)
  512.   else
  513.     setmetatable (tbl, arraymeta)
  514.   end
  515.   while true do
  516.     pos = scanwhite (str, pos)
  517.     if not pos then return unterminated (str, what, startpos) end
  518.     local char = strsub (str, pos, pos)
  519.     if char == closechar then
  520.       return tbl, pos + 1
  521.     end
  522.     local val1, err
  523.     val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
  524.     if err then return nil, pos, err end
  525.     pos = scanwhite (str, pos)
  526.     if not pos then return unterminated (str, what, startpos) end
  527.     char = strsub (str, pos, pos)
  528.     if char == ":" then
  529.       if val1 == nil then
  530.         return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")"
  531.       end
  532.       pos = scanwhite (str, pos + 1)
  533.       if not pos then return unterminated (str, what, startpos) end
  534.       local val2
  535.       val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
  536.       if err then return nil, pos, err end
  537.       tbl[val1] = val2
  538.       pos = scanwhite (str, pos)
  539.       if not pos then return unterminated (str, what, startpos) end
  540.       char = strsub (str, pos, pos)
  541.     else
  542.       n = n + 1
  543.       tbl[n] = val1
  544.     end
  545.     if char == "," then
  546.       pos = pos + 1
  547.     end
  548.   end
  549. end
  550.  
  551. scanvalue = function (str, pos, nullval, objectmeta, arraymeta)
  552.   pos = pos or 1
  553.   pos = scanwhite (str, pos)
  554.   if not pos then
  555.     return nil, strlen (str) + 1, "no valid JSON value (reached the end)"
  556.   end
  557.   local char = strsub (str, pos, pos)
  558.   if char == "{" then
  559.     return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta)
  560.   elseif char == "[" then
  561.     return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta)
  562.   elseif char == "\"" then
  563.     return scanstring (str, pos)
  564.   else
  565.     local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos)
  566.     if pstart then
  567.       local number = str2num (strsub (str, pstart, pend))
  568.       if number then
  569.         return number, pend + 1
  570.       end
  571.     end
  572.     pstart, pend = strfind (str, "^%a%w*", pos)
  573.     if pstart then
  574.       local name = strsub (str, pstart, pend)
  575.       if name == "true" then
  576.         return true, pend + 1
  577.       elseif name == "false" then
  578.         return false, pend + 1
  579.       elseif name == "null" then
  580.         return nullval, pend + 1
  581.       end
  582.     end
  583.     return nil, pos, "no valid JSON value at " .. loc (str, pos)
  584.   end
  585. end
  586.  
  587. local function optionalmetatables(...)
  588.   if select("#", ...) > 0 then
  589.     return ...
  590.   else
  591.     return {__jsontype = 'object'}, {__jsontype = 'array'}
  592.   end
  593. end
  594.  
  595. function json.decode (str, pos, nullval, ...)
  596.   local objectmeta, arraymeta = optionalmetatables(...)
  597.   return scanvalue (str, pos, nullval, objectmeta, arraymeta)
  598. end
  599.  
  600. function json.use_lpeg ()
  601.   local g = require ("lpeg")
  602.  
  603.   if g.version() == "0.11" then
  604.     error "due to a bug in LPeg 0.11, it cannot be used for JSON matching"
  605.   end
  606.  
  607.   local pegmatch = g.match
  608.   local P, S, R = g.P, g.S, g.R
  609.  
  610.   local function ErrorCall (str, pos, msg, state)
  611.     if not state.msg then
  612.       state.msg = msg .. " at " .. loc (str, pos)
  613.       state.pos = pos
  614.     end
  615.     return false
  616.   end
  617.  
  618.   local function Err (msg)
  619.     return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall)
  620.   end
  621.  
  622.   local SingleLineComment = P"//" * (1 - S"\n\r")^0
  623.   local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/"
  624.   local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0
  625.  
  626.   local PlainChar = 1 - S"\"\\\n\r"
  627.   local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars
  628.   local HexDigit = R("09", "af", "AF")
  629.   local function UTF16Surrogate (match, pos, high, low)
  630.     high, low = tonumber (high, 16), tonumber (low, 16)
  631.     if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then
  632.       return true, unichar ((high - 0xD800)  * 0x400 + (low - 0xDC00) + 0x10000)
  633.     else
  634.       return false
  635.     end
  636.   end
  637.   local function UTF16BMP (hex)
  638.     return unichar (tonumber (hex, 16))
  639.   end
  640.   local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit))
  641.   local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP
  642.   local Char = UnicodeEscape + EscapeSequence + PlainChar
  643.   local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string")
  644.   local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0))
  645.   local Fractal = P"." * R"09"^0
  646.   local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1
  647.   local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num
  648.   local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1)
  649.   local SimpleValue = Number + String + Constant
  650.   local ArrayContent, ObjectContent
  651.  
  652.   -- The functions parsearray and parseobject parse only a single value/pair
  653.   -- at a time and store them directly to avoid hitting the LPeg limits.
  654.   local function parsearray (str, pos, nullval, state)
  655.     local obj, cont
  656.     local npos
  657.     local t, nt = {}, 0
  658.     repeat
  659.       obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state)
  660.       if not npos then break end
  661.       pos = npos
  662.       nt = nt + 1
  663.       t[nt] = obj
  664.     until cont == 'last'
  665.     return pos, setmetatable (t, state.arraymeta)
  666.   end
  667.  
  668.   local function parseobject (str, pos, nullval, state)
  669.     local obj, key, cont
  670.     local npos
  671.     local t = {}
  672.     repeat
  673.       key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state)
  674.       if not npos then break end
  675.       pos = npos
  676.       t[key] = obj
  677.     until cont == 'last'
  678.     return pos, setmetatable (t, state.objectmeta)
  679.   end
  680.  
  681.   local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected")
  682.   local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected")
  683.   local Value = Space * (Array + Object + SimpleValue)
  684.   local ExpectedValue = Value + Space * Err "value expected"
  685.   ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp()
  686.   local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue)
  687.   ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp()
  688.   local DecodeValue = ExpectedValue * g.Cp ()
  689.  
  690.   function json.decode (str, pos, nullval, ...)
  691.     local state = {}
  692.     state.objectmeta, state.arraymeta = optionalmetatables(...)
  693.     local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state)
  694.     if state.msg then
  695.       return nil, state.pos, state.msg
  696.     else
  697.       return obj, retpos
  698.     end
  699.   end
  700.  
  701.   -- use this function only once:
  702.   json.use_lpeg = function () return json end
  703.  
  704.   json.using_lpeg = true
  705.  
  706.   return json -- so you can get the module using json = require "dkjson".use_lpeg()
  707. end
  708.  
  709. if always_try_using_lpeg then
  710.   pcall (json.use_lpeg)
  711. end
  712.  
  713. return json
Add Comment
Please, Sign In to add comment