dkjson.lua 22 KB

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