diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2012-05-10 17:54:18 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2012-05-10 17:54:18 +0200 |
commit | 443293aaf09eca7c3b88d621d056c4effee2c248 (patch) | |
tree | a35c7b2354295b5b73462c0806e04e2deef58171 | |
parent | 4f9022f24c4591391637121c7274d9855b37bd93 (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.
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&); }; } @@ -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 |