dkjson.lua (22552B)
1 -- Module options: 2 local always_use_lpeg = false 3 local register_global_module_table = false 4 local global_module_name = "json" 5 6 --[==[ 7 8 David Kolf's JSON module for Lua 5.1 - 5.4 9 10 Version 2.8 11 12 13 For the documentation see the corresponding readme.txt or visit 14 <http://dkolf.de/dkjson-lua/>. 15 16 You can contact the author by sending an e-mail to 'david' at the 17 domain 'dkolf.de'. 18 19 20 Copyright (C) 2010-2024 David Heiko Kolf 21 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: 29 30 The above copyright notice and this permission notice shall be 31 included in all copies or substantial portions of the Software. 32 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. 41 42 --]==] 43 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, string.find, string.len, string.format 51 local strmatch = string.match 52 local concat = table.concat 53 54 local json = { version = "dkjson 2.8" } 55 56 local jsonlpeg = {} 57 58 if register_global_module_table then 59 if always_use_lpeg then 60 _G[global_module_name] = jsonlpeg 61 else 62 _G[global_module_name] = json 63 end 64 end 65 66 local _ENV = nil -- blocking globals in Lua 5.2 and later 67 68 pcall(function() 69 -- Enable access to blocked metatables. 70 -- Don't worry, this module doesn't change anything in them. 71 local debmeta = require("debug").getmetatable 72 if debmeta then 73 getmetatable = debmeta 74 end 75 end) 76 77 json.null = setmetatable({}, { 78 __tojson = function() 79 return "null" 80 end, 81 }) 82 83 local function isarray(tbl) 84 local max, n, arraylen = 0, 0, 0 85 for k, v in pairs(tbl) do 86 if k == "n" and type(v) == "number" then 87 arraylen = v 88 if v > max then 89 max = v 90 end 91 else 92 if type(k) ~= "number" or k < 1 or floor(k) ~= k then 93 return false 94 end 95 if k > max then 96 max = k 97 end 98 n = n + 1 99 end 100 end 101 if max > 10 and max > arraylen and max > n * 2 then 102 return false -- don't create an array with too many holes 103 end 104 return true, max 105 end 106 107 local escapecodes = { 108 ['"'] = '\\"', 109 ["\\"] = "\\\\", 110 ["\b"] = "\\b", 111 ["\f"] = "\\f", 112 ["\n"] = "\\n", 113 ["\r"] = "\\r", 114 ["\t"] = "\\t", 115 } 116 117 local function escapeutf8(uchar) 118 local value = escapecodes[uchar] 119 if value then 120 return value 121 end 122 local a, b, c, d = strbyte(uchar, 1, 4) 123 a, b, c, d = a or 0, b or 0, c or 0, d or 0 124 if a <= 0x7f then 125 value = a 126 elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then 127 value = (a - 0xc0) * 0x40 + b - 0x80 128 elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then 129 value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80 130 elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then 131 value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80 132 else 133 return "" 134 end 135 if value <= 0xffff then 136 return strformat("\\u%.4x", value) 137 elseif value <= 0x10ffff then 138 -- encode as UTF-16 surrogate pair 139 value = value - 0x10000 140 local highsur, lowsur = 0xD800 + floor(value / 0x400), 0xDC00 + (value % 0x400) 141 return strformat("\\u%.4x\\u%.4x", highsur, lowsur) 142 else 143 return "" 144 end 145 end 146 147 local function fsub(str, pattern, repl) 148 -- gsub always builds a new string in a buffer, even when no match 149 -- exists. First using find should be more efficient when most strings 150 -- don't contain the pattern. 151 if strfind(str, pattern) then 152 return gsub(str, pattern, repl) 153 else 154 return str 155 end 156 end 157 158 local function quotestring(value) 159 -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js 160 value = fsub(value, '[%z\1-\31"\\\127]', escapeutf8) 161 if strfind(value, "[\194\216\220\225\226\239]") then 162 value = fsub(value, "\194[\128-\159\173]", escapeutf8) 163 value = fsub(value, "\216[\128-\132]", escapeutf8) 164 value = fsub(value, "\220\143", escapeutf8) 165 value = fsub(value, "\225\158[\180\181]", escapeutf8) 166 value = fsub(value, "\226\128[\140-\143\168-\175]", escapeutf8) 167 value = fsub(value, "\226\129[\160-\175]", escapeutf8) 168 value = fsub(value, "\239\187\191", escapeutf8) 169 value = fsub(value, "\239\191[\176-\191]", escapeutf8) 170 end 171 return '"' .. value .. '"' 172 end 173 json.quotestring = quotestring 174 175 local function replace(str, o, n) 176 local i, j = strfind(str, o, 1, true) 177 if i then 178 return strsub(str, 1, i - 1) .. n .. strsub(str, j + 1, -1) 179 else 180 return str 181 end 182 end 183 184 -- locale independent num2str and str2num functions 185 local decpoint, numfilter 186 187 local function updatedecpoint() 188 decpoint = strmatch(tostring(0.5), "([^05+])") 189 -- build a filter that can be used to remove group separators 190 numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+" 191 end 192 193 updatedecpoint() 194 195 local function num2str(num) 196 return replace(fsub(tostring(num), numfilter, ""), decpoint, ".") 197 end 198 199 local function str2num(str) 200 local num = tonumber(replace(str, ".", decpoint)) 201 if not num then 202 updatedecpoint() 203 num = tonumber(replace(str, ".", decpoint)) 204 end 205 return num 206 end 207 208 local function addnewline2(level, buffer, buflen) 209 buffer[buflen + 1] = "\n" 210 buffer[buflen + 2] = strrep(" ", level) 211 buflen = buflen + 2 212 return buflen 213 end 214 215 function json.addnewline(state) 216 if state.indent then 217 state.bufferlen = addnewline2(state.level or 0, state.buffer, state.bufferlen or #state.buffer) 218 end 219 end 220 221 local encode2 -- forward declaration 222 223 local function addpair(key, value, prev, indent, level, buffer, buflen, tables, globalorder, state) 224 local kt = type(key) 225 if kt ~= "string" and kt ~= "number" then 226 return nil, "type '" .. kt .. "' is not supported as a key by JSON." 227 end 228 if prev then 229 buflen = buflen + 1 230 buffer[buflen] = "," 231 end 232 if indent then 233 buflen = addnewline2(level, buffer, buflen) 234 end 235 -- When Lua is compiled with LUA_NOCVTN2S this will fail when 236 -- numbers are mixed into the keys of the table. JSON keys are always 237 -- strings, so this would be an implicit conversion too and the failure 238 -- is intentional. 239 buffer[buflen + 1] = quotestring(key) 240 buffer[buflen + 2] = ":" 241 return encode2(value, indent, level, buffer, buflen + 2, tables, globalorder, state) 242 end 243 244 local function appendcustom(res, buffer, state) 245 local buflen = state.bufferlen 246 if type(res) == "string" then 247 buflen = buflen + 1 248 buffer[buflen] = res 249 end 250 return buflen 251 end 252 253 local function exception(reason, value, state, buffer, buflen, defaultmessage) 254 defaultmessage = defaultmessage or reason 255 local handler = state.exception 256 if not handler then 257 return nil, defaultmessage 258 else 259 state.bufferlen = buflen 260 local ret, msg = handler(reason, value, state, defaultmessage) 261 if not ret then 262 return nil, msg or defaultmessage 263 end 264 return appendcustom(ret, buffer, state) 265 end 266 end 267 268 function json.encodeexception(reason, value, state, defaultmessage) 269 return quotestring("<" .. defaultmessage .. ">") 270 end 271 272 encode2 = function(value, indent, level, buffer, buflen, tables, globalorder, state) 273 local valtype = type(value) 274 local valmeta = getmetatable(value) 275 valmeta = type(valmeta) == "table" and valmeta -- only tables 276 local valtojson = valmeta and valmeta.__tojson 277 if valtojson then 278 if tables[value] then 279 return exception("reference cycle", value, state, buffer, buflen) 280 end 281 tables[value] = true 282 state.bufferlen = buflen 283 local ret, msg = valtojson(value, state) 284 if not ret then 285 return exception("custom encoder failed", value, state, buffer, buflen, msg) 286 end 287 tables[value] = nil 288 buflen = appendcustom(ret, buffer, state) 289 elseif value == nil then 290 buflen = buflen + 1 291 buffer[buflen] = "null" 292 elseif valtype == "number" then 293 local s 294 if value ~= value or value >= huge or -value >= huge then 295 -- This is the behaviour of the original JSON implementation. 296 s = "null" 297 else 298 s = num2str(value) 299 end 300 buflen = buflen + 1 301 buffer[buflen] = s 302 elseif valtype == "boolean" then 303 buflen = buflen + 1 304 buffer[buflen] = value and "true" or "false" 305 elseif valtype == "string" then 306 buflen = buflen + 1 307 buffer[buflen] = quotestring(value) 308 elseif valtype == "table" then 309 if tables[value] then 310 return exception("reference cycle", value, state, buffer, buflen) 311 end 312 tables[value] = true 313 level = level + 1 314 local isa, n = isarray(value) 315 if n == 0 and valmeta and valmeta.__jsontype == "object" then 316 isa = false 317 end 318 local msg 319 if isa then -- JSON array 320 buflen = buflen + 1 321 buffer[buflen] = "[" 322 for i = 1, n do 323 buflen, msg = encode2(value[i], indent, level, buffer, buflen, tables, globalorder, state) 324 if not buflen then 325 return nil, msg 326 end 327 if i < n then 328 buflen = buflen + 1 329 buffer[buflen] = "," 330 end 331 end 332 buflen = buflen + 1 333 buffer[buflen] = "]" 334 else -- JSON object 335 local prev = false 336 buflen = buflen + 1 337 buffer[buflen] = "{" 338 local order = valmeta and valmeta.__jsonorder or globalorder 339 if order then 340 local used = {} 341 n = #order 342 for i = 1, n do 343 local k = order[i] 344 local v = value[k] 345 if v ~= nil then 346 used[k] = true 347 buflen, msg = addpair(k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) 348 if not buflen then 349 return nil, msg 350 end 351 prev = true -- add a seperator before the next element 352 end 353 end 354 for k, v in pairs(value) do 355 if not used[k] then 356 buflen, msg = addpair(k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) 357 if not buflen then 358 return nil, msg 359 end 360 prev = true -- add a seperator before the next element 361 end 362 end 363 else -- unordered 364 for k, v in pairs(value) do 365 buflen, msg = addpair(k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) 366 if not buflen then 367 return nil, msg 368 end 369 prev = true -- add a seperator before the next element 370 end 371 end 372 if indent then 373 buflen = addnewline2(level - 1, buffer, buflen) 374 end 375 buflen = buflen + 1 376 buffer[buflen] = "}" 377 end 378 tables[value] = nil 379 else 380 return exception( 381 "unsupported type", 382 value, 383 state, 384 buffer, 385 buflen, 386 "type '" .. valtype .. "' is not supported by JSON." 387 ) 388 end 389 return buflen 390 end 391 392 function json.encode(value, state) 393 state = state or {} 394 local oldbuffer = state.buffer 395 local buffer = oldbuffer or {} 396 state.buffer = buffer 397 updatedecpoint() 398 local ret, msg = encode2( 399 value, 400 state.indent, 401 state.level or 0, 402 buffer, 403 state.bufferlen or 0, 404 state.tables or {}, 405 state.keyorder, 406 state 407 ) 408 if not ret then 409 error(msg, 2) 410 elseif oldbuffer == buffer then 411 state.bufferlen = ret 412 return true 413 else 414 state.bufferlen = nil 415 state.buffer = nil 416 return concat(buffer) 417 end 418 end 419 420 local function loc(str, where) 421 local line, pos, linepos = 1, 1, 0 422 while true do 423 pos = strfind(str, "\n", pos, true) 424 if pos and pos < where then 425 line = line + 1 426 linepos = pos 427 pos = pos + 1 428 else 429 break 430 end 431 end 432 return strformat("line %d, column %d", line, where - linepos) 433 end 434 435 local function unterminated(str, what, where) 436 return nil, strlen(str) + 1, "unterminated " .. what .. " at " .. loc(str, where) 437 end 438 439 local function scanwhite(str, pos) 440 while true do 441 pos = strfind(str, "%S", pos) 442 if not pos then 443 return nil 444 end 445 local sub2 = strsub(str, pos, pos + 1) 446 if sub2 == "\239\187" and strsub(str, pos + 2, pos + 2) == "\191" then 447 -- UTF-8 Byte Order Mark 448 pos = pos + 3 449 elseif sub2 == "//" then 450 pos = strfind(str, "[\n\r]", pos + 2) 451 if not pos then 452 return nil 453 end 454 elseif sub2 == "/*" then 455 pos = strfind(str, "*/", pos + 2) 456 if not pos then 457 return nil 458 end 459 pos = pos + 2 460 else 461 return pos 462 end 463 end 464 end 465 466 local escapechars = { 467 ['"'] = '"', 468 ["\\"] = "\\", 469 ["/"] = "/", 470 ["b"] = "\b", 471 ["f"] = "\f", 472 ["n"] = "\n", 473 ["r"] = "\r", 474 ["t"] = "\t", 475 } 476 477 local function unichar(value) 478 if value < 0 then 479 return nil 480 elseif value <= 0x007f then 481 return strchar(value) 482 elseif value <= 0x07ff then 483 return strchar(0xc0 + floor(value / 0x40), 0x80 + (floor(value) % 0x40)) 484 elseif value <= 0xffff then 485 return strchar(0xe0 + floor(value / 0x1000), 0x80 + (floor(value / 0x40) % 0x40), 0x80 + (floor(value) % 0x40)) 486 elseif value <= 0x10ffff then 487 return strchar( 488 0xf0 + floor(value / 0x40000), 489 0x80 + (floor(value / 0x1000) % 0x40), 490 0x80 + (floor(value / 0x40) % 0x40), 491 0x80 + (floor(value) % 0x40) 492 ) 493 else 494 return nil 495 end 496 end 497 498 local function scanstring(str, pos) 499 local lastpos = pos + 1 500 local buffer, n = {}, 0 501 while true do 502 local nextpos = strfind(str, '["\\]', lastpos) 503 if not nextpos then 504 return unterminated(str, "string", pos) 505 end 506 if nextpos > lastpos then 507 n = n + 1 508 buffer[n] = strsub(str, lastpos, nextpos - 1) 509 end 510 if strsub(str, nextpos, nextpos) == '"' then 511 lastpos = nextpos + 1 512 break 513 else 514 local escchar = strsub(str, nextpos + 1, nextpos + 1) 515 local value 516 if escchar == "u" then 517 value = tonumber(strsub(str, nextpos + 2, nextpos + 5), 16) 518 if value then 519 local value2 520 if 0xD800 <= value and value <= 0xDBff then 521 -- we have the high surrogate of UTF-16. Check if there is a 522 -- low surrogate escaped nearby to combine them. 523 if strsub(str, nextpos + 6, nextpos + 7) == "\\u" then 524 value2 = tonumber(strsub(str, nextpos + 8, nextpos + 11), 16) 525 if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then 526 value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000 527 else 528 value2 = nil -- in case it was out of range for a low surrogate 529 end 530 end 531 end 532 value = value and unichar(value) 533 if value then 534 if value2 then 535 lastpos = nextpos + 12 536 else 537 lastpos = nextpos + 6 538 end 539 end 540 end 541 end 542 if not value then 543 value = escapechars[escchar] or escchar 544 lastpos = nextpos + 2 545 end 546 n = n + 1 547 buffer[n] = value 548 end 549 end 550 if n == 1 then 551 return buffer[1], lastpos 552 elseif n > 1 then 553 return concat(buffer), lastpos 554 else 555 return "", lastpos 556 end 557 end 558 559 local scanvalue -- forward declaration 560 561 local function scantable(what, closechar, str, startpos, nullval, objectmeta, arraymeta) 562 local tbl, n = {}, 0 563 local pos = startpos + 1 564 if what == "object" then 565 setmetatable(tbl, objectmeta) 566 else 567 setmetatable(tbl, arraymeta) 568 end 569 while true do 570 pos = scanwhite(str, pos) 571 if not pos then 572 return unterminated(str, what, startpos) 573 end 574 local char = strsub(str, pos, pos) 575 if char == closechar then 576 return tbl, pos + 1 577 end 578 local val1, err 579 val1, pos, err = scanvalue(str, pos, nullval, objectmeta, arraymeta) 580 if err then 581 return nil, pos, err 582 end 583 pos = scanwhite(str, pos) 584 if not pos then 585 return unterminated(str, what, startpos) 586 end 587 char = strsub(str, pos, pos) 588 if char == ":" then 589 if val1 == nil then 590 return nil, pos, "cannot use nil as table index (at " .. loc(str, pos) .. ")" 591 end 592 pos = scanwhite(str, pos + 1) 593 if not pos then 594 return unterminated(str, what, startpos) 595 end 596 local val2 597 val2, pos, err = scanvalue(str, pos, nullval, objectmeta, arraymeta) 598 if err then 599 return nil, pos, err 600 end 601 tbl[val1] = val2 602 pos = scanwhite(str, pos) 603 if not pos then 604 return unterminated(str, what, startpos) 605 end 606 char = strsub(str, pos, pos) 607 else 608 n = n + 1 609 tbl[n] = val1 610 end 611 if char == "," then 612 pos = pos + 1 613 end 614 end 615 end 616 617 scanvalue = function(str, pos, nullval, objectmeta, arraymeta) 618 pos = pos or 1 619 pos = scanwhite(str, pos) 620 if not pos then 621 return nil, strlen(str) + 1, "no valid JSON value (reached the end)" 622 end 623 local char = strsub(str, pos, pos) 624 if char == "{" then 625 return scantable("object", "}", str, pos, nullval, objectmeta, arraymeta) 626 elseif char == "[" then 627 return scantable("array", "]", str, pos, nullval, objectmeta, arraymeta) 628 elseif char == '"' then 629 return scanstring(str, pos) 630 else 631 local pstart, pend = strfind(str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos) 632 if pstart then 633 local number = str2num(strsub(str, pstart, pend)) 634 if number then 635 return number, pend + 1 636 end 637 end 638 pstart, pend = strfind(str, "^%a%w*", pos) 639 if pstart then 640 local name = strsub(str, pstart, pend) 641 if name == "true" then 642 return true, pend + 1 643 elseif name == "false" then 644 return false, pend + 1 645 elseif name == "null" then 646 return nullval, pend + 1 647 end 648 end 649 return nil, pos, "no valid JSON value at " .. loc(str, pos) 650 end 651 end 652 653 local function optionalmetatables(...) 654 if select("#", ...) > 0 then 655 return ... 656 else 657 return { __jsontype = "object" }, { __jsontype = "array" } 658 end 659 end 660 661 function json.decode(str, pos, nullval, ...) 662 local objectmeta, arraymeta = optionalmetatables(...) 663 return scanvalue(str, pos, nullval, objectmeta, arraymeta) 664 end 665 666 function json.use_lpeg() 667 local g = require("lpeg") 668 669 if type(g.version) == "function" and g.version() == "0.11" then 670 error("due to a bug in LPeg 0.11, it cannot be used for JSON matching") 671 end 672 673 local pegmatch = g.match 674 local P, S, R = g.P, g.S, g.R 675 676 local function ErrorCall(str, pos, msg, state) 677 if not state.msg then 678 state.msg = msg .. " at " .. loc(str, pos) 679 state.pos = pos 680 end 681 return false 682 end 683 684 local function Err(msg) 685 return g.Cmt(g.Cc(msg) * g.Carg(2), ErrorCall) 686 end 687 688 local function ErrorUnterminatedCall(str, pos, what, state) 689 return ErrorCall(str, pos - 1, "unterminated " .. what, state) 690 end 691 692 local SingleLineComment = P("//") * (1 - S("\n\r")) ^ 0 693 local MultiLineComment = P("/*") * (1 - P("*/")) ^ 0 * P("*/") 694 local Space = (S(" \n\r\t") + P("\239\187\191") + SingleLineComment + MultiLineComment) ^ 0 695 696 local function ErrUnterminated(what) 697 return g.Cmt(g.Cc(what) * g.Carg(2), ErrorUnterminatedCall) 698 end 699 700 local PlainChar = 1 - S('"\\\n\r') 701 local EscapeSequence = (P("\\") * g.C(S('"\\/bfnrt') + Err("unsupported escape sequence"))) / escapechars 702 local HexDigit = R("09", "af", "AF") 703 local function UTF16Surrogate(match, pos, high, low) 704 high, low = tonumber(high, 16), tonumber(low, 16) 705 if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then 706 return true, unichar((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000) 707 else 708 return false 709 end 710 end 711 local function UTF16BMP(hex) 712 return unichar(tonumber(hex, 16)) 713 end 714 local U16Sequence = (P("\\u") * g.C(HexDigit * HexDigit * HexDigit * HexDigit)) 715 local UnicodeEscape = g.Cmt(U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence / UTF16BMP 716 local Char = UnicodeEscape + EscapeSequence + PlainChar 717 local String = P('"') * (g.Cs(Char ^ 0) * P('"') + ErrUnterminated("string")) 718 local Integer = P("-") ^ -1 * (P("0") + (R("19") * R("09") ^ 0)) 719 local Fractal = P(".") * R("09") ^ 0 720 local Exponent = (S("eE")) * (S("+-")) ^ -1 * R("09") ^ 1 721 local Number = (Integer * Fractal ^ -1 * Exponent ^ -1) / str2num 722 local Constant = P("true") * g.Cc(true) + P("false") * g.Cc(false) + P("null") * g.Carg(1) 723 local SimpleValue = Number + String + Constant 724 local ArrayContent, ObjectContent 725 726 -- The functions parsearray and parseobject parse only a single value/pair 727 -- at a time and store them directly to avoid hitting the LPeg limits. 728 local function parsearray(str, pos, nullval, state) 729 local obj, cont 730 local start = pos 731 local npos 732 local t, nt = {}, 0 733 repeat 734 obj, cont, npos = pegmatch(ArrayContent, str, pos, nullval, state) 735 if cont == "end" then 736 return ErrorUnterminatedCall(str, start, "array", state) 737 end 738 pos = npos 739 if cont == "cont" or cont == "last" then 740 nt = nt + 1 741 t[nt] = obj 742 end 743 until cont ~= "cont" 744 return pos, setmetatable(t, state.arraymeta) 745 end 746 747 local function parseobject(str, pos, nullval, state) 748 local obj, key, cont 749 local start = pos 750 local npos 751 local t = {} 752 repeat 753 key, obj, cont, npos = pegmatch(ObjectContent, str, pos, nullval, state) 754 if cont == "end" then 755 return ErrorUnterminatedCall(str, start, "object", state) 756 end 757 pos = npos 758 if cont == "cont" or cont == "last" then 759 t[key] = obj 760 end 761 until cont ~= "cont" 762 return pos, setmetatable(t, state.objectmeta) 763 end 764 765 local Array = P("[") * g.Cmt(g.Carg(1) * g.Carg(2), parsearray) 766 local Object = P("{") * g.Cmt(g.Carg(1) * g.Carg(2), parseobject) 767 local Value = Space * (Array + Object + SimpleValue) 768 local ExpectedValue = Value + Space * Err("value expected") 769 local ExpectedKey = String + Err("key expected") 770 local End = P(-1) * g.Cc("end") 771 local ErrInvalid = Err("invalid JSON") 772 ArrayContent = ( 773 Value * Space * (P(",") * g.Cc("cont") + P("]") * g.Cc("last") + End + ErrInvalid) 774 + g.Cc(nil) * (P("]") * g.Cc("empty") + End + ErrInvalid) 775 ) * g.Cp() 776 local Pair = g.Cg(Space * ExpectedKey * Space * (P(":") + Err("colon expected")) * ExpectedValue) 777 ObjectContent = ( 778 g.Cc(nil) * g.Cc(nil) * P("}") * g.Cc("empty") 779 + End 780 + (Pair * Space * (P(",") * g.Cc("cont") + P("}") * g.Cc("last") + End + ErrInvalid) + ErrInvalid) 781 ) * g.Cp() 782 local DecodeValue = ExpectedValue * g.Cp() 783 784 jsonlpeg.version = json.version 785 jsonlpeg.encode = json.encode 786 jsonlpeg.null = json.null 787 jsonlpeg.quotestring = json.quotestring 788 jsonlpeg.addnewline = json.addnewline 789 jsonlpeg.encodeexception = json.encodeexception 790 jsonlpeg.using_lpeg = true 791 792 function jsonlpeg.decode(str, pos, nullval, ...) 793 local state = {} 794 state.objectmeta, state.arraymeta = optionalmetatables(...) 795 local obj, retpos = pegmatch(DecodeValue, str, pos, nullval, state) 796 if state.msg then 797 return nil, state.pos, state.msg 798 else 799 return obj, retpos 800 end 801 end 802 803 -- cache result of this function: 804 json.use_lpeg = function() 805 return jsonlpeg 806 end 807 jsonlpeg.use_lpeg = json.use_lpeg 808 809 return jsonlpeg 810 end 811 812 if always_use_lpeg then 813 return json.use_lpeg() 814 end 815 816 return json