summaryrefslogtreecommitdiff
path: root/cli/source.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2012-05-10 17:54:18 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-05-10 17:54:18 +0200
commit443293aaf09eca7c3b88d621d056c4effee2c248 (patch)
treea35c7b2354295b5b73462c0806e04e2deef58171 /cli/source.cxx
parent4f9022f24c4591391637121c7274d9855b37bd93 (diff)
Implement option class inheritance
For now multiple, non-virtual inheritance is supported. An option class can now also be declared abstract using the class c = 0 {...}; syntax. New option, --exclude-base, controls whether base class information is present in usage and documentation.
Diffstat (limited to 'cli/source.cxx')
-rw-r--r--cli/source.cxx452
1 files changed, 306 insertions, 146 deletions
diff --git a/cli/source.cxx b/cli/source.cxx
index 1d1726e..d49caf8 100644
--- a/cli/source.cxx
+++ b/cli/source.cxx
@@ -279,7 +279,7 @@ namespace
// If we have both the long and the short descriptions, use
// the short one. Otherwise, use the first sentence from the
- // long one ubless --long-usage was specified.
+ // long one unless --long-usage was specified.
//
string d;
@@ -429,11 +429,66 @@ namespace
//
//
+ struct base_parse: traversal::class_, context
+ {
+ base_parse (context& c): context (c) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ os << "// " << escape (c.name ()) << " base" << endl
+ << "//" << endl
+ << "if (" << fq_name (c) << "::_parse (o, s))" << endl
+ << "return true;"
+ << endl;
+ }
+ };
+
+ //
+ //
+ struct base_desc: traversal::class_, context
+ {
+ base_desc (context& c): context (c) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ os << "// " << escape (c.name ()) << " base" << endl
+ << "//" << endl
+ << fq_name (c) << "::fill (os);"
+ << endl;
+ }
+ };
+
+ struct base_usage: traversal::class_, context
+ {
+ base_usage (context& c): context (c) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ os << "// " << escape (c.name ()) << " base" << endl
+ << "//" << endl
+ << fq_name (c) << "::print_usage (os);"
+ << endl;
+ }
+ };
+
+ //
+ //
struct class_: traversal::class_, context
{
class_ (context& c)
- : context (c), option_map_ (c), option_desc_ (c)
+ : context (c),
+ base_parse_ (c),
+ base_desc_ (c),
+ base_usage_ (c),
+ option_map_ (c),
+ option_desc_ (c)
{
+ inherits_base_parse_ >> base_parse_;
+ inherits_base_desc_ >> base_desc_;
+ inherits_base_usage_ >> base_usage_;
names_option_map_ >> option_map_;
names_option_desc_ >> option_desc_;
}
@@ -442,7 +497,10 @@ namespace
traverse (type& c)
{
string name (escape (c.name ()));
+
+ bool abst (c.abstract ());
bool ho (has<semantics::option> (c));
+ bool hb (c.inherits_begin () != c.inherits_end ());
os << "// " << name << endl
<< "//" << endl
@@ -453,94 +511,109 @@ namespace
string um (cli + "::unknown_mode");
os << name << "::" << endl
- << name << " (int& argc," << endl
- << "char** argv," << endl
- << "bool erase," << endl
- << um << " opt," << endl
- << um << " arg)";
+ << name << " ()";
{
option_init init (*this);
traversal::names names_init (init);
names (c, names_init);
}
os << "{"
- << cli << "::argv_scanner s (argc, argv, erase);"
- << "_parse (s, opt, arg);"
<< "}";
- os << name << "::" << endl
- << name << " (int start," << endl
- << "int& argc," << endl
- << "char** argv," << endl
- << "bool erase," << endl
- << um << " opt," << endl
- << um << " arg)";
+ if (!abst)
{
- option_init init (*this);
- traversal::names names_init (init);
- names (c, names_init);
- }
- os << "{"
- << cli << "::argv_scanner s (start, argc, argv, erase);"
- << "_parse (s, opt, arg);"
- << "}";
+ os << name << "::" << endl
+ << name << " (int& argc," << endl
+ << "char** argv," << endl
+ << "bool erase," << endl
+ << um << " opt," << endl
+ << um << " arg)";
+ {
+ option_init init (*this);
+ traversal::names names_init (init);
+ names (c, names_init);
+ }
+ os << "{"
+ << cli << "::argv_scanner s (argc, argv, erase);"
+ << "_parse (s, opt, arg);"
+ << "}";
- os << name << "::" << endl
- << name << " (int& argc," << endl
- << "char** argv," << endl
- << "int& end," << endl
- << "bool erase," << endl
- << um << " opt," << endl
- << um << " arg)";
- {
- option_init init (*this);
- traversal::names names_init (init);
- names (c, names_init);
- }
- os << "{"
- << cli << "::argv_scanner s (argc, argv, erase);"
- << "_parse (s, opt, arg);"
- << "end = s.end ();"
- << "}";
+ os << name << "::" << endl
+ << name << " (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "bool erase," << endl
+ << um << " opt," << endl
+ << um << " arg)";
+ {
+ option_init init (*this);
+ traversal::names names_init (init);
+ names (c, names_init);
+ }
+ os << "{"
+ << cli << "::argv_scanner s (start, argc, argv, erase);"
+ << "_parse (s, opt, arg);"
+ << "}";
- os << name << "::" << endl
- << name << " (int start," << endl
- << "int& argc," << endl
- << "char** argv," << endl
- << "int& end," << endl
- << "bool erase," << endl
- << um << " opt," << endl
- << um << " arg)";
- {
- option_init init (*this);
- traversal::names names_init (init);
- names (c, names_init);
- }
- os << "{"
- << cli << "::argv_scanner s (start, argc, argv, erase);"
- << "_parse (s, opt, arg);"
- << "end = s.end ();"
- << "}";
+ os << name << "::" << endl
+ << name << " (int& argc," << endl
+ << "char** argv," << endl
+ << "int& end," << endl
+ << "bool erase," << endl
+ << um << " opt," << endl
+ << um << " arg)";
+ {
+ option_init init (*this);
+ traversal::names names_init (init);
+ names (c, names_init);
+ }
+ os << "{"
+ << cli << "::argv_scanner s (argc, argv, erase);"
+ << "_parse (s, opt, arg);"
+ << "end = s.end ();"
+ << "}";
- os << name << "::" << endl
- << name << " (" << cli << "::scanner& s," << endl
- << um << " opt," << endl
- << um << " arg)";
- {
- option_init init (*this);
- traversal::names names_init (init);
- names (c, names_init);
+ os << name << "::" << endl
+ << name << " (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "int& end," << endl
+ << "bool erase," << endl
+ << um << " opt," << endl
+ << um << " arg)";
+ {
+ option_init init (*this);
+ traversal::names names_init (init);
+ names (c, names_init);
+ }
+ os << "{"
+ << cli << "::argv_scanner s (start, argc, argv, erase);"
+ << "_parse (s, opt, arg);"
+ << "end = s.end ();"
+ << "}";
+
+ os << name << "::" << endl
+ << name << " (" << cli << "::scanner& s," << endl
+ << um << " opt," << endl
+ << um << " arg)";
+ {
+ option_init init (*this);
+ traversal::names names_init (init);
+ names (c, names_init);
+ }
+ os << "{"
+ << "_parse (s, opt, arg);"
+ << "}";
}
- os << "{"
- << "_parse (s, opt, arg);"
- << "}";
// Usage.
//
if (usage)
{
+ bool b (hb && !options.exclude_base ());
+
os << "void " << name << "::" << endl
- << "print_usage (::std::ostream&" << (ho ? " os" : "") << ")"
+ << "print_usage (::std::ostream&" << (ho || b ? " os" : "") << ")"
<< "{";
// Calculate option length.
@@ -548,12 +621,61 @@ namespace
size_t len (0);
{
semantics::option* o (0);
- option_length t (*this, len, o);
- traversal::names n (t);
- names (c, n);
-
size_t max (options.option_length ());
+ // We need to go into our bases unless --exclude-base was
+ // specified.
+ //
+ {
+ traversal::class_ ct;
+ option_length olt (*this, len, o);
+ traversal::inherits i;
+ traversal::names n;
+
+ if (b)
+ ct >> i >> ct;
+
+ ct >> n >> olt;
+ ct.traverse (c);
+
+ // Now do the same for each base and issue a warning if any
+ // base has shorter option length than derived.
+ //
+ if (b && max == 0)
+ {
+ size_t d_len (len);
+ semantics::option* d_o (o);
+
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end (); ++i)
+ {
+ type& b (i->base ());
+
+ len = 0;
+ ct.traverse (b);
+
+ if (len == d_len)
+ continue;
+
+ cerr << c.file () << ":" << c.line () << ":" << c.column ()
+ << " warning: derived class option length is greater "
+ << "than that of a base class '" << b.name () << "'"
+ << endl;
+
+ cerr << b.file () << ":" << b.line () << ":" << b.column ()
+ << " note: class '" << b.name () << "' is defined here"
+ << endl;
+
+ cerr << c.file () << ":" << c.line () << ":" << c.column ()
+ << " note: use --option-length to specify uniform length"
+ << endl;
+ }
+
+ len = d_len;
+ o = d_o;
+ }
+ }
+
if (max != 0)
{
if (len > max)
@@ -568,6 +690,11 @@ namespace
}
}
+ // Call our bases.
+ //
+ if (b)
+ inherits (c, inherits_base_usage_);
+
// Print option usage.
//
{
@@ -591,15 +718,26 @@ namespace
os << "struct " << desc << "_init"
<< "{"
<< desc << "_init (" << cli << "::options& os)"
+ << "{"
+ << name << "::fill (os);"
+ << "}"
+ << "};";
+
+ os << "static " << desc << "_init " << desc << "_init_ (" <<
+ desc << "_);"
+ << endl;
+
+ os << "void " << name << "::" << endl
+ << "fill (" << cli << "::options& " << (ho || hb ? " os)" : ")")
<< "{";
+ // Add the entries from our bases first so that our entires
+ // override any conflicts.
+ //
+ inherits (c, inherits_base_desc_);
names (c, names_option_desc_);
- os << "}"
- << "};"
- << "static " << desc << "_init " << desc << "_init_ (" <<
- desc << "_);"
- << endl;
+ os << "}";
os << "const " << cli << "::options& " << name << "::" << endl
<< "description ()"
@@ -608,7 +746,7 @@ namespace
<< "};";
}
- // _parse()
+ // _parse ()
//
string map ("_cli_" + name + "_map");
@@ -632,57 +770,93 @@ namespace
<< "static " << map << "_init " << map << "_init_;"
<< endl;
- bool pfx (!opt_prefix.empty ());
- bool sep (!opt_sep.empty ());
-
- os << "void " << name << "::" << endl
- << "_parse (" << cli << "::scanner& s," << endl
- << um << " opt_mode," << endl
- << um << " arg_mode)"
- << "{";
-
- if (sep)
- os << "bool opt = true;" // Still recognizing options.
- << endl;
-
- os << "while (s.more ())"
+ os << "bool " << name << "::" << endl
+ << "_parse (const char* o, " << cli << "::scanner& s)"
<< "{"
- << "const char* o = s.peek ();";
-
- if (sep)
- os << endl
- << "if (std::strcmp (o, \"" << opt_sep << "\") == 0)"
- << "{"
- << "s.skip ();" // We don't want to remove the separator.
- << "opt = false;"
- << "continue;"
- << "}"
- << map << "::const_iterator i (" << endl
- << "opt ? " << map << "_.find (o) : " << map << "_.end ());";
- else
- os << map << "::const_iterator i (" << map << "_.find (o));";
-
- os << endl
+ << map << "::const_iterator i (" << map << "_.find (o));"
+ << endl
<< "if (i != " << map << "_.end ())"
<< "{"
<< "(*(i->second)) (*this, s);"
+ << "return true;"
<< "}";
- // Unknown option.
+ // Try our bases, from left-to-right.
//
- if (pfx)
+ inherits (c, inherits_base_parse_);
+
+ os << "return false;"
+ << "}";
+
+ if (!abst)
{
- size_t n (opt_prefix.size ());
+ bool pfx (!opt_prefix.empty ());
+ bool sep (!opt_sep.empty ());
- os << "else if (";
+ os << "void " << name << "::" << endl
+ << "_parse (" << cli << "::scanner& s," << endl
+ << um << " opt_mode," << endl
+ << um << " arg_mode)"
+ << "{";
if (sep)
- os << "opt && ";
+ os << "bool opt = true;" // Still recognizing options.
+ << endl;
- os << "std::strncmp (o, \"" << opt_prefix << "\", " <<
- n << ") == 0 && o[" << n << "] != '\\0')"
+ os << "while (s.more ())"
<< "{"
- << "switch (opt_mode)"
+ << "const char* o = s.peek ();";
+
+ if (sep)
+ os << endl
+ << "if (std::strcmp (o, \"" << opt_sep << "\") == 0)"
+ << "{"
+ << "s.skip ();" // We don't want to remove the separator.
+ << "opt = false;"
+ << "continue;"
+ << "}";
+
+ os << "if (" << (sep ? "opt && " : "") << "_parse (o, s));";
+
+ // Unknown option.
+ //
+ if (pfx)
+ {
+ size_t n (opt_prefix.size ());
+
+ os << "else if (";
+
+ if (sep)
+ os << "opt && ";
+
+ os << "std::strncmp (o, \"" << opt_prefix << "\", " <<
+ n << ") == 0 && o[" << n << "] != '\\0')"
+ << "{"
+ << "switch (opt_mode)"
+ << "{"
+ << "case " << cli << "::unknown_mode::skip:" << endl
+ << "{"
+ << "s.skip ();"
+ << "continue;"
+ << "}"
+ << "case " << cli << "::unknown_mode::stop:" << endl
+ << "{"
+ << "break;"
+ << "}"
+ << "case " << cli << "::unknown_mode::fail:" << endl
+ << "{"
+ << "throw " << cli << "::unknown_option (o);"
+ << "}"
+ << "}" // switch
+ << "break;" // The stop case.
+ << "}";
+ }
+
+ // Unknown argument.
+ //
+ os << "else"
+ << "{"
+ << "switch (arg_mode)"
<< "{"
<< "case " << cli << "::unknown_mode::skip:" << endl
<< "{"
@@ -695,41 +869,27 @@ namespace
<< "}"
<< "case " << cli << "::unknown_mode::fail:" << endl
<< "{"
- << "throw " << cli << "::unknown_option (o);"
+ << "throw " << cli << "::unknown_argument (o);"
<< "}"
<< "}" // switch
<< "break;" // The stop case.
+ << "}"
+
+ << "}" // for
<< "}";
}
-
- // Unknown argument.
- //
- os << "else"
- << "{"
- << "switch (arg_mode)"
- << "{"
- << "case " << cli << "::unknown_mode::skip:" << endl
- << "{"
- << "s.skip ();"
- << "continue;"
- << "}"
- << "case " << cli << "::unknown_mode::stop:" << endl
- << "{"
- << "break;"
- << "}"
- << "case " << cli << "::unknown_mode::fail:" << endl
- << "{"
- << "throw " << cli << "::unknown_argument (o);"
- << "}"
- << "}" // switch
- << "break;" // The stop case.
- << "}"
-
- << "}" // for
- << "}";
}
private:
+ base_parse base_parse_;
+ traversal::inherits inherits_base_parse_;
+
+ base_desc base_desc_;
+ traversal::inherits inherits_base_desc_;
+
+ base_usage base_usage_;
+ traversal::inherits inherits_base_usage_;
+
option_map option_map_;
traversal::names names_option_map_;