style
[RRG-proxmark3.git] / client / lualibs / dkjson.lua
blob3bfbec2e72203d32249a9972125a3646602c6db6
1 -- Module options:
2 local always_use_lpeg = false
3 local register_global_module_table = false
4 local global_module_name = 'json'
6 --[==[
8 David Kolf's JSON module for Lua 5.1 - 5.4
10 Version 2.8
13 For the documentation see the corresponding readme.txt or visit
14 <http://dkolf.de/dkjson-lua/>.
16 You can contact the author by sending an e-mail to 'david' at the
17 domain 'dkolf.de'.
20 Copyright (C) 2010-2024 David Heiko Kolf
22 Permission is hereby granted, free of charge, to any person obtaining
23 a copy of this software and associated documentation files (the
24 "Software"), to deal in the Software without restriction, including
25 without limitation the rights to use, copy, modify, merge, publish,
26 distribute, sublicense, and/or sell copies of the Software, and to
27 permit persons to whom the Software is furnished to do so, subject to
28 the following conditions:
30 The above copyright notice and this permission notice shall be
31 included in all copies or substantial portions of the Software.
33 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
34 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
35 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
36 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
37 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
38 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
39 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40 SOFTWARE.
42 --]==]
44 -- global dependencies:
45 local pairs, type, tostring, tonumber, getmetatable, setmetatable =
46 pairs, type, tostring, tonumber, getmetatable, setmetatable
47 local error, require, pcall, select = error, require, pcall, select
48 local floor, huge = math.floor, math.huge
49 local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat =
50 string.rep, string.gsub, string.sub, string.byte, string.char,
51 string.find, string.len, string.format
52 local strmatch = string.match
53 local concat = table.concat
55 local json = { version = "dkjson 2.8" }
57 local jsonlpeg = {}
59 if register_global_module_table then
60 if always_use_lpeg then
61 _G[global_module_name] = jsonlpeg
62 else
63 _G[global_module_name] = json
64 end
65 end
67 local _ENV = nil -- blocking globals in Lua 5.2 and later
69 pcall (function()
70 -- Enable access to blocked metatables.
71 -- Don't worry, this module doesn't change anything in them.
72 local debmeta = require "debug".getmetatable
73 if debmeta then getmetatable = debmeta end
74 end)
76 json.null = setmetatable ({}, {
77 __tojson = function () return "null" end
80 local function isarray (tbl)
81 local max, n, arraylen = 0, 0, 0
82 for k,v in pairs (tbl) do
83 if k == 'n' and type(v) == 'number' then
84 arraylen = v
85 if v > max then
86 max = v
87 end
88 else
89 if type(k) ~= 'number' or k < 1 or floor(k) ~= k then
90 return false
91 end
92 if k > max then
93 max = k
94 end
95 n = n + 1
96 end
97 end
98 if max > 10 and max > arraylen and max > n * 2 then
99 return false -- don't create an array with too many holes
101 return true, max
104 local escapecodes = {
105 ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f",
106 ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t"
109 local function escapeutf8 (uchar)
110 local value = escapecodes[uchar]
111 if value then
112 return value
114 local a, b, c, d = strbyte (uchar, 1, 4)
115 a, b, c, d = a or 0, b or 0, c or 0, d or 0
116 if a <= 0x7f then
117 value = a
118 elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then
119 value = (a - 0xc0) * 0x40 + b - 0x80
120 elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then
121 value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80
122 elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then
123 value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80
124 else
125 return ""
127 if value <= 0xffff then
128 return strformat ("\\u%.4x", value)
129 elseif value <= 0x10ffff then
130 -- encode as UTF-16 surrogate pair
131 value = value - 0x10000
132 local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400)
133 return strformat ("\\u%.4x\\u%.4x", highsur, lowsur)
134 else
135 return ""
139 local function fsub (str, pattern, repl)
140 -- gsub always builds a new string in a buffer, even when no match
141 -- exists. First using find should be more efficient when most strings
142 -- don't contain the pattern.
143 if strfind (str, pattern) then
144 return gsub (str, pattern, repl)
145 else
146 return str
150 local function quotestring (value)
151 -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js
152 value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8)
153 if strfind (value, "[\194\216\220\225\226\239]") then
154 value = fsub (value, "\194[\128-\159\173]", escapeutf8)
155 value = fsub (value, "\216[\128-\132]", escapeutf8)
156 value = fsub (value, "\220\143", escapeutf8)
157 value = fsub (value, "\225\158[\180\181]", escapeutf8)
158 value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8)
159 value = fsub (value, "\226\129[\160-\175]", escapeutf8)
160 value = fsub (value, "\239\187\191", escapeutf8)
161 value = fsub (value, "\239\191[\176-\191]", escapeutf8)
163 return "\"" .. value .. "\""
165 json.quotestring = quotestring
167 local function replace(str, o, n)
168 local i, j = strfind (str, o, 1, true)
169 if i then
170 return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1)
171 else
172 return str
176 -- locale independent num2str and str2num functions
177 local decpoint, numfilter
179 local function updatedecpoint ()
180 decpoint = strmatch(tostring(0.5), "([^05+])")
181 -- build a filter that can be used to remove group separators
182 numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+"
185 updatedecpoint()
187 local function num2str (num)
188 return replace(fsub(tostring(num), numfilter, ""), decpoint, ".")
191 local function str2num (str)
192 local num = tonumber(replace(str, ".", decpoint))
193 if not num then
194 updatedecpoint()
195 num = tonumber(replace(str, ".", decpoint))
197 return num
200 local function addnewline2 (level, buffer, buflen)
201 buffer[buflen+1] = "\n"
202 buffer[buflen+2] = strrep (" ", level)
203 buflen = buflen + 2
204 return buflen
207 function json.addnewline (state)
208 if state.indent then
209 state.bufferlen = addnewline2 (state.level or 0,
210 state.buffer, state.bufferlen or #(state.buffer))
214 local encode2 -- forward declaration
216 local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state)
217 local kt = type (key)
218 if kt ~= 'string' and kt ~= 'number' then
219 return nil, "type '" .. kt .. "' is not supported as a key by JSON."
221 if prev then
222 buflen = buflen + 1
223 buffer[buflen] = ","
225 if indent then
226 buflen = addnewline2 (level, buffer, buflen)
228 -- When Lua is compiled with LUA_NOCVTN2S this will fail when
229 -- numbers are mixed into the keys of the table. JSON keys are always
230 -- strings, so this would be an implicit conversion too and the failure
231 -- is intentional.
232 buffer[buflen+1] = quotestring (key)
233 buffer[buflen+2] = ":"
234 return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state)
237 local function appendcustom(res, buffer, state)
238 local buflen = state.bufferlen
239 if type (res) == 'string' then
240 buflen = buflen + 1
241 buffer[buflen] = res
243 return buflen
246 local function exception(reason, value, state, buffer, buflen, defaultmessage)
247 defaultmessage = defaultmessage or reason
248 local handler = state.exception
249 if not handler then
250 return nil, defaultmessage
251 else
252 state.bufferlen = buflen
253 local ret, msg = handler (reason, value, state, defaultmessage)
254 if not ret then return nil, msg or defaultmessage end
255 return appendcustom(ret, buffer, state)
259 function json.encodeexception(reason, value, state, defaultmessage)
260 return quotestring("<" .. defaultmessage .. ">")
263 encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state)
264 local valtype = type (value)
265 local valmeta = getmetatable (value)
266 valmeta = type (valmeta) == 'table' and valmeta -- only tables
267 local valtojson = valmeta and valmeta.__tojson
268 if valtojson then
269 if tables[value] then
270 return exception('reference cycle', value, state, buffer, buflen)
272 tables[value] = true
273 state.bufferlen = buflen
274 local ret, msg = valtojson (value, state)
275 if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end
276 tables[value] = nil
277 buflen = appendcustom(ret, buffer, state)
278 elseif value == nil then
279 buflen = buflen + 1
280 buffer[buflen] = "null"
281 elseif valtype == 'number' then
282 local s
283 if value ~= value or value >= huge or -value >= huge then
284 -- This is the behaviour of the original JSON implementation.
285 s = "null"
286 else
287 s = num2str (value)
289 buflen = buflen + 1
290 buffer[buflen] = s
291 elseif valtype == 'boolean' then
292 buflen = buflen + 1
293 buffer[buflen] = value and "true" or "false"
294 elseif valtype == 'string' then
295 buflen = buflen + 1
296 buffer[buflen] = quotestring (value)
297 elseif valtype == 'table' then
298 if tables[value] then
299 return exception('reference cycle', value, state, buffer, buflen)
301 tables[value] = true
302 level = level + 1
303 local isa, n = isarray (value)
304 if n == 0 and valmeta and valmeta.__jsontype == 'object' then
305 isa = false
307 local msg
308 if isa then -- JSON array
309 buflen = buflen + 1
310 buffer[buflen] = "["
311 for i = 1, n do
312 buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state)
313 if not buflen then return nil, msg end
314 if i < n then
315 buflen = buflen + 1
316 buffer[buflen] = ","
319 buflen = buflen + 1
320 buffer[buflen] = "]"
321 else -- JSON object
322 local prev = false
323 buflen = buflen + 1
324 buffer[buflen] = "{"
325 local order = valmeta and valmeta.__jsonorder or globalorder
326 if order then
327 local used = {}
328 n = #order
329 for i = 1, n do
330 local k = order[i]
331 local v = value[k]
332 if v ~= nil then
333 used[k] = true
334 buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
335 if not buflen then return nil, msg end
336 prev = true -- add a seperator before the next element
339 for k,v in pairs (value) do
340 if not used[k] then
341 buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
342 if not buflen then return nil, msg end
343 prev = true -- add a seperator before the next element
346 else -- unordered
347 for k,v in pairs (value) do
348 buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
349 if not buflen then return nil, msg end
350 prev = true -- add a seperator before the next element
353 if indent then
354 buflen = addnewline2 (level - 1, buffer, buflen)
356 buflen = buflen + 1
357 buffer[buflen] = "}"
359 tables[value] = nil
360 else
361 return exception ('unsupported type', value, state, buffer, buflen,
362 "type '" .. valtype .. "' is not supported by JSON.")
364 return buflen
367 function json.encode (value, state)
368 state = state or {}
369 local oldbuffer = state.buffer
370 local buffer = oldbuffer or {}
371 state.buffer = buffer
372 updatedecpoint()
373 local ret, msg = encode2 (value, state.indent, state.level or 0,
374 buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state)
375 if not ret then
376 error (msg, 2)
377 elseif oldbuffer == buffer then
378 state.bufferlen = ret
379 return true
380 else
381 state.bufferlen = nil
382 state.buffer = nil
383 return concat (buffer)
387 local function loc (str, where)
388 local line, pos, linepos = 1, 1, 0
389 while true do
390 pos = strfind (str, "\n", pos, true)
391 if pos and pos < where then
392 line = line + 1
393 linepos = pos
394 pos = pos + 1
395 else
396 break
399 return strformat ("line %d, column %d", line, where - linepos)
402 local function unterminated (str, what, where)
403 return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where)
406 local function scanwhite (str, pos)
407 while true do
408 pos = strfind (str, "%S", pos)
409 if not pos then return nil end
410 local sub2 = strsub (str, pos, pos + 1)
411 if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then
412 -- UTF-8 Byte Order Mark
413 pos = pos + 3
414 elseif sub2 == "//" then
415 pos = strfind (str, "[\n\r]", pos + 2)
416 if not pos then return nil end
417 elseif sub2 == "/*" then
418 pos = strfind (str, "*/", pos + 2)
419 if not pos then return nil end
420 pos = pos + 2
421 else
422 return pos
427 local escapechars = {
428 ["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f",
429 ["n"] = "\n", ["r"] = "\r", ["t"] = "\t"
432 local function unichar (value)
433 if value < 0 then
434 return nil
435 elseif value <= 0x007f then
436 return strchar (value)
437 elseif value <= 0x07ff then
438 return strchar (0xc0 + floor(value/0x40),
439 0x80 + (floor(value) % 0x40))
440 elseif value <= 0xffff then
441 return strchar (0xe0 + floor(value/0x1000),
442 0x80 + (floor(value/0x40) % 0x40),
443 0x80 + (floor(value) % 0x40))
444 elseif value <= 0x10ffff then
445 return strchar (0xf0 + floor(value/0x40000),
446 0x80 + (floor(value/0x1000) % 0x40),
447 0x80 + (floor(value/0x40) % 0x40),
448 0x80 + (floor(value) % 0x40))
449 else
450 return nil
454 local function scanstring (str, pos)
455 local lastpos = pos + 1
456 local buffer, n = {}, 0
457 while true do
458 local nextpos = strfind (str, "[\"\\]", lastpos)
459 if not nextpos then
460 return unterminated (str, "string", pos)
462 if nextpos > lastpos then
463 n = n + 1
464 buffer[n] = strsub (str, lastpos, nextpos - 1)
466 if strsub (str, nextpos, nextpos) == "\"" then
467 lastpos = nextpos + 1
468 break
469 else
470 local escchar = strsub (str, nextpos + 1, nextpos + 1)
471 local value
472 if escchar == "u" then
473 value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16)
474 if value then
475 local value2
476 if 0xD800 <= value and value <= 0xDBff then
477 -- we have the high surrogate of UTF-16. Check if there is a
478 -- low surrogate escaped nearby to combine them.
479 if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then
480 value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16)
481 if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then
482 value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000
483 else
484 value2 = nil -- in case it was out of range for a low surrogate
488 value = value and unichar (value)
489 if value then
490 if value2 then
491 lastpos = nextpos + 12
492 else
493 lastpos = nextpos + 6
498 if not value then
499 value = escapechars[escchar] or escchar
500 lastpos = nextpos + 2
502 n = n + 1
503 buffer[n] = value
506 if n == 1 then
507 return buffer[1], lastpos
508 elseif n > 1 then
509 return concat (buffer), lastpos
510 else
511 return "", lastpos
515 local scanvalue -- forward declaration
517 local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta)
518 local tbl, n = {}, 0
519 local pos = startpos + 1
520 if what == 'object' then
521 setmetatable (tbl, objectmeta)
522 else
523 setmetatable (tbl, arraymeta)
525 while true do
526 pos = scanwhite (str, pos)
527 if not pos then return unterminated (str, what, startpos) end
528 local char = strsub (str, pos, pos)
529 if char == closechar then
530 return tbl, pos + 1
532 local val1, err
533 val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
534 if err then return nil, pos, err end
535 pos = scanwhite (str, pos)
536 if not pos then return unterminated (str, what, startpos) end
537 char = strsub (str, pos, pos)
538 if char == ":" then
539 if val1 == nil then
540 return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")"
542 pos = scanwhite (str, pos + 1)
543 if not pos then return unterminated (str, what, startpos) end
544 local val2
545 val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
546 if err then return nil, pos, err end
547 tbl[val1] = val2
548 pos = scanwhite (str, pos)
549 if not pos then return unterminated (str, what, startpos) end
550 char = strsub (str, pos, pos)
551 else
552 n = n + 1
553 tbl[n] = val1
555 if char == "," then
556 pos = pos + 1
561 scanvalue = function (str, pos, nullval, objectmeta, arraymeta)
562 pos = pos or 1
563 pos = scanwhite (str, pos)
564 if not pos then
565 return nil, strlen (str) + 1, "no valid JSON value (reached the end)"
567 local char = strsub (str, pos, pos)
568 if char == "{" then
569 return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta)
570 elseif char == "[" then
571 return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta)
572 elseif char == "\"" then
573 return scanstring (str, pos)
574 else
575 local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos)
576 if pstart then
577 local number = str2num (strsub (str, pstart, pend))
578 if number then
579 return number, pend + 1
582 pstart, pend = strfind (str, "^%a%w*", pos)
583 if pstart then
584 local name = strsub (str, pstart, pend)
585 if name == "true" then
586 return true, pend + 1
587 elseif name == "false" then
588 return false, pend + 1
589 elseif name == "null" then
590 return nullval, pend + 1
593 return nil, pos, "no valid JSON value at " .. loc (str, pos)
597 local function optionalmetatables(...)
598 if select("#", ...) > 0 then
599 return ...
600 else
601 return {__jsontype = 'object'}, {__jsontype = 'array'}
605 function json.decode (str, pos, nullval, ...)
606 local objectmeta, arraymeta = optionalmetatables(...)
607 return scanvalue (str, pos, nullval, objectmeta, arraymeta)
610 function json.use_lpeg ()
611 local g = require ("lpeg")
613 if type(g.version) == 'function' and g.version() == "0.11" then
614 error "due to a bug in LPeg 0.11, it cannot be used for JSON matching"
617 local pegmatch = g.match
618 local P, S, R = g.P, g.S, g.R
620 local function ErrorCall (str, pos, msg, state)
621 if not state.msg then
622 state.msg = msg .. " at " .. loc (str, pos)
623 state.pos = pos
625 return false
628 local function Err (msg)
629 return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall)
632 local function ErrorUnterminatedCall (str, pos, what, state)
633 return ErrorCall (str, pos - 1, "unterminated " .. what, state)
636 local SingleLineComment = P"//" * (1 - S"\n\r")^0
637 local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/"
638 local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0
640 local function ErrUnterminated (what)
641 return g.Cmt (g.Cc (what) * g.Carg (2), ErrorUnterminatedCall)
644 local PlainChar = 1 - S"\"\\\n\r"
645 local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars
646 local HexDigit = R("09", "af", "AF")
647 local function UTF16Surrogate (match, pos, high, low)
648 high, low = tonumber (high, 16), tonumber (low, 16)
649 if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then
650 return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000)
651 else
652 return false
655 local function UTF16BMP (hex)
656 return unichar (tonumber (hex, 16))
658 local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit))
659 local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP
660 local Char = UnicodeEscape + EscapeSequence + PlainChar
661 local String = P"\"" * (g.Cs (Char ^ 0) * P"\"" + ErrUnterminated "string")
662 local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0))
663 local Fractal = P"." * R"09"^0
664 local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1
665 local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num
666 local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1)
667 local SimpleValue = Number + String + Constant
668 local ArrayContent, ObjectContent
670 -- The functions parsearray and parseobject parse only a single value/pair
671 -- at a time and store them directly to avoid hitting the LPeg limits.
672 local function parsearray (str, pos, nullval, state)
673 local obj, cont
674 local start = pos
675 local npos
676 local t, nt = {}, 0
677 repeat
678 obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state)
679 if cont == 'end' then
680 return ErrorUnterminatedCall (str, start, "array", state)
682 pos = npos
683 if cont == 'cont' or cont == 'last' then
684 nt = nt + 1
685 t[nt] = obj
687 until cont ~= 'cont'
688 return pos, setmetatable (t, state.arraymeta)
691 local function parseobject (str, pos, nullval, state)
692 local obj, key, cont
693 local start = pos
694 local npos
695 local t = {}
696 repeat
697 key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state)
698 if cont == 'end' then
699 return ErrorUnterminatedCall (str, start, "object", state)
701 pos = npos
702 if cont == 'cont' or cont == 'last' then
703 t[key] = obj
705 until cont ~= 'cont'
706 return pos, setmetatable (t, state.objectmeta)
709 local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray)
710 local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject)
711 local Value = Space * (Array + Object + SimpleValue)
712 local ExpectedValue = Value + Space * Err "value expected"
713 local ExpectedKey = String + Err "key expected"
714 local End = P(-1) * g.Cc'end'
715 local ErrInvalid = Err "invalid JSON"
716 ArrayContent = (Value * Space * (P"," * g.Cc'cont' + P"]" * g.Cc'last'+ End + ErrInvalid) + g.Cc(nil) * (P"]" * g.Cc'empty' + End + ErrInvalid)) * g.Cp()
717 local Pair = g.Cg (Space * ExpectedKey * Space * (P":" + Err "colon expected") * ExpectedValue)
718 ObjectContent = (g.Cc(nil) * g.Cc(nil) * P"}" * g.Cc'empty' + End + (Pair * Space * (P"," * g.Cc'cont' + P"}" * g.Cc'last' + End + ErrInvalid) + ErrInvalid)) * g.Cp()
719 local DecodeValue = ExpectedValue * g.Cp ()
721 jsonlpeg.version = json.version
722 jsonlpeg.encode = json.encode
723 jsonlpeg.null = json.null
724 jsonlpeg.quotestring = json.quotestring
725 jsonlpeg.addnewline = json.addnewline
726 jsonlpeg.encodeexception = json.encodeexception
727 jsonlpeg.using_lpeg = true
729 function jsonlpeg.decode (str, pos, nullval, ...)
730 local state = {}
731 state.objectmeta, state.arraymeta = optionalmetatables(...)
732 local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state)
733 if state.msg then
734 return nil, state.pos, state.msg
735 else
736 return obj, retpos
740 -- cache result of this function:
741 json.use_lpeg = function () return jsonlpeg end
742 jsonlpeg.use_lpeg = json.use_lpeg
744 return jsonlpeg
747 if always_use_lpeg then
748 return json.use_lpeg()
751 return json