diff --git a/json11.cpp b/json11.cpp index 0aa125b..ebd7e93 100644 --- a/json11.cpp +++ b/json11.cpp @@ -338,6 +338,7 @@ struct JsonParser { size_t i; string &err; bool failed; + const JsonParse strategy; /* fail(msg, err_ret = Json()) * @@ -364,13 +365,74 @@ struct JsonParser { 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() * * Return the next non-whitespace character. If the end of the input is reached, * flag an error and return 0. */ char get_next_token() { - consume_whitespace(); + consume_garbage(); if (i == str.size()) return fail("unexpected end of input", 0); @@ -657,12 +719,12 @@ struct JsonParser { } }; -Json Json::parse(const string &in, string &err) { - JsonParser parser { in, 0, err, false }; +Json Json::parse(const string &in, string &err, JsonParse strategy) { + JsonParser parser { in, 0, err, false, strategy }; Json result = parser.parse_json(0); // Check for any trailing garbage - parser.consume_whitespace(); + parser.consume_garbage(); if (parser.i != in.size()) 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 -vector Json::parse_multi(const string &in, string &err) { - JsonParser parser { in, 0, err, false }; +vector Json::parse_multi(const string &in, + string &err, + JsonParse strategy) { + JsonParser parser { in, 0, err, false, strategy }; vector json_vec; while (parser.i != in.size() && !parser.failed) { json_vec.push_back(parser.parse_json(0)); // Check for another object - parser.consume_whitespace(); + parser.consume_garbage(); } return json_vec; } diff --git a/json11.hpp b/json11.hpp index fe9bba4..a99e241 100644 --- a/json11.hpp +++ b/json11.hpp @@ -58,6 +58,10 @@ namespace json11 { +enum JsonParse { + STANDARD, COMMENTS +}; + class JsonValue; class Json final { @@ -145,17 +149,24 @@ public: } // 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 char * in, std::string & err) { + static Json parse(const std::string & in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD); + static Json parse(const char * in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD) { if (in) { - return parse(std::string(in), err); + return parse(std::string(in), err, strategy); } else { err = "null input"; return nullptr; } } // Parse multiple objects, concatenated or separated by whitespace - static std::vector parse_multi(const std::string & in, std::string & err); + static std::vector 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; diff --git a/test.cpp b/test.cpp index 8b62aeb..bd60705 100644 --- a/test.cpp +++ b/test.cpp @@ -58,6 +58,86 @@ int main(int argc, char **argv) { 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 l1 { 1, 2, 3 }; std::vector l2 { 1, 2, 3 }; std::set l3 { 1, 2, 3 };