From 276f68ee4d55befa8922378199b4281c82d4fd93 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 18 Apr 2019 20:15:46 +0300 Subject: Add parse_manifest() and serialize_manifest() functions --- libbutl/manifest-parser.cxx | 42 ++++ libbutl/manifest-parser.ixx | 17 ++ libbutl/manifest-parser.mxx | 47 +++- libbutl/manifest-serializer.cxx | 21 +- libbutl/manifest-serializer.mxx | 13 ++ tests/manifest-parser/driver.cxx | 486 ++++++++++++++++++++++++--------------- 6 files changed, 428 insertions(+), 198 deletions(-) diff --git a/libbutl/manifest-parser.cxx b/libbutl/manifest-parser.cxx index 697273a..49b9ee0 100644 --- a/libbutl/manifest-parser.cxx +++ b/libbutl/manifest-parser.cxx @@ -10,6 +10,7 @@ #ifndef __cpp_lib_modules #include +#include #include #include #include @@ -28,6 +29,7 @@ module butl.manifest_parser; import std.core; import std.io; #endif +import butl.optional; import butl.char_scanner; import butl.manifest_types; #endif @@ -481,4 +483,44 @@ namespace butl line (0), column (0), description (d) { } + + // parse_manifest + // + static bool + try_parse_manifest (manifest_parser& p, + vector& r, + bool allow_eos) + { + // Read the format version or eos pair. Note that the version is verified + // by the parser. + // + manifest_name_value nv (p.next ()); + + // Bail out if eos is reached and is allowed. + // + if (nv.empty () && allow_eos) + return false; + + if (!nv.name.empty () || nv.empty ()) + throw manifest_parsing (p.name (), + nv.value_line, nv.value_column, + "start of manifest expected"); + + for (nv = p.next (); !nv.empty (); nv = p.next ()) + r.push_back (move (nv)); + + return true; + } + + bool + try_parse_manifest (manifest_parser& p, vector& r) + { + return try_parse_manifest (p, r, true /* allow_eos */); + } + + void + parse_manifest (manifest_parser& p, std::vector& r) + { + try_parse_manifest (p, r, false /* allow_eos */); + } } diff --git a/libbutl/manifest-parser.ixx b/libbutl/manifest-parser.ixx index 4ffe3c5..1dbdcd5 100644 --- a/libbutl/manifest-parser.ixx +++ b/libbutl/manifest-parser.ixx @@ -11,4 +11,21 @@ namespace butl do { parse_next (r); } while (filter_ && !filter_ (r)); return r; } + + inline optional> + try_parse_manifest (manifest_parser& p) + { + std::vector r; + return try_parse_manifest (p, r) + ? optional> (move (r)) + : nullopt; + } + + inline std::vector + parse_manifest (manifest_parser& p) + { + std::vector r; + parse_manifest (p, r); + return r; + } } diff --git a/libbutl/manifest-parser.mxx b/libbutl/manifest-parser.mxx index f39c9d2..a23d64f 100644 --- a/libbutl/manifest-parser.mxx +++ b/libbutl/manifest-parser.mxx @@ -10,6 +10,7 @@ #ifndef __cpp_lib_modules #include +#include #include #include // uint64_t #include // pair, move() @@ -25,9 +26,11 @@ export module butl.manifest_parser; import std.core; import std.io; #endif +import butl.optional; import butl.char_scanner; import butl.manifest_types; #else +#include #include #include #endif @@ -72,16 +75,15 @@ LIBBUTL_MODEXPORT namespace butl const std::string& name () const {return name_;} - // The first returned pair is special "start-of-manifest" with - // empty name and value being the format version: {"", ""}. - // After that we have a sequence of ordinary pairs which are - // the manifest. At the end of the manifest we have the special - // "end-of-manifest" pair with empty name and value: {"", ""}. - // After that we can either get another start-of-manifest pair - // (in which case the whole sequence repeats from the beginning) - // or we get another end-of-manifest pair which signals the end - // of stream (aka EOF). To put it another way, the parse sequence - // always has the following form: + // The first returned pair is special "start-of-manifest" with empty name + // and value being the format version: {"", ""}. After that we have a + // sequence of ordinary pairs which are the manifest. At the end of the + // manifest we have the special "end-of-manifest" pair with empty name and + // value: {"", ""}. After that we can either get another start-of-manifest + // pair (in which case the whole sequence repeats from the beginning) or + // we get another end-of-manifest-like pair which signals the end of + // stream (aka EOF) and which we will call the end-of-stream pair. To put + // it another way, the parse sequence always has the following form: // // ({"", ""} {"", ""}* {"", ""})* {"", ""} // @@ -120,6 +122,31 @@ LIBBUTL_MODEXPORT namespace butl enum {start, body, end} s_ = start; std::string version_; // Current format version. }; + + // Parse and return a single manifest. Throw manifest_parsing in case of an + // error. + // + // Note that the returned manifest doesn't contain the format version nor + // the end-of-manifest/stream pairs. + // + LIBBUTL_SYMEXPORT std::vector + parse_manifest (manifest_parser&); + + // As above but append the manifest values to an existing list. + // + LIBBUTL_SYMEXPORT void + parse_manifest (manifest_parser&, std::vector&); + + // As above but return nullopt if eos is reached before reading any values. + // + LIBBUTL_SYMEXPORT optional> + try_parse_manifest (manifest_parser&); + + // As above but append the manifest values to an existing list returning + // false if eos is reached before reading any values. + // + LIBBUTL_SYMEXPORT bool + try_parse_manifest (manifest_parser&, std::vector&); } #include diff --git a/libbutl/manifest-serializer.cxx b/libbutl/manifest-serializer.cxx index 059c374..4284ec6 100644 --- a/libbutl/manifest-serializer.cxx +++ b/libbutl/manifest-serializer.cxx @@ -10,6 +10,7 @@ #ifndef __cpp_lib_modules #include +#include #include #include @@ -27,7 +28,7 @@ module butl.manifest_serializer; import std.core; import std.io; #endif -import butl.char_scanner; +import butl.manifest_types; #endif #endif @@ -319,4 +320,22 @@ namespace butl : runtime_error (format (n, d)), name (n), description (d) { } + + // serialize_manifest + // + void + serialize_manifest (manifest_serializer& s, + const vector& nvs, + bool eos) + { + s.next ("", "1"); // Start of manifest. + + for (const manifest_name_value& nv: nvs) + s.next (nv.name, nv.value); + + s.next ("", ""); // End of manifest. + + if (eos) + s.next ("", ""); // End of stream. + } } diff --git a/libbutl/manifest-serializer.mxx b/libbutl/manifest-serializer.mxx index 1b3ace8..2a8d32e 100644 --- a/libbutl/manifest-serializer.mxx +++ b/libbutl/manifest-serializer.mxx @@ -10,6 +10,7 @@ #ifndef __cpp_lib_modules #include +#include #include #include // size_t #include // runtime_error @@ -24,6 +25,9 @@ export module butl.manifest_serializer; import std.core; import std.io; #endif +import butl.manifest_types; +#else +#include #endif #include @@ -136,6 +140,15 @@ LIBBUTL_MODEXPORT namespace butl bool long_lines_; const std::function filter_; }; + + // Serialize a manifest to a stream adding the leading format version pair + // and the trailing end-of-manifest pair. Unless eos is false, then also + // write the end-of-stream pair. + // + LIBBUTL_SYMEXPORT void + serialize_manifest (manifest_serializer&, + const std::vector&, + bool eos = true); } #include diff --git a/tests/manifest-parser/driver.cxx b/tests/manifest-parser/driver.cxx index f157039..d11ba8f 100644 --- a/tests/manifest-parser/driver.cxx +++ b/tests/manifest-parser/driver.cxx @@ -19,240 +19,352 @@ import std.core; import std.io; #endif +import butl.optional; import butl.manifest_parser; #else +#include #include #endif using namespace std; -using namespace butl; -using pairs = vector>; +namespace butl +{ + using butl::optional; + using butl::nullopt; -static bool -test (const char* manifest, - const pairs& expected, - manifest_parser::filter_function = {}); + using pairs = vector>; -static bool -fail (const char* manifest); + static optional + to_pairs (optional>&&); -int -main () -{ - // Whitespaces and comments. - // - assert (test (" \t", {{"",""}})); - assert (test (" \t\n \n\n", {{"",""}})); - assert (test ("# one\n #two", {{"",""}})); + static bool + equal (const optional& actual, const optional& expected); - // Test encountering eos at various points. + // Test manifest as it is represented in the stream, including format + // version and end-of-manifest values. // - assert (test ("", {{"",""}})); - assert (test (" ", {{"",""}})); - assert (test ("\n", {{"",""}})); - assert (fail ("a")); - assert (test (":1\na:", {{"","1"},{"a", ""},{"",""},{"",""}})); + static bool + test (const char* manifest, + const pairs& expected, + manifest_parser::filter_function = {}); - // Invalid manifests. - // - assert (fail ("a:")); // format version pair expected - assert (fail (":")); // format version value expected - assert (fail (":9")); // unsupported format version - assert (fail ("a")); // ':' expected after name - assert (fail ("a b")); // ':' expected after name - assert (fail ("a\tb")); // ':' expected after name - assert (fail ("a\nb")); // ':' expected after name - assert (fail (":1\na:b\n:9")); // unsupported format version - - // Empty manifest. - // - assert (test (":1", {{"","1"},{"",""},{"",""}})); - assert (test (" \t :1", {{"","1"},{"",""},{"",""}})); - assert (test (" \t : 1", {{"","1"},{"",""},{"",""}})); - assert (test (" \t : 1 ", {{"","1"},{"",""},{"",""}})); - assert (test (":1\n", {{"","1"},{"",""},{"",""}})); - assert (test (":1 \n", {{"","1"},{"",""},{"",""}})); - - // Single manifest. - // - assert (test (":1\na:x", {{"","1"},{"a", "x"},{"",""},{"",""}})); - assert (test (":1\na:x\n", {{"","1"},{"a","x"},{"",""},{"",""}})); - assert (test (":1\na:x\nb:y", - {{"","1"},{"a","x"},{"b","y"},{"",""},{"",""}})); - assert (test (":1\na:x\n\tb : y\n #comment", - {{"","1"},{"a","x"},{"b","y"},{"",""},{"",""}})); - - // Multiple manifests. - // - assert (test (":1\na:x\n:\nb:y", - {{"","1"},{"a", "x"},{"",""}, - {"","1"},{"b", "y"},{"",""},{"",""}})); - assert (test (":1\na:x\n:1\nb:y", - {{"","1"},{"a", "x"},{"",""}, - {"","1"},{"b", "y"},{"",""},{"",""}})); - assert (test (":1\na:x\n:\nb:y\n:\nc:z\n", - {{"","1"},{"a", "x"},{"",""}, - {"","1"},{"b", "y"},{"",""}, - {"","1"},{"c", "z"},{"",""},{"",""}})); - - // Name parsing. - // - assert (test (":1\nabc:", {{"","1"},{"abc",""},{"",""},{"",""}})); - assert (test (":1\nabc :", {{"","1"},{"abc",""},{"",""},{"",""}})); - assert (test (":1\nabc\t:", {{"","1"},{"abc",""},{"",""},{"",""}})); + static bool + fail (const char* manifest); - // Simple value parsing. + // Test manifest as it is returned by parse_manifest(), without the special + // format version and end-of-manifest values. // - assert (test (":1\na: \t xyz \t ", {{"","1"},{"a","xyz"},{"",""},{"",""}})); + static bool + test_parse (const char* manifest, const optional& expected); - // Simple value escaping. - // - assert (test (":1\na:x\\", {{"","1"},{"a","x"},{"",""},{"",""}})); - assert (test (":1\na:x\\\ny", {{"","1"},{"a","xy"},{"",""},{"",""}})); - assert (test (":1\na:x\\\\\nb:", - {{"","1"},{"a","x\\"},{"b",""},{"",""},{"",""}})); - assert (test (":1\na:x\\\\\\\nb:", - {{"","1"},{"a","x\\\\"},{"b",""},{"",""},{"",""}})); - - // Simple value literal newline. - // - assert (test (":1\na:x\\\n\\", {{"","1"},{"a","x\n"},{"",""},{"",""}})); - assert (test (":1\na:x\\\n\\\ny", {{"","1"},{"a","x\ny"},{"",""},{"",""}})); - assert (test (":1\na:x\\\n\\\ny\\\n\\\nz", - {{"","1"},{"a","x\ny\nz"},{"",""},{"",""}})); + static bool + fail_parse (const char* manifest); - // Multi-line value parsing. - // - assert (test (":1\na:\\", {{"","1"},{"a", ""},{"",""},{"",""}})); - assert (test (":1\na:\\\n", {{"","1"},{"a", ""},{"",""},{"",""}})); - assert (test (":1\na:\\x", {{"","1"},{"a", "\\x"},{"",""},{"",""}})); - assert (test (":1\na:\\\n\\", {{"","1"},{"a", ""},{"",""},{"",""}})); - assert (test (":1\na:\\\n\\\n", {{"","1"},{"a", ""},{"",""},{"",""}})); - assert (test (":1\na:\\\n\\x\n\\", - {{"","1"},{"a", "\\x"},{"",""},{"",""}})); - assert (test (":1\na:\\\nx\ny", {{"","1"},{"a", "x\ny"},{"",""},{"",""}})); - assert (test (":1\na:\\\n \n#\t\n\\", - {{"","1"},{"a", " \n#\t"},{"",""},{"",""}})); - assert (test (":1\na:\\\n\n\n\\", {{"","1"},{"a", "\n"},{"",""},{"",""}})); - - // Multi-line value escaping. - // - assert (test (":1\na:\\\nx\\", {{"","1"},{"a","x"},{"",""},{"",""}})); - assert (test (":1\na:\\\nx\\\ny\n\\", - {{"","1"},{"a","xy"},{"",""},{"",""}})); - assert (test (":1\na:\\\nx\\\\\n\\\nb:", - {{"","1"},{"a","x\\"},{"b",""},{"",""},{"",""}})); - assert (test (":1\na:\\\nx\\\\\\\n\\\nb:", - {{"","1"},{"a","x\\\\"},{"b",""},{"",""},{"",""}})); - - // Manifest value splitting (into the value/comment pair). - // + int + main () { - auto p (manifest_parser::split_comment ("value\\; text ; comment text")); - assert (p.first == "value; text" && p.second == "comment text"); + // Whitespaces and comments. + // + assert (test (" \t", {{"",""}})); + assert (test (" \t\n \n\n", {{"",""}})); + assert (test ("# one\n #two", {{"",""}})); + + // Test encountering eos at various points. + // + assert (test ("", {{"",""}})); + assert (test (" ", {{"",""}})); + assert (test ("\n", {{"",""}})); + assert (fail ("a")); + assert (test (":1\na:", {{"","1"},{"a", ""},{"",""},{"",""}})); + + // Invalid manifests. + // + assert (fail ("a:")); // format version pair expected + assert (fail (":")); // format version value expected + assert (fail (":9")); // unsupported format version + assert (fail ("a")); // ':' expected after name + assert (fail ("a b")); // ':' expected after name + assert (fail ("a\tb")); // ':' expected after name + assert (fail ("a\nb")); // ':' expected after name + assert (fail (":1\na:b\n:9")); // unsupported format version + + // Empty manifest. + // + assert (test (":1", {{"","1"},{"",""},{"",""}})); + assert (test (" \t :1", {{"","1"},{"",""},{"",""}})); + assert (test (" \t : 1", {{"","1"},{"",""},{"",""}})); + assert (test (" \t : 1 ", {{"","1"},{"",""},{"",""}})); + assert (test (":1\n", {{"","1"},{"",""},{"",""}})); + assert (test (":1 \n", {{"","1"},{"",""},{"",""}})); + + // Single manifest. + // + assert (test (":1\na:x", {{"","1"},{"a", "x"},{"",""},{"",""}})); + assert (test (":1\na:x\n", {{"","1"},{"a","x"},{"",""},{"",""}})); + assert (test (":1\na:x\nb:y", + {{"","1"},{"a","x"},{"b","y"},{"",""},{"",""}})); + assert (test (":1\na:x\n\tb : y\n #comment", + {{"","1"},{"a","x"},{"b","y"},{"",""},{"",""}})); + + // Multiple manifests. + // + assert (test (":1\na:x\n:\nb:y", + {{"","1"},{"a", "x"},{"",""}, + {"","1"},{"b", "y"},{"",""},{"",""}})); + assert (test (":1\na:x\n:1\nb:y", + {{"","1"},{"a", "x"},{"",""}, + {"","1"},{"b", "y"},{"",""},{"",""}})); + assert (test (":1\na:x\n:\nb:y\n:\nc:z\n", + {{"","1"},{"a", "x"},{"",""}, + {"","1"},{"b", "y"},{"",""}, + {"","1"},{"c", "z"},{"",""},{"",""}})); + + // Name parsing. + // + assert (test (":1\nabc:", {{"","1"},{"abc",""},{"",""},{"",""}})); + assert (test (":1\nabc :", {{"","1"},{"abc",""},{"",""},{"",""}})); + assert (test (":1\nabc\t:", {{"","1"},{"abc",""},{"",""},{"",""}})); + + // Simple value parsing. + // + assert (test (":1\na: \t xyz \t ", + {{"","1"},{"a","xyz"},{"",""},{"",""}})); + + // Simple value escaping. + // + assert (test (":1\na:x\\", {{"","1"},{"a","x"},{"",""},{"",""}})); + assert (test (":1\na:x\\\ny", {{"","1"},{"a","xy"},{"",""},{"",""}})); + assert (test (":1\na:x\\\\\nb:", + {{"","1"},{"a","x\\"},{"b",""},{"",""},{"",""}})); + assert (test (":1\na:x\\\\\\\nb:", + {{"","1"},{"a","x\\\\"},{"b",""},{"",""},{"",""}})); + + // Simple value literal newline. + // + assert (test (":1\na:x\\\n\\", {{"","1"},{"a","x\n"},{"",""},{"",""}})); + assert (test (":1\na:x\\\n\\\ny", + {{"","1"},{"a","x\ny"},{"",""},{"",""}})); + assert (test (":1\na:x\\\n\\\ny\\\n\\\nz", + {{"","1"},{"a","x\ny\nz"},{"",""},{"",""}})); + + // Multi-line value parsing. + // + assert (test (":1\na:\\", {{"","1"},{"a", ""},{"",""},{"",""}})); + assert (test (":1\na:\\\n", {{"","1"},{"a", ""},{"",""},{"",""}})); + assert (test (":1\na:\\x", {{"","1"},{"a", "\\x"},{"",""},{"",""}})); + assert (test (":1\na:\\\n\\", {{"","1"},{"a", ""},{"",""},{"",""}})); + assert (test (":1\na:\\\n\\\n", {{"","1"},{"a", ""},{"",""},{"",""}})); + assert (test (":1\na:\\\n\\x\n\\", + {{"","1"},{"a", "\\x"},{"",""},{"",""}})); + assert (test (":1\na:\\\nx\ny", {{"","1"},{"a", "x\ny"},{"",""},{"",""}})); + assert (test (":1\na:\\\n \n#\t\n\\", + {{"","1"},{"a", " \n#\t"},{"",""},{"",""}})); + assert (test (":1\na:\\\n\n\n\\", {{"","1"},{"a", "\n"},{"",""},{"",""}})); + + // Multi-line value escaping. + // + assert (test (":1\na:\\\nx\\", {{"","1"},{"a","x"},{"",""},{"",""}})); + assert (test (":1\na:\\\nx\\\ny\n\\", + {{"","1"},{"a","xy"},{"",""},{"",""}})); + assert (test (":1\na:\\\nx\\\\\n\\\nb:", + {{"","1"},{"a","x\\"},{"b",""},{"",""},{"",""}})); + assert (test (":1\na:\\\nx\\\\\\\n\\\nb:", + {{"","1"},{"a","x\\\\"},{"b",""},{"",""},{"",""}})); + + // Manifest value splitting (into the value/comment pair). + // + { + auto p (manifest_parser::split_comment ("value\\; text ; comment text")); + assert (p.first == "value; text" && p.second == "comment text"); + } + + { + auto p (manifest_parser::split_comment ("value")); + assert (p.first == "value" && p.second == ""); + } + + { + auto p (manifest_parser::split_comment ("; comment")); + assert (p.first == "" && p.second == "comment"); + } + + // Filtering. + // + assert (test (":1\na: abc\nb: bca\nc: cab", + {{"","1"},{"a","abc"},{"c","cab"},{"",""},{"",""}}, + [] (manifest_name_value& nv) {return nv.name != "b";})); + + assert (test (":1\na: abc\nb: bca", + {{"","1"},{"ax","abc."},{"bx","bca."},{"",""},{"",""}}, + [] (manifest_name_value& nv) + { + if (!nv.name.empty ()) + { + nv.name += 'x'; + nv.value += '.'; + } + + return true; + })); + + // Test parse_manifest(). + // + test_parse ("", nullopt); + test_parse (":1", pairs ()); + test_parse (":1\na: abc\nb: cde", pairs ({{"a", "abc"}, {"b", "cde"}})); + + fail_parse ("# abc"); + fail_parse ("a: abc"); + + // Parse the manifest list. + // + { + istringstream is (":1\na: abc\nb: bcd\n:\nx: xyz"); + is.exceptions (istream::failbit | istream::badbit); + manifest_parser p (is, ""); + + equal (to_pairs (try_parse_manifest (p)), + pairs ({{"a", "abc"}, {"b", "bcd"}})); + + equal (to_pairs (try_parse_manifest (p)), pairs ({{"x", "xyz"}})); + equal (to_pairs (try_parse_manifest (p)), nullopt); + } + + return 0; } + static ostream& + operator<< (ostream& os, const pairs& ps) { - auto p (manifest_parser::split_comment ("value")); - assert (p.first == "value" && p.second == ""); + os << '{'; + + bool f (true); + for (const auto& p: ps) + os << (f ? (f = false, "") : ",") + << '{' << p.first << ',' << p.second << '}'; + + os << '}'; + return os; } + static ostream& + operator<< (ostream& os, const optional& ps) { - auto p (manifest_parser::split_comment ("; comment")); - assert (p.first == "" && p.second == "comment"); + return ps ? (os << *ps) : (os << "[null]"); } - // Filtering. - // - assert (test (":1\na: abc\nb: bca\nc: cab", - {{"","1"},{"a","abc"},{"c","cab"},{"",""},{"",""}}, - [] (manifest_name_value& nv) {return nv.name != "b";})); - - assert (test (":1\na: abc\nb: bca", - {{"","1"},{"ax","abc."},{"bx","bca."},{"",""},{"",""}}, - [] (manifest_name_value& nv) - { - if (!nv.name.empty ()) - { - nv.name += 'x'; - nv.value += '.'; - } - - return true; - })); -} + static bool + equal (const optional& a, const optional& e) + { + if (a != e) + { + cerr << "actual: " << a << endl + << "expect: " << e << endl; -static ostream& -operator<< (ostream& os, const pairs& ps) -{ - os << '{'; + return false; + } - bool f (true); - for (const auto& p: ps) - os << (f ? (f = false, "") : ",") - << '{' << p.first << ',' << p.second << '}'; + return true; + } - os << '}'; - return os; -} + static optional + to_pairs (optional>&& nvs) + { + if (!nvs) + return nullopt; -static pairs -parse (const char* m, manifest_parser::filter_function f = {}) -{ - istringstream is (m); - is.exceptions (istream::failbit | istream::badbit); - manifest_parser p (is, "", move (f)); + pairs r; + for (manifest_name_value& nv: *nvs) + r.emplace_back (move (nv.name), move (nv.value)); - pairs r; + return r; + } - for (bool eom (true), eos (false); !eos; ) + static pairs + parse (const char* m, manifest_parser::filter_function f = {}) { - manifest_name_value nv (p.next ()); + istringstream is (m); + is.exceptions (istream::failbit | istream::badbit); + manifest_parser p (is, "", move (f)); - if (nv.empty ()) // End pair. + pairs r; + + for (bool eom (true), eos (false); !eos; ) { - eos = eom; - eom = true; + manifest_name_value nv (p.next ()); + + if (nv.empty ()) // End pair. + { + eos = eom; + eom = true; + } + else + eom = false; + + r.emplace_back (move (nv.name), move (nv.value)); } - else - eom = false; - r.emplace_back (move (nv.name), move (nv.value)); + return r; } - return r; -} - -static bool -test (const char* m, const pairs& e, manifest_parser::filter_function f) -{ - pairs r (parse (m, move (f))); + static bool + test (const char* m, const pairs& e, manifest_parser::filter_function f) + { + return equal (parse (m, move (f)), e); + } - if (r != e) + static bool + fail (const char* m) { - cerr << "actual: " << r << endl - << "expect: " << e << endl; + try + { + pairs r (parse (m)); + cerr << "nofail: " << r << endl; + return false; + } + catch (const manifest_parsing&) + { + //cerr << e << endl; + } - return false; + return true; } - return true; -} - -static bool -fail (const char* m) -{ - try + static bool + test_parse (const char* m, const optional& e) { - pairs r (parse (m)); - cerr << "nofail: " << r << endl; - return false; + istringstream is (m); + is.exceptions (istream::failbit | istream::badbit); + manifest_parser p (is, ""); + + return equal (to_pairs (try_parse_manifest (p)), e); } - catch (const manifest_parsing&) + + static bool + fail_parse (const char* m) { - //cerr << e << endl; + try + { + istringstream is (m); + is.exceptions (istream::failbit | istream::badbit); + manifest_parser p (is, ""); + + optional r (to_pairs (parse_manifest (p))); + cerr << "nofail: " << r << endl; + return false; + } + catch (const manifest_parsing&) + { + //cerr << e << endl; + } + + return true; } +} - return true; +int +main () +{ + return butl::main (); } -- cgit v1.1