diff --git a/core/include/gnuradio-4.0/YamlPmt.hpp b/core/include/gnuradio-4.0/YamlPmt.hpp index 1c4ba64e..5131e9df 100644 --- a/core/include/gnuradio-4.0/YamlPmt.hpp +++ b/core/include/gnuradio-4.0/YamlPmt.hpp @@ -223,6 +223,16 @@ struct ParseContext { return lines[lineIdx].substr(columnIdx).starts_with(sv); } + bool startsWithToken(std::string_view sv) const { + if (atEndOfLine()) { + return false; + } + if (columnIdx + sv.size() < lines[lineIdx].size() && !std::isspace(lines[lineIdx][columnIdx + sv.size()])) { + return false; + } + return lines[lineIdx].substr(columnIdx).starts_with(sv); + } + bool startsWith(char c) const { if (atEndOfLine()) { return false; @@ -246,6 +256,14 @@ struct ParseContext { return false; } + bool consumeIfStartsWithToken(std::string_view sv) { + if (startsWithToken(sv)) { + consume(sv.size()); + return true; + } + return false; + } + char front() const { return lines[lineIdx][columnIdx]; } void skipToNextLine() { @@ -786,14 +804,26 @@ inline std::expected parseKey(ParseContext& ctx, std::s } ctx.consume(2 * quoteOffset + length); ctx.consumeSpaces(); - if (!ctx.consumeIfStartsWith(':')) { + if (!ctx.consumeIfStartsWithToken(":")) { return std::unexpected(ctx.makeError("Could not find key/value separator ':'")); } return *maybeKey; } // not quoted - auto colonPos = ctx.remainingLine().find(':'); + auto colonPos = [](auto sv) { + for (auto pos = 0UZ; pos < sv.size(); ++pos) { + pos = sv.find(':', pos); + if (pos == std::string_view::npos) { + return pos; + } + if (pos == sv.size() - 1 || std::isspace(sv[pos + 1])) { + return pos; + } + } + return std::string_view::npos; + }(ctx.remainingLine()); + auto commentPos = ctx.remainingLine().find('#'); if (colonPos == std::string_view::npos || (commentPos != std::string_view::npos && commentPos < colonPos)) { return std::unexpected(ctx.makeError("Could not find key/value separator ':'")); diff --git a/core/test/qa_YamlPmt.cpp b/core/test/qa_YamlPmt.cpp index 92416cee..33d538ef 100644 --- a/core/test/qa_YamlPmt.cpp +++ b/core/test/qa_YamlPmt.cpp @@ -178,6 +178,7 @@ empty: !!str "" spaces_only: !!str " " value_with_colon: "value: with colon" value_with_colon2: "value:\n with colon" +value_with_colon3: std::ranges multiline1: !!str | First line Second line @@ -229,6 +230,7 @@ unprintable_chars: !!str "\0\x01\x02\x03\x04\x05\x00\x06\x07\x08\x09\x0A\x0B\x0C expected["spaces_only"] = " "s; expected["value_with_colon"] = "value: with colon"s; expected["value_with_colon2"] = "value:\n with colon"s; + expected["value_with_colon3"] = "std::ranges"s; expected["multiline1"] = "First line\nSecond line\nThird line with trailing newline\nNull bytes (\x00\x00)\nThis is a quoted backslash \"\\\"\n"s; expected["multiline2"] = "This is a long paragraph that will be folded into a single line with trailing newlines This is a quoted backslash \"\\\" These are some invalid escapes \\q\\xZZ\\x\n\n"s; expected["multiline3"] = "First line\nSecond line\nThird line without trailing newline"s; @@ -724,6 +726,7 @@ Key with spaces: !!int8 42 "key with CR \r": !!int8 48 "key with backslash \\": !!int8 49 "key with quote \"": !!int8 50 +key::with::colons: !!int8 51 )yaml"; pmtv::map_t expected; @@ -737,6 +740,7 @@ Key with spaces: !!int8 42 expected["key with CR \r"] = static_cast(48); expected["key with backslash \\"] = static_cast(49); expected["key with quote \""] = static_cast(50); + expected["key::with::colons"] = static_cast(51); testYAML(src, expected); };