commit
a6a661e924
78
json11.cpp
78
json11.cpp
|
@ -338,6 +338,7 @@ struct JsonParser {
|
||||||
size_t i;
|
size_t i;
|
||||||
string &err;
|
string &err;
|
||||||
bool failed;
|
bool failed;
|
||||||
|
const JsonParse strategy;
|
||||||
|
|
||||||
/* fail(msg, err_ret = Json())
|
/* fail(msg, err_ret = Json())
|
||||||
*
|
*
|
||||||
|
@ -364,13 +365,74 @@ struct JsonParser {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* consume_comment()
|
||||||
|
*
|
||||||
|
* Advance comments (c-style inline and multiline).
|
||||||
|
*/
|
||||||
|
bool consume_comment() {
|
||||||
|
bool comment_found = false;
|
||||||
|
if (str[i] == '/') {
|
||||||
|
i++;
|
||||||
|
if (i == str.size())
|
||||||
|
return fail("unexpected end of input inside comment", 0);
|
||||||
|
if (str[i] == '/') { // inline comment
|
||||||
|
i++;
|
||||||
|
if (i == str.size())
|
||||||
|
return fail("unexpected end of input inside inline comment", 0);
|
||||||
|
// advance until next line
|
||||||
|
while (str[i] != '\n') {
|
||||||
|
i++;
|
||||||
|
if (i == str.size())
|
||||||
|
return fail("unexpected end of input inside inline comment", 0);
|
||||||
|
}
|
||||||
|
comment_found = true;
|
||||||
|
}
|
||||||
|
else if (str[i] == '*') { // multiline comment
|
||||||
|
i++;
|
||||||
|
if (i > str.size()-2)
|
||||||
|
return fail("unexpected end of input inside multi-line comment", 0);
|
||||||
|
// advance until closing tokens
|
||||||
|
while (!(str[i] == '*' && str[i+1] == '/')) {
|
||||||
|
i++;
|
||||||
|
if (i > str.size()-2)
|
||||||
|
return fail(
|
||||||
|
"unexpected end of input inside multi-line comment", 0);
|
||||||
|
}
|
||||||
|
i += 2;
|
||||||
|
if (i == str.size())
|
||||||
|
return fail(
|
||||||
|
"unexpected end of input inside multi-line comment", 0);
|
||||||
|
comment_found = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return fail("malformed comment", 0);
|
||||||
|
}
|
||||||
|
return comment_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* consume_garbage()
|
||||||
|
*
|
||||||
|
* Advance until the current character is non-whitespace and non-comment.
|
||||||
|
*/
|
||||||
|
void consume_garbage() {
|
||||||
|
consume_whitespace();
|
||||||
|
if(strategy == JsonParse::COMMENTS) {
|
||||||
|
bool comment_found = false;
|
||||||
|
do {
|
||||||
|
comment_found = consume_comment();
|
||||||
|
consume_whitespace();
|
||||||
|
}
|
||||||
|
while(comment_found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* get_next_token()
|
/* get_next_token()
|
||||||
*
|
*
|
||||||
* Return the next non-whitespace character. If the end of the input is reached,
|
* Return the next non-whitespace character. If the end of the input is reached,
|
||||||
* flag an error and return 0.
|
* flag an error and return 0.
|
||||||
*/
|
*/
|
||||||
char get_next_token() {
|
char get_next_token() {
|
||||||
consume_whitespace();
|
consume_garbage();
|
||||||
if (i == str.size())
|
if (i == str.size())
|
||||||
return fail("unexpected end of input", 0);
|
return fail("unexpected end of input", 0);
|
||||||
|
|
||||||
|
@ -657,12 +719,12 @@ struct JsonParser {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Json Json::parse(const string &in, string &err) {
|
Json Json::parse(const string &in, string &err, JsonParse strategy) {
|
||||||
JsonParser parser { in, 0, err, false };
|
JsonParser parser { in, 0, err, false, strategy };
|
||||||
Json result = parser.parse_json(0);
|
Json result = parser.parse_json(0);
|
||||||
|
|
||||||
// Check for any trailing garbage
|
// Check for any trailing garbage
|
||||||
parser.consume_whitespace();
|
parser.consume_garbage();
|
||||||
if (parser.i != in.size())
|
if (parser.i != in.size())
|
||||||
return parser.fail("unexpected trailing " + esc(in[parser.i]));
|
return parser.fail("unexpected trailing " + esc(in[parser.i]));
|
||||||
|
|
||||||
|
@ -670,14 +732,16 @@ Json Json::parse(const string &in, string &err) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Documented in json11.hpp
|
// Documented in json11.hpp
|
||||||
vector<Json> Json::parse_multi(const string &in, string &err) {
|
vector<Json> Json::parse_multi(const string &in,
|
||||||
JsonParser parser { in, 0, err, false };
|
string &err,
|
||||||
|
JsonParse strategy) {
|
||||||
|
JsonParser parser { in, 0, err, false, strategy };
|
||||||
|
|
||||||
vector<Json> json_vec;
|
vector<Json> json_vec;
|
||||||
while (parser.i != in.size() && !parser.failed) {
|
while (parser.i != in.size() && !parser.failed) {
|
||||||
json_vec.push_back(parser.parse_json(0));
|
json_vec.push_back(parser.parse_json(0));
|
||||||
// Check for another object
|
// Check for another object
|
||||||
parser.consume_whitespace();
|
parser.consume_garbage();
|
||||||
}
|
}
|
||||||
return json_vec;
|
return json_vec;
|
||||||
}
|
}
|
||||||
|
|
19
json11.hpp
19
json11.hpp
|
@ -58,6 +58,10 @@
|
||||||
|
|
||||||
namespace json11 {
|
namespace json11 {
|
||||||
|
|
||||||
|
enum JsonParse {
|
||||||
|
STANDARD, COMMENTS
|
||||||
|
};
|
||||||
|
|
||||||
class JsonValue;
|
class JsonValue;
|
||||||
|
|
||||||
class Json final {
|
class Json final {
|
||||||
|
@ -145,17 +149,24 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse. If parse fails, return Json() and assign an error message to err.
|
// Parse. If parse fails, return Json() and assign an error message to err.
|
||||||
static Json parse(const std::string & in, std::string & err);
|
static Json parse(const std::string & in,
|
||||||
static Json parse(const char * in, std::string & err) {
|
std::string & err,
|
||||||
|
JsonParse strategy = JsonParse::STANDARD);
|
||||||
|
static Json parse(const char * in,
|
||||||
|
std::string & err,
|
||||||
|
JsonParse strategy = JsonParse::STANDARD) {
|
||||||
if (in) {
|
if (in) {
|
||||||
return parse(std::string(in), err);
|
return parse(std::string(in), err, strategy);
|
||||||
} else {
|
} else {
|
||||||
err = "null input";
|
err = "null input";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Parse multiple objects, concatenated or separated by whitespace
|
// Parse multiple objects, concatenated or separated by whitespace
|
||||||
static std::vector<Json> parse_multi(const std::string & in, std::string & err);
|
static std::vector<Json> parse_multi(
|
||||||
|
const std::string & in,
|
||||||
|
std::string & err,
|
||||||
|
JsonParse strategy = JsonParse::STANDARD);
|
||||||
|
|
||||||
bool operator== (const Json &rhs) const;
|
bool operator== (const Json &rhs) const;
|
||||||
bool operator< (const Json &rhs) const;
|
bool operator< (const Json &rhs) const;
|
||||||
|
|
80
test.cpp
80
test.cpp
|
@ -58,6 +58,86 @@ int main(int argc, char **argv) {
|
||||||
std::cout << " - " << k.dump() << "\n";
|
std::cout << " - " << k.dump() << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const string comment_test = R"({
|
||||||
|
// comment /* with nested comment */
|
||||||
|
"a": 1,
|
||||||
|
// comment
|
||||||
|
// continued
|
||||||
|
"b": "text",
|
||||||
|
/* multi
|
||||||
|
line
|
||||||
|
comment */
|
||||||
|
// and single-line comment
|
||||||
|
"c": [1, 2, 3]
|
||||||
|
})";
|
||||||
|
|
||||||
|
string err_comment;
|
||||||
|
auto json_comment = Json::parse(
|
||||||
|
comment_test, err_comment, JsonParse::COMMENTS);
|
||||||
|
if (!err_comment.empty()) {
|
||||||
|
printf("Failed: %s\n", err_comment.c_str());
|
||||||
|
} else {
|
||||||
|
printf("Result: %s\n", json_comment.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
string failing_comment_test = R"({
|
||||||
|
/* bad comment
|
||||||
|
"a": 1,
|
||||||
|
})";
|
||||||
|
|
||||||
|
string err_failing_comment;
|
||||||
|
auto json_failing_comment = Json::parse(
|
||||||
|
failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
|
||||||
|
if (!err_failing_comment.empty()) {
|
||||||
|
printf("Failed: %s\n", err_failing_comment.c_str());
|
||||||
|
} else {
|
||||||
|
printf("Result: %s\n", json_failing_comment.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
failing_comment_test = R"({
|
||||||
|
/ / bad comment })";
|
||||||
|
|
||||||
|
json_failing_comment = Json::parse(
|
||||||
|
failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
|
||||||
|
if (!err_failing_comment.empty()) {
|
||||||
|
printf("Failed: %s\n", err_failing_comment.c_str());
|
||||||
|
} else {
|
||||||
|
printf("Result: %s\n", json_failing_comment.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
failing_comment_test = R"({// bad comment })";
|
||||||
|
|
||||||
|
json_failing_comment = Json::parse(
|
||||||
|
failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
|
||||||
|
if (!err_failing_comment.empty()) {
|
||||||
|
printf("Failed: %s\n", err_failing_comment.c_str());
|
||||||
|
} else {
|
||||||
|
printf("Result: %s\n", json_failing_comment.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
failing_comment_test = R"({
|
||||||
|
"a": 1
|
||||||
|
}/)";
|
||||||
|
|
||||||
|
json_failing_comment = Json::parse(
|
||||||
|
failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
|
||||||
|
if (!err_failing_comment.empty()) {
|
||||||
|
printf("Failed: %s\n", err_failing_comment.c_str());
|
||||||
|
} else {
|
||||||
|
printf("Result: %s\n", json_failing_comment.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
failing_comment_test = R"({/* bad
|
||||||
|
comment *})";
|
||||||
|
|
||||||
|
json_failing_comment = Json::parse(
|
||||||
|
failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
|
||||||
|
if (!err_failing_comment.empty()) {
|
||||||
|
printf("Failed: %s\n", err_failing_comment.c_str());
|
||||||
|
} else {
|
||||||
|
printf("Result: %s\n", json_failing_comment.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
std::list<int> l1 { 1, 2, 3 };
|
std::list<int> l1 { 1, 2, 3 };
|
||||||
std::vector<int> l2 { 1, 2, 3 };
|
std::vector<int> l2 { 1, 2, 3 };
|
||||||
std::set<int> l3 { 1, 2, 3 };
|
std::set<int> l3 { 1, 2, 3 };
|
||||||
|
|
Loading…
Reference in New Issue