Skip to content

Commit

Permalink
Improve handling of errors in comments, and make comment tests self-c…
Browse files Browse the repository at this point in the history
…hecking.

This fixes a bug where trailing inline comments were rejected, and also several cases where comment parsing would fail, but the overall parser would return a non-null value.

Fixes dropbox#101
  • Loading branch information
Andrew Twyman committed Apr 3, 2017
1 parent 0e409dc commit 3bafee9
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 65 deletions.
25 changes: 13 additions & 12 deletions json11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -384,16 +384,12 @@ struct JsonParser final {
if (str[i] == '/') {
i++;
if (i == str.size())
return fail("unexpected end of input inside comment", false);
return fail("unexpected end of input after start of comment", false);
if (str[i] == '/') { // inline comment
i++;
if (i == str.size())
return fail("unexpected end of input inside inline comment", false);
// advance until next line
while (str[i] != '\n') {
// advance until next line, or end of input
while (i < str.size() && str[i] != '\n') {
i++;
if (i == str.size())
return fail("unexpected end of input inside inline comment", false);
}
comment_found = true;
}
Expand All @@ -409,9 +405,6 @@ struct JsonParser final {
"unexpected end of input inside multi-line comment", false);
}
i += 2;
if (i == str.size())
return fail(
"unexpected end of input inside multi-line comment", false);
comment_found = true;
}
else
Expand All @@ -430,6 +423,7 @@ struct JsonParser final {
bool comment_found = false;
do {
comment_found = consume_comment();
if (failed) return;
consume_whitespace();
}
while(comment_found);
Expand All @@ -443,6 +437,7 @@ struct JsonParser final {
*/
char get_next_token() {
consume_garbage();
if (failed) return (char)0;
if (i == str.size())
return fail("unexpected end of input", (char)0);

Expand Down Expand Up @@ -736,6 +731,8 @@ Json Json::parse(const string &in, string &err, JsonParse strategy) {

// Check for any trailing garbage
parser.consume_garbage();
if (parser.failed)
return Json();
if (parser.i != in.size())
return parser.fail("unexpected trailing " + esc(in[parser.i]));

Expand All @@ -752,10 +749,14 @@ vector<Json> Json::parse_multi(const string &in,
vector<Json> json_vec;
while (parser.i != in.size() && !parser.failed) {
json_vec.push_back(parser.parse_json(0));
if (parser.failed)
break;

// Check for another object
parser.consume_garbage();
if (!parser.failed)
parser_stop_pos = parser.i;
if (parser.failed)
break;
parser_stop_pos = parser.i;
}
return json_vec;
}
Expand Down
101 changes: 48 additions & 53 deletions test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ JSON11_TEST_CASE(json11_test) {
R"({"k1":"v1", "k2":42, "k3":["a",123,true,false,null]})";

string err;
auto json = Json::parse(simple_test, err);
const auto json = Json::parse(simple_test, err);

std::cout << "k1: " << json["k1"].string_value() << "\n";
std::cout << "k3: " << json["k3"].dump() << "\n";
Expand All @@ -73,86 +73,77 @@ JSON11_TEST_CASE(json11_test) {
std::cout << " - " << k.dump() << "\n";
}

const string comment_test = R"({
string comment_test = R"({
// comment /* with nested comment */
"a": 1,
// comment
// continued
"b": "text",
/* multi
line
comment */
comment
// line-comment-inside-multiline-comment
*/
// and single-line comment
// and single-line comment /* multiline inside single line */
"c": [1, 2, 3]
// and single-line comment at end of object
})";

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());
}
JSON11_TEST_ASSERT(!json_comment.is_null());
JSON11_TEST_ASSERT(err_comment.empty());

string failing_comment_test = R"({
/* bad comment
"a": 1,
// another comment to make C parsers which don't understand raw strings happy */
})";
comment_test = "{\"a\": 1}//trailing line comment";
json_comment = Json::parse(
comment_test, err_comment, JsonParse::COMMENTS);
JSON11_TEST_ASSERT(!json_comment.is_null());
JSON11_TEST_ASSERT(err_comment.empty());

