fes

Free Easy Site
Log | Files | Refs | README | LICENSE

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