summaryrefslogtreecommitdiff
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
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.
-rw-r--r--cli/header.cxx150
-rw-r--r--cli/html.cxx10
-rw-r--r--cli/lexer.cxx3
-rw-r--r--cli/man.cxx10
-rw-r--r--cli/options.cli5
-rw-r--r--cli/options.cxx138
-rw-r--r--cli/options.hxx52
-rw-r--r--cli/options.ixx41
-rw-r--r--cli/parser.cxx184
-rw-r--r--cli/parser.hxx19
-rw-r--r--cli/runtime-header.cxx4
-rw-r--r--cli/semantics/class.cxx18
-rw-r--r--cli/semantics/class.hxx86
-rw-r--r--cli/semantics/elements.cxx13
-rw-r--r--cli/semantics/elements.hxx9
-rw-r--r--cli/source.cxx452
-rw-r--r--cli/token.hxx1
-rw-r--r--cli/traversal/class.cxx23
-rw-r--r--cli/traversal/class.hxx15
-rw-r--r--doc/cli.13
-rw-r--r--doc/cli.xhtml3
-rw-r--r--doc/language.txt12
-rw-r--r--tests/inheritance/driver.cxx36
-rw-r--r--tests/inheritance/makefile76
-rw-r--r--tests/inheritance/test.cli26
-rw-r--r--tests/inheritance/test.std4
-rw-r--r--tests/lexer/driver.cxx3
-rw-r--r--tests/lexer/test-006.cli4
-rw-r--r--tests/lexer/test-006.std2
-rw-r--r--tests/makefile2
-rw-r--r--tests/parser/common.cli1
-rw-r--r--tests/parser/makefile2
-rw-r--r--tests/parser/test-001-base.cli (renamed from tests/parser/base.cli)0
-rw-r--r--tests/parser/test-001-common.cli1
-rw-r--r--tests/parser/test-001.cli4
-rw-r--r--tests/parser/test-003.cli20
-rw-r--r--tests/parser/test-007-base.cli11
-rw-r--r--tests/parser/test-007.cli29
-rw-r--r--tests/parser/test-007.std0
39 files changed, 1222 insertions, 250 deletions
diff --git a/cli/header.cxx b/cli/header.cxx
index fb714ed..e16b592 100644
--- a/cli/header.cxx
+++ b/cli/header.cxx
@@ -65,6 +65,31 @@ namespace
//
//
+ struct base: traversal::class_, context
+ {
+ base (context& c): context (c), first_ (true) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ if (first_)
+ {
+ os << ": ";
+ first_ = false;
+ }
+ else
+ os << "," << endl
+ << " ";
+
+ os << "public " << fq_name (c);
+ }
+
+ private:
+ bool first_;
+ };
+
+ //
+ //
struct class_: traversal::class_, context
{
class_ (context& c)
@@ -79,59 +104,67 @@ namespace
virtual void
traverse (type& c)
{
+ bool abst (c.abstract ());
string name (escape (c.name ()));
+ string um (cli + "::unknown_mode");
- os << "class " << name
- << "{"
- << "public:" << endl
- << endl;
+ os << "class " << name;
+
+ {
+ base b (*this);
+ traversal::inherits i (b);
+ inherits (c, i);
+ }
+
+ os << "{"
+ << "public:" << endl;
// c-tors
//
- string um (cli + "::unknown_mode");
-
- os << name << " (int& argc," << endl
- << "char** argv," << endl
- << "bool erase = false," << endl
- << um << " option = " << um << "::fail," << endl
- << um << " argument = " << um << "::stop);"
- << endl;
+ if (!abst)
+ {
+ os << name << " (int& argc," << endl
+ << "char** argv," << endl
+ << "bool erase = false," << endl
+ << um << " option = " << um << "::fail," << endl
+ << um << " argument = " << um << "::stop);"
+ << endl;
- os << name << " (int start," << endl
- << "int& argc," << endl
- << "char** argv," << endl
- << "bool erase = false," << endl
- << um << " option = " << um << "::fail," << endl
- << um << " argument = " << um << "::stop);"
- << endl;
+ os << name << " (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "bool erase = false," << endl
+ << um << " option = " << um << "::fail," << endl
+ << um << " argument = " << um << "::stop);"
+ << endl;
- os << name << " (int& argc," << endl
- << "char** argv," << endl
- << "int& end," << endl
- << "bool erase = false," << endl
- << um << " option = " << um << "::fail," << endl
- << um << " argument = " << um << "::stop);"
- << endl;
+ os << name << " (int& argc," << endl
+ << "char** argv," << endl
+ << "int& end," << endl
+ << "bool erase = false," << endl
+ << um << " option = " << um << "::fail," << endl
+ << um << " argument = " << um << "::stop);"
+ << endl;
- os << name << " (int start," << endl
- << "int& argc," << endl
- << "char** argv," << endl
- << "int& end," << endl
- << "bool erase = false," << endl
- << um << " option = " << um << "::fail," << endl
- << um << " argument = " << um << "::stop);"
- << endl;
+ os << name << " (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "int& end," << endl
+ << "bool erase = false," << endl
+ << um << " option = " << um << "::fail," << endl
+ << um << " argument = " << um << "::stop);"
+ << endl;
- os << name << " (" << cli << "::scanner&," << endl
- << um << " option = " << um << "::fail," << endl
- << um << " argument = " << um << "::stop);"
- << endl;
+ os << name << " (" << cli << "::scanner&," << endl
+ << um << " option = " << um << "::fail," << endl
+ << um << " argument = " << um << "::stop);"
+ << endl;
+ }
//
//
os << "// Option accessors" << (modifier ? " and modifiers." : ".") << endl
- << "//" << endl
- << endl;
+ << "//" << endl;
names (c, names_option_);
@@ -157,15 +190,40 @@ namespace
<< endl;
}
- // _parse()
+ os << "// Implementation details." << endl
+ << "//" << endl
+ << "protected:" << endl;
+
+ // default c-tor
+ //
+ os << name << " ();"
+ << endl;
+
+ // fill ()
+ //
+ if (options.generate_description ())
+ os << "friend struct _cli_" + name + "_desc_init;"
+ << endl
+ << "static void" << endl
+ << "fill (" << cli << "::options&);"
+ << endl;
+
+ // _parse ()
//
- os << "private:" << endl
- << "void" << endl
- << "_parse (" << cli << "::scanner&," << endl
- << um << " option," << endl
- << um << " argument);"
+ os << "bool" << endl
+ << "_parse (const char*, " << cli << "::scanner&);"
<< endl;
+ // _parse ()
+ //
+ if (!abst)
+ os << "private:" << endl
+ << "void" << endl
+ << "_parse (" << cli << "::scanner&," << endl
+ << um << " option," << endl
+ << um << " argument);"
+ << endl;
+
// Data members.
//
os << "public:" << endl; //@@ tmp
diff --git a/cli/html.cxx b/cli/html.cxx
index c440854..8a9e97d 100644
--- a/cli/html.cxx
+++ b/cli/html.cxx
@@ -160,6 +160,9 @@ namespace
class_ (context& c)
: context (c), option_ (c)
{
+ *this >> inherits_base_ >> base_ >> inherits_base_;
+ base_ >> names_option_;
+
names_option_ >> option_;
}
@@ -179,15 +182,20 @@ namespace
os << "<dl class=\"options\">" << endl;
+ if (!options.exclude_base ())
+ inherits (c, inherits_base_);
+
names (c, names_option_);
os << "</dl>" << endl;
}
private:
- bool generated_;
option option_;
traversal::names names_option_;
+
+ traversal::class_ base_;
+ traversal::inherits inherits_base_;
};
}
diff --git a/cli/lexer.cxx b/cli/lexer.cxx
index baa8423..bf16ce5 100644
--- a/cli/lexer.cxx
+++ b/cli/lexer.cxx
@@ -155,7 +155,8 @@ next ()
get ();
return token (token::p_dcolon, c.line (), c.column ());
}
- break;
+
+ return token (token::p_colon, c.line (), c.column ());
}
case '{':
{
diff --git a/cli/man.cxx b/cli/man.cxx
index 93c6920..ac5d9b1 100644
--- a/cli/man.cxx
+++ b/cli/man.cxx
@@ -120,6 +120,9 @@ namespace
class_ (context& c)
: context (c), option_ (c)
{
+ *this >> inherits_base_ >> base_ >> inherits_base_;
+ base_ >> names_option_;
+
names_option_ >> option_;
}
@@ -137,13 +140,18 @@ namespace
return;
}
+ if (!options.exclude_base ())
+ inherits (c, inherits_base_);
+
names (c, names_option_);
}
private:
- bool generated_;
option option_;
traversal::names names_option_;
+
+ traversal::class_ base_;
+ traversal::inherits inherits_base_;
};
}
diff --git a/cli/options.cli b/cli/options.cli
index d21efa7..1462385 100644
--- a/cli/options.cli
+++ b/cli/options.cli
@@ -78,6 +78,11 @@ class options
files, and would like their usage to have the same indentation level."
};
+ bool --exclude-base
+ {
+ "Exclude base class information from usage and documentation."
+ };
+
std::string --cli-namespace = "::cli"
{
"<ns>",
diff --git a/cli/options.cxx b/cli/options.cxx
index eaef875..2d45b05 100644
--- a/cli/options.cxx
+++ b/cli/options.cxx
@@ -214,15 +214,24 @@ namespace cli
// See if the next argument is the file option.
//
const char* a (base::peek ());
+ const option_info* oi;
- if (!skip_ && a == option_)
+ if (!skip_ && (oi = find (a)))
{
base::next ();
if (!base::more ())
- throw missing_value (option_);
+ throw missing_value (oi->option);
- load (base::next ());
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (base::next (), oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (base::next ());
if (!args_.empty ())
return true;
@@ -276,12 +285,22 @@ namespace cli
args_.pop_front ();
}
+ const argv_file_scanner::option_info* argv_file_scanner::
+ find (const char* a) const
+ {
+ for (std::size_t i (0); i < options_count_; ++i)
+ if (std::strcmp (a, options_[i].option) == 0)
+ return &options_[i];
+
+ return 0;
+ }
+
void argv_file_scanner::
- load (const char* file)
+ load (const std::string& file)
{
using namespace std;
- ifstream is (file);
+ ifstream is (file.c_str ());
if (!is.is_open ())
throw file_io_failure (file);
@@ -361,8 +380,22 @@ namespace cli
s2 = string (s2, 1, n - 2);
}
- if (!skip_ && s1 == option_)
- load (s2.c_str ());
+ const option_info* oi;
+ if (!skip_ && (oi = find (s1.c_str ())))
+ {
+ if (s2.empty ())
+ throw missing_value (oi->option);
+
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (s2.c_str (), oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (s2);
+ }
else
{
args_.push_back (s1);
@@ -378,11 +411,11 @@ namespace cli
static void
parse (X& x, scanner& s)
{
- const char* o (s.next ());
+ std::string o (s.next ());
if (s.more ())
{
- const char* v (s.next ());
+ std::string v (s.next ());
std::istringstream is (v);
if (!(is >> x && is.eof ()))
throw invalid_value (o, v);
@@ -425,8 +458,7 @@ namespace cli
parse (std::vector<X>& c, scanner& s)
{
X x;
- bool dummy;
- parser<X>::parse (x, dummy, s);
+ parser<X>::parse (x, s);
c.push_back (x);
}
};
@@ -438,8 +470,7 @@ namespace cli
parse (std::set<X>& c, scanner& s)
{
X x;
- bool dummy;
- parser<X>::parse (x, dummy, s);
+ parser<X>::parse (x, s);
c.insert (x);
}
};
@@ -450,7 +481,7 @@ namespace cli
static void
parse (std::map<K, V>& m, scanner& s)
{
- const char* o (s.next ());
+ std::string o (s.next ());
if (s.more ())
{
@@ -517,6 +548,46 @@ namespace cli
//
options::
+options ()
+: help_ (),
+ version_ (),
+ output_dir_ (),
+ generate_modifier_ (),
+ generate_specifier_ (),
+ generate_description_ (),
+ generate_file_scanner_ (),
+ suppress_inline_ (),
+ suppress_undocumented_ (),
+ suppress_usage_ (),
+ long_usage_ (),
+ option_length_ (0),
+ exclude_base_ (),
+ cli_namespace_ ("::cli"),
+ generate_cxx_ (),
+ generate_man_ (),
+ generate_html_ (),
+ man_prologue_ (),
+ man_epilogue_ (),
+ html_prologue_ (),
+ html_epilogue_ (),
+ class__ (),
+ stdout__ (),
+ hxx_suffix_ (".hxx"),
+ ixx_suffix_ (".ixx"),
+ cxx_suffix_ (".cxx"),
+ man_suffix_ (".1"),
+ html_suffix_ (".html"),
+ option_prefix_ ("-"),
+ option_separator_ ("--"),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ guard_prefix_ (),
+ reserved_name_ (),
+ options_file_ ()
+{
+}
+
+options::
options (int& argc,
char** argv,
bool erase,
@@ -534,6 +605,7 @@ options (int& argc,
suppress_usage_ (),
long_usage_ (),
option_length_ (0),
+ exclude_base_ (),
cli_namespace_ ("::cli"),
generate_cxx_ (),
generate_man_ (),
@@ -580,6 +652,7 @@ options (int start,
suppress_usage_ (),
long_usage_ (),
option_length_ (0),
+ exclude_base_ (),
cli_namespace_ ("::cli"),
generate_cxx_ (),
generate_man_ (),
@@ -626,6 +699,7 @@ options (int& argc,
suppress_usage_ (),
long_usage_ (),
option_length_ (0),
+ exclude_base_ (),
cli_namespace_ ("::cli"),
generate_cxx_ (),
generate_man_ (),
@@ -674,6 +748,7 @@ options (int start,
suppress_usage_ (),
long_usage_ (),
option_length_ (0),
+ exclude_base_ (),
cli_namespace_ ("::cli"),
generate_cxx_ (),
generate_man_ (),
@@ -718,6 +793,7 @@ options (::cli::scanner& s,
suppress_usage_ (),
long_usage_ (),
option_length_ (0),
+ exclude_base_ (),
cli_namespace_ ("::cli"),
generate_cxx_ (),
generate_man_ (),
@@ -770,7 +846,8 @@ print_usage (::std::ostream& os)
os << "--suppress-undocumented Suppress the generation of documentation entries" << ::std::endl
<< " for undocumented options." << ::std::endl;
- os << "--suppress-usage Suppress the generation of the usage printing code." << ::std::endl;
+ os << "--suppress-usage Suppress the generation of the usage printing" << ::std::endl
+ << " code." << ::std::endl;
os << "--long-usage If no short documentation string is provided, use" << ::std::endl
<< " the complete long documentation string in usage." << ::std::endl;
@@ -778,6 +855,9 @@ print_usage (::std::ostream& os)
os << "--option-length <len> Indent option descriptions <len> characters when" << ::std::endl
<< " printing usage." << ::std::endl;
+ os << "--exclude-base Exclude base class information from usage and" << ::std::endl
+ << " documentation." << ::std::endl;
+
os << "--cli-namespace <ns> Generate the CLI support types in the <ns>" << ::std::endl
<< " namespace ('cli' by default)." << ::std::endl;
@@ -796,8 +876,8 @@ print_usage (::std::ostream& os)
os << "--html-prologue <file> Insert the content of <file> at the beginning of" << ::std::endl
<< " the HTML file." << ::std::endl;
- os << "--html-epilogue <file> Insert the content of <file> at the end of the HTML" << ::std::endl
- << " file." << ::std::endl;
+ os << "--html-epilogue <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " HTML file." << ::std::endl;
os << "--class <fq-name> Generate the man page or HTML documentation only" << ::std::endl
<< " for the <fq-name> options class." << ::std::endl;
@@ -879,6 +959,8 @@ struct _cli_options_map_init
&::cli::thunk< options, bool, &options::long_usage_ >;
_cli_options_map_["--option-length"] =
&::cli::thunk< options, std::size_t, &options::option_length_ >;
+ _cli_options_map_["--exclude-base"] =
+ &::cli::thunk< options, bool, &options::exclude_base_ >;
_cli_options_map_["--cli-namespace"] =
&::cli::thunk< options, std::string, &options::cli_namespace_ >;
_cli_options_map_["--generate-cxx"] =
@@ -928,6 +1010,20 @@ struct _cli_options_map_init
static _cli_options_map_init _cli_options_map_init_;
+bool options::
+_parse (const char* o, ::cli::scanner& s)
+{
+ _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+ if (i != _cli_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+}
+
void options::
_parse (::cli::scanner& s,
::cli::unknown_mode opt_mode,
@@ -946,13 +1042,7 @@ _parse (::cli::scanner& s,
continue;
}
- _cli_options_map::const_iterator i (
- opt ? _cli_options_map_.find (o) : _cli_options_map_.end ());
-
- if (i != _cli_options_map_.end ())
- {
- (*(i->second)) (*this, s);
- }
+ if (opt && _parse (o, s));
else if (opt && std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
{
switch (opt_mode)
diff --git a/cli/options.hxx b/cli/options.hxx
index a7a7094..8042669 100644
--- a/cli/options.hxx
+++ b/cli/options.hxx
@@ -8,6 +8,7 @@
#include <deque>
#include <iosfwd>
#include <string>
+#include <cstddef>
#include <exception>
namespace cli
@@ -239,13 +240,37 @@ namespace cli
public:
argv_file_scanner (int& argc,
char** argv,
- const std::string& file_option,
+ const std::string& option,
+ bool erase = false);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
bool erase = false);
+ struct option_info
+ {
+ // If search_func is not NULL, it is called, with the arg
+ // value as the second argument, to locate the options file.
+ // If it returns an empty string, then the file is ignored.
+ //
+ const char* option;
+ std::string (*search_func) (const char*, void* arg);
+ void* arg;
+ };
+
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false);
+
argv_file_scanner (int start,
int& argc,
char** argv,
- const std::string& file_option,
+ const option_info* options,
+ std::size_t options_count,
bool erase = false);
virtual bool
@@ -261,12 +286,19 @@ namespace cli
skip ();
private:
+ const option_info*
+ find (const char*) const;
+
void
- load (const char* file);
+ load (const std::string& file);
typedef argv_scanner base;
const std::string option_;
+ option_info option_info_;
+ const option_info* options_;
+ std::size_t options_count_;
+
std::string hold_;
std::deque<std::string> args_;
bool skip_;
@@ -284,7 +316,6 @@ namespace cli
class options
{
public:
-
options (int& argc,
char** argv,
bool erase = false,
@@ -319,7 +350,6 @@ class options
// Option accessors.
//
-
const bool&
help () const;
@@ -356,6 +386,9 @@ class options
const std::size_t&
option_length () const;
+ const bool&
+ exclude_base () const;
+
const std::string&
cli_namespace () const;
@@ -427,6 +460,14 @@ class options
static void
print_usage (::std::ostream&);
+ // Implementation details.
+ //
+ protected:
+ options ();
+
+ bool
+ _parse (const char*, ::cli::scanner&);
+
private:
void
_parse (::cli::scanner&,
@@ -446,6 +487,7 @@ class options
bool suppress_usage_;
bool long_usage_;
std::size_t option_length_;
+ bool exclude_base_;
std::string cli_namespace_;
bool generate_cxx_;
bool generate_man_;
diff --git a/cli/options.ixx b/cli/options.ixx
index f2072fe..05c9f9a 100644
--- a/cli/options.ixx
+++ b/cli/options.ixx
@@ -141,8 +141,12 @@ namespace cli
bool erase)
: argv_scanner (argc, argv, erase),
option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
skip_ (false)
{
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
}
inline argv_file_scanner::
@@ -153,6 +157,37 @@ namespace cli
bool erase)
: argv_scanner (start, argc, argv, erase),
option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase)
+ : argv_scanner (argc, argv, erase),
+ options_ (options),
+ options_count_ (options_count),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase)
+ : argv_scanner (start, argc, argv, erase),
+ options_ (options),
+ options_count_ (options_count),
skip_ (false)
{
}
@@ -233,6 +268,12 @@ option_length () const
return this->option_length_;
}
+inline const bool& options::
+exclude_base () const
+{
+ return this->exclude_base_;
+}
+
inline const std::string& options::
cli_namespace () const
{
diff --git a/cli/parser.cxx b/cli/parser.cxx
index 9cefdbc..00803f2 100644
--- a/cli/parser.cxx
+++ b/cli/parser.cxx
@@ -32,7 +32,8 @@ const char* keywords[] =
"double"
};
-const char* punctuation[] = {";", ",", "::", "{", "}", /*"(", ")",*/ "=", "|"};
+const char* punctuation[] = {
+ ";", ",", ":", "::", "{", "}", /*"(", ")",*/ "=", "|"};
// Output the token type and value in a format suitable for diagnostics.
//
@@ -412,17 +413,75 @@ class_def ()
throw error ();
}
- auto_restore<scope> new_scope (scope_);
-
+ class_* n (0);
if (valid_)
{
- class_& n (root_->new_node<class_> (*path_, t.line (), t.column ()));
- root_->new_edge<names> (*scope_, n, t.identifier ());
- new_scope.set (&n);
+ n = &root_->new_node<class_> (*path_, t.line (), t.column ());
+ root_->new_edge<names> (*scope_, *n, t.identifier ());
}
t = lexer_->next ();
+ // inheritance-spec
+ //
+ if (t.punctuation () == token::p_colon)
+ {
+ for (;;)
+ {
+ t = lexer_->next ();
+ size_t line (t.line ()), col (t.column ());
+
+ string name;
+ if (!qualified_name (t, name))
+ {
+ cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: "
+ << "expected qualified name instead of " << t << endl;
+ throw error ();
+ }
+
+ string ns;
+
+ // If it is a fully-qualifed name, then start from the global namespace.
+ // Otherwise, from the current scope.
+ //
+ if (name[0] == ':')
+ name = string (name, 2, string::npos);
+ else
+ ns = scope_->fq_name ();
+
+ if (class_* b = lookup<class_> (ns, name))
+ root_->new_edge<inherits> (*n, *b);
+ else
+ {
+ cerr << *path_ << ':' << line << ':' << col << ": error: "
+ << "unable to resolve base class '" << name << "'" << endl;
+ valid_ = false;
+ }
+
+ if (t.punctuation () != token::p_comma)
+ break;
+ }
+ }
+
+ // abstract-spec
+ //
+ if (t.punctuation () == token::p_eq)
+ {
+ t = lexer_->next ();
+
+ if (t.type () != token::t_int_lit || t.literal () != "0")
+ {
+ cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: "
+ << "expected '0' instead of " << t << endl;
+ throw error ();
+ }
+
+ if (n != 0)
+ n->abstract (true);
+
+ t = lexer_->next ();
+ }
+
if (t.punctuation () != token::p_lcbrace)
{
cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: "
@@ -430,11 +489,13 @@ class_def ()
throw error ();
}
+ auto_restore<scope> new_scope (scope_, n);
+
// decl-seq
//
t = lexer_->next ();
- while (true)
+ for (;;)
{
try
{
@@ -494,7 +555,7 @@ option_def (token& t)
// option-name-seq
//
names::name_list nl;
- while (true)
+ for (;;)
{
switch (t.type ())
{
@@ -780,7 +841,7 @@ qualified_name (token& t, string& r)
t = lexer_->next ();
}
- while (true)
+ for (;;)
{
if (t.type () != token::t_identifier)
{
@@ -1189,3 +1250,108 @@ fundamental_type (token& t, string& r)
return true;
}
+
+template <typename T>
+T* parser::
+lookup (string const& ss, string const& name, cli_unit* unit, bool outer)
+{
+ if (unit == 0)
+ unit = cur_;
+
+ // Resolve the starting scope in this unit, if any.
+ //
+ string::size_type b (0), e;
+ scope* s (0);
+
+ do
+ {
+ e = ss.find ("::", b);
+ string n (ss, b, e == string::npos ? e : e - b);
+
+ if (n.empty ())
+ s = unit;
+ else
+ {
+ scope::names_iterator_pair ip (s->find (n));
+
+ for (s = 0; ip.first != ip.second; ++ip.first)
+ if (s = dynamic_cast<scope*> (&ip.first->named ()))
+ break;
+
+ if (s == 0)
+ break; // No such scope in this unit.
+ }
+
+ b = e;
+
+ if (b == string::npos)
+ break;
+
+ b += 2;
+ } while (true);
+
+ // If we have the starting scope, then try to resolve the name in it.
+ //
+ if (s != 0)
+ {
+ b = 0;
+
+ do
+ {
+ e = name.find ("::", b);
+ string n (name, b, e == string::npos ? e : e - b);
+
+ scope::names_iterator_pair ip (s->find (n));
+
+ // If this is the last name, then see if we have the desired type.
+ //
+ if (e == string::npos)
+ {
+ for (; ip.first != ip.second; ++ip.first)
+ if (T* r = dynamic_cast<T*> (&ip.first->named ()))
+ return r;
+ }
+ // Otherwise, this should be a scope.
+ //
+ else
+ {
+ for (s = 0; ip.first != ip.second; ++ip.first)
+ if (s = dynamic_cast<scope*> (&ip.first->named ()))
+ break;
+
+ if (s == 0)
+ break; // No such inner scope.
+ }
+
+ b = e;
+
+ if (b == string::npos)
+ break;
+
+ b += 2;
+ } while (true);
+ }
+
+ // If we are here, then that means the lookup didn't find anything in
+ // this unit. The next step is to examine all the included units.
+ //
+ for (cli_unit::includes_iterator i (unit->includes_begin ());
+ i != unit->includes_end ();
+ ++i)
+ {
+ if (cli_includes* ci = dynamic_cast<cli_includes*> (&*i))
+ if (T* r = lookup<T> (ss, name, &ci->includee (), false))
+ return r;
+ }
+
+ // If we still haven't found anything, then the next step is to search
+ // one-outer scope, unless it is the global namespace.
+ //
+ if (outer && !ss.empty ())
+ {
+ string n (ss, 0, ss.rfind ("::"));
+ return lookup<T> (n, name, unit, false);
+ }
+
+ return 0;
+}
diff --git a/cli/parser.hxx b/cli/parser.hxx
index c93b9a0..34d3450 100644
--- a/cli/parser.hxx
+++ b/cli/parser.hxx
@@ -56,6 +56,25 @@ private:
void
recover (token& t);
+ // Lookup a name in the specified starting scope. Empty scope denotes
+ // the global namespace. Starting scope should be a fully-qualified
+ // name while name can be qualified but should not be fully-qualified
+ // (to lookup a fully-qualified name use the global namespace as the
+ // starting scope).
+ //
+ // If starting unit is not specified, the lookup is performed in the
+ // current unit. It then continues in all the units that the starting
+ // unit includes, transitively.
+ //
+ // The outer flag specifies whether to search the outer scopes.
+ //
+ template <typename T>
+ T*
+ lookup (std::string const& scope,
+ std::string const& name,
+ semantics::cli_unit* unit = 0,
+ bool outer = true);
+
private:
bool valid_;
semantics::path const* path_;
diff --git a/cli/runtime-header.cxx b/cli/runtime-header.cxx
index a1cf114..f0b3cc1 100644
--- a/cli/runtime-header.cxx
+++ b/cli/runtime-header.cxx
@@ -364,7 +364,7 @@ generate_runtime_header (context& ctx)
<< "bool flag," << endl
<< "const std::string& default_value);"
<< endl
- << "private:"
+ << "private:" << endl
<< "std::string name_;"
<< "option_names aliases_;"
<< "bool flag_;"
@@ -381,7 +381,7 @@ generate_runtime_header (context& ctx)
<< endl
<< "void" << endl
<< "push_back (const option&);"
- << "private:"
+ << "private:" << endl
<< "typedef std::map<std::string, container_type::size_type> map_type;"
<< "map_type map_;"
<< "};";
diff --git a/cli/semantics/class.cxx b/cli/semantics/class.cxx
index 1af31dc..9a6fc85 100644
--- a/cli/semantics/class.cxx
+++ b/cli/semantics/class.cxx
@@ -19,9 +19,21 @@ namespace semantics
{
using compiler::type_info;
- type_info ti (typeid (class_));
- ti.add_base (typeid (scope));
- insert (ti);
+ // inherits
+ //
+ {
+ type_info ti (typeid (inherits));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // class_
+ //
+ {
+ type_info ti (typeid (class_));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
}
} init_;
}
diff --git a/cli/semantics/class.hxx b/cli/semantics/class.hxx
index be0b9f9..f5f12c8 100644
--- a/cli/semantics/class.hxx
+++ b/cli/semantics/class.hxx
@@ -6,17 +6,101 @@
#ifndef CLI_SEMANTICS_CLASS_HXX
#define CLI_SEMANTICS_CLASS_HXX
+#include <vector>
+
#include <semantics/elements.hxx>
namespace semantics
{
+ class class_;
+
+ class inherits: public edge
+ {
+ public:
+ class_&
+ base () const
+ {
+ return *base_;
+ }
+
+ class_&
+ derived () const
+ {
+ return *derived_;
+ }
+
+ public:
+ void
+ set_left_node (class_& n)
+ {
+ derived_ = &n;
+ }
+
+ void
+ set_right_node (class_& n)
+ {
+ base_ = &n;
+ }
+
+ protected:
+ class_* base_;
+ class_* derived_;
+ };
+
class class_: public scope
{
+ private:
+ typedef std::vector<inherits*> inherits_list;
+
+ public:
+ bool
+ abstract () const
+ {
+ return abstract_;
+ }
+
+ void
+ abstract (bool a)
+ {
+ abstract_ = a;
+ }
+
+ public:
+ typedef pointer_iterator<inherits_list::const_iterator> inherits_iterator;
+
+ inherits_iterator
+ inherits_begin () const
+ {
+ return inherits_.begin ();
+ }
+
+ inherits_iterator
+ inherits_end () const
+ {
+ return inherits_.end ();
+ }
+
public:
class_ (path const& file, size_t line, size_t column)
- : node (file, line, column)
+ : node (file, line, column), abstract_ (false)
+ {
+ }
+
+ void
+ add_edge_left (inherits& e)
{
+ inherits_.push_back (&e);
}
+
+ void
+ add_edge_right (inherits&) {}
+
+ using scope::add_edge_left;
+ using scope::add_edge_right;
+
+ private:
+ bool abstract_;
+ inherits_list inherits_;
};
}
diff --git a/cli/semantics/elements.cxx b/cli/semantics/elements.cxx
index 04d6e03..3e87248 100644
--- a/cli/semantics/elements.cxx
+++ b/cli/semantics/elements.cxx
@@ -9,6 +9,19 @@
namespace semantics
{
+ // nameable
+ //
+ string nameable::
+ fq_name () const
+ {
+ string const& n (name ());
+
+ if (n.empty ())
+ return n;
+ else
+ return scope ().fq_name () + "::" + n;
+ }
+
// scope
//
diff --git a/cli/semantics/elements.hxx b/cli/semantics/elements.hxx
index 6dd6f78..e650f37 100644
--- a/cli/semantics/elements.hxx
+++ b/cli/semantics/elements.hxx
@@ -254,12 +254,21 @@ namespace semantics
return named_->name ();
}
+ string
+ fq_name () const;
+
scope_type&
scope ()
{
return named_->scope ();
}
+ scope_type const&
+ scope () const
+ {
+ return named_->scope ();
+ }
+
names&
named ()
{
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_;
diff --git a/cli/token.hxx b/cli/token.hxx
index cfa3afd..22679d3 100644
--- a/cli/token.hxx
+++ b/cli/token.hxx
@@ -77,6 +77,7 @@ public:
{
p_semi,
p_comma,
+ p_colon,
p_dcolon,
p_lcbrace,
p_rcbrace,
diff --git a/cli/traversal/class.cxx b/cli/traversal/class.cxx
index 8262e5d..3f178b6 100644
--- a/cli/traversal/class.cxx
+++ b/cli/traversal/class.cxx
@@ -7,15 +7,38 @@
namespace traversal
{
+ // inherits
+ //
+ void inherits::
+ traverse (type& i)
+ {
+ dispatch (i.base ());
+ }
+
+ // class_
+ //
void class_::
traverse (type& c)
{
pre (c);
+ inherits (c);
names (c);
post (c);
}
void class_::
+ inherits (type& c)
+ {
+ inherits (c, *this);
+ }
+
+ void class_::
+ inherits (type& c, edge_dispatcher& d)
+ {
+ iterate_and_dispatch (c.inherits_begin (), c.inherits_end (), d);
+ }
+
+ void class_::
pre (type&)
{
}
diff --git a/cli/traversal/class.hxx b/cli/traversal/class.hxx
index ef4433e..37f5f64 100644
--- a/cli/traversal/class.hxx
+++ b/cli/traversal/class.hxx
@@ -11,6 +11,15 @@
namespace traversal
{
+ struct inherits: edge<semantics::inherits>
+ {
+ inherits () {}
+ inherits (node_dispatcher& n) {node_traverser (n);}
+
+ virtual void
+ traverse (type&);
+ };
+
struct class_: scope_template<semantics::class_>
{
virtual void
@@ -20,6 +29,12 @@ namespace traversal
pre (type&);
virtual void
+ inherits (type&);
+
+ virtual void
+ inherits (type&, edge_dispatcher&);
+
+ virtual void
post (type&);
};
}
diff --git a/doc/cli.1 b/doc/cli.1
index 3c4e64c..67c397d 100644
--- a/doc/cli.1
+++ b/doc/cli.1
@@ -105,6 +105,9 @@ Indent option descriptions \fIlen\fP characters when printing usage\. This
is useful when you have multiple options classes, potentially in separate
files, and would like their usage to have the same indentation level\.
+.IP "\fB--exclude-base\fP"
+Exclude base class information from usage and documentation\.
+
.IP "\fB--cli-namespace\fP \fIns\fP"
Generate the CLI support types in the \fIns\fP namespace (\fBcli\fP by
default)\. The namespace can be nested, for example \fBdetails::cli\fP\. If
diff --git a/doc/cli.xhtml b/doc/cli.xhtml
index 58ff1f2..fdcde80 100644
--- a/doc/cli.xhtml
+++ b/doc/cli.xhtml
@@ -128,6 +128,9 @@
is useful when you have multiple options classes, potentially in separate
files, and would like their usage to have the same indentation level.</dd>
+ <dt><code><b>--exclude-base</b></code></dt>
+ <dd>Exclude base class information from usage and documentation.</dd>
+
<dt><code><b>--cli-namespace</b></code> <i>ns</i></dt>
<dd>Generate the CLI support types in the <i>ns</i> namespace
(<code><b>cli</b></code> by default). The namespace can be nested, for
diff --git a/doc/language.txt b/doc/language.txt
index 0a5ee5f..445d743 100644
--- a/doc/language.txt
+++ b/doc/language.txt
@@ -42,7 +42,17 @@ namespace-body:
decl-seq(opt)
class-def:
- "class" identifier "{" option-def-seq(opt) "};"
+ "class" identifier inheritance-spec(opt) abstract-spec(opt) "{" option-def-seq(opt) "};"
+
+inheritance-spec:
+ ":" base-seq
+
+base-seq:
+ qualified-name
+ base-seq "," qualified-name
+
+abstract-spec:
+ "=" "0"
option-def-seq:
option-def
diff --git a/tests/inheritance/driver.cxx b/tests/inheritance/driver.cxx
new file mode 100644
index 0000000..1c4d32c
--- /dev/null
+++ b/tests/inheritance/driver.cxx
@@ -0,0 +1,36 @@
+// file : tests/inheritance/driver.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+// Test option class inheritance.
+//
+
+#include <string>
+#include <cassert>
+#include <iostream>
+
+#include "test.hxx"
+
+using namespace std;
+
+int
+main (int argc, char* argv[])
+{
+ const cli::options& d (options::description ());
+
+ assert (d.size () == 4);
+ assert (d[0].name () == "--very-long-flag");
+ assert (d[1].name () == "-i");
+ assert (d[2].name () == "-s");
+ assert (d[3].name () == "--string");
+
+ options o (argc, argv);
+
+ assert (o.very_long_flag ());
+ assert (o.s () == "short");
+ assert (o.i () == 123);
+ assert (o.string () == "long");
+
+ options::print_usage (cout);
+}
diff --git a/tests/inheritance/makefile b/tests/inheritance/makefile
new file mode 100644
index 0000000..3d99a99
--- /dev/null
+++ b/tests/inheritance/makefile
@@ -0,0 +1,76 @@
+# file : tests/inheritance/makefile
+# author : Boris Kolpackov <boris@codesynthesis.com>
+# copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+# license : MIT; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make
+
+cxx_tun := driver.cxx
+cli_tun := test.cli
+
+#
+#
+cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o) $(cli_tun:.cli=.o))
+cxx_od := $(cxx_obj:.o=.o.d)
+
+driver := $(out_base)/driver
+test := $(out_base)/.test
+clean := $(out_base)/.clean
+
+# Build.
+#
+$(driver): $(cxx_obj)
+$(cxx_obj) $(cxx_od): cpp_options := -I$(out_base)
+
+genf := $(cli_tun:.cli=.hxx) $(cli_tun:.cli=.ixx) $(cli_tun:.cli=.cxx)
+gen := $(addprefix $(out_base)/,$(genf))
+
+$(gen): $(out_root)/cli/cli
+$(gen): cli := $(out_root)/cli/cli
+$(gen): cli_options := --generate-description --option-length 17
+
+$(call include-dep,$(cxx_od),$(cxx_obj),$(gen))
+
+# Alias for default target.
+#
+$(out_base)/: $(driver)
+
+# Test.
+#
+$(test): driver := $(driver)
+$(test): $(driver) $(src_base)/test.std
+ $(call message,test $$1,$$1 --very-long-flag -s short -i 123 \
+--string long >$(out_base)/test.out,$(driver))
+ $(call message,,diff -u $(src_base)/test.std $(out_base)/test.out)
+ $(call message,,rm -f $(out_base)/test.out)
+
+# Clean.
+#
+$(clean): \
+ $(driver).o.clean \
+ $(addsuffix .cxx.clean,$(cxx_obj)) \
+ $(addsuffix .cxx.clean,$(cxx_od)) \
+ $(addprefix $(out_base)/,$(cli_tun:.cli=.cxx.cli.clean))
+
+# Generated .gitignore.
+#
+ifeq ($(out_base),$(src_base))
+$(driver): | $(out_base)/.gitignore
+
+$(out_base)/.gitignore: files := driver $(genf)
+$(clean): $(out_base)/.gitignore.clean
+
+$(call include,$(bld_root)/git/gitignore.make)
+endif
+
+# How to.
+#
+$(call include,$(bld_root)/cxx/o-e.make)
+$(call include,$(bld_root)/cxx/cxx-o.make)
+$(call include,$(bld_root)/cxx/cxx-d.make)
+$(call include,$(scf_root)/cli/cli-cxx.make)
+
+# Dependencies.
+#
+$(call import,$(src_root)/cli/makefile)
+
diff --git a/tests/inheritance/test.cli b/tests/inheritance/test.cli
new file mode 100644
index 0000000..481a771
--- /dev/null
+++ b/tests/inheritance/test.cli
@@ -0,0 +1,26 @@
+// file : tests/inheritance/test.cli
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+include <string>;
+
+class base1 = 0
+{
+ bool --very-long-flag {"Long flag."};
+};
+
+class base2
+{
+ std::string -s {"<str>", "Short string."};
+};
+
+class interm: base1
+{
+ int -i = 1 {"<num>", "Integer."};
+};
+
+class options: interm, base2
+{
+ std::string --string {"<str>", "Long string."};
+};
diff --git a/tests/inheritance/test.std b/tests/inheritance/test.std
new file mode 100644
index 0000000..4c93225
--- /dev/null
+++ b/tests/inheritance/test.std
@@ -0,0 +1,4 @@
+--very-long-flag Long flag.
+-i <num> Integer.
+-s <str> Short string.
+--string <str> Long string.
diff --git a/tests/lexer/driver.cxx b/tests/lexer/driver.cxx
index 20dbf9d..9f5ac9e 100644
--- a/tests/lexer/driver.cxx
+++ b/tests/lexer/driver.cxx
@@ -28,7 +28,8 @@ const char* keywords[] =
"double"
};
-const char* punctuation[] = {";", ",", "::", "{", "}", /*"(", ")",*/ "=", "|"};
+const char* punctuation[] = {
+ ";", ",", ":", "::", "{", "}", /*"(", ")",*/ "=", "|"};
int
main (int argc, char* argv[])
diff --git a/tests/lexer/test-006.cli b/tests/lexer/test-006.cli
index 706f0f2..d67cea7 100644
--- a/tests/lexer/test-006.cli
+++ b/tests/lexer/test-006.cli
@@ -11,4 +11,6 @@ baz */ "b";
- /* a
a
a*/ 5
-// eos \ No newline at end of file
+// eos
+:
+::
diff --git a/tests/lexer/test-006.std b/tests/lexer/test-006.std
index 65f47cc..82709e0 100644
--- a/tests/lexer/test-006.std
+++ b/tests/lexer/test-006.std
@@ -4,4 +4,6 @@
-5
;
-5
+:
+::
<EOS>
diff --git a/tests/makefile b/tests/makefile
index 432b0a3..9ac2655 100644
--- a/tests/makefile
+++ b/tests/makefile
@@ -5,7 +5,7 @@
include $(dir $(lastword $(MAKEFILE_LIST)))../build/bootstrap.make
-tests := ctor erase file lexer parser specifier
+tests := ctor erase file inheritance lexer parser specifier
default := $(out_base)/
test := $(out_base)/.test
diff --git a/tests/parser/common.cli b/tests/parser/common.cli
deleted file mode 100644
index c0c7262..0000000
--- a/tests/parser/common.cli
+++ /dev/null
@@ -1 +0,0 @@
-include "base.cli";
diff --git a/tests/parser/makefile b/tests/parser/makefile
index eaceabd..490fbf3 100644
--- a/tests/parser/makefile
+++ b/tests/parser/makefile
@@ -7,7 +7,7 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make
cxx_tun := driver.cxx
-tests := 000 001 002 003 004 005 006
+tests := 000 001 002 003 004 005 006 007
#
#
diff --git a/tests/parser/base.cli b/tests/parser/test-001-base.cli
index e69de29..e69de29 100644
--- a/tests/parser/base.cli
+++ b/tests/parser/test-001-base.cli
diff --git a/tests/parser/test-001-common.cli b/tests/parser/test-001-common.cli
new file mode 100644
index 0000000..73fb928
--- /dev/null
+++ b/tests/parser/test-001-common.cli
@@ -0,0 +1 @@
+include "test-001-base.cli";
diff --git a/tests/parser/test-001.cli b/tests/parser/test-001.cli
index 473a238..52f7f8e 100644
--- a/tests/parser/test-001.cli
+++ b/tests/parser/test-001.cli
@@ -2,5 +2,5 @@
//
include <string>;
include "types.hxx";
-include "common.cli";
-include "../parser/base.cli";
+include "test-001-common.cli";
+include "../parser/test-001-base.cli";
diff --git a/tests/parser/test-003.cli b/tests/parser/test-003.cli
index bfd8c72..2920b16 100644
--- a/tests/parser/test-003.cli
+++ b/tests/parser/test-003.cli
@@ -1,9 +1,23 @@
-// class-def
+// class-def, inheritance-spec, abstract-spec
//
class c1
{
};
-class c2
+class c2 = 0
{
-}; \ No newline at end of file
+};
+
+class c3: c1, ::c2
+{
+};
+
+namespace n1
+{
+ class c {};
+}
+
+class c4: n1::c = 0
+{
+};
+
diff --git a/tests/parser/test-007-base.cli b/tests/parser/test-007-base.cli
new file mode 100644
index 0000000..7c02a39
--- /dev/null
+++ b/tests/parser/test-007-base.cli
@@ -0,0 +1,11 @@
+class b1 {};
+
+namespace n1
+{
+ class b2 {};
+
+ namespace i1
+ {
+ class b3 {};
+ }
+}
diff --git a/tests/parser/test-007.cli b/tests/parser/test-007.cli
new file mode 100644
index 0000000..3e5ca7f
--- /dev/null
+++ b/tests/parser/test-007.cli
@@ -0,0 +1,29 @@
+// base class lookup
+//
+
+include "test-007-base.cli";
+
+class c1 {};
+class c2: c1 {};
+class c3: ::c1 {};
+
+namespace n1
+{
+ class c4 {};
+ class c5: c4 {};
+ class c6: n1::c4 {};
+ class c7: ::n1::c4 {};
+
+ class c8: b2 {}; // From included.
+ class c9: i1::b3 {}; // From included.
+
+ namespace i1
+ {
+ class c10: c4 {}; // Outer scope.
+ class c11: b3 {}; // From included.
+ class c12: b2 {}; // Outer scope from included.
+ }
+}
+
+class c13: n1::c4 {};
+class c14: ::n1::c4 {};
diff --git a/tests/parser/test-007.std b/tests/parser/test-007.std
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/parser/test-007.std