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