Advertisement
Guest User

Json Codea Library

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