From 369470005607b9501a769be0ae2a4c79c90bad84 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sun, 8 Nov 2009 15:35:19 +0200 Subject: Implement usage generation Also migrate the CLI compiler usage handling to the auto-generated version. --- cli/cli.cxx | 13 +-- cli/context.cxx | 196 +++++++++++++++++++++++++++++++++++- cli/context.hxx | 14 +++ cli/generator.cxx | 64 ++---------- cli/generator.hxx | 3 - cli/header.cxx | 12 ++- cli/lexer.cxx | 8 +- cli/options.cli | 93 ++++++++++++++--- cli/options.cxx | 94 ++++++++++++++---- cli/options.hxx | 42 +++++--- cli/options.ixx | 38 ++++--- cli/parser.cxx | 88 ++++++++++++++-- cli/source.cxx | 293 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ cli/usage.hxx | 181 --------------------------------- 14 files changed, 814 insertions(+), 325 deletions(-) delete mode 100644 cli/usage.hxx diff --git a/cli/cli.cxx b/cli/cli.cxx index a8e7fae..761b9a9 100644 --- a/cli/cli.cxx +++ b/cli/cli.cxx @@ -9,7 +9,6 @@ #include -#include "usage.hxx" #include "options.hxx" #include "parser.hxx" #include "generator.hxx" @@ -48,17 +47,7 @@ int main (int argc, char* argv[]) << endl << "Options:" << endl; - compiler::ostream_filter filt (e); - - e << "--help" << endl - << " Print usage information and exit." - << endl; - - e << "--version" << endl - << " Print version and exit." - << endl; - - generator::usage (); + options::print_usage (e); return 0; } diff --git a/cli/context.cxx b/cli/context.cxx index 7398da8..849b7cb 100644 --- a/cli/context.cxx +++ b/cli/context.cxx @@ -3,6 +3,8 @@ // copyright : Copyright (c) 2009 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file +#include + #include "context.hxx" using namespace std; @@ -90,11 +92,14 @@ namespace } context:: -context (ostream& os_, semantics::cli_unit& unit_, options_type const& ops) +context (ostream& os_, + semantics::cli_unit& unit_, + options_type const& ops) : data_ (new (shared) data), os (os_), unit (unit_), options (ops), + usage (!options.suppress_usage ()), inl (data_->inl_), opt_prefix (options.option_prefix ()), opt_sep (options.option_separator ()), @@ -114,6 +119,7 @@ context (context& c) os (c.os), unit (c.unit), options (c.options), + usage (c.usage), inl (c.inl), opt_prefix (c.opt_prefix), opt_sep (c.opt_sep), @@ -192,6 +198,194 @@ escape (string const& name) const return r; } +string context:: +format (string const& s, output_type ot) +{ + string r; + r.reserve (s.size ()); + + bool escape (false); + std::stack blocks; // Bit 0: code; 1: italic; 2: bold. + + for (size_t i (0), n (s.size ()); i < n; ++i) + { + if (escape) + { + switch (s[i]) + { + case '\\': + { + r += '\\'; + break; + } + case '"': + { + r += '"'; + break; + } + case '\'': + { + r += '\''; + break; + } + case 'c': + { + unsigned char b (1); + size_t j (i + 1); + + if (j < n) + { + if (s[j] == 'i') + { + b |= 2; + j++; + + if (j < n && s[j] == 'b') + { + b |= 4; + j++; + } + } + else if (s[j] == 'b') + { + b |= 4; + j++; + + if (j < n && s[j] == 'i') + { + b |= 2; + j++; + } + } + } + + if (j < n && s[j] == '{') + { + i = j; + blocks.push (b); + break; + } + + r += 'c'; + break; + } + case 'i': + { + unsigned char b (2); + size_t j (i + 1); + + if (j < n) + { + if (s[j] == 'c') + { + b |= 1; + j++; + + if (j < n && s[j] == 'b') + { + b |= 4; + j++; + } + } + else if (s[j] == 'b') + { + b |= 4; + j++; + + if (j < n && s[j] == 'c') + { + b |= 1; + j++; + } + } + } + + if (j < n && s[j] == '{') + { + i = j; + blocks.push (b); + break; + } + + r += 'i'; + break; + } + case 'b': + { + unsigned char b (4); + size_t j (i + 1); + + if (j < n) + { + if (s[j] == 'c') + { + b |= 1; + j++; + + if (j < n && s[j] == 'i') + { + b |= 2; + j++; + } + } + else if (s[j] == 'i') + { + b |= 2; + j++; + + if (j < n && s[j] == 'c') + { + b |= 1; + j++; + } + } + } + + if (j < n && s[j] == '{') + { + i = j; + blocks.push (b); + break; + } + + r += 'b'; + break; + } + case '}': + { + r += '}'; + break; + } + } + + escape = false; + } + else if (s[i] == '\\') + { + escape = true; + } + else if (s[i] == '\n') + { + switch (ot) + { + case ot_plain: + { + r += '\n'; + break; + } + } + } + else if (!blocks.empty () && s[i] == '}') + { + blocks.pop (); + } + else + r += s[i]; + } + + return r; +} + // namespace // diff --git a/cli/context.hxx b/cli/context.hxx index 1f2facb..194c008 100644 --- a/cli/context.hxx +++ b/cli/context.hxx @@ -20,6 +20,8 @@ using std::endl; +class generation_failed {}; + class context { public: @@ -36,6 +38,8 @@ public: semantics::cli_unit& unit; options_type const& options; + bool usage; + string const& inl; string const& opt_prefix; string const& opt_sep; @@ -59,6 +63,16 @@ public: string escape (string const&) const; + // Format the documentation string. + // + enum output_type + { + ot_plain + }; + + static string + format (string const&, output_type); + public: static string const& ename (semantics::nameable& n) diff --git a/cli/generator.cxx b/cli/generator.cxx index 89270bb..e0e5d4a 100644 --- a/cli/generator.cxx +++ b/cli/generator.cxx @@ -31,64 +31,6 @@ using namespace cutl; using semantics::path; -void generator:: -usage () -{ - ostream& e (cerr); - - e << "--output-dir | -o " << endl - << " Write generated files to ." << endl; - - e << "--suppress-inline" << endl - << " Generate all functions non-inline." << endl; - - e << "--hxx-suffix " << endl - << " Use instead of the default '.hxx' to\n" - << " construct the name of the generated header file." - << endl; - - e << "--ixx-suffix " << endl - << " Use instead of the default '.ixx' to\n" - << " construct the name of the generated inline file." - << endl; - - e << "--cxx-suffix " << endl - << " Use instead of the default '.cxx' to\n" - << " construct the name of the generated source file." - << endl; - - e << "--option-prefix " << endl - << " Use instead of the default '-' as an\n" - << " option prefix." - << endl; - - e << "--option-separator " << endl - << " Use instead of the default '--' as an\n" - << " optional separator between options and arguments." - << endl; - - e << "--include-with-brackets" << endl - << " Use angle brackets (<>) instead of quotes (\"\") in\n" - << " generated #include directives." - << endl; - - e << "--include-prefix " << endl - << " Add to generated #include directive\n" - << " paths." - << endl; - - e << "--guard-prefix " << endl - << " Add to generated header inclusion guards." - << endl; - - e << "--reserved-name " << endl - << " Add to the list of names that should not\n" - << " be used as identifiers. The name can optionally\n" - << " be followed by '=' and the replacement name that\n" - << " should be used instead." - << endl; -} - namespace { static char const header[] = @@ -285,6 +227,12 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p) auto_rm.cancel (); } + catch (const generation_failed&) + { + // Code generation failed. Diagnostics has already been issued. + // + throw failed (); + } catch (semantics::invalid_path const& e) { cerr << "error: '" << e.path () << "' is not a valid filesystem path" diff --git a/cli/generator.hxx b/cli/generator.hxx index 112c20d..c9b449d 100644 --- a/cli/generator.hxx +++ b/cli/generator.hxx @@ -14,9 +14,6 @@ class generator public: generator (); - static void - usage (); - class failed {}; void diff --git a/cli/header.cxx b/cli/header.cxx index 9662d3e..b06c2bf 100644 --- a/cli/header.cxx +++ b/cli/header.cxx @@ -100,11 +100,21 @@ namespace // os << "// Option accessors." << endl << "//" << endl - << "public:" << endl << endl; names (c, names_option_); + // usage + // + if (usage) + { + os << "// Print usage information." << endl + << "//" << endl + << "static void" << endl + << "print_usage (::std::ostream&);" + << endl; + } + // _parse() // os << "private:" << endl diff --git a/cli/lexer.cxx b/cli/lexer.cxx index 12e182c..7bde749 100644 --- a/cli/lexer.cxx +++ b/cli/lexer.cxx @@ -400,7 +400,7 @@ char_literal (xchar c) // them with \', as in '\\'. // if (c == '\\' && p == '\\') - p = '.'; + p = '\0'; else p = c; } @@ -439,7 +439,7 @@ string lexer:: string_literal_trailer () { string r; - char p ('"'); + char p ('\0'); while (true) { @@ -458,10 +458,10 @@ string_literal_trailer () break; // We need to keep track of \\ escapings so we don't confuse - // them with \', as in '\\'. + // them with \", as in "\\". // if (c == '\\' && p == '\\') - p = '.'; + p = '\0'; else p = c; } diff --git a/cli/options.cli b/cli/options.cli index a5fe0ae..3216e08 100644 --- a/cli/options.cli +++ b/cli/options.cli @@ -10,26 +10,93 @@ include ; include ; include ; +include ; class options { - bool --help; - bool --version; + bool --help {"Print usage information and exit."}; + bool --version {"Print version and exit."}; - std::string --output-dir | -o; + std::string --output-dir | -o + { + "", + "Write generated files to ." + }; - bool --suppress-inline; + bool --suppress-inline + { + "Generate all functions non-inline." + }; - std::string --hxx-suffix = ".hxx"; - std::string --ixx-suffix = ".ixx"; - std::string --cxx-suffix = ".cxx"; + bool --suppress-usage + { + "Suppress generation of usage printing code." + }; - std::string --option-prefix = "-"; - std::string --option-separator = "--"; + std::size_t --option-length = 0 + { + "", + "Indent option description characters when printing usage." + }; - bool --include-with-brackets; - std::string --include-prefix; - std::string --guard-prefix; + std::string --hxx-suffix = ".hxx" + { + "", + "Use instead of the default '.hxx' to construct the name of + the generated header file." + }; - std::map --reserved-name; + std::string --ixx-suffix = ".ixx" + { + "", + "Use instead of the default '.ixx' to construct the name of + the generated inline file." + }; + + std::string --cxx-suffix = ".cxx" + { + "", + "Use instead of the default '.cxx' to construct the name of + the generated source file." + }; + + std::string --option-prefix = "-" + { + "", + "Use instead of the default '-' as an option prefix." + }; + + std::string --option-separator = "--" + { + "", + "Use instead of the default '--' as an optional separator between + options and arguments." + }; + + bool --include-with-brackets + { + "Use angle brackets (<>) instead of quotes (\"\") in generated #include + directives." + }; + + std::string --include-prefix + { + "", + "Add to generated #include directive paths." + }; + + std::string --guard-prefix + { + "", + "Add to generated header inclusion guards." + }; + + std::map --reserved-name + { + "=", + "Add to the list of names that should not be used as identifiers. + The name can optionally be followed by '=' and the replacement + name that should be used instead.", + "" + }; }; diff --git a/cli/options.cxx b/cli/options.cxx index 4264369..3835155 100644 --- a/cli/options.cxx +++ b/cli/options.cxx @@ -142,7 +142,7 @@ namespace cli parse (std::vector& v, char** argv, int n) { X x; - int i (parser::parse (x, argv, n)); + int i = parser::parse (x, argv, n); v.push_back (x); return i; } @@ -155,7 +155,7 @@ namespace cli parse (std::set& s, char** argv, int n) { X x; - int i (parser::parse (x, argv, n)); + int i = parser::parse (x, argv, n); s.insert (x); return i; } @@ -170,7 +170,7 @@ namespace cli if (n > 1) { std::string s (argv[1]); - std::string::size_type p (s.find ('=')); + std::string::size_type p = s.find ('='); if (p == std::string::npos) { @@ -242,6 +242,8 @@ options (int argc, version_ (), output_dir_ (), suppress_inline_ (), + suppress_usage_ (), + option_length_ (0), hxx_suffix_ (".hxx"), ixx_suffix_ (".ixx"), cxx_suffix_ (".cxx"), @@ -265,6 +267,8 @@ options (int start, version_ (), output_dir_ (), suppress_inline_ (), + suppress_usage_ (), + option_length_ (0), hxx_suffix_ (".hxx"), ixx_suffix_ (".ixx"), cxx_suffix_ (".cxx"), @@ -288,6 +292,8 @@ options (int argc, version_ (), output_dir_ (), suppress_inline_ (), + suppress_usage_ (), + option_length_ (0), hxx_suffix_ (".hxx"), ixx_suffix_ (".ixx"), cxx_suffix_ (".cxx"), @@ -312,6 +318,8 @@ options (int start, version_ (), output_dir_ (), suppress_inline_ (), + suppress_usage_ (), + option_length_ (0), hxx_suffix_ (".hxx"), ixx_suffix_ (".ixx"), cxx_suffix_ (".cxx"), @@ -325,6 +333,50 @@ options (int start, end = _parse (start, argc, argv, opt, arg); } +void options:: +print_usage (::std::ostream& os) +{ + os << "--help Print usage information and exit." << ::std::endl; + + os << "--version Print version and exit." << ::std::endl; + + os << "--output-dir|-o Write generated files to ." << ::std::endl; + + os << "--suppress-inline Generate all functions non-inline." << ::std::endl; + + os << "--suppress-usage Suppress generation of usage printing code." << ::std::endl; + + os << "--option-length Indent option description characters when" << ::std::endl + << " printing usage." << ::std::endl; + + os << "--hxx-suffix Use instead of the default '.hxx' to" << ::std::endl + << " construct the name of the generated header file." << ::std::endl; + + os << "--ixx-suffix Use instead of the default '.ixx' to" << ::std::endl + << " construct the name of the generated inline file." << ::std::endl; + + os << "--cxx-suffix Use instead of the default '.cxx' to" << ::std::endl + << " construct the name of the generated source file." << ::std::endl; + + os << "--option-prefix Use instead of the default '-' as an" << ::std::endl + << " option prefix." << ::std::endl; + + os << "--option-separator Use instead of the default '--' as an" << ::std::endl + << " optional separator between options and arguments." << ::std::endl; + + os << "--include-with-brackets Use angle brackets (<>) instead of quotes (\"\") in" << ::std::endl + << " generated #include directives." << ::std::endl; + + os << "--include-prefix Add to generated #include directive paths." << ::std::endl; + + os << "--guard-prefix Add to generated header inclusion guards." << ::std::endl; + + os << "--reserved-name = Add to the list of names that should not be" << ::std::endl + << " used as identifiers. The name can optionally be" << ::std::endl + << " followed by '=' and the replacement name that" << ::std::endl + << " should be used instead." << ::std::endl; +} + typedef std::map _cli_options_map; @@ -336,33 +388,37 @@ struct _cli_options_map_init _cli_options_map_init () { _cli_options_map_["--help"] = - &::cli::thunk; + &::cli::thunk< options, bool, &options::help_ >; _cli_options_map_["--version"] = - &::cli::thunk; + &::cli::thunk< options, bool, &options::version_ >; _cli_options_map_["--output-dir"] = - &::cli::thunk; + &::cli::thunk< options, std::string, &options::output_dir_ >; _cli_options_map_["-o"] = - &::cli::thunk; + &::cli::thunk< options, std::string, &options::output_dir_ >; _cli_options_map_["--suppress-inline"] = - &::cli::thunk; + &::cli::thunk< options, bool, &options::suppress_inline_ >; + _cli_options_map_["--suppress-usage"] = + &::cli::thunk< options, bool, &options::suppress_usage_ >; + _cli_options_map_["--option-length"] = + &::cli::thunk< options, std::size_t, &options::option_length_ >; _cli_options_map_["--hxx-suffix"] = - &::cli::thunk; + &::cli::thunk< options, std::string, &options::hxx_suffix_ >; _cli_options_map_["--ixx-suffix"] = - &::cli::thunk; + &::cli::thunk< options, std::string, &options::ixx_suffix_ >; _cli_options_map_["--cxx-suffix"] = - &::cli::thunk; + &::cli::thunk< options, std::string, &options::cxx_suffix_ >; _cli_options_map_["--option-prefix"] = - &::cli::thunk; + &::cli::thunk< options, std::string, &options::option_prefix_ >; _cli_options_map_["--option-separator"] = - &::cli::thunk; + &::cli::thunk< options, std::string, &options::option_separator_ >; _cli_options_map_["--include-with-brackets"] = - &::cli::thunk; + &::cli::thunk< options, bool, &options::include_with_brackets_ >; _cli_options_map_["--include-prefix"] = - &::cli::thunk; + &::cli::thunk< options, std::string, &options::include_prefix_ >; _cli_options_map_["--guard-prefix"] = - &::cli::thunk; + &::cli::thunk< options, std::string, &options::guard_prefix_ >; _cli_options_map_["--reserved-name"] = - &::cli::thunk, &options::reserved_name_>; + &::cli::thunk< options, std::map, &options::reserved_name_ >; } } _cli_options_map_init_; @@ -373,11 +429,11 @@ _parse (int start, ::cli::unknown_mode opt_mode, ::cli::unknown_mode arg_mode) { - bool opt (true); + bool opt = true; for (; start < argc;) { - const char* s (argv[start]); + const char* s = argv[start]; if (std::strcmp (s, "--") == 0) { diff --git a/cli/options.hxx b/cli/options.hxx index 4d33939..6f1210f 100644 --- a/cli/options.hxx +++ b/cli/options.hxx @@ -141,6 +141,8 @@ namespace cli #include +#include + class options { public: @@ -171,47 +173,57 @@ class options // Option accessors. // - public: - bool const& + const bool& help () const; - bool const& + const bool& version () const; - std::string const& + const std::string& output_dir () const; - bool const& + const bool& suppress_inline () const; - std::string const& + const bool& + suppress_usage () const; + + const std::size_t& + option_length () const; + + const std::string& hxx_suffix () const; - std::string const& + const std::string& ixx_suffix () const; - std::string const& + const std::string& cxx_suffix () const; - std::string const& + const std::string& option_prefix () const; - std::string const& + const std::string& option_separator () const; - bool const& + const bool& include_with_brackets () const; - std::string const& + const std::string& include_prefix () const; - std::string const& + const std::string& guard_prefix () const; - std::map const& + const std::map& reserved_name () const; + // Print usage information. + // + static void + print_usage (::std::ostream&); + private: int _parse (int start, @@ -225,6 +237,8 @@ class options bool version_; std::string output_dir_; bool suppress_inline_; + bool suppress_usage_; + std::size_t option_length_; std::string hxx_suffix_; std::string ixx_suffix_; std::string cxx_suffix_; diff --git a/cli/options.ixx b/cli/options.ixx index 7ec5579..77ec790 100644 --- a/cli/options.ixx +++ b/cli/options.ixx @@ -88,79 +88,91 @@ namespace cli // options // -inline bool const& options:: +inline const bool& options:: help () const { return help_; } -inline bool const& options:: +inline const bool& options:: version () const { return version_; } -inline std::string const& options:: +inline const std::string& options:: output_dir () const { return output_dir_; } -inline bool const& options:: +inline const bool& options:: suppress_inline () const { return suppress_inline_; } -inline std::string const& options:: +inline const bool& options:: +suppress_usage () const +{ + return suppress_usage_; +} + +inline const std::size_t& options:: +option_length () const +{ + return option_length_; +} + +inline const std::string& options:: hxx_suffix () const { return hxx_suffix_; } -inline std::string const& options:: +inline const std::string& options:: ixx_suffix () const { return ixx_suffix_; } -inline std::string const& options:: +inline const std::string& options:: cxx_suffix () const { return cxx_suffix_; } -inline std::string const& options:: +inline const std::string& options:: option_prefix () const { return option_prefix_; } -inline std::string const& options:: +inline const std::string& options:: option_separator () const { return option_separator_; } -inline bool const& options:: +inline const bool& options:: include_with_brackets () const { return include_with_brackets_; } -inline std::string const& options:: +inline const std::string& options:: include_prefix () const { return include_prefix_; } -inline std::string const& options:: +inline const std::string& options:: guard_prefix () const { return guard_prefix_; } -inline std::map const& options:: +inline const std::map& options:: reserved_name () const { return reserved_name_; diff --git a/cli/parser.cxx b/cli/parser.cxx index 639ddf4..aee3bc3 100644 --- a/cli/parser.cxx +++ b/cli/parser.cxx @@ -410,11 +410,22 @@ option_def (token& t) // string r; string const& l (t.literal ()); + char p ('\0'); for (size_t i (0), n (l.size ()); i < n; ++i) { - if (l[i] != '"' || (i != 0 && l[i - 1] == '\\')) - r += l[i]; + if (l[i] == '"' && p != '\\') + continue; + + // We need to keep track of \\ escapings so we don't confuse + // them with \", as in "\\". + // + if (l[i] == '\\' && p == '\\') + p = '\0'; + else + p = l[i]; + + r += l[i]; } nl.push_back (r); @@ -549,16 +560,81 @@ option_def (token& t) { // Get rid of '"'. // - string r; + string t1, t2; string const& l (t.literal ()); + char p ('\0'); for (size_t i (0), n (l.size ()); i < n; ++i) { - if (l[i] != '"' || (i != 0 && l[i - 1] == '\\')) - r += l[i]; + if (l[i] == '"' && p != '\\') + continue; + + // We need to keep track of \\ escapings so we don't confuse + // them with \", as in "\\". + // + if (l[i] == '\\' && p == '\\') + p = '\0'; + else + p = l[i]; + + t1 += l[i]; + } + + // Get rid of leading and trailing spaces in each line. + // + if (t1.size () != 0) + { + bool more (true); + size_t b (0), e; + + while (more) + { + e = t1.find ('\n', b); + + if (e == string::npos) + { + e = t1.size (); + more = false; + } + + while (b < e && (t1[b] == 0x20 || t1[b] == 0x0D || t1[b] == 0x09)) + ++b; + + --e; + + while (e > b && (t1[e] == 0x20 || t1[e] == 0x0D || t1[e] == 0x09)) + --e; + + if (b <= e) + t2.append (t1, b, e - b + 1); + + if (more) + { + t2 += '\n'; + b = e + 2; + } + } + } + + // Replace every single newlines with single space and all + // multiple new lines (paragraph marker) with a single newline. + // + t1.clear (); + for (size_t i (0), n (t2.size ()); i < n; ++i) + { + if (t2[i] == '\n') + { + size_t j (i); + for (; i + 1 < n && t2[i + 1] == '\n'; ++i) ; + + if (j != 0 && i + 1 != n) // Strip leading and trailing newlines. + t1 += i != j ? '\n' : ' '; + } + else + t1 += t2[i]; } - o->doc ().push_back (r); + o->doc ().push_back (t1); } t = lexer_->next (); diff --git a/cli/source.cxx b/cli/source.cxx index 7a23c30..2f835fa 100644 --- a/cli/source.cxx +++ b/cli/source.cxx @@ -3,8 +3,12 @@ // copyright : Copyright (c) 2009 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file +#include + #include "source.hxx" +using std::cerr; + namespace { // @@ -85,6 +89,251 @@ namespace // // + struct option_length: traversal::option, context + { + option_length (context& c, size_t& l, type*& o) + : context (c), length_ (l), option_ (o) + { + } + + virtual void + traverse (type& o) + { + using semantics::names; + + size_t l (0); + names& n (o.named ()); + + for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i) + { + if (l != 0) + l++; // '|' seperator. + + l += i->size (); + } + + string type (o.type ().name ()); + + if (type != "bool") + { + l++; // ' ' seperator + + type::doc_list const& d (o.doc ()); + + if (d.size () > 0) + l += d[0].size (); + else + l += 5; // + } + + if (l > length_) + { + length_ = l; + option_ = &o; + } + } + + private: + size_t& length_; + type*& option_; + }; + + // + // + struct option_usage: traversal::option, context + { + option_usage (context& c, size_t l) : context (c), length_ (l) {} + + virtual void + traverse (type& o) + { + using semantics::names; + + size_t l (0); + names& n (o.named ()); + + os << "os << \""; + + for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i) + { + if (l != 0) + { + os << '|'; + l++; + } + + os << *i; + l += i->size (); + } + + type::doc_list const& doc (o.doc ()); + string type (o.type ().name ()); + + if (type != "bool") + { + os << ' '; + l++; + + if (doc.size () > 0) + { + os << escape_str (doc[0]); + l += doc[0].size (); + } + else + { + os << ""; + l += 5; + } + } + + // If we have both the long and the short descriptions, use + // the short one. Otherwise, use the first sentence from the + // long one. + // + string d; + + if (type == "bool") + { + if (doc.size () > 1) + d = doc[0]; + else if (doc.size () > 0) + d = frist_sentence (doc[0]); + } + else + { + if (doc.size () > 2) + d = doc[1]; + else if (doc.size () > 1) + d = frist_sentence (doc[1]); + } + + // Format the documentation string. + // + d = format (d, ot_plain); + + if (!d.empty ()) + { + pad (length_ - l); + + size_t b (0), e (0), i (0); + + for (size_t n (d.size ()); i < n; ++i) + { + if (d[i] == ' ' || d[i] == '\n') + e = i; + + if (d[i] == '\n' || i - b == 79 - length_) + { + if (b != 0) + { + os << endl + << " << \""; + pad (); + } + + string s (d, b, (e != b ? e : i) - b); + os << escape_str (s) << "\" << ::std::endl"; + + if (d[i] == '\n') + os << endl + << " << ::std::endl"; + + b = e = (e != b ? e : i) + 1; + } + } + + // Flush the last line. + // + if (b != i) + { + if (b != 0) + { + os << endl + << " << \""; + pad (); + } + + string s (d, b, i - b); + os << escape_str (s) << "\" << ::std::endl"; + } + } + else + os << "\" << std::endl"; + + os << ";" + << endl; + } + + private: + void + pad (size_t n) + { + for (; n > 0; --n) + os << ' '; + + os << ' '; // Space between arg and description. + } + + void + pad () + { + pad (length_); + } + + string + frist_sentence (string const& s) + { + size_t p (s.find ('.')); + + // Add some heuristics here: check that there is a space + // (or end of text) after the period. + // + while (p != string::npos && + p + 1 <= s.size () && + s[p + 1] != ' ' && + s[p + 1] != '\n') + p = s.find ('.', p + 1); + + return p == string::npos ? s : string (s, 0, p + 1); + } + + string + escape_str (string const& s) + { + string r; + r.reserve (s.size ()); + + for (size_t i (0), n (s.size ()); i < n; ++i) + { + switch (s[i]) + { + case '\\': + { + r += "\\\\"; + break; + } + case '"': + { + r += "\\\""; + break; + } + default: + { + r += s[i]; + break; + } + } + } + + return r; + } + + private: + size_t length_; + }; + + // + // struct class_: traversal::class_, context { class_ (context& c) @@ -170,6 +419,50 @@ namespace << "end = _parse (start, argc, argv, opt, arg);" << "}"; + // usage + // + if (usage) + { + os << "void " << name << "::" << endl + << "print_usage (::std::ostream& os)" + << "{"; + + // Calculate option length. + // + 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 ()); + + if (max != 0) + { + if (len > max) + { + cerr << o->file () << ":" << o->line () << ":" << o->column () + << " error: option length " << len << " is greater than " + << max << " specified with --option-length" << endl; + throw generation_failed (); + } + + len = max; + } + } + + // Print option usage. + // + { + option_usage t (*this, len); + traversal::names n (t); + names (c, n); + } + + os << "}"; + } + // _parse() // string map ("_cli_" + name + "_map"); diff --git a/cli/usage.hxx b/cli/usage.hxx deleted file mode 100644 index 3aeb30b..0000000 --- a/cli/usage.hxx +++ /dev/null @@ -1,181 +0,0 @@ -// file : cli/usage.hxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009 Code Synthesis Tools CC -// license : MIT; see accompanying LICENSE file - -#ifndef CLI_USAGE_HXX -#define CLI_USAGE_HXX - -#include // std::size_t -#include // std::abort - -#include - -template -class usage_indenter: public cutl::compiler::code_stream -{ -public: - usage_indenter (cutl::compiler::code_stream& out) - : out_ (out), - option_length_ (0), - construct_ (con_newline) - { - } - -private: - usage_indenter (usage_indenter const&); - - usage_indenter& - operator= (usage_indenter const&); - -public: - virtual void - put (C c) - { - switch (c) - { - case '\n': - { - switch (construct_) - { - case con_newline: - { - out_.put (c); - break; - } - case con_option: - { - construct_ = con_newline; - break; - } - case con_description: - { - out_.put (c); - construct_ = con_newline; - break; - } - default: - { - std::abort (); - } - } - - break; - } - case '-': - { - switch (construct_) - { - case con_newline: - { - construct_ = con_option; - option_length_ = 0; - output_indentation (); - out_.put (c); - ++option_length_; - break; - } - case con_option: - { - ++option_length_; - //fall through - } - case con_description: - { - out_.put (c); - break; - } - default: - { - std::abort (); - } - } - - break; - } - default: - { - switch (construct_) - { - case con_newline: - { - construct_ = con_description; - output_indentation (); - out_.put (c); - break; - } - case con_option: - { - ++option_length_; - //fall through - } - default: - { - out_.put (c); - break; - } - } - - break; - } - } - } - - virtual void - unbuffer () - { - } - -private: - void - output_indentation () - { - std::size_t spaces; - - switch (construct_) - { - case con_option: - { - spaces = 2; - option_length_ += 2; - break; - } - case con_description: - { - spaces = 29; - - if (option_length_) - { - if (option_length_ > spaces) - spaces = 1; - else - spaces -= option_length_; - - option_length_ = 0; - } - - break; - } - default: - { - std::abort (); - } - } - - while (spaces--) - out_.put (' '); - } - -private: - cutl::compiler::code_stream& out_; - size_t option_length_; - - enum construct - { - con_newline, - con_option, - con_description - } construct_; -}; - -#endif // CLI_USAGE_HXX -- cgit v1.1