mirror of
https://github.com/qpdf/qpdf.git
synced 2025-01-03 07:12:28 +00:00
Change JSON parser to parse from an InputSource
This commit is contained in:
parent
e5f3910c3e
commit
05fda4afa2
3
TODO
3
TODO
@ -51,9 +51,6 @@ library, when context is available, to have a pipeline rather than a
|
|||||||
FILE* or std::ostream. This makes it possible for people to capture
|
FILE* or std::ostream. This makes it possible for people to capture
|
||||||
output more flexibly.
|
output more flexibly.
|
||||||
|
|
||||||
JSON::parse should work from an InputSource. BufferInputSource can
|
|
||||||
already start with a std::string.
|
|
||||||
|
|
||||||
Have a json blob defined by a function that takes a pipeline and
|
Have a json blob defined by a function that takes a pipeline and
|
||||||
writes data to the pipeline. It's writer should create a Pl_Base64 ->
|
writes data to the pipeline. It's writer should create a Pl_Base64 ->
|
||||||
Pl_Concatenate in front of the pipeline passed to write and call the
|
Pl_Concatenate in front of the pipeline passed to write and call the
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class Pipeline;
|
class Pipeline;
|
||||||
|
class InputSource;
|
||||||
|
|
||||||
class JSON
|
class JSON
|
||||||
{
|
{
|
||||||
@ -249,10 +250,13 @@ class JSON
|
|||||||
virtual bool arrayItem(JSON const& value) = 0;
|
virtual bool arrayItem(JSON const& value) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a JSON object from a string. See above for information
|
// Create a JSON object from a string.
|
||||||
// about how to use the Reactor.
|
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
static JSON parse(std::string const&, Reactor* reactor = nullptr);
|
static JSON parse(std::string const&);
|
||||||
|
// Create a JSON object from an input source. See above for
|
||||||
|
// information about how to use the Reactor.
|
||||||
|
QPDF_DLL
|
||||||
|
static JSON parse(InputSource&, Reactor* reactor = nullptr);
|
||||||
|
|
||||||
// parse calls setOffsets to set the inclusive start and
|
// parse calls setOffsets to set the inclusive start and
|
||||||
// non-inclusive end offsets of an object relative to its input
|
// non-inclusive end offsets of an object relative to its input
|
||||||
|
255
libqpdf/JSON.cc
255
libqpdf/JSON.cc
@ -1,8 +1,7 @@
|
|||||||
#include <qpdf/JSON.hh>
|
#include <qpdf/JSON.hh>
|
||||||
|
|
||||||
#include <qpdf/Pipeline.hh>
|
#include <qpdf/BufferInputSource.hh>
|
||||||
#include <qpdf/Pl_String.hh>
|
#include <qpdf/Pl_String.hh>
|
||||||
#include <qpdf/QIntC.hh>
|
|
||||||
#include <qpdf/QTC.hh>
|
#include <qpdf/QTC.hh>
|
||||||
#include <qpdf/QUtil.hh>
|
#include <qpdf/QUtil.hh>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@ -521,7 +520,8 @@ namespace
|
|||||||
class JSONParser
|
class JSONParser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
JSONParser(JSON::Reactor* reactor) :
|
JSONParser(InputSource& is, JSON::Reactor* reactor) :
|
||||||
|
is(is),
|
||||||
reactor(reactor),
|
reactor(reactor),
|
||||||
lex_state(ls_top),
|
lex_state(ls_top),
|
||||||
number_before_point(0),
|
number_before_point(0),
|
||||||
@ -529,16 +529,16 @@ namespace
|
|||||||
number_after_e(0),
|
number_after_e(0),
|
||||||
number_saw_point(false),
|
number_saw_point(false),
|
||||||
number_saw_e(false),
|
number_saw_e(false),
|
||||||
cstr(nullptr),
|
bytes(0),
|
||||||
end(nullptr),
|
p(buf),
|
||||||
tok_start(nullptr),
|
u_count(0),
|
||||||
tok_end(nullptr),
|
offset(0),
|
||||||
p(nullptr),
|
done(false),
|
||||||
parser_state(ps_top)
|
parser_state(ps_top)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<JSON> parse(std::string const& s);
|
std::shared_ptr<JSON> parse();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void getToken();
|
void getToken();
|
||||||
@ -564,8 +564,10 @@ namespace
|
|||||||
ls_alpha,
|
ls_alpha,
|
||||||
ls_string,
|
ls_string,
|
||||||
ls_backslash,
|
ls_backslash,
|
||||||
|
ls_u4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
InputSource& is;
|
||||||
JSON::Reactor* reactor;
|
JSON::Reactor* reactor;
|
||||||
lex_state_e lex_state;
|
lex_state_e lex_state;
|
||||||
size_t number_before_point;
|
size_t number_before_point;
|
||||||
@ -573,11 +575,13 @@ namespace
|
|||||||
size_t number_after_e;
|
size_t number_after_e;
|
||||||
bool number_saw_point;
|
bool number_saw_point;
|
||||||
bool number_saw_e;
|
bool number_saw_e;
|
||||||
char const* cstr;
|
char buf[16384];
|
||||||
char const* end;
|
size_t bytes;
|
||||||
char const* tok_start;
|
|
||||||
char const* tok_end;
|
|
||||||
char const* p;
|
char const* p;
|
||||||
|
size_t u_count;
|
||||||
|
size_t offset;
|
||||||
|
bool done;
|
||||||
|
std::string token;
|
||||||
parser_state_e parser_state;
|
parser_state_e parser_state;
|
||||||
std::vector<std::shared_ptr<JSON>> stack;
|
std::vector<std::shared_ptr<JSON>> stack;
|
||||||
std::vector<parser_state_e> ps_stack;
|
std::vector<parser_state_e> ps_stack;
|
||||||
@ -661,28 +665,35 @@ JSONParser::decode_string(std::string const& str)
|
|||||||
void
|
void
|
||||||
JSONParser::getToken()
|
JSONParser::getToken()
|
||||||
{
|
{
|
||||||
while (p < end) {
|
enum { append, ignore, reread } action = append;
|
||||||
|
bool ready = false;
|
||||||
|
token.clear();
|
||||||
|
while (!done) {
|
||||||
|
if (p == (buf + bytes)) {
|
||||||
|
p = buf;
|
||||||
|
bytes = is.read(buf, sizeof(buf));
|
||||||
|
if (bytes == 0) {
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (*p == 0) {
|
if (*p == 0) {
|
||||||
QTC::TC("libtests", "JSON parse null character");
|
QTC::TC("libtests", "JSON parse null character");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: null character at offset " +
|
"JSON: null character at offset " +
|
||||||
QUtil::int_to_string(p - cstr));
|
QUtil::uint_to_string(offset));
|
||||||
}
|
}
|
||||||
|
action = append;
|
||||||
switch (lex_state) {
|
switch (lex_state) {
|
||||||
case ls_top:
|
case ls_top:
|
||||||
if (*p == '"') {
|
if (*p == '"') {
|
||||||
tok_start = p;
|
|
||||||
tok_end = nullptr;
|
|
||||||
lex_state = ls_string;
|
lex_state = ls_string;
|
||||||
} else if (QUtil::is_space(*p)) {
|
} else if (QUtil::is_space(*p)) {
|
||||||
// ignore
|
action = ignore;
|
||||||
} else if ((*p >= 'a') && (*p <= 'z')) {
|
} else if ((*p >= 'a') && (*p <= 'z')) {
|
||||||
tok_start = p;
|
|
||||||
tok_end = nullptr;
|
|
||||||
lex_state = ls_alpha;
|
lex_state = ls_alpha;
|
||||||
} else if (*p == '-') {
|
} else if (*p == '-') {
|
||||||
tok_start = p;
|
|
||||||
tok_end = nullptr;
|
|
||||||
lex_state = ls_number;
|
lex_state = ls_number;
|
||||||
number_before_point = 0;
|
number_before_point = 0;
|
||||||
number_after_point = 0;
|
number_after_point = 0;
|
||||||
@ -690,8 +701,6 @@ JSONParser::getToken()
|
|||||||
number_saw_point = false;
|
number_saw_point = false;
|
||||||
number_saw_e = false;
|
number_saw_e = false;
|
||||||
} else if ((*p >= '0') && (*p <= '9')) {
|
} else if ((*p >= '0') && (*p <= '9')) {
|
||||||
tok_start = p;
|
|
||||||
tok_end = nullptr;
|
|
||||||
lex_state = ls_number;
|
lex_state = ls_number;
|
||||||
number_before_point = 1;
|
number_before_point = 1;
|
||||||
number_after_point = 0;
|
number_after_point = 0;
|
||||||
@ -699,8 +708,6 @@ JSONParser::getToken()
|
|||||||
number_saw_point = false;
|
number_saw_point = false;
|
||||||
number_saw_e = false;
|
number_saw_e = false;
|
||||||
} else if (*p == '.') {
|
} else if (*p == '.') {
|
||||||
tok_start = p;
|
|
||||||
tok_end = nullptr;
|
|
||||||
lex_state = ls_number;
|
lex_state = ls_number;
|
||||||
number_before_point = 0;
|
number_before_point = 0;
|
||||||
number_after_point = 0;
|
number_after_point = 0;
|
||||||
@ -708,12 +715,11 @@ JSONParser::getToken()
|
|||||||
number_saw_point = true;
|
number_saw_point = true;
|
||||||
number_saw_e = false;
|
number_saw_e = false;
|
||||||
} else if (strchr("{}[]:,", *p)) {
|
} else if (strchr("{}[]:,", *p)) {
|
||||||
tok_start = p;
|
ready = true;
|
||||||
tok_end = p + 1;
|
|
||||||
} else {
|
} else {
|
||||||
QTC::TC("libtests", "JSON parse bad character");
|
QTC::TC("libtests", "JSON parse bad character");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": unexpected character " + std::string(p, 1));
|
": unexpected character " + std::string(p, 1));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -731,12 +737,12 @@ JSONParser::getToken()
|
|||||||
if (number_saw_e) {
|
if (number_saw_e) {
|
||||||
QTC::TC("libtests", "JSON parse point after e");
|
QTC::TC("libtests", "JSON parse point after e");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": numeric literal: decimal point after e");
|
": numeric literal: decimal point after e");
|
||||||
} else if (number_saw_point) {
|
} else if (number_saw_point) {
|
||||||
QTC::TC("libtests", "JSON parse duplicate point");
|
QTC::TC("libtests", "JSON parse duplicate point");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": numeric literal: decimal point already seen");
|
": numeric literal: decimal point already seen");
|
||||||
} else {
|
} else {
|
||||||
number_saw_point = true;
|
number_saw_point = true;
|
||||||
@ -745,7 +751,7 @@ JSONParser::getToken()
|
|||||||
if (number_saw_e) {
|
if (number_saw_e) {
|
||||||
QTC::TC("libtests", "JSON parse duplicate e");
|
QTC::TC("libtests", "JSON parse duplicate e");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": numeric literal: e already seen");
|
": numeric literal: e already seen");
|
||||||
} else {
|
} else {
|
||||||
number_saw_e = true;
|
number_saw_e = true;
|
||||||
@ -756,18 +762,19 @@ JSONParser::getToken()
|
|||||||
} else {
|
} else {
|
||||||
QTC::TC("libtests", "JSON parse unexpected sign");
|
QTC::TC("libtests", "JSON parse unexpected sign");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": numeric literal: unexpected sign");
|
": numeric literal: unexpected sign");
|
||||||
}
|
}
|
||||||
} else if (QUtil::is_space(*p)) {
|
} else if (QUtil::is_space(*p)) {
|
||||||
tok_end = p;
|
action = ignore;
|
||||||
|
ready = true;
|
||||||
} else if (strchr("{}[]:,", *p)) {
|
} else if (strchr("{}[]:,", *p)) {
|
||||||
tok_end = p;
|
action = reread;
|
||||||
--p;
|
ready = true;
|
||||||
} else {
|
} else {
|
||||||
QTC::TC("libtests", "JSON parse numeric bad character");
|
QTC::TC("libtests", "JSON parse numeric bad character");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": numeric literal: unexpected character " +
|
": numeric literal: unexpected character " +
|
||||||
std::string(p, 1));
|
std::string(p, 1));
|
||||||
}
|
}
|
||||||
@ -777,21 +784,22 @@ JSONParser::getToken()
|
|||||||
if ((*p >= 'a') && (*p <= 'z')) {
|
if ((*p >= 'a') && (*p <= 'z')) {
|
||||||
// okay
|
// okay
|
||||||
} else if (QUtil::is_space(*p)) {
|
} else if (QUtil::is_space(*p)) {
|
||||||
tok_end = p;
|
action = ignore;
|
||||||
|
ready = true;
|
||||||
} else if (strchr("{}[]:,", *p)) {
|
} else if (strchr("{}[]:,", *p)) {
|
||||||
tok_end = p;
|
action = reread;
|
||||||
--p;
|
ready = true;
|
||||||
} else {
|
} else {
|
||||||
QTC::TC("libtests", "JSON parse keyword bad character");
|
QTC::TC("libtests", "JSON parse keyword bad character");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": keyword: unexpected character " + std::string(p, 1));
|
": keyword: unexpected character " + std::string(p, 1));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ls_string:
|
case ls_string:
|
||||||
if (*p == '"') {
|
if (*p == '"') {
|
||||||
tok_end = p + 1;
|
ready = true;
|
||||||
} else if (*p == '\\') {
|
} else if (*p == '\\') {
|
||||||
lex_state = ls_backslash;
|
lex_state = ls_backslash;
|
||||||
}
|
}
|
||||||
@ -802,56 +810,70 @@ JSONParser::getToken()
|
|||||||
if (strchr("\\\"/bfnrt", *p)) {
|
if (strchr("\\\"/bfnrt", *p)) {
|
||||||
lex_state = ls_string;
|
lex_state = ls_string;
|
||||||
} else if (*p == 'u') {
|
} else if (*p == 'u') {
|
||||||
if (p + 4 >= end) {
|
lex_state = ls_u4;
|
||||||
QTC::TC("libtests", "JSON parse premature end of u");
|
u_count = 0;
|
||||||
throw std::runtime_error(
|
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
|
||||||
": \\u must be followed by four characters");
|
|
||||||
}
|
|
||||||
for (size_t i = 1; i <= 4; ++i) {
|
|
||||||
if (!QUtil::is_hex_digit(p[i])) {
|
|
||||||
QTC::TC("libtests", "JSON parse bad hex after u");
|
|
||||||
throw std::runtime_error(
|
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
|
||||||
": \\u must be followed by four hex digits");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p += 4;
|
|
||||||
lex_state = ls_string;
|
|
||||||
} else {
|
} else {
|
||||||
QTC::TC("libtests", "JSON parse backslash bad character");
|
QTC::TC("libtests", "JSON parse backslash bad character");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": invalid character after backslash: " +
|
": invalid character after backslash: " +
|
||||||
std::string(p, 1));
|
std::string(p, 1));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ls_u4:
|
||||||
|
if (!QUtil::is_hex_digit(*p)) {
|
||||||
|
QTC::TC("libtests", "JSON parse bad hex after u");
|
||||||
|
throw std::runtime_error(
|
||||||
|
"JSON: offset " +
|
||||||
|
QUtil::uint_to_string(offset - u_count - 1) +
|
||||||
|
": \\u must be followed by four hex digits");
|
||||||
|
}
|
||||||
|
if (++u_count == 4) {
|
||||||
|
lex_state = ls_string;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
++p;
|
switch (action) {
|
||||||
if (tok_start && tok_end) {
|
case reread:
|
||||||
|
break;
|
||||||
|
case append:
|
||||||
|
token.append(1, *p);
|
||||||
|
// fall through
|
||||||
|
case ignore:
|
||||||
|
++p;
|
||||||
|
++offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ready) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (p == end) {
|
if (done) {
|
||||||
if (tok_start && (!tok_end)) {
|
if ((!token.empty()) && (!ready)) {
|
||||||
switch (lex_state) {
|
switch (lex_state) {
|
||||||
case ls_top:
|
case ls_top:
|
||||||
// Can't happen
|
// Can't happen
|
||||||
throw std::logic_error(
|
throw std::logic_error("tok_start set in ls_top while parsing");
|
||||||
"tok_start set in ls_top while parsing " +
|
|
||||||
std::string(cstr));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ls_number:
|
case ls_number:
|
||||||
case ls_alpha:
|
case ls_alpha:
|
||||||
tok_end = p;
|
// okay
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ls_u4:
|
||||||
|
QTC::TC("libtests", "JSON parse premature end of u");
|
||||||
|
throw std::runtime_error(
|
||||||
|
"JSON: offset " +
|
||||||
|
QUtil::uint_to_string(offset - u_count - 1) +
|
||||||
|
": \\u must be followed by four characters");
|
||||||
|
|
||||||
case ls_string:
|
case ls_string:
|
||||||
case ls_backslash:
|
case ls_backslash:
|
||||||
QTC::TC("libtests", "JSON parse unterminated string");
|
QTC::TC("libtests", "JSON parse unterminated string");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": unterminated string");
|
": unterminated string");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -862,28 +884,25 @@ JSONParser::getToken()
|
|||||||
void
|
void
|
||||||
JSONParser::handleToken()
|
JSONParser::handleToken()
|
||||||
{
|
{
|
||||||
if (!(tok_start && tok_end)) {
|
if (token.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get token value.
|
|
||||||
std::string value(tok_start, tok_end);
|
|
||||||
|
|
||||||
if (parser_state == ps_done) {
|
if (parser_state == ps_done) {
|
||||||
QTC::TC("libtests", "JSON parse junk after object");
|
QTC::TC("libtests", "JSON parse junk after object");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": material follows end of object: " + value);
|
": material follows end of object: " + token);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Git string value
|
// Git string value
|
||||||
std::string s_value;
|
std::string s_value;
|
||||||
if (lex_state == ls_string) {
|
if (lex_state == ls_string) {
|
||||||
// Token includes the quotation marks
|
// Token includes the quotation marks
|
||||||
if (tok_end - tok_start < 2) {
|
if (token.length() < 2) {
|
||||||
throw std::logic_error("JSON string length < 2");
|
throw std::logic_error("JSON string length < 2");
|
||||||
}
|
}
|
||||||
s_value = decode_string(value);
|
s_value = decode_string(token);
|
||||||
}
|
}
|
||||||
// Based on the lexical state and value, figure out whether we are
|
// Based on the lexical state and value, figure out whether we are
|
||||||
// looking at an item or a delimiter. It will always be exactly
|
// looking at an item or a delimiter. It will always be exactly
|
||||||
@ -891,12 +910,14 @@ JSONParser::handleToken()
|
|||||||
|
|
||||||
std::shared_ptr<JSON> item;
|
std::shared_ptr<JSON> item;
|
||||||
char delimiter = '\0';
|
char delimiter = '\0';
|
||||||
|
// Already verified that token is not empty
|
||||||
|
char first_char = token.at(0);
|
||||||
switch (lex_state) {
|
switch (lex_state) {
|
||||||
case ls_top:
|
case ls_top:
|
||||||
switch (*tok_start) {
|
switch (first_char) {
|
||||||
case '{':
|
case '{':
|
||||||
item = std::make_shared<JSON>(JSON::makeDictionary());
|
item = std::make_shared<JSON>(JSON::makeDictionary());
|
||||||
item->setStart(QIntC::to_size(tok_start - cstr));
|
item->setStart(offset - token.length());
|
||||||
if (reactor) {
|
if (reactor) {
|
||||||
reactor->dictionaryStart();
|
reactor->dictionaryStart();
|
||||||
}
|
}
|
||||||
@ -904,14 +925,14 @@ JSONParser::handleToken()
|
|||||||
|
|
||||||
case '[':
|
case '[':
|
||||||
item = std::make_shared<JSON>(JSON::makeArray());
|
item = std::make_shared<JSON>(JSON::makeArray());
|
||||||
item->setStart(QIntC::to_size(tok_start - cstr));
|
item->setStart(offset - token.length());
|
||||||
if (reactor) {
|
if (reactor) {
|
||||||
reactor->arrayStart();
|
reactor->arrayStart();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
delimiter = *tok_start;
|
delimiter = first_char;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -920,38 +941,38 @@ JSONParser::handleToken()
|
|||||||
if (number_saw_point && (number_after_point == 0)) {
|
if (number_saw_point && (number_after_point == 0)) {
|
||||||
QTC::TC("libtests", "JSON parse decimal with no digits");
|
QTC::TC("libtests", "JSON parse decimal with no digits");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": decimal point with no digits");
|
": decimal point with no digits");
|
||||||
}
|
}
|
||||||
if ((number_before_point > 1) &&
|
if ((number_before_point > 1) &&
|
||||||
((tok_start[0] == '0') ||
|
((first_char == '0') ||
|
||||||
((tok_start[0] == '-') && (tok_start[1] == '0')))) {
|
((first_char == '-') && (token.at(1) == '0')))) {
|
||||||
QTC::TC("libtests", "JSON parse leading zero");
|
QTC::TC("libtests", "JSON parse leading zero");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": number with leading zero");
|
": number with leading zero");
|
||||||
}
|
}
|
||||||
if ((number_before_point == 0) && (number_after_point == 0)) {
|
if ((number_before_point == 0) && (number_after_point == 0)) {
|
||||||
QTC::TC("libtests", "JSON parse number no digits");
|
QTC::TC("libtests", "JSON parse number no digits");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": number with no digits");
|
": number with no digits");
|
||||||
}
|
}
|
||||||
item = std::make_shared<JSON>(JSON::makeNumber(value));
|
item = std::make_shared<JSON>(JSON::makeNumber(token));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ls_alpha:
|
case ls_alpha:
|
||||||
if (value == "true") {
|
if (token == "true") {
|
||||||
item = std::make_shared<JSON>(JSON::makeBool(true));
|
item = std::make_shared<JSON>(JSON::makeBool(true));
|
||||||
} else if (value == "false") {
|
} else if (token == "false") {
|
||||||
item = std::make_shared<JSON>(JSON::makeBool(false));
|
item = std::make_shared<JSON>(JSON::makeBool(false));
|
||||||
} else if (value == "null") {
|
} else if (token == "null") {
|
||||||
item = std::make_shared<JSON>(JSON::makeNull());
|
item = std::make_shared<JSON>(JSON::makeNull());
|
||||||
} else {
|
} else {
|
||||||
QTC::TC("libtests", "JSON parse invalid keyword");
|
QTC::TC("libtests", "JSON parse invalid keyword");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": invalid keyword " + value);
|
": invalid keyword " + token);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -960,7 +981,9 @@ JSONParser::handleToken()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ls_backslash:
|
case ls_backslash:
|
||||||
throw std::logic_error("tok_end is set while state = ls_backslash");
|
case ls_u4:
|
||||||
|
throw std::logic_error(
|
||||||
|
"tok_end is set while state = ls_backslash or ls_u4");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -981,21 +1004,21 @@ JSONParser::handleToken()
|
|||||||
case ps_dict_after_key:
|
case ps_dict_after_key:
|
||||||
QTC::TC("libtests", "JSON parse expected colon");
|
QTC::TC("libtests", "JSON parse expected colon");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": expected ':'");
|
": expected ':'");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ps_dict_after_item:
|
case ps_dict_after_item:
|
||||||
QTC::TC("libtests", "JSON parse expected , or }");
|
QTC::TC("libtests", "JSON parse expected , or }");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": expected ',' or '}'");
|
": expected ',' or '}'");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ps_array_after_item:
|
case ps_array_after_item:
|
||||||
QTC::TC("libtests", "JSON parse expected, or ]");
|
QTC::TC("libtests", "JSON parse expected, or ]");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": expected ',' or ']'");
|
": expected ',' or ']'");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1004,7 +1027,7 @@ JSONParser::handleToken()
|
|||||||
if (lex_state != ls_string) {
|
if (lex_state != ls_string) {
|
||||||
QTC::TC("libtests", "JSON parse string as dict key");
|
QTC::TC("libtests", "JSON parse string as dict key");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": expect string as dictionary key");
|
": expect string as dictionary key");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1023,7 +1046,7 @@ JSONParser::handleToken()
|
|||||||
{
|
{
|
||||||
QTC::TC("libtests", "JSON parse unexpected }");
|
QTC::TC("libtests", "JSON parse unexpected }");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": unexpected dictionary end delimiter");
|
": unexpected dictionary end delimiter");
|
||||||
}
|
}
|
||||||
} else if (delimiter == ']') {
|
} else if (delimiter == ']') {
|
||||||
@ -1033,14 +1056,14 @@ JSONParser::handleToken()
|
|||||||
{
|
{
|
||||||
QTC::TC("libtests", "JSON parse unexpected ]");
|
QTC::TC("libtests", "JSON parse unexpected ]");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": unexpected array end delimiter");
|
": unexpected array end delimiter");
|
||||||
}
|
}
|
||||||
} else if (delimiter == ':') {
|
} else if (delimiter == ':') {
|
||||||
if (parser_state != ps_dict_after_key) {
|
if (parser_state != ps_dict_after_key) {
|
||||||
QTC::TC("libtests", "JSON parse unexpected :");
|
QTC::TC("libtests", "JSON parse unexpected :");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": unexpected colon");
|
": unexpected colon");
|
||||||
}
|
}
|
||||||
} else if (delimiter == ',') {
|
} else if (delimiter == ',') {
|
||||||
@ -1048,7 +1071,7 @@ JSONParser::handleToken()
|
|||||||
(parser_state == ps_array_after_item))) {
|
(parser_state == ps_array_after_item))) {
|
||||||
QTC::TC("libtests", "JSON parse unexpected ,");
|
QTC::TC("libtests", "JSON parse unexpected ,");
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": unexpected comma");
|
": unexpected comma");
|
||||||
}
|
}
|
||||||
} else if (delimiter != '\0') {
|
} else if (delimiter != '\0') {
|
||||||
@ -1074,7 +1097,7 @@ JSONParser::handleToken()
|
|||||||
next_state = ps_stack.back();
|
next_state = ps_stack.back();
|
||||||
ps_stack.pop_back();
|
ps_stack.pop_back();
|
||||||
auto tos = stack.back();
|
auto tos = stack.back();
|
||||||
tos->setEnd(QIntC::to_size(tok_end - cstr));
|
tos->setEnd(offset);
|
||||||
if (reactor) {
|
if (reactor) {
|
||||||
reactor->containerEnd(*tos);
|
reactor->containerEnd(*tos);
|
||||||
}
|
}
|
||||||
@ -1086,8 +1109,8 @@ JSONParser::handleToken()
|
|||||||
"JSONParser::handleToken: unexpected delimiter in transition");
|
"JSONParser::handleToken: unexpected delimiter in transition");
|
||||||
} else if (item.get()) {
|
} else if (item.get()) {
|
||||||
if (!(item->isArray() || item->isDictionary())) {
|
if (!(item->isArray() || item->isDictionary())) {
|
||||||
item->setStart(QIntC::to_size(tok_start - cstr));
|
item->setStart(offset - token.length());
|
||||||
item->setEnd(QIntC::to_size(tok_end - cstr));
|
item->setEnd(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<JSON> tos;
|
std::shared_ptr<JSON> tos;
|
||||||
@ -1149,23 +1172,17 @@ JSONParser::handleToken()
|
|||||||
}
|
}
|
||||||
if (ps_stack.size() > 500) {
|
if (ps_stack.size() > 500) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"JSON: offset " + QUtil::int_to_string(p - cstr) +
|
"JSON: offset " + QUtil::uint_to_string(offset) +
|
||||||
": maximum object depth exceeded");
|
": maximum object depth exceeded");
|
||||||
}
|
}
|
||||||
parser_state = next_state;
|
parser_state = next_state;
|
||||||
tok_start = nullptr;
|
|
||||||
tok_end = nullptr;
|
|
||||||
lex_state = ls_top;
|
lex_state = ls_top;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<JSON>
|
std::shared_ptr<JSON>
|
||||||
JSONParser::parse(std::string const& s)
|
JSONParser::parse()
|
||||||
{
|
{
|
||||||
cstr = s.c_str();
|
while (!done) {
|
||||||
end = cstr + s.length();
|
|
||||||
p = cstr;
|
|
||||||
|
|
||||||
while (p < end) {
|
|
||||||
getToken();
|
getToken();
|
||||||
handleToken();
|
handleToken();
|
||||||
}
|
}
|
||||||
@ -1181,10 +1198,18 @@ JSONParser::parse(std::string const& s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
JSON
|
JSON
|
||||||
JSON::parse(std::string const& s, Reactor* reactor)
|
JSON::parse(InputSource& is, Reactor* reactor)
|
||||||
{
|
{
|
||||||
JSONParser jp(reactor);
|
JSONParser jp(is, reactor);
|
||||||
return *jp.parse(s);
|
return *jp.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON
|
||||||
|
JSON::parse(std::string const& s)
|
||||||
|
{
|
||||||
|
BufferInputSource bis("json input", s);
|
||||||
|
JSONParser jp(bis, nullptr);
|
||||||
|
return *jp.parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <qpdf/FileInputSource.hh>
|
||||||
#include <qpdf/JSON.hh>
|
#include <qpdf/JSON.hh>
|
||||||
#include <qpdf/QUtil.hh>
|
#include <qpdf/QUtil.hh>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@ -103,11 +104,8 @@ main(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
std::shared_ptr<char> buf;
|
FileInputSource is(filename);
|
||||||
size_t size;
|
std::cout << JSON::parse(is, reactor.get()).unparse() << std::endl;
|
||||||
QUtil::read_file_into_memory(filename, buf, size);
|
|
||||||
std::string s(buf.get(), size);
|
|
||||||
std::cout << JSON::parse(s, reactor.get()).unparse() << std::endl;
|
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
std::cerr << "exception: " << filename << ": " << e.what() << std::endl;
|
std::cerr << "exception: " << filename << ": " << e.what() << std::endl;
|
||||||
return 2;
|
return 2;
|
||||||
|
Loading…
Reference in New Issue
Block a user