Quipex

Disk/mem/redworks/programs/cc-get-bin/dkjson.lua

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