diff --git a/src/c4/yml/common.cpp b/src/c4/yml/common.cpp index 8719b35e..b72523d6 100644 --- a/src/c4/yml/common.cpp +++ b/src/c4/yml/common.cpp @@ -9,24 +9,34 @@ namespace c4 { namespace yml { #ifndef RYML_NO_DEFAULT_CALLBACKS -namespace { -void error_impl(const char* msg, size_t length, Location loc, void * /*user_data*/) +void report_error_impl(const char* msg, size_t length, Location loc, FILE *f) { + if(!f) + { + f = stderr; + } if(loc) { if(!loc.name.empty()) { - fprintf(stderr, "%.*s:", (int)loc.name.len, loc.name.str); + fprintf(f, "%.*s:", (int)loc.name.len, loc.name.str); } - fprintf(stderr, "%zu:%zu:", loc.line, loc.col); + fprintf(f, "%zu:%zu:", loc.line, loc.col); if(loc.offset) { - fprintf(stderr, " (%zuB):", loc.offset); + fprintf(f, " (%zuB):", loc.offset); } } - fprintf(stderr, "%.*s\n", (int)length, msg); - fflush(stderr); + fprintf(f, "ERROR: %.*s\n", (int)length, msg); + fflush(f); +} + +namespace { + +void error_impl(const char* msg, size_t length, Location loc, void * /*user_data*/) +{ + report_error_impl(msg, length, loc, nullptr); ::abort(); } diff --git a/src/c4/yml/common.hpp b/src/c4/yml/common.hpp index b509b53f..435838d6 100644 --- a/src/c4/yml/common.hpp +++ b/src/c4/yml/common.hpp @@ -124,17 +124,20 @@ struct Location : public LineCol * std::terminate()/std::abort(). */ using pfn_error = void (*)(const char* msg, size_t msg_len, Location location, void *user_data); +/** trigger an error: call the current error callback. */ void error(const char *msg, size_t msg_len, Location loc); +/** @overload error */ inline void error(const char *msg, size_t msg_len) { error(msg, msg_len, Location{}); } - +/** @overload error */ template inline void error(const char (&msg)[N], Location loc) { error(msg, N-1, loc); } +/** @overload error */ template inline void error(const char (&msg)[N]) { diff --git a/src/c4/yml/parse.cpp b/src/c4/yml/parse.cpp index cee6b03c..7525ed03 100644 --- a/src/c4/yml/parse.cpp +++ b/src/c4/yml/parse.cpp @@ -1645,22 +1645,18 @@ substr Parser::_scan_plain_scalar_expl(csubstr currscalar, csubstr peeked_line) { _c4dbgpf("rscalar[EXPL]: found special character '%c' at %zu, stopping: '%.*s'", peeked_line[pos], pos, _c4prsp(peeked_line.left_of(pos).trimr("\r\n"))); peeked_line = peeked_line.left_of(pos); - _advance_to_peeked(peeked_line); + _line_progressed(peeked_line.end() - m_state->line_contents.rem.begin()); break; } _c4dbgpf("rscalar[EXPL]: append another line, full: '%.*s'", _c4prsp(peeked_line.trimr("\r\n"))); if(!first) { - if(!_advance_to_peeked()) - { - break; - } + RYML_CHECK(_advance_to_peeked()); } peeked_line = _scan_to_next_nonempty_line(/*indentation*/0); if(peeked_line.empty()) { - _c4dbgp("rscalar[EXPL]: ... finished."); - break; + _c4err("expected token or continuation"); } pos = peeked_line.first_of(chars); first = false; @@ -1683,7 +1679,7 @@ substr Parser::_scan_plain_scalar_impl(csubstr currscalar, csubstr peeked_line, while(true) { _c4dbgpf("rscalar[IMPL]: continuing... ref_indentation=%zu", indentation); - if(peeked_line.begins_with("...")) + if(peeked_line.begins_with("...") || peeked_line.begins_with("---")) { _c4dbgpf("rscalar[IMPL]: document termination next -- bail now '%.*s'", _c4prsp(peeked_line.trimr("\r\n"))); break; @@ -1712,19 +1708,16 @@ substr Parser::_scan_plain_scalar_impl(csubstr currscalar, csubstr peeked_line, { _line_progressed(peeked_line.find(": ")); _c4err("': ' is not a valid token in plain flow (unquoted) scalars"); - break; } else if(peeked_line.ends_with(':')) { _line_progressed(peeked_line.find(':')); _c4err("lines cannot end with ':' in plain flow (unquoted) scalars"); - break; } else if(peeked_line.find(" #") != npos) { _line_progressed(peeked_line.find(" #")); _c4err("' #' is not a valid token in plain flow (unquoted) scalars"); - break; } _c4dbgpf("rscalar[IMPL]: append another line: (len=%zu)'%.*s'", peeked_line.len, _c4prsp(peeked_line.trimr("\r\n"))); @@ -1779,26 +1772,6 @@ csubstr Parser::_scan_to_next_nonempty_line(size_t indentation) return {}; } -// returns false when the file finished -bool Parser::_advance_to_peeked(csubstr peeked_part) -{ - RYML_ASSERT(m_state->line_contents.rem.contains(peeked_part)); - _line_progressed(peeked_part.end() - m_state->line_contents.rem.begin()); - if(m_state->line_contents.rem.empty()) - { - _line_ended(); // advances to the peeked-at line, consuming all remaining (probably newline) characters on the current line - RYML_ASSERT(m_state->line_contents.rem.first_of("\r\n") == csubstr::npos); - _c4dbgpf("advance to peeked: scan more... pos=%zu len=%zu", m_state->pos.offset, m_buf.len); - _scan_line(); // puts the peeked-at line in the buffer - if(_finished_file()) - { - _c4dbgp("rscalar[IMPL]: finished file!"); - return false; - } - } - return true; -} - // returns false when the file finished bool Parser::_advance_to_peeked() { @@ -1809,7 +1782,7 @@ bool Parser::_advance_to_peeked() _scan_line(); // puts the peeked-at line in the buffer if(_finished_file()) { - _c4dbgp("rscalar[IMPL]: finished file!"); + _c4dbgp("rscalar: finished file!"); return false; } return true; diff --git a/src/c4/yml/parse.hpp b/src/c4/yml/parse.hpp index 27738fc7..61fccf52 100644 --- a/src/c4/yml/parse.hpp +++ b/src/c4/yml/parse.hpp @@ -94,7 +94,6 @@ class Parser csubstr _peek_next_line(size_t pos=npos) const; bool _advance_to_peeked(); - bool _advance_to_peeked(csubstr peeked_portion); void _scan_line(); bool _scan_scalar(csubstr *scalar); diff --git a/test/plain_scalar.cpp b/test/plain_scalar.cpp index e44e33c3..f5b790f4 100644 --- a/test/plain_scalar.cpp +++ b/test/plain_scalar.cpp @@ -28,7 +28,11 @@ namespace yml { "plain scalar, do not accept ' #', at line start, but accept on first line", \ "plain scalar, do not accept ' #', at line end", \ "plain scalar, accept '#'", \ - "plain scalar, explicit" + "plain scalar, explicit", \ + "plain scalar, explicit, early end, seq", \ + "plain scalar, explicit, early end, map", \ + "plain scalar, multiple docs", \ + "plain scalar, multiple docs, termination" CASE_GROUP(PLAIN_SCALAR) @@ -372,9 +376,10 @@ C("plain scalar, do not accept ':' at line end", HAS_PARSE_ERROR, R"(- Several lines of text, with special:characters, like:this-or-this - - and some "quotes" of various 'types'. - But this: must cause a parse error. + But this must cause a parse error: + - well, did it? )", - LineCol(4, 11) + LineCol(4, 36) ), C("plain scalar, do not accept ' #', at line start", HAS_PARSE_ERROR, @@ -443,6 +448,51 @@ and yet more, deindented } ), +C("plain scalar, explicit, early end, seq", HAS_PARSE_ERROR, +R"([ + a plain scalar + with several lines +)", + LineCol(4, 1) +), + +C("plain scalar, explicit, early end, map", HAS_PARSE_ERROR, +R"({foo: + a plain scalar + with several lines +)", + LineCol(4, 1) +), + +C("plain scalar, multiple docs", +R"(--- +- a plain scalar + with several lines +--- +- a second plain scalar + with several lines +)", + N(STREAM, L{ + N(DOCSEQ, L{N("a plain scalar with several lines")}), + N(DOCSEQ, L{N("a second plain scalar with several lines")}), + }) +), + +C("plain scalar, multiple docs, termination", +R"(--- +- a plain scalar + with several lines +... +--- +- a second plain scalar + with several lines +)", + N(STREAM, L{ + N(DOCSEQ, L{N("a plain scalar with several lines")}), + N(DOCSEQ, L{N("a second plain scalar with several lines")}), + }) +), + ) } diff --git a/test/test_case.cpp b/test/test_case.cpp index 49df307c..96d94528 100644 --- a/test/test_case.cpp +++ b/test/test_case.cpp @@ -114,8 +114,17 @@ void test_arena_not_shared(Tree const& a, Tree const& b) //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- +// ensure coverage of the default callback report +#ifndef RYML_NO_DEFAULT_CALLBACKS +extern void report_error_impl(const char* msg, size_t len, Location loc, FILE *file); +#endif + std::string format_error(const char* msg, size_t len, Location loc) { + // ensure coverage of the default callback report + #ifndef RYML_NO_DEFAULT_CALLBACKS + report_error_impl(msg, len, loc, nullptr); + #endif if(!loc) return msg; std::string out; if(!loc.name.empty()) c4::formatrs(append, &out, "{}:", loc.name); @@ -186,10 +195,6 @@ void ExpectError::do_check(std::function fn, Location expected_location) { EXPECT_EQ(e.error_location.offset, context.expected_location.offset); } - if(!context.expected_location.name.empty()) - { - EXPECT_EQ(e.error_location.name, context.expected_location.name); - } } }; EXPECT_TRUE(context.m_got_an_error); diff --git a/test/test_case.hpp b/test/test_case.hpp index 33198f98..3052d827 100644 --- a/test/test_case.hpp +++ b/test/test_case.hpp @@ -413,7 +413,7 @@ struct Case template Case(csubstr name_, int f_, const char (&src_)[N], Args&& ...args) : name(name_), src(src_), root(std::forward(args)...), flags((TestCaseFlags_e)f_), expected_location() {} //! create a test case with an error on an expected location - template Case(csubstr name_, int f_, const char (&src_)[N], LineCol loc) : name(name_), src(src_), root(), flags((TestCaseFlags_e)f_), expected_location(loc.line, loc.col) {} + template Case(csubstr name_, int f_, const char (&src_)[N], LineCol loc) : name(name_), src(src_), root(), flags((TestCaseFlags_e)f_), expected_location(name, loc.line, loc.col) {} };