summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli-tests/position/buildfile9
-rw-r--r--cli-tests/position/driver.cxx44
-rw-r--r--cli-tests/position/test.cli13
-rw-r--r--cli-tests/position/testscript36
-rw-r--r--cli/cli/runtime-header.cxx79
-rw-r--r--cli/cli/runtime-inline.cxx63
-rw-r--r--cli/cli/runtime-source.cxx75
-rw-r--r--cli/doc/buildfile4
-rw-r--r--cli/manifest2
9 files changed, 280 insertions, 45 deletions
diff --git a/cli-tests/position/buildfile b/cli-tests/position/buildfile
new file mode 100644
index 0000000..371cc54
--- /dev/null
+++ b/cli-tests/position/buildfile
@@ -0,0 +1,9 @@
+# file : position/buildfile
+# license : MIT; see accompanying LICENSE file
+
+exe{driver}: {hxx cxx}{* -test} cli.cxx{test} testscript
+
+cxx.poptions =+ "-I$out_base"
+
+cli.cxx{test}: cli{test}
+cli.options = --generate-file-scanner --generate-specifier
diff --git a/cli-tests/position/driver.cxx b/cli-tests/position/driver.cxx
new file mode 100644
index 0000000..eebb5d0
--- /dev/null
+++ b/cli-tests/position/driver.cxx
@@ -0,0 +1,44 @@
+// file : position/driver.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+// Test argument/option position.
+//
+#include <iostream>
+
+#include "test.hxx"
+
+using namespace std;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ cli::argv_file_scanner scan (argc, argv, "--file");
+ options ops (scan);
+
+ if (ops.a_specified ())
+ cout << ops.a ().second << ": " << "-a " << ops.a ().first << endl;
+
+ for (const pair<int, size_t>& b: ops.b ())
+ {
+ cout << b.second << ": " << "-b " << b.first << endl;
+ }
+
+ while (scan.more ())
+ {
+ // Note that calling position() inside `cout << ...` depends on order of
+ // argument evaluation.
+ //
+ size_t p (scan.position ());
+ cout << p << ": " << scan.next () << endl;
+ }
+
+ cout << "max: " << scan.position () << endl;
+ }
+ catch (const cli::exception& e)
+ {
+ cerr << e << endl;
+ }
+}
diff --git a/cli-tests/position/test.cli b/cli-tests/position/test.cli
new file mode 100644
index 0000000..7fc8655
--- /dev/null
+++ b/cli-tests/position/test.cli
@@ -0,0 +1,13 @@
+// file : position/test.cli
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+include <vector>;
+include <utility>;
+include <cstddef>;
+
+class options
+{
+ std::pair<int, std::size_t> -a;
+ std::vector<std::pair<int, std::size_t> > -b;
+};
diff --git a/cli-tests/position/testscript b/cli-tests/position/testscript
new file mode 100644
index 0000000..568e571
--- /dev/null
+++ b/cli-tests/position/testscript
@@ -0,0 +1,36 @@
+# file : position/testscript
+# license : MIT; see accompanying LICENSE file
+
+: basics
+:
+$* -b 1 -a 2 -b 3 foo bar baz >>EOO
+3: -a 2
+1: -b 1
+5: -b 3
+7: foo
+8: bar
+9: baz
+max: 10
+EOO
+
+: override
+:
+$* -a 1 -a 2 >>EOO
+3: -a 2
+max: 5
+EOO
+
+: file
+:
+cat <<EOI >=test.ops;
+-a 2
+EOI
+$* -b 1 --file test.ops -b 2 foo bar baz >>EOO
+5: -a 2
+1: -b 1
+7: -b 2
+9: foo
+10: bar
+11: baz
+max: 12
+EOO
diff --git a/cli/cli/runtime-header.cxx b/cli/cli/runtime-header.cxx
index adf70dd..5bbe5c6 100644
--- a/cli/cli/runtime-header.cxx
+++ b/cli/cli/runtime-header.cxx
@@ -308,12 +308,20 @@ generate_runtime_header (context& ctx)
// scanner
//
- os << "// Command line argument scanner interface." << endl
- << "//" << endl
- << "// The values returned by next() are guaranteed to be valid" << endl
- << "// for the two previous arguments up until a call to a third" << endl
- << "// peek() or next()." << endl
- << "//" << endl
+ os << "// Command line argument scanner interface." << endl
+ << "//" << endl
+ << "// The values returned by next() are guaranteed to be valid" << endl
+ << "// for the two previous arguments up until a call to a third" << endl
+ << "// peek() or next()." << endl
+ << "//" << endl
+ << "// The position() function returns a monotonically-increasing" << endl
+ << "// number which, if stored, can later be used to determine the"<< endl
+ << "// relative position of the argument returned by the following"<< endl
+ << "// call to next(). Note that if multiple scanners are used to" << endl
+ << "// extract arguments from multiple sources, then the end" << endl
+ << "// position of the previous scanner should be used as the" << endl
+ << "// start position of the next." << endl
+ << "//" << endl
<< "class scanner"
<< "{"
<< "public:" << endl
@@ -331,6 +339,9 @@ generate_runtime_header (context& ctx)
<< endl
<< "virtual void" << endl
<< "skip () = 0;"
+ << endl
+ << "virtual std::size_t" << endl
+ << "position () = 0;"
<< "};";
// argv_scanner
@@ -338,8 +349,16 @@ generate_runtime_header (context& ctx)
os << "class argv_scanner: public scanner"
<< "{"
<< "public:" << endl
- << "argv_scanner (int& argc, char** argv, bool erase = false);"
- << "argv_scanner (int start, int& argc, char** argv, bool erase = false);"
+ << "argv_scanner (int& argc," << endl
+ << "char** argv," << endl
+ << "bool erase = false," << endl
+ << "std::size_t start_position = 0);"
+ << endl
+ << "argv_scanner (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "bool erase = false," << endl
+ << "std::size_t start_position = 0);"
<< endl
<< "int" << endl
<< "end () const;"
@@ -356,7 +375,11 @@ generate_runtime_header (context& ctx)
<< "virtual void" << endl
<< "skip ();"
<< endl
- << "private:" << endl
+ << "virtual std::size_t" << endl
+ << "position ();"
+ << endl
+ << "protected:" << endl
+ << "std::size_t start_position_;"
<< "int i_;"
<< "int& argc_;"
<< "char** argv_;"
@@ -370,14 +393,15 @@ generate_runtime_header (context& ctx)
os << "class vector_scanner: public scanner"
<< "{"
<< "public:" << endl
- << "vector_scanner (const std::vector<std::string>&, " <<
- "std::size_t start = 0);"
+ << "vector_scanner (const std::vector<std::string>&," << endl
+ << "std::size_t start = 0," << endl
+ << "std::size_t start_position = 0);"
<< endl
<< "std::size_t" << endl
<< "end () const;"
<< endl
<< "void" << endl
- << "reset (std::size_t start = 0);"
+ << "reset (std::size_t start = 0, std::size_t start_position = 0);"
<< endl
<< "virtual bool" << endl
<< "more ();"
@@ -391,7 +415,11 @@ generate_runtime_header (context& ctx)
<< "virtual void" << endl
<< "skip ();"
<< endl
+ << "virtual std::size_t" << endl
+ << "position ();"
+ << endl
<< "private:" << endl
+ << "std::size_t start_position_;"
<< "const std::vector<std::string>& v_;"
<< "std::size_t i_;"
<< "};";
@@ -407,16 +435,19 @@ generate_runtime_header (context& ctx)
<< "argv_file_scanner (int& argc," << endl
<< "char** argv," << endl
<< "const std::string& option," << endl
- << "bool erase = false);"
+ << "bool erase = false," << endl
+ << "std::size_t start_position = 0);"
<< endl
<< "argv_file_scanner (int start," << endl
<< "int& argc," << endl
<< "char** argv," << endl
<< "const std::string& option," << endl
- << "bool erase = false);"
+ << "bool erase = false," << endl
+ << "std::size_t start_position = 0);"
<< endl
<< "argv_file_scanner (const std::string& file," << endl
- << "const std::string& option);"
+ << "const std::string& option," << endl
+ << "std::size_t start_position = 0);"
<< endl
<< "struct option_info"
<< "{"
@@ -432,18 +463,21 @@ generate_runtime_header (context& ctx)
<< "char** argv," << endl
<< "const option_info* options," << endl
<< "std::size_t options_count," << endl
- << "bool erase = false);"
+ << "bool erase = false," << endl
+ << "std::size_t start_position = 0);"
<< endl
<< "argv_file_scanner (int start," << endl
<< "int& argc," << endl
<< "char** argv," << endl
<< "const option_info* options," << endl
<< "std::size_t options_count," << endl
- << "bool erase = false);"
+ << "bool erase = false," << endl
+ << "std::size_t start_position = 0);"
<< endl
<< "argv_file_scanner (const std::string& file," << endl
<< "const option_info* options = 0," << endl
- << "std::size_t options_count = 0);"
+ << "std::size_t options_count = 0," << endl
+ << "std::size_t start_position = 0);"
<< endl
<< "virtual bool" << endl
<< "more ();"
@@ -457,6 +491,9 @@ generate_runtime_header (context& ctx)
<< "virtual void" << endl
<< "skip ();"
<< endl
+ << "virtual std::size_t" << endl
+ << "position ();"
+ << endl
<< "// Return the file path if the peeked at argument came from a file and" << endl
<< "// the empty string otherwise. The reference is guaranteed to be valid" << endl
<< "// till the end of the scanner lifetime." << endl
@@ -529,12 +566,18 @@ generate_runtime_header (context& ctx)
<< "virtual void" << endl
<< "skip ();"
<< endl
+ << "virtual std::size_t" << endl
+ << "position ();"
+ << endl
<< "// The group is only available after the call to next()" << endl
<< "// (and skip() -- in case one needs to make sure the group" << endl
<< "// was empty, or some such) and is only valid (and must be" << endl
<< "// handled) until the next call to any of the scanner" << endl
<< "// functions (including more())." << endl
<< "//" << endl
+ << "// Note also that argument positions within each group start"<< endl
+ << "// from 0." << endl
+ << "//" << endl
<< "scanner&" << endl
<< "group ();"
<< endl
diff --git a/cli/cli/runtime-inline.cxx b/cli/cli/runtime-inline.cxx
index 8f0e84c..e3dce1b 100644
--- a/cli/cli/runtime-inline.cxx
+++ b/cli/cli/runtime-inline.cxx
@@ -232,14 +232,29 @@ generate_runtime_inline (context& ctx)
<< "//" << endl;
os << inl << "argv_scanner::" << endl
- << "argv_scanner (int& argc, char** argv, bool erase)" << endl
- << ": i_ (1), argc_ (argc), argv_ (argv), erase_ (erase)"
+ << "argv_scanner (int& argc," << endl
+ << "char** argv," << endl
+ << "bool erase," << endl
+ << "std::size_t sp)" << endl
+ << ": start_position_ (sp + 1)," << endl
+ << " i_ (1)," << endl
+ << " argc_ (argc)," << endl
+ << " argv_ (argv)," << endl
+ << " erase_ (erase)"
<< "{"
<< "}";
os << inl << "argv_scanner::" << endl
- << "argv_scanner (int start, int& argc, char** argv, bool erase)" << endl
- << ": i_ (start), argc_ (argc), argv_ (argv), erase_ (erase)"
+ << "argv_scanner (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "bool erase," << endl
+ << "std::size_t sp)" << endl
+ << ": start_position_ (sp + static_cast<std::size_t> (start))," << endl
+ << " i_ (start)," << endl
+ << " argc_ (argc)," << endl
+ << " argv_ (argv)," << endl
+ << " erase_ (erase)"
<< "{"
<< "}";
@@ -257,9 +272,10 @@ generate_runtime_inline (context& ctx)
<< "//" << endl;
os << inl << "vector_scanner::" << endl
- << "vector_scanner (const std::vector<std::string>& v, " <<
- "std::size_t i)" << endl
- << ": v_ (v), i_ (i)"
+ << "vector_scanner (const std::vector<std::string>& v," << endl
+ << "std::size_t i," << endl
+ << "std::size_t sp)" << endl
+ << ": start_position_ (sp), v_ (v), i_ (i)"
<< "{"
<< "}";
@@ -270,9 +286,10 @@ generate_runtime_inline (context& ctx)
<< "}";
os << inl << "void vector_scanner::" << endl
- << "reset (std::size_t i)"
+ << "reset (std::size_t i, std::size_t sp)"
<< "{"
<< "i_ = i;"
+ << "start_position_ = sp;"
<< "}";
}
@@ -289,8 +306,9 @@ generate_runtime_inline (context& ctx)
<< "argv_file_scanner (int& argc," << endl
<< "char** argv," << endl
<< "const std::string& option," << endl
- << "bool erase)" << endl
- << ": argv_scanner (argc, argv, erase)," << endl
+ << "bool erase," << endl
+ << "std::size_t sp)" << endl
+ << ": argv_scanner (argc, argv, erase, sp)," << endl
<< " option_ (option)," << endl
<< " options_ (&option_info_)," << endl
<< " options_count_ (1)," << endl
@@ -308,8 +326,9 @@ generate_runtime_inline (context& ctx)
<< "int& argc," << endl
<< "char** argv," << endl
<< "const std::string& option," << endl
- << "bool erase)" << endl
- << ": argv_scanner (start, argc, argv, erase)," << endl
+ << "bool erase," << endl
+ << "std::size_t sp)" << endl
+ << ": argv_scanner (start, argc, argv, erase, sp)," << endl
<< " option_ (option)," << endl
<< " options_ (&option_info_)," << endl
<< " options_count_ (1)," << endl
@@ -324,8 +343,9 @@ generate_runtime_inline (context& ctx)
os << inl << "argv_file_scanner::" << endl
<< "argv_file_scanner (const std::string& file," << endl
- << "const std::string& option)" << endl
- << ": argv_scanner (0, zero_argc_, 0)," << endl
+ << "const std::string& option," << endl
+ << "std::size_t sp)" << endl
+ << ": argv_scanner (0, zero_argc_, 0, sp)," << endl
<< " option_ (option)," << endl
<< " options_ (&option_info_)," << endl
<< " options_count_ (1)," << endl
@@ -345,8 +365,9 @@ generate_runtime_inline (context& ctx)
<< "char** argv," << endl
<< "const option_info* options," << endl
<< "std::size_t options_count," << endl
- << "bool erase)" << endl
- << ": argv_scanner (argc, argv, erase)," << endl
+ << "bool erase," << endl
+ << "std::size_t sp)" << endl
+ << ": argv_scanner (argc, argv, erase, sp)," << endl
<< " options_ (options)," << endl
<< " options_count_ (options_count)," << endl
<< " i_ (1)";
@@ -362,8 +383,9 @@ generate_runtime_inline (context& ctx)
<< "char** argv," << endl
<< "const option_info* options," << endl
<< "std::size_t options_count," << endl
- << "bool erase)" << endl
- << ": argv_scanner (start, argc, argv, erase)," << endl
+ << "bool erase," << endl
+ << "std::size_t sp)" << endl
+ << ": argv_scanner (start, argc, argv, erase, sp)," << endl
<< " options_ (options)," << endl
<< " options_count_ (options_count)," << endl
<< " i_ (1)";
@@ -376,8 +398,9 @@ generate_runtime_inline (context& ctx)
os << inl << "argv_file_scanner::" << endl
<< "argv_file_scanner (const std::string& file," << endl
<< "const option_info* options," << endl
- << "std::size_t options_count)" << endl
- << ": argv_scanner (0, zero_argc_, 0)," << endl
+ << "std::size_t options_count," << endl
+ << "std::size_t sp)" << endl
+ << ": argv_scanner (0, zero_argc_, 0, sp)," << endl
<< " options_ (options)," << endl
<< " options_count_ (options_count)," << endl
<< " i_ (1)";
diff --git a/cli/cli/runtime-source.cxx b/cli/cli/runtime-source.cxx
index 3864163..81eab4a 100644
--- a/cli/cli/runtime-source.cxx
+++ b/cli/cli/runtime-source.cxx
@@ -15,6 +15,7 @@ generate_runtime_source (context& ctx, bool complete)
<< "#include <set>" << endl
<< "#include <string>" << endl
<< "#include <vector>" << endl
+ << "#include <utility>" << endl // pair
<< "#include <ostream>" << endl
<< "#include <sstream>" << endl;
@@ -259,6 +260,10 @@ generate_runtime_source (context& ctx, bool complete)
// argv_scanner
//
+ // Note that due to the erase logic we cannot just add i_ to
+ // start_position and so have to increment it instead. See also
+ // argv_file_scanner that continues with this logic.
+ //
os << "// argv_scanner" << endl
<< "//" << endl
@@ -295,6 +300,7 @@ generate_runtime_source (context& ctx, bool complete)
<< "else" << endl
<< "++i_;"
<< endl
+ << "++start_position_;"
<< "return r;"
<< "}"
<< "else" << endl
@@ -304,10 +310,19 @@ generate_runtime_source (context& ctx, bool complete)
<< "void argv_scanner::" << endl
<< "skip ()"
<< "{"
- << "if (i_ < argc_)" << endl
+ << "if (i_ < argc_)"
+ << "{"
<< "++i_;"
+ << "++start_position_;"
+ << "}"
<< "else" << endl
<< "throw eos_reached ();"
+ << "}"
+
+ << "std::size_t argv_scanner::" << endl
+ << "position ()"
+ << "{"
+ << "return start_position_;"
<< "}";
// vector_scanner
@@ -348,11 +363,19 @@ generate_runtime_source (context& ctx, bool complete)
<< "++i_;"
<< "else" << endl
<< "throw eos_reached ();"
+ << "}"
+
+ << "std::size_t vector_scanner::" << endl
+ << "position ()"
+ << "{"
+ << "return start_position_ + i_;"
<< "}";
}
// argv_file_scanner
//
+ // Note that we continue incrementing start_position like argv_scanner.
+ //
if (ctx.options.generate_file_scanner ())
{
bool sep (!ctx.opt_sep.empty ());
@@ -486,6 +509,7 @@ generate_runtime_source (context& ctx, bool complete)
<< "{"
<< "hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);"
<< "args_.pop_front ();"
+ << "++start_position_;"
<< "return hold_[i_].c_str ();"
<< "}"
<< "}"
@@ -498,8 +522,11 @@ generate_runtime_source (context& ctx, bool complete)
<< endl
<< "if (args_.empty ())" << endl
<< "return base::skip ();"
- << "else" << endl
+ << "else"
+ << "{"
<< "args_.pop_front ();"
+ << "++start_position_;"
+ << "}"
<< "}"
<< "const argv_file_scanner::option_info* argv_file_scanner::" << endl
@@ -512,6 +539,12 @@ generate_runtime_source (context& ctx, bool complete)
<< "return 0;"
<< "}"
+ << "std::size_t argv_file_scanner::" << endl
+ << "position ()"
+ << "{"
+ << "return start_position_;"
+ << "}"
+
<< "void argv_file_scanner::" << endl
<< "load (const std::string& file)"
<< "{"
@@ -734,6 +767,12 @@ generate_runtime_source (context& ctx, bool complete)
<< "scan_group (skipped);"
<< "}"
+ << "std::size_t group_scanner::" << endl
+ << "position ()"
+ << "{"
+ << "return scan_.position ();"
+ << "}"
+
<< "void group_scanner::" << endl
<< "scan_group (state st)"
<< "{"
@@ -746,6 +785,11 @@ generate_runtime_source (context& ctx, bool complete)
<< "throw unexpected_group (arg_[i_], group_scan_.next ());"
<< "}"
+ // Note that while it may seem like a good idea to pass
+ // scan_.position() to reset() below, the trailing group positions
+ // will overlap with the argument's. So it seems best to start
+ // positions of each argument in a group from 0.
+ //
<< "if (state_ != peeked)"
<< "{"
<< "arg_[i_ == 0 ? ++i_ : --i_].clear ();"
@@ -934,6 +978,28 @@ generate_runtime_source (context& ctx, bool complete)
os << "};";
+ // parser<std::pair<X, std::size_t>>
+ //
+ os << "template <typename X>" << endl
+ << "struct parser<std::pair<X, std::size_t> >"
+ << "{";
+
+ os << "static void" << endl
+ << "parse (std::pair<X, std::size_t>& x, " << (sp ? "bool& xs, " : "") << "scanner& s)"
+ << "{"
+ << "x.second = s.position ();"
+ << "parser<X>::parse (x.first, " << (sp ? "xs, " : "") << "s);"
+ << "}";
+
+ if (gen_merge)
+ os << "static void" << endl
+ << "merge (std::pair<X, std::size_t>& b, const std::pair<X, std::size_t>& a)"
+ << "{"
+ << "b = a;"
+ << "}";
+
+ os << "};";
+
// parser<std::vector<X>>
//
os << "template <typename X>" << endl
@@ -1001,6 +1067,7 @@ generate_runtime_source (context& ctx, bool complete)
<< endl
<< "if (s.more ())"
<< "{"
+ << "std::size_t pos (s.position ());"
<< "std::string ov (s.next ());"
<< "std::string::size_type p = ov.find ('=');"
<< endl
@@ -1020,13 +1087,13 @@ generate_runtime_source (context& ctx, bool complete)
os << "if (!kstr.empty ())"
<< "{"
<< "av[1] = const_cast<char*> (kstr.c_str ());"
- << "argv_scanner s (0, ac, av);"
+ << "argv_scanner s (0, ac, av, false, pos);"
<< "parser<K>::parse (k, " << (sp ? "dummy, " : "") << "s);"
<< "}"
<< "if (!vstr.empty ())"
<< "{"
<< "av[1] = const_cast<char*> (vstr.c_str ());"
- << "argv_scanner s (0, ac, av);"
+ << "argv_scanner s (0, ac, av, false, pos);"
<< "parser<V>::parse (v, " << (sp ? "dummy, " : "") << "s);"
<< "}"
<< "m[k] = v;"
diff --git a/cli/doc/buildfile b/cli/doc/buildfile
index 78f0e62..10e3536 100644
--- a/cli/doc/buildfile
+++ b/cli/doc/buildfile
@@ -53,7 +53,7 @@ if ($cli != [null])
clean = ($src_root != $out_root)
}
{{
- diag cli $>: ($<[0])
+ diag cli $> : ($<[0]) # @@ TMP
# Note that the date change doesn't change the script semantics, thus the
# variable is defined locally.
@@ -74,7 +74,7 @@ if ($cli != [null])
clean = ($src_root != $out_root)
}
{{
- diag cli $>: ($<[0])
+ diag cli $> : ($<[0]) # @@ TMP
$cli --generate-html $man_options \
--html-prologue-file $path($<[1]) \
--html-epilogue-file $path($<[2]) \
diff --git a/cli/manifest b/cli/manifest
index 7205efd..69896fb 100644
--- a/cli/manifest
+++ b/cli/manifest
@@ -12,7 +12,7 @@ doc-url: https://www.codesynthesis.com/projects/cli/doc/guide/
src-url: https://git.codesynthesis.com/cgit/cli/cli/tree/cli/
email: cli-users@codesynthesis.com ; Mailing list
build-warning-email: builds@codesynthesis.com
-builds: all
+builds: host
requires: c++14
depends: * build2 >= 0.13.0
depends: * bpkg >= 0.13.0