dkjson.lua 22 KB

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