Merge pull request #42 from capitalaslash/detect_comments

Detect and ignore comments
mutable-v2
Andrew Twyman 2015-12-03 14:26:10 -08:00
commit a6a661e924
3 changed files with 166 additions and 11 deletions

View File

@ -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;
} }

View File

@ -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;

View File

@ -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 };