From 443293aaf09eca7c3b88d621d056c4effee2c248 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 10 May 2012 17:54:18 +0200 Subject: 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. --- cli/header.cxx | 150 ++++++++++----- cli/html.cxx | 10 +- cli/lexer.cxx | 3 +- cli/man.cxx | 10 +- cli/options.cli | 5 + cli/options.cxx | 138 +++++++++++--- cli/options.hxx | 52 +++++- cli/options.ixx | 41 ++++ cli/parser.cxx | 184 +++++++++++++++++- cli/parser.hxx | 19 ++ cli/runtime-header.cxx | 4 +- cli/semantics/class.cxx | 18 +- cli/semantics/class.hxx | 86 ++++++++- cli/semantics/elements.cxx | 13 ++ cli/semantics/elements.hxx | 9 + cli/source.cxx | 452 ++++++++++++++++++++++++++++++--------------- cli/token.hxx | 1 + cli/traversal/class.cxx | 23 +++ cli/traversal/class.hxx | 15 ++ 19 files changed, 994 insertions(+), 239 deletions(-) (limited to 'cli') 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 << "
" << endl; + if (!options.exclude_base ()) + inherits (c, inherits_base_); + names (c, names_option_); os << "
" << 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" { "", 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& c, scanner& s) { X x; - bool dummy; - parser::parse (x, dummy, s); + parser::parse (x, s); c.push_back (x); } }; @@ -438,8 +470,7 @@ namespace cli parse (std::set& c, scanner& s) { X x; - bool dummy; - parser::parse (x, dummy, s); + parser::parse (x, s); c.insert (x); } }; @@ -450,7 +481,7 @@ namespace cli static void parse (std::map& 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 Indent option descriptions 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 Generate the CLI support types in the " << ::std::endl << " namespace ('cli' by default)." << ::std::endl; @@ -796,8 +876,8 @@ print_usage (::std::ostream& os) os << "--html-prologue Insert the content of at the beginning of" << ::std::endl << " the HTML file." << ::std::endl; - os << "--html-epilogue Insert the content of at the end of the HTML" << ::std::endl - << " file." << ::std::endl; + os << "--html-epilogue Insert the content of at the end of the" << ::std::endl + << " HTML file." << ::std::endl; os << "--class Generate the man page or HTML documentation only" << ::std::endl << " for the 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 #include #include +#include #include 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 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 new_scope (scope_); - + class_* n (0); if (valid_) { - class_& n (root_->new_node (*path_, t.line (), t.column ())); - root_->new_edge (*scope_, n, t.identifier ()); - new_scope.set (&n); + n = &root_->new_node (*path_, t.line (), t.column ()); + root_->new_edge (*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 (ns, name)) + root_->new_edge (*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 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 +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 (&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 (&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 (&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 (&*i)) + if (T* r = lookup (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 (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 + 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 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 + #include 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_list; + + public: + bool + abstract () const + { + return abstract_; + } + + void + abstract (bool a) + { + abstract_ = a; + } + + public: + typedef pointer_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 (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 + { + inherits () {} + inherits (node_dispatcher& n) {node_traverser (n);} + + virtual void + traverse (type&); + }; + struct class_: scope_template { virtual void @@ -20,6 +29,12 @@ namespace traversal pre (type&); virtual void + inherits (type&); + + virtual void + inherits (type&, edge_dispatcher&); + + virtual void post (type&); }; } -- cgit v1.1