diff --git a/Makefile.am b/Makefile.am index f9594a5624..4d0811286e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -141,7 +141,7 @@ endif ### Tests (make check) -TESTS = tests/mantest tests/jqtest tests/shtest tests/utf8test tests/base64test +TESTS = tests/mantest tests/jqtest tests/shtest tests/utf8test tests/base64test tests/commenttest if !WIN32 TESTS += tests/optionaltest endif @@ -233,6 +233,7 @@ EXTRA_DIST = $(DOC_FILES) $(man_MANS) $(TESTS) $(TEST_LOG_COMPILER) \ tests/setup tests/torture/input0.json \ tests/optional.test tests/man.test tests/manonig.test \ tests/jq.test tests/onig.test tests/base64.test \ + tests/commenttest \ tests/utf8-truncate.jq tests/jq-f-test.sh \ tests/no-main-program.jq tests/yes-main-program.jq diff --git a/src/jv.h b/src/jv.h index 083509ec26..cf9b8401f0 100644 --- a/src/jv.h +++ b/src/jv.h @@ -234,6 +234,7 @@ enum { JV_PARSE_SEQ = 1, JV_PARSE_STREAMING = 2, JV_PARSE_STREAM_ERRORS = 4, + JV_PARSE_STRIP_COMMENTS = 8 }; jv jv_parse(const char* string); diff --git a/src/jv_parse.c b/src/jv_parse.c index 9755b8ac47..5f9375b046 100644 --- a/src/jv_parse.c +++ b/src/jv_parse.c @@ -31,6 +31,7 @@ enum last_seen { }; struct jv_parser { + pfunc (*scan)(struct jv_parser* p, char ch, jv* out); const char* curr_buf; int curr_buf_length; int curr_buf_pos; @@ -65,9 +66,14 @@ struct jv_parser { unsigned int last_ch_was_ws:1; }; +static pfunc scan_json(struct jv_parser* p, char ch, jv* out); +static pfunc scan_line_comment(struct jv_parser* p, char ch, jv* out); +static pfunc scan_slash_comment(struct jv_parser* p, char ch, jv* out); +static pfunc scan_c_comment(struct jv_parser* p, char ch, jv* out); static void parser_init(struct jv_parser* p, int flags) { p->flags = flags; + p->scan = scan_json; if ((p->flags & JV_PARSE_STREAMING)) { p->path = jv_array(); } else { @@ -639,12 +645,42 @@ static int stream_is_top_num(struct jv_parser* p) { #define is_top_num(p) \ (((p)->flags & JV_PARSE_STREAMING) ? stream_is_top_num((p)) : parse_is_top_num((p))) -static pfunc scan(struct jv_parser* p, char ch, jv* out) { - p->column++; - if (ch == '\n') { - p->line++; - p->column = 0; + +static pfunc scan_line_comment(struct jv_parser* p, char ch, jv* out) { + if(ch == '\n') + p->scan = scan_json; + return 0; +} + +static pfunc scan_c_comment_close(struct jv_parser* p, char ch, jv* out) { + if(ch == '/') { + p->scan = scan_json; + } else { + p->scan = scan_c_comment; + } + return 0; +} + +static pfunc scan_c_comment(struct jv_parser* p, char ch, jv* out) { + if(ch == '*') { + p->scan = scan_c_comment_close; + } + return 0; +} + +static pfunc scan_slash_comment(struct jv_parser* p, char ch, jv* out) { + if(ch == '/') { + p->scan = scan_line_comment; + return 0; } + if(ch == '*') { + p->scan = scan_c_comment; + return 0; + } + return "Incomplete comment token; slash must be followed by another slash or asterisk"; +} + +static pfunc scan_json(struct jv_parser* p, char ch, jv* out) { if ((p->flags & JV_PARSE_SEQ) && ch == '\036' /* ASCII RS; see draft-ietf-json-sequence-07 */) { if (check_truncation(p)) { @@ -662,9 +698,15 @@ static pfunc scan(struct jv_parser* p, char ch, jv* out) { *out = jv_invalid(); return OK; } + presult answer = 0; p->last_ch_was_ws = 0; if (p->st == JV_PARSER_NORMAL) { + if(ch == '/' && (p->flags & JV_PARSE_STRIP_COMMENTS)) { + p->scan = scan_slash_comment; + return answer; + } + chclass cls = classify(ch); if (cls == WHITESPACE) p->last_ch_was_ws = 1; @@ -705,6 +747,15 @@ static pfunc scan(struct jv_parser* p, char ch, jv* out) { return answer; } +static pfunc scan(struct jv_parser* p, char ch, jv* out) { + p->column++; + if (ch == '\n') { + p->line++; + p->column = 0; + } + return p->scan(p, ch, out); +} + struct jv_parser* jv_parser_new(int flags) { struct jv_parser* p = jv_mem_alloc(sizeof(struct jv_parser)); parser_init(p, flags); diff --git a/src/main.c b/src/main.c index 832330802e..73338741aa 100644 --- a/src/main.c +++ b/src/main.c @@ -94,6 +94,7 @@ static void usage(int code, int keep_it_short) { " --stream-errors implies --stream and report parse error as\n" " an array;\n" " --seq parse input/output as application/json-seq;\n" + " --strip-comments accept and strip comments from input;\n" " -f, --from-file file load filter from the file;\n" " -L directory search modules from the directory;\n" " --arg name value set $name to the string value;\n" @@ -471,6 +472,10 @@ int main(int argc, char* argv[]) { parser_flags |= JV_PARSE_STREAMING; continue; } + if (isoption(argv[i], 0, "strip-comments", &short_opts)) { + parser_flags |= JV_PARSE_STRIP_COMMENTS; + continue; + } if (isoption(argv[i], 0, "stream-errors", &short_opts)) { parser_flags |= JV_PARSE_STREAMING | JV_PARSE_STREAM_ERRORS; continue; diff --git a/tests/commenttest b/tests/commenttest new file mode 100755 index 0000000000..388f85ad49 --- /dev/null +++ b/tests/commenttest @@ -0,0 +1,15 @@ +#!/bin/sh + +. "${0%/*}/setup" "$@" + +if [ "$(echo '{"hi":"there"/*comment*/}' | $VALGRIND $Q $JQ --strip-comments '.hi')" != '"there"' ]; then + echo "C-style comment test failed" + exit 1 +fi + +if [ "$(printf '{"hi"://comment\n"there"}' | $VALGRIND $Q $JQ --strip-comments '.hi')" != '"there"' ]; then + echo "C++-style comment test failed" + exit 1 +fi + +exit 0