aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-04-18 20:15:46 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-04-25 19:43:36 +0300
commit276f68ee4d55befa8922378199b4281c82d4fd93 (patch)
treeaf1c1b94fddec588feefe79a86e6463094ba2d8e
parent7a1c91dbdbde1c4feeaa701592365bb4b7cf2562 (diff)
Add parse_manifest() and serialize_manifest() functions
-rw-r--r--libbutl/manifest-parser.cxx42
-rw-r--r--libbutl/manifest-parser.ixx17
-rw-r--r--libbutl/manifest-parser.mxx47
-rw-r--r--libbutl/manifest-serializer.cxx21
-rw-r--r--libbutl/manifest-serializer.mxx13
-rw-r--r--tests/manifest-parser/driver.cxx486
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 <string>
+#include <vector>
#include <cstdint>
#include <utility>
#include <stdexcept>
@@ -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<manifest_name_value>& 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<manifest_name_value>& r)
+ {
+ return try_parse_manifest (p, r, true /* allow_eos */);
+ }
+
+ void
+ parse_manifest (manifest_parser& p, std::vector<manifest_name_value>& 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<std::vector<manifest_name_value>>
+ try_parse_manifest (manifest_parser& p)
+ {
+ std::vector<manifest_name_value> r;
+ return try_parse_manifest (p, r)
+ ? optional<std::vector<manifest_name_value>> (move (r))
+ : nullopt;
+ }
+
+ inline std::vector<manifest_name_value>
+ parse_manifest (manifest_parser& p)
+ {
+ std::vector<manifest_name_value> 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 <string>
+#include <vector>
#include <iosfwd>
#include <cstdint> // uint64_t
#include <utility> // 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 <libbutl/optional.mxx>
#include <libbutl/char-scanner.mxx>
#include <libbutl/manifest-types.mxx>
#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: {"", "<ver>"}.
- // 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: {"", "<ver>"}. 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:
//
// ({"", "<ver>"} {"<name>", "<value>"}* {"", ""})* {"", ""}
//
@@ -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<manifest_name_value>
+ parse_manifest (manifest_parser&);
+
+ // As above but append the manifest values to an existing list.
+ //
+ LIBBUTL_SYMEXPORT void
+ parse_manifest (manifest_parser&, std::vector<manifest_name_value>&);
+
+ // As above but return nullopt if eos is reached before reading any values.
+ //
+ LIBBUTL_SYMEXPORT optional<std::vector<manifest_name_value>>
+ 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<manifest_name_value>&);
}
#include <libbutl/manifest-parser.ixx>
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 <string>
+#include <vector>
#include <cstddef>
#include <stdexcept>
@@ -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<manifest_name_value>& 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 <string>
+#include <vector>
#include <iosfwd>
#include <cstddef> // size_t
#include <stdexcept> // runtime_error
@@ -24,6 +25,9 @@ export module butl.manifest_serializer;
import std.core;
import std.io;
#endif
+import butl.manifest_types;
+#else
+#include <libbutl/manifest-types.mxx>
#endif
#include <libbutl/export.hxx>
@@ -136,6 +140,15 @@ LIBBUTL_MODEXPORT namespace butl
bool long_lines_;
const std::function<filter_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<manifest_name_value>&,
+ bool eos = true);
}
#include <libbutl/manifest-serializer.ixx>
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 <libbutl/optional.mxx>
#include <libbutl/manifest-parser.mxx>
#endif
using namespace std;
-using namespace butl;
-using pairs = vector<pair<string, string>>;
+namespace butl
+{
+ using butl::optional;
+ using butl::nullopt;
-static bool
-test (const char* manifest,
- const pairs& expected,
- manifest_parser::filter_function = {});
+ using pairs = vector<pair<string, string>>;
-static bool
-fail (const char* manifest);
+ static optional<pairs>
+ to_pairs (optional<vector<manifest_name_value>>&&);
-int
-main ()
-{
- // Whitespaces and comments.
- //
- assert (test (" \t", {{"",""}}));
- assert (test (" \t\n \n\n", {{"",""}}));
- assert (test ("# one\n #two", {{"",""}}));
+ static bool
+ equal (const optional<pairs>& actual, const optional<pairs>& 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<pairs>& 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<pairs>& 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<pairs>& a, const optional<pairs>& 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<pairs>
+ to_pairs (optional<vector<manifest_name_value>>&& 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<pairs>& 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<pairs> 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 ();
}