-- http://creativecommons.org/licenses/by/3.0/deed.en_US
--
-- It can be used for any purpose so long as: the copyright notice above,
-- the web-page links above, and the 'AUTHOR_NOTE' string below are
-- 1) the copyright notice above is maintained
-- maintained. Enjoy.
-- 2) the web-page links above are maintained
-- 3) the 'AUTHOR_NOTE' string below is maintained
--
local VERSION = '2016110320160916.20'19 -- version history at end of file
local AUTHOR_NOTE = "-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json) version 2016110320160916.2019 ]-"
--
--
--
-- If the JSON text passed to decode() has trailing garbage (e.g. as with the JSON "[123]xyzzy"),
-- the method
--
-- JSON:onTrailingGarbage(json_text, ___location, parsed_value, etc)
--
-- is invoked, where:
--
-- json_text is the original JSON text being parsed,
-- ___location is the count of bytes into json_text where the garbage starts (6 in the example),
-- parsed_value is the Lua result of what was successfully parsed ({123} in the example),
-- etc is as above.
--
-- If JSON:onTrailingGarbage() does not abort, it should return the value decode() should return,
-- or nil + an error message.
--
-- local new_value, error_message = JSON:onTrailingGarbage()
--
-- The default handler just invokes JSON:onDecodeError("trailing garbage"...), but you can have
-- this package ignore trailing garbage via
--
-- function JSON:onTrailingGarbage(json_text, ___location, parsed_value, etc)
-- return parsed_value
-- end
--
--
-- onDecodeOfNilError
-- onDecodeOfHTMLError
-- onTrailingGarbage
-- onEncodeError
--
if text then
if ___location then
message = string.format("%s at bytechar %d of: %s", message, ___location, text)
else
message = string.format("%s: %s", message, text)
assert(false, message)
end
end
function OBJDEF:onTrailingGarbage(json_text, ___location, parsed_value, etc)
return self:onDecodeError("trailing garbage", json_text, ___location, etc)
end
if not integer_part then
self:onDecodeError("expected number", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
if not as_number then
self:onDecodeError("bad number", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
if text:sub(start,start) ~= '"' then
self:onDecodeError("expected string's opening quote", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
self:onDecodeError("unclosed string", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
if text:sub(start,start) ~= '{' then
self:onDecodeError("expected '{'", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
if text:sub(i, i) ~= ':' then
self:onDecodeError("expected colon", text, i, options.etc)
return nil, i -- in case the error method doesn't abort, return something sensible
end
if text:sub(i, i) ~= ',' then
self:onDecodeError("expected comma or '}'", text, i, options.etc)
return nil, i -- in case the error method doesn't abort, return something sensible
end
self:onDecodeError("unclosed '{'", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
if text:sub(start,start) ~= '[' then
self:onDecodeError("expected '['", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
end
if text:sub(i, i) ~= ',' then
self:onDecodeError("expected comma or ']['", text, i, options.etc)
return nil, i -- in case the error method doesn't abort, return something sensible
end
i = skip_whitespace(text, i + 1)
end
self:onDecodeError("unclosed '['", text, start, options.etc)
return nil, i -- in case the error method doesn't abort, return something sensible
end
if start > text:len() then
self:onDecodeError("unexpected end of string", text, nil, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
else
self:onDecodeError("can't parse JSON", text, start, options.etc)
return nil, 1 -- in case the error method doesn't abort, return something sensible
end
end
if type(self) ~= 'table' or self.__index ~= OBJDEF then
local error_message = OBJDEF:onDecodeError("JSON:decode must be called in method format", nil, nil, options.etc)
OBJDEF:onDecodeError(error_message, nil, nil, options.etc)
return nil, error_message -- in case the error method doesn't abort, return something sensible
end
if text == nil then
local error_message = self:onDecodeOfNilError(string.format("nil passed to JSON:decode()"), nil, nil, options.etc)
self:onDecodeOfNilError(error_message, nil, nil, options.etc)
return nil, error_message -- in case the error method doesn't abort, return something sensible
elseif type(text) ~= 'string' then
local error_message = self:onDecodeError(string.format("expected string argument to JSON:decode(), got %s", type(text)), nil, nil, options.etc)
self:onDecodeError(string.format("%s, got %s", error_message, type(text)), nil, nil, options.etc)
return nil, error_message -- in case the error method doesn't abort, return something sensible
end
if text:match('^%s*$') then
-- an empty string is nothing, but not an error
return nil
end
if text:match('^%s*<') then
-- Can't be JSON... we'll assume it's HTML
local error_message = self:onDecodeOfHTMLError(string.format("HTMLhtml passed to JSON:decode()"), text, nil, options.etc)
self:onDecodeOfHTMLError(error_message, text, nil, options.etc)
return nil, error_message -- in case the error method doesn't abort, return something sensible
end
--
if text:sub(1,1):byte() == 0 or (text:len() >= 2 and text:sub(2,2):byte() == 0) then
local error_message = self:onDecodeError("JSON package groks only UTF-8, sorry", text, nil, options.etc)
self:onDecodeError(error_message, text, nil, options.etc)
return nil, error_message -- in case the error method doesn't abort, return something sensible
end
end
local success, value = pcall(grok_one, self, text, 1, options)
--
-- Finally, go parse it
--
local success, value, next_i = pcall(grok_one, self, text, 1, options)
if success then
return value
if next_i ~= #text + 1 then
-- something's left over after we parsed the first thing.... whitespace is allowed.
next_i = skip_whitespace(text, next_i)
-- if we have something left over now, it's trailing garbage
local error_message
if next_i ~= #text + 1 then
value, error_message = self:onTrailingGarbage(text, next_i, value, options.etc)
end
end
return value, error_message
else
-- if JSON:onDecodeError() didn't abort out of the pcall, we'll have received the error message here as "value", so pass it along as an assert.
-- If JSON:onDecodeError() didn't abort out of the pcall, we'll have received
-- the error message here as "value", so pass it along as an assert.
local error_message = value
if self.assert then
self.assert(false, error_messagevalue)
else
assert(false, error_messagevalue)
end
-- and if we're still here, return a nil and throw the error message on as a second arg
return nil, error_messagevalue
end
end
end
local function top_level_encode(self, value, etc, options)
local val = encode_value(self, value, {}, etc, options)
if val == nil then
--PRIVATE("may need to revert to the previous public verison if I can't figure out what the guy wanted")
return val
else
return val
end
end
function OBJDEF:encode(value, etc, options)
end
return top_level_encodeencode_value(self, value, {}, etc, options)
end
end
return top_level_encodeencode_value(self, value, {}, etc, options)
end
--
-- Version history:
--
-- 20161103.20 Used to silently ignore trailing garbage when decoding. Now fails via JSON:onTrailingGarbage()
-- http://seriot.ch/parsing_json.php
--
-- Built-in error message about "expected comma or ']'" had mistakenly referred to '['
--
-- Updated the built-in error reporting to refer to bytes rather than characters.
--
-- The decode() method no longer assumes that error handlers abort.
--
-- Made the VERSION string a string instead of a number
--
--
-- 20160916.19 Fixed the isNumber.__index assignment (thanks to Jack Taylor)
|