comment_test = "{\"a\": 1}/*trailing multi-line comment*/";
json_comment = Json::parse(
comment_test, err_comment, JsonParse::COMMENTS);
JSON11_TEST_ASSERT(!json_comment.is_null());
JSON11_TEST_ASSERT(err_comment.empty());

string failing_comment_test = "{\n/* unterminated comment\n\"a\": 1,\n}";
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 })";
JSON11_TEST_ASSERT(json_failing_comment.is_null());
JSON11_TEST_ASSERT(!err_failing_comment.empty());

failing_comment_test = "{\n/* unterminated trailing 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 })";
JSON11_TEST_ASSERT(json_failing_comment.is_null());
JSON11_TEST_ASSERT(!err_failing_comment.empty());

failing_comment_test = "{\n/ / 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
}/)";
JSON11_TEST_ASSERT(json_failing_comment.is_null());
JSON11_TEST_ASSERT(!err_failing_comment.empty());

failing_comment_test = "{// 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());
}
JSON11_TEST_ASSERT(json_failing_comment.is_null());
JSON11_TEST_ASSERT(!err_failing_comment.empty());

failing_comment_test = R"({/* bad
comment *})";
failing_comment_test = "{\n\"a\": 1\n}/";
json_failing_comment = Json::parse(
failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
JSON11_TEST_ASSERT(json_failing_comment.is_null());
JSON11_TEST_ASSERT(!err_failing_comment.empty());

failing_comment_test = "{/* bad\ncomment *}";
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());
}
JSON11_TEST_ASSERT(json_failing_comment.is_null());
JSON11_TEST_ASSERT(!err_failing_comment.empty());

std::list<int> l1 { 1, 2, 3 };
std::vector<int> l2 { 1, 2, 3 };
Expand All @@ -165,13 +156,14 @@ JSON11_TEST_CASE(json11_test) {
JSON11_TEST_ASSERT(Json(m1) == Json(m2));

// Json literals
Json obj = Json::object({
const Json obj = Json::object({
{ "k1", "v1" },
{ "k2", 42.0 },
{ "k3", Json::array({ "a", 123.0, true, false, nullptr }) },
});

std::cout << "obj: " << obj.dump() << "\n";
JSON11_TEST_ASSERT(obj.dump() == "{\"k1\": \"v1\", \"k2\": 42, \"k3\": [\"a\", 123, true, false, null]}");

JSON11_TEST_ASSERT(Json("a").number_value() == 0);
JSON11_TEST_ASSERT(Json("a").string_value() == "a");
Expand Down Expand Up @@ -231,13 +223,15 @@ JSON11_TEST_CASE(json11_test) {
}
}
}

Json my_json = Json::object {
{ "key1", "value1" },
{ "key2", false },
{ "key3", Json::array { 1, 2, 3 } },
};
std::string json_str = my_json.dump();
printf("%s\n", json_str.c_str());
std::string json_obj_str = my_json.dump();
std::cout << "json_obj_str: " << json_obj_str << "\n";
JSON11_TEST_ASSERT(json_obj_str == "{\"key1\": \"value1\", \"key2\": false, \"key3\": [1, 2, 3]}");

class Point {
public:
Expand All @@ -249,7 +243,8 @@ JSON11_TEST_CASE(json11_test) {

std::vector<Point> points = { { 1, 2 }, { 10, 20 }, { 100, 200 } };
std::string points_json = Json(points).dump();
printf("%s\n", points_json.c_str());
std::cout << "points_json: " << points_json << "\n";
JSON11_TEST_ASSERT(points_json == "[[1, 2], [10, 20], [100, 200]]");
}

#if JSON11_TEST_STANDALONE_MAIN
Expand Down

0 comments on commit 3bafee9

Please sign in to comment.