dkjson.lua 25 KB

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