From 720c5a33b6a49cf328fdd7611f49153cf8f60247 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 8 Apr 2020 14:51:57 +0300 Subject: Separate tests and examples into individual packages Also make cli module to be explicitly enabled via the config.cli configuration variable. --- cli/cli/.gitignore | 7 + cli/cli/buildfile | 75 ++ cli/cli/cli.cxx | 165 +++ cli/cli/context.cxx | 2742 ++++++++++++++++++++++++++++++++++++++ cli/cli/context.hxx | 295 ++++ cli/cli/generator.cxx | 584 ++++++++ cli/cli/generator.hxx | 28 + cli/cli/header.cxx | 383 ++++++ cli/cli/header.hxx | 13 + cli/cli/html.cxx | 348 +++++ cli/cli/html.hxx | 13 + cli/cli/inline.cxx | 108 ++ cli/cli/inline.hxx | 13 + cli/cli/lexer.cxx | 604 +++++++++ cli/cli/lexer.hxx | 142 ++ cli/cli/lexer.ixx | 91 ++ cli/cli/lexer.test.cxx | 122 ++ cli/cli/lexer.test.testscript | 191 +++ cli/cli/man.cxx | 278 ++++ cli/cli/man.hxx | 13 + cli/cli/name-processor.cxx | 193 +++ cli/cli/name-processor.hxx | 13 + cli/cli/option-types.cxx | 43 + cli/cli/option-types.hxx | 33 + cli/cli/options.cli | 662 +++++++++ cli/cli/options.cxx | 2146 +++++++++++++++++++++++++++++ cli/cli/options.hxx | 1632 +++++++++++++++++++++++ cli/cli/options.ixx | 2319 ++++++++++++++++++++++++++++++++ cli/cli/parser.cxx | 1728 ++++++++++++++++++++++++ cli/cli/parser.hxx | 91 ++ cli/cli/parser.test.cxx | 45 + cli/cli/parser.test.testscript | 242 ++++ cli/cli/runtime-header.cxx | 647 +++++++++ cli/cli/runtime-header.hxx | 13 + cli/cli/runtime-inline.cxx | 508 +++++++ cli/cli/runtime-inline.hxx | 13 + cli/cli/runtime-source.cxx | 1052 +++++++++++++++ cli/cli/runtime-source.hxx | 13 + cli/cli/semantics.hxx | 16 + cli/cli/semantics/class.cxx | 39 + cli/cli/semantics/class.hxx | 106 ++ cli/cli/semantics/doc.cxx | 27 + cli/cli/semantics/doc.hxx | 22 + cli/cli/semantics/elements.cxx | 129 ++ cli/cli/semantics/elements.hxx | 407 ++++++ cli/cli/semantics/expression.cxx | 27 + cli/cli/semantics/expression.hxx | 76 ++ cli/cli/semantics/namespace.cxx | 27 + cli/cli/semantics/namespace.hxx | 26 + cli/cli/semantics/option.cxx | 47 + cli/cli/semantics/option.hxx | 189 +++ cli/cli/semantics/unit.cxx | 63 + cli/cli/semantics/unit.hxx | 310 +++++ cli/cli/semantics/unit.txx | 108 ++ cli/cli/source.cxx | 1374 +++++++++++++++++++ cli/cli/source.hxx | 13 + cli/cli/token.hxx | 135 ++ cli/cli/token.ixx | 88 ++ cli/cli/traversal.hxx | 16 + cli/cli/traversal/class.cxx | 49 + cli/cli/traversal/class.hxx | 41 + cli/cli/traversal/doc.hxx | 16 + cli/cli/traversal/elements.cxx | 14 + cli/cli/traversal/elements.hxx | 142 ++ cli/cli/traversal/expression.hxx | 16 + cli/cli/traversal/namespace.cxx | 26 + cli/cli/traversal/namespace.hxx | 26 + cli/cli/traversal/option.cxx | 59 + cli/cli/traversal/option.hxx | 74 + cli/cli/traversal/unit.cxx | 46 + cli/cli/traversal/unit.hxx | 58 + cli/cli/txt.cxx | 301 +++++ cli/cli/txt.hxx | 46 + cli/cli/version.hxx.in | 45 + 74 files changed, 21812 insertions(+) create mode 100644 cli/cli/.gitignore create mode 100644 cli/cli/buildfile create mode 100644 cli/cli/cli.cxx create mode 100644 cli/cli/context.cxx create mode 100644 cli/cli/context.hxx create mode 100644 cli/cli/generator.cxx create mode 100644 cli/cli/generator.hxx create mode 100644 cli/cli/header.cxx create mode 100644 cli/cli/header.hxx create mode 100644 cli/cli/html.cxx create mode 100644 cli/cli/html.hxx create mode 100644 cli/cli/inline.cxx create mode 100644 cli/cli/inline.hxx create mode 100644 cli/cli/lexer.cxx create mode 100644 cli/cli/lexer.hxx create mode 100644 cli/cli/lexer.ixx create mode 100644 cli/cli/lexer.test.cxx create mode 100644 cli/cli/lexer.test.testscript create mode 100644 cli/cli/man.cxx create mode 100644 cli/cli/man.hxx create mode 100644 cli/cli/name-processor.cxx create mode 100644 cli/cli/name-processor.hxx create mode 100644 cli/cli/option-types.cxx create mode 100644 cli/cli/option-types.hxx create mode 100644 cli/cli/options.cli create mode 100644 cli/cli/options.cxx create mode 100644 cli/cli/options.hxx create mode 100644 cli/cli/options.ixx create mode 100644 cli/cli/parser.cxx create mode 100644 cli/cli/parser.hxx create mode 100644 cli/cli/parser.test.cxx create mode 100644 cli/cli/parser.test.testscript create mode 100644 cli/cli/runtime-header.cxx create mode 100644 cli/cli/runtime-header.hxx create mode 100644 cli/cli/runtime-inline.cxx create mode 100644 cli/cli/runtime-inline.hxx create mode 100644 cli/cli/runtime-source.cxx create mode 100644 cli/cli/runtime-source.hxx create mode 100644 cli/cli/semantics.hxx create mode 100644 cli/cli/semantics/class.cxx create mode 100644 cli/cli/semantics/class.hxx create mode 100644 cli/cli/semantics/doc.cxx create mode 100644 cli/cli/semantics/doc.hxx create mode 100644 cli/cli/semantics/elements.cxx create mode 100644 cli/cli/semantics/elements.hxx create mode 100644 cli/cli/semantics/expression.cxx create mode 100644 cli/cli/semantics/expression.hxx create mode 100644 cli/cli/semantics/namespace.cxx create mode 100644 cli/cli/semantics/namespace.hxx create mode 100644 cli/cli/semantics/option.cxx create mode 100644 cli/cli/semantics/option.hxx create mode 100644 cli/cli/semantics/unit.cxx create mode 100644 cli/cli/semantics/unit.hxx create mode 100644 cli/cli/semantics/unit.txx create mode 100644 cli/cli/source.cxx create mode 100644 cli/cli/source.hxx create mode 100644 cli/cli/token.hxx create mode 100644 cli/cli/token.ixx create mode 100644 cli/cli/traversal.hxx create mode 100644 cli/cli/traversal/class.cxx create mode 100644 cli/cli/traversal/class.hxx create mode 100644 cli/cli/traversal/doc.hxx create mode 100644 cli/cli/traversal/elements.cxx create mode 100644 cli/cli/traversal/elements.hxx create mode 100644 cli/cli/traversal/expression.hxx create mode 100644 cli/cli/traversal/namespace.cxx create mode 100644 cli/cli/traversal/namespace.hxx create mode 100644 cli/cli/traversal/option.cxx create mode 100644 cli/cli/traversal/option.hxx create mode 100644 cli/cli/traversal/unit.cxx create mode 100644 cli/cli/traversal/unit.hxx create mode 100644 cli/cli/txt.cxx create mode 100644 cli/cli/txt.hxx create mode 100644 cli/cli/version.hxx.in (limited to 'cli/cli') diff --git a/cli/cli/.gitignore b/cli/cli/.gitignore new file mode 100644 index 0000000..903d015 --- /dev/null +++ b/cli/cli/.gitignore @@ -0,0 +1,7 @@ +cli +version.hxx + +# Unit test executables and Testscript output directories (can be symlinks). +# +*.test +test-*.test diff --git a/cli/cli/buildfile b/cli/cli/buildfile new file mode 100644 index 0000000..ff562c2 --- /dev/null +++ b/cli/cli/buildfile @@ -0,0 +1,75 @@ +# file : cli/buildfile +# license : MIT; see accompanying LICENSE file + +import libs = libcutl%lib{cutl} + +./: exe{cli}: cxx{cli} libue{cli} + +# Target metadata, see also --build2-metadata in cli.cxx. +# +exe{cli}: +{ + cli.version = $version.project_id + cli.checksum = $version +} + +libue{cli}: {hxx ixx txx cxx}{** -cli -version -options -**.test...} \ + {hxx}{version} {hxx ixx cxx}{options} \ + $libs + +hxx{version}: in{version} $src_root/manifest + +# Unit tests. +# +exe{*.test}: +{ + test = true + install = false +} + +for t: cxx{**.test...} +{ + d = $directory($t) + n = $name($t)... + + ./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n} + $d/exe{$n}: libue{cli}: bin.whole = false +} + +# Build options. +# +# Pass the copyright notice extracted from the LICENSE file. +# +copyright = $process.run_regex(cat $src_root/LICENSE, \ + 'Copyright \(c\) (.+)\.', \ + '\1') + +obj{cli}: cxx.poptions += -DCLI_COPYRIGHT=\"$copyright\" + +# Generated options parsing code. +# +# @@ This will eventually be replaced with an ah hoc recipe. +# +if ($config.cli != [null] && $config.cli != false) +{ + cli.cxx{options}: cli{options} + + cli.options += --include-with-brackets --include-prefix cli \ +--guard-prefix CLI --generate-file-scanner --generate-specifier \ +--generate-modifier --reserved-name stdout + + cli.cxx{*}: + { + # Include the generated cli files into the distribution and don't remove + # them when cleaning in src (so that clean results in a state identical to + # distributed). + # + dist = true + clean = ($src_root != $out_root) + + # We keep the generated code in the repository so copy it back to src + # in case of a forwarded configuration. + # + backlink = overwrite + } +} diff --git a/cli/cli/cli.cxx b/cli/cli/cli.cxx new file mode 100644 index 0000000..c57caee --- /dev/null +++ b/cli/cli/cli.cxx @@ -0,0 +1,165 @@ +// file : cli/cli.cxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#include +#include +#include // unique_ptr +#include +#include + +#include + +#include + +#include +#include +#include + +#include + +using namespace std; +using namespace cutl; + +int +main (int argc, char* argv[]) +{ + ostream& e (cerr); + const char* file (0); + + try + { + cli::argv_file_scanner scan (argc, argv, "--options-file"); + options ops (scan); + + // Handle --build2-metadata (see also buildfile). + // + if (ops.build2_metadata ()) + { + ostream& o (cout); + + o << "# build2 buildfile cli" << endl + << "cli.version = '" << CLI_VERSION_ID << '\'' << endl + << "cli.checksum = '" << CLI_CHECKSUM << '\'' << endl; + + return 0; + } + + // Handle --version + // + if (ops.version ()) + { + ostream& o (cout); + + o << "CLI (command line interface compiler) " << CLI_VERSION_ID << endl + << "Copyright (c) " << CLI_COPYRIGHT << "." << endl; + + o << "This is free software; see the source for copying conditions. " + << "There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS " + << "FOR A PARTICULAR PURPOSE." << endl; + + return 0; + } + + // Handle --help + // + if (ops.help ()) + { + ostream& o (cout); + + o << "Usage: " << argv[0] << " [options] file" << endl + << "Options:" << endl; + + options::print_usage (o); + + return 0; + } + + if (!scan.more ()) + { + e << "error: no input file specified" << endl + << "info: try '" << argv[0] << " --help' for more information" << endl; + + return 1; + } + + // Extract include search paths. + // + parser::paths include_paths; + for (vector::const_iterator i (ops.include_path ().begin ()); + i != ops.include_path ().end (); ++i) + { + // Invalid path exception is handled below. + // + include_paths.push_back (semantics::path (*i)); + } + + // Open the input file. + // + file = scan.next (); + semantics::path path (file); + + ifstream ifs (path.string ().c_str ()); + if (!ifs.is_open ()) + { + e << path << ": error: unable to open in read mode" << endl; + return 1; + } + + ifs.exceptions (ifstream::failbit | ifstream::badbit); + + // Parse and generate. + // + parser p (include_paths); + unique_ptr unit (p.parse (ifs, path)); + + // Merge documentation variables from the command line. + // + for (map::const_iterator i (ops.docvar ().begin ()); + i != ops.docvar ().end (); + ++i) + { + using semantics::doc; + + // Values specified in the .cli file override command line. + // + if (unit->lookup ("", "var: " + i->first) != 0) + continue; + + doc& d (unit->new_node (semantics::path (""), 0, 0)); + unit->new_edge (*unit, d, "var: " + i->first); + d.push_back (i->second); + } + + generator g; + g.generate (ops, *unit, path); + } + catch (cli::exception const& ex) + { + e << ex << endl; + return 1; + } + catch (semantics::invalid_path const& ex) + { + e << "error: '" << ex.path () << "' is not a valid filesystem path" + << endl; + return 1; + } + catch (std::ios_base::failure const&) + { + e << file << ": error: read failure" << endl; + return 1; + } + catch (parser::invalid_input const&) + { + // Diagnostics has already been issued by the parser. + // + return 1; + } + catch (generator::failed const&) + { + // Diagnostics has already been issued by the generator. + // + return 1; + } +} diff --git a/cli/cli/context.cxx b/cli/cli/context.cxx new file mode 100644 index 0000000..54bb988 --- /dev/null +++ b/cli/cli/context.cxx @@ -0,0 +1,2742 @@ +// file : cli/context.cxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#include +#include +#include // strncmp() +#include +#include +#include + +#include + +using namespace std; + +namespace +{ + char const* keywords[] = + { + "NULL", + "and", + "asm", + "auto", + "bitand", + "bitor", + "bool", + "break", + "case", + "catch", + "char", + "class", + "compl", + "const", + "const_cast", + "continue", + "default", + "delete", + "do", + "double", + "dynamic_cast", + "else", + "end_eq", + "enum", + "explicit", + "export", + "extern", + "false", + "float", + "for", + "friend", + "goto", + "if", + "inline", + "int", + "long", + "mutable", + "namespace", + "new", + "not", + "not_eq", + "operator", + "or", + "or_eq", + "private", + "protected", + "public", + "register", + "reinterpret_cast", + "return", + "short", + "signed", + "sizeof", + "static", + "static_cast", + "struct", + "switch", + "template", + "this", + "throw", + "true", + "try", + "typedef", + "typeid", + "typename", + "union", + "unsigned", + "using", + "virtual", + "void", + "volatile", + "wchar_t", + "while", + "xor", + "xor_eq" + }; +} + +context:: +context (ostream& os_, + output_type ot_, + semantics::cli_unit& unit_, + options_type const& ops) + : data_ (new (shared) data), + os (os_), + unit (unit_), + options (ops), + ot (ot_), + gen_modifier (options.generate_modifier ()), + gen_specifier (options.generate_specifier () || + options.generate_merge ()), + gen_parse (options.generate_parse ()), + gen_merge (options.generate_merge ()), + inl (data_->inl_), + opt_prefix (options.option_prefix ()), + opt_sep (options.option_separator ()), + cli (data_->cli_), + reserved_name_map (options.reserved_name ()), + keyword_set (data_->keyword_set_), + link_regex (data_->link_regex_), + id_set (data_->id_set_), + ref_set (data_->ref_set_), + heading_map (data_->heading_map_), + toc (data_->toc_), + tocs (data_->tocs_) +{ + if (options.suppress_usage ()) + gen_usage = ut_none; + else + { + if (options.long_usage ()) + gen_usage = options.short_usage () ? ut_both : ut_long; + else + gen_usage = ut_short; + } + + if (!options.suppress_inline ()) + data_->inl_ = "inline "; + + data_->cli_ = options.cli_namespace (); + + if (!cli.empty () && cli[0] != ':') + data_->cli_ = "::" + data_->cli_; + + for (size_t i (0); i < sizeof (keywords) / sizeof (char*); ++i) + data_->keyword_set_.insert (keywords[i]); + + // Link regex. + // + for (vector::const_iterator i (ops.link_regex ().begin ()); + i != ops.link_regex ().end (); ++i) + { + try + { + data_->link_regex_.push_back (regexsub (*i)); + } + catch (const regex_format& e) + { + cerr << "error: invalid regex '" << *i << "': " << e.what () << endl; + throw generation_failed (); + } + } + + toc = 0; +} + +context:: +context (context& c) + : data_ (c.data_), + os (c.os), + unit (c.unit), + options (c.options), + ot (c.ot), + gen_modifier (c.gen_modifier), + gen_specifier (c.gen_specifier), + gen_parse (c.gen_parse), + gen_merge (c.gen_merge), + gen_usage (c.gen_usage), + inl (c.inl), + opt_prefix (c.opt_prefix), + opt_sep (c.opt_sep), + cli (c.cli), + reserved_name_map (c.reserved_name_map), + keyword_set (c.keyword_set), + link_regex (c.link_regex), + id_set (c.id_set), + ref_set (c.ref_set), + heading_map (c.heading_map), + toc (c.toc), + tocs (c.tocs) +{ +} + +string context:: +escape (string const& name) const +{ + typedef string::size_type size; + + string r; + size n (name.size ()); + + // In most common cases we will have that many characters. + // + r.reserve (n); + + for (size i (0); i < n; ++i) + { + char c (name[i]); + + if (i == 0) + { + if (!((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + c == '_')) + r = (c >= '0' && c <= '9') ? "cxx_" : "cxx"; + } + + if (!((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '_')) + r += '_'; + else + r += c; + } + + if (r.empty ()) + r = "cxx"; + + // Custom reserved words. + // + reserved_name_map_type::const_iterator i (reserved_name_map.find (r)); + + if (i != reserved_name_map.end ()) + { + if (!i->second.empty ()) + return i->second; + else + r += L'_'; + } + + // Keywords + // + if (keyword_set.find (r) != keyword_set.end ()) + { + r += '_'; + + // Re-run custom words. + // + i = reserved_name_map.find (r); + + if (i != reserved_name_map.end ()) + { + if (!i->second.empty ()) + return i->second; + else + r += L'_'; + } + } + + return r; +} + +string context:: +process_link_target (const string& tg) +{ + bool t (options.link_regex_trace ()); + + if (t) + cerr << "link: '" << tg << "'" << endl; + + string r; + bool found (false); + + for (regex_mapping::const_iterator i (link_regex.begin ()); + i != link_regex.end (); ++i) + { + if (t) + cerr << "try: '" << i->regex () << "' : "; + + if (i->match (tg)) + { + r = i->replace (tg); + found = true; + + if (t) + cerr << "'" << r << "' : "; + } + + if (t) + cerr << (found ? '+' : '-') << endl; + + if (found) + break; + } + + return found ? r : tg; +} + + +string context:: +translate_arg (string const& s, std::set& set) +{ + string r; + r.reserve (s.size ()); + set.clear (); + + size_t p (string::npos); + + for (size_t i (0), n (s.size ()); i < n; ++i) + { + if (p == string::npos && s[i] == '<') + { + p = i; + r += "\\ci{"; + continue; + } + + if (p != string::npos && s[i] == '>') + { + set.insert (string (s, p + 1, i - p - 1)); + r += '}'; + p = string::npos; + continue; + } + + if (p != string::npos && s[i] == '}' && s[i - 1] != '\\') + { + r += "\\}"; + continue; + } + + r += s[i]; + } + + return r; +} + +string context:: +translate (string const& s, std::set const& set) +{ + string r; + r.reserve (s.size ()); + + bool pre (false); + size_t p (string::npos); + + for (size_t i (0), n (s.size ()); i < n; ++i) + { + char c (s[i]); + + // Skip pre-formatted fragments. + // + if (c == (pre ? 0x03 : 0x02)) + { + pre = !pre; + + if (p != string::npos) + { + assert (pre); + r.append (s, p, i - p); + p = string::npos; + } + } + else if (!pre) + { + if (p == string::npos && c == '<') + { + p = i; + continue; + } + + if (p != string::npos) + { + if (c == '>') + { + string a (s, p + 1, i - p - 1); + + if (set.find (a) != set.end ()) + { + r += "\\ci{"; + + for (size_t j (0), n (a.size ()); j < n; ++j) + { + if (a[j] == '}' && (j == 0 || a[j - 1] != '\\')) + r += "\\}"; + else + r += a[j]; + } + + r += '}'; + } + else + { + r += '<'; + r += a; + r += '>'; + } + p = string::npos; + } + continue; + } + } + + r += c; + } + + // If we found the opening '<' but no closing '>', add the rest. + // + if (p != string::npos) + r.append (s, p, string::npos); + + return r; +} + +void context:: +format_line (output_type ot, string& r, const char* l, size_t n) +{ + bool color (options.ansi_color ()); + + typedef unsigned char span; // Mask. + + const span code = 1; + const span itlc = 2; + const span bold = 4; + const span link = 8; + const span note = 16; + + vector spans; + + string link_target; + string link_section; // If not empty, man section; target is man name. + bool link_empty (false); // Link has no text. + + bool escape (false); + for (size_t i (0); i < n; ++i) + { + char c (l[i]); + + if (escape) + { + bool new_span (false); + + switch (c) + { + case ' ': + { + // Non-ignorable space. + // + switch (ot) + { + case ot_plain: + { + r += ' '; + break; + } + case ot_html: + { + r += " "; + break; + } + case ot_man: + { + r += "\\ "; + break; + } + } + + break; + } + case '-': + { + // N-dash. If someone wants m-dash, can use \-- with \-{}- as + // a way to "escape" an n-dash followed by hyphen. + // + switch (ot) + { + case ot_plain: + { + r += "--"; + break; + } + case ot_html: + { + r += "–"; + break; + } + case ot_man: + { + r += "\\(en"; + break; + } + } + + break; + } + case 'n': + { + switch (ot) + { + case ot_plain: + { + r += '\n'; + break; + } + case ot_html: + { + if (!r.empty () && r[r.size () - 1] != '\n') + r += '\n'; + + r += "
"; + break; + } + case ot_man: + { + if (!r.empty () && r[r.size () - 1] != '\n') + r += '\n'; + + // Note that if we have several consecutive breaks, they + // will be collapsed into a single one. No, .sp doesn't + // work (or, more exactly, will only work for two breaks). + // + r += ".br"; + break; + } + } + + // Skip following spaces. + // + for (; i + 1 < n && l[i + 1] == ' '; ++i) ; + + switch (ot) + { + case ot_plain: break; + case ot_html: + case ot_man: + { + if (i + 1 < n) // More text in this paragraph? + r += "\n"; + + break; + } + } + + break; + } + case 'c': + { + span s (code); + size_t j (i + 1); + + if (j < n) + { + if (l[j] == 'i') + { + s |= itlc; + j++; + + if (j < n && l[j] == 'b') + { + s |= bold; + j++; + } + } + else if (l[j] == 'b') + { + s |= bold; + j++; + + if (j < n && l[j] == 'i') + { + s |= itlc; + j++; + } + } + } + + if (j < n && l[j] == '{') + { + i = j; + spans.push_back (s); + new_span = true; + break; + } + + r += 'c'; + break; + } + case 'i': + { + span s (itlc); + size_t j (i + 1); + + if (j < n) + { + if (l[j] == 'c') + { + s |= code; + j++; + + if (j < n && l[j] == 'b') + { + s |= bold; + j++; + } + } + else if (l[j] == 'b') + { + s |= bold; + j++; + + if (j < n && l[j] == 'c') + { + s |= code; + j++; + } + } + } + + if (j < n && l[j] == '{') + { + i = j; + spans.push_back (s); + new_span = true; + break; + } + + r += 'i'; + break; + } + case 'b': + { + span s (bold); + size_t j (i + 1); + + if (j < n) + { + if (l[j] == 'c') + { + s |= code; + j++; + + if (j < n && l[j] == 'i') + { + s |= itlc; + j++; + } + } + else if (l[j] == 'i') + { + s |= itlc; + j++; + + if (j < n && l[j] == 'c') + { + s |= code; + j++; + } + } + } + + if (j < n && l[j] == '{') + { + i = j; + spans.push_back (s); + new_span = true; + break; + } + + r += 'b'; + break; + } + case 'l': + { + if (i + 1 < n && l[i + 1] == '{') + { + string& t (link_target); + + if (!t.empty ()) + { + cerr << "error: nested links in documentation paragraph '" + << string (l, 0, n) << "'" << endl; + throw generation_failed (); + } + + // Find the end of the link target. + // + size_t b (++i + 1), e (b); + for (; i + 1 < n; ++i) + { + char c (l[i + 1]); + + if (c == ' ' || c == '}') + { + e = i + 1; + + if (c == ' ') // Skip spaces. + for (++i; i + 1 < n && l[i + 1] == ' '; ++i) ; + + break; + } + } + + // Run the link target through format_line(ot_plain) to handle + // escaping (e.g., \\$). + // + format_line (ot_plain, t, l + b, e - b); + + if (t.empty ()) + { + cerr << "error: missing link target in documentation paragraph '" + << string (l, 0, n) << "'" << endl; + throw generation_failed (); + } + + // See if link_target is a man page name/section. + // + size_t o (t.find ('(')); + + if (o != string::npos) + { + link_section.assign (t, o + 1, t.size () - o - 2); + + if (t[t.size () - 1] != ')' || link_section.empty ()) + { + cerr << "error: missing man section in '" << t << "'" << endl; + throw generation_failed (); + } + + string n (t, 0, o); + + if (n.empty ()) + { + cerr << "error: missing man page in '" << t << "'" << endl; + throw generation_failed (); + } + + t = n; + } + else + link_section.clear (); + + // If this is a local fragment reference, add it to the set to be + // verified at the end. + // + if (t[0] == '#') + { + assert (link_section.empty ()); // Not a man page. + ref_set.insert (string (t, 1, string::npos)); + } + + link_empty = i + 1 < n && l[i + 1] == '}'; + + spans.push_back (link); + new_span = true; + break; + } + + r += 'l'; + break; + } + case 'N': + { + if (i + 1 < n && l[i + 1] == '{') + { + ++i; + spans.push_back (note); + new_span = true; + break; + } + + r += 'N'; + break; + } + case '\\': + { + switch (ot) + { + case ot_man: + { + r += "\\e"; + break; + } + default: + { + r += '\\'; + break; + } + } + break; + } + case '"': + { + r += '"'; + break; + } + case '\'': + { + r += '\''; + break; + } + case '}': + { + r += '}'; + break; + } + case '|': + { + r += '|'; + break; + } + default: + { + cerr << "error: unknown escape sequence '\\" << c << "' in " + << "documentation paragraph '" << string (l, 0, n) << "'" + << endl; + throw generation_failed (); + } + } + + // If we just added a new span, add corresponding output markup. + // + if (new_span) + { + span s (spans.back ()); + + span es (0); // Effective span. + for (vector::iterator i (spans.begin ()); + i != spans.end (); + ++i) + es |= *i & (code | itlc | bold); + + switch (ot) + { + case ot_plain: + { + if ((s & note) != 0) + { + r += "[Note: "; + } + else if ((s & link) == 0) + { + if (color) + { + if (s & bold) + r += "\033[1m"; + + if (s & itlc) + r += "\033[4m"; + } + } + + break; + } + case ot_html: + { + if (s & note) + { + r += ""; + } + else if (s & link) + { + r += ""; + } + else + { + if (s & code) + r += ""; + + if (s & itlc) + r += ""; + + if (s & bold) + r += ""; + } + + break; + } + case ot_man: + { + if ((s & note) != 0) + { + cerr << "error: \\N{} in man output not yet supported" << endl; + throw generation_failed (); + } + + if ((s & link) == 0) + { + if ((es & itlc) && (es & bold)) + r += "\\f(BI"; + else if (es & itlc) + r += "\\fI"; + else if (es & bold) + r += "\\fB"; + } + + break; + } + } + } + + escape = false; + } + else // Not escape. + { + switch (c) + { + case '\\': + { + escape = true; + break; + } + case '.': + { + if (ot == ot_man) + r += "\\."; + else + r += '.'; + break; + } + case '}': + { + if (!spans.empty ()) + { + span s (spans.back ()); + spans.pop_back (); + + span es (0); // New effective span. + for (vector::iterator i (spans.begin ()); + i != spans.end (); + ++i) + es |= *i & (code | itlc | bold); + + switch (ot) + { + case ot_plain: + { + if ((s & note) != 0) + { + r += ']'; + } + else if ((s & link) != 0) + { + string t (link_section.empty () + ? link_target + : link_target + "(" + link_section + ")"); + + string pt (process_link_target (t)); + + if (pt.empty ()) + { + if (link_empty) + { + cerr << "error: link target '" << t << "' became empty " + << "and link text is also empty" << endl; + throw generation_failed (); + } + } + else + { + if (!link_empty) + r += " ("; + + if (link_section.empty ()) + r += pt; + else + { + if (color) + r += "\033[1m"; + + r += pt; + + if (color) + r += "\033[0m"; + } + + if (!link_empty) + r += ")"; + } + } + else + { + if (color) + { + // While there are codes to turn off bold (22) and + // underline (24), it is not clear how widely they + // are supported. + // + r += "\033[0m"; // Clear all. + + if (es & bold) + r += "\033[1m"; + + if (es & itlc) + r += "\033[4m"; + } + } + + break; + } + case ot_html: + { + if ((s & note) != 0) + { + r += ""; + } + else if ((s & link) != 0) + { + if (link_empty) + { + if (link_section.empty ()) + r += link_target; + else + { + r += ""; + r += link_target + "(" + link_section + ")"; + r += ""; + } + } + + r += ""; + } + else + { + if (s & bold) + r += ""; + + if (s & itlc) + r += ""; + + if (s & code) + r += ""; + } + + break; + } + case ot_man: + { + assert ((s & note) == 0); + + if ((s & link) != 0) + { + string t (link_section.empty () + ? link_target + : link_target + "(" + link_section + ")"); + + string pt (process_link_target (t)); + + if (pt.empty ()) + { + if (link_empty) + { + cerr << "error: link target '" << t << "' became empty " + << "and link text is also empty" << endl; + throw generation_failed (); + } + } + else + { + if (!link_empty) + r += " ("; + + if (link_section.empty ()) + r += pt; + else + r += "\\fB" + pt + "\\fP"; + + if (!link_empty) + r += ")"; + } + } + else + { + // At first sight, \fP (select previous font) looks like + // exactly what we need here. However, it doesn't quite + // have the stack semantics that we need. + // + if ((es & itlc) && (es & bold)) + r += "\\f(BI"; + else if (es & itlc) + r += "\\fI"; + else if (es & bold) + r += "\\fB"; + else + r += "\\fR"; + } + + break; + } + } + + if (s & link) + link_target.clear (); + + break; + } + } + // Fall through. + default: + r += c; + break; + } + } + } + + if (escape) + { + cerr << "error: unterminated escape sequence in documentation " + << "paragraph '" << string (l, 0, n) << "'" << endl; + throw generation_failed (); + } + + if (!spans.empty ()) + { + unsigned char b (spans.back ()); + string bs; + + if (b & code) bs += 'c'; + if (b & itlc) bs += 'i'; + if (b & bold) bs += 'b'; + if (b & link) bs = 'l'; + if (b & note) bs = 'N'; + + + cerr << "error: unterminated formatting span '\\" << bs << "' " + << "in documentation paragraph '" << string (l, 0, n) << "'" << endl; + throw generation_failed (); + } +} + +struct block +{ + // The semantic meaning of \hN and their mapping to HTML is as follows: + // + // \h0 - preface

+ // \H - part

+ // \h1 - chapter

+ // \h - section

or

+ // \h2 - sub-section

+ // + // In HTML, if \h0 or \h1 was seen, then \h is automatically mappend to + //

. Otherwise it is

. + // + // The specifier (0, H, 1, h, 2) is stored in header. + // + enum kind_type {h, ul, ol, dl, li, note, text, pre}; + + kind_type kind; + bool para; // True if first text fragment should be in own paragraph. + + string id; + string header; + string value; + string trailer; + + block (kind_type k, bool p, const string& i, const string& h = "") + : kind (k), para (p), id (i), header (h) {} +}; + +static const char* block_kind_str[] = { + "\\h", "\\ul", "\\ol", "\\dl", "\\li", "\\N", "text", "preformatted text"}; + +inline ostream& +operator<< (ostream& os, block::kind_type k) +{ + return os << block_kind_str[k]; +} + +// Given the contents of a block element, convert any leading/trailing
+// elements to top/bottom margins. While conceptually it would seem better to +// translate them to padding, margins actually give better practical results +// since they overlap with container's margins. +// +static string +html_margin (string& v) +{ + size_t top (0), bot (0); + + const char* b (v.c_str ()); + const char* e (v.c_str () + v.size ()); + + for (; e - b >= 5 && strncmp (b, "
", 5) == 0; ++top) + { + b += 5; + + if (b != e && *b == '\n') // Remove following newline, if any. + ++b; + } + + for (; e - b >= 5 && strncmp (e - 5, "
", 5) == 0; ++bot) + { + e -= 5; + + if (e != b && *(e - 1) == '\n') // Remove preceding newline, if any. + --e; + } + + if (top == 0 && bot == 0) + return ""; + + string t; + t.swap (v); + v.assign (b, e - b); + + ostringstream os; + + if (top != 0) + os << "margin-top:" << top << "em"; + + if (bot != 0) + os << (top != 0 ? ";" : "") << "margin-bottom:" << bot << "em"; + + return os.str (); +} + +// The same idea except there are no margins. So we just strip .br. +// +static void +man_margin (string& v) +{ + size_t top (0), bot (0); + + const char* b (v.c_str ()); + const char* e (v.c_str () + v.size ()); + + for (; e - b >= 3 && strncmp (b, ".br", 3) == 0; ++top) + { + b += 3; + + if (b != e && *b == '\n') // Remove following newline, if any. + ++b; + } + + for (; e - b >= 3 && strncmp (e - 3, ".br", 3) == 0; ++bot) + { + e -= 3; + + if (e != b && *(e - 1) == '\n') // Remove preceding newline, if any. + --e; + } + + if (top != 0 || bot != 0) + { + string t; + t.swap (v); + v.assign (b, e - b); + } +} + +string context:: +format (semantics::scope& scope, string const& s, bool para) +{ + stack blocks; + blocks.push (block (block::text, para, "")); // Top-level. + + // Number of li in ol. Since we don't support nested lists, we don't + // need to push it into the stack. + // + size_t ol_count (0); + + // Mapping of \h to HTML tag. By default it is

until we encounter + // \h0 or \h1 at which point we change it to

. + // + char html_h ('1'); + + bool last (false); + for (size_t b (0), e; !last; b = e + 1) + { + bool pre (s[b] == 0x02); + + const char* l; + size_t n; + string subst; // Variable-substituted. + + if (pre) + { + ++b; // Skip 0x02. + + e = s.find (0x03, b); + assert (e != string::npos); + + l = s.c_str () + b; + n = e - b; + + ++e; // Skip newline that follows 0x03. + last = (e == s.size ()); + } + else + { + e = s.find ('\n', b); + last = (e == string::npos); + + l = s.c_str () + b; + n = (last ? s.size () : e) - b; + + // Perform variable expansions (\$var$). Also detect the switch + // to/from TOC mode. + // + unsigned short t (toc); + if (substitute (scope, l, n, subst)) + { + if (subst.empty ()) + continue; + + if (t != toc) + { + // This is a TOC prologue/epilogue (returned by start/end_toc()) and + // we shouldn't be formatting it. + // + assert (blocks.size () == 1); + string& v (blocks.top ().value); + + // Require that \$TOC$ appears in its own doc string. Failed this + // it is tricky to get indentation right. + // + if (!v.empty () || !last) + { + cerr << "error: TOC variable should be in its own documentation " + << "string" << endl; + throw generation_failed (); + } + + switch (ot) + { + case ot_plain: break; + case ot_html: + { + // Different "newline protocol" inside TOC. + // + v += subst; + + if (toc) + v += '\n'; + } + case ot_man: break; + } + + continue; + } + + l = subst.c_str (); + n = subst.size (); + } + } + + // If this is TOC phase 2, then we simply ignore everything. + // + if (toc == 2) + continue; + + const char* ol (l); // Original, full line for diagnostics. + size_t on (n); + + // First determine what kind of paragraph block this is. + // + block::kind_type k (block::h); + string id; + string header; + string trailer; + + size_t pop (0); // Pop count. + + if (pre) + { + k = block::pre; + } + else + { + // \h \H + // + if (n >= 3 && + l[0] == '\\' && + (l[1] == 'h' || l[1] == 'H') && + (l[2] == '|' || l[2] == '#')) + { + k = block::h; + header = l[1]; + l += 3; + n -= 3; + } + // + // \h0 \h1 \h2 + // + else if (n >= 4 && + l[0] == '\\' && + l[1] == 'h' && + (l[3] == '|' || l[3] == '#')) + { + if (l[2] != '0' && l[2] != '1' && l[2] != '2') + { + cerr << "error: '0', '1', or '2' expected in \\hN| in '" + << string (ol, 0, on) << "'" << endl; + throw generation_failed (); + } + + k = block::h; + header = l[2]; + l += 4; + n -= 4; + } + // + // \ul \ol \dl + // + else if (n >= 4 && + l[0] == '\\' && + (l[1] == 'u' || l[1] == 'o' || l[1] == 'd') && + l[2] == 'l' && + (l[3] == '|' || l[3] == '#')) + { + switch (l[1]) + { + case 'u': k = block::ul; break; + case 'o': k = block::ol; break; + case 'd': k = block::dl; break; + } + + l += 4; + n -= 4; + } + // + // \li + // + else if (n >= 4 && + l[0] == '\\' && + l[1] == 'l' && + l[2] == 'i' && + (l[3] == '|' || l[3] == '#')) + { + k = block::li; + l += 4; + n -= 4; + } + // + // \N (note) + // + else if (n >= 3 && + l[0] == '\\' && + l[1] == 'N' && + (l[2] == '|' || l[2] == '#')) + { + k = block::note; + l += 3; + n -= 3; + } + else + k = block::text; + + // Get the id, if present. + // + if (k != block::text && *(l - 1) == '#') + { + for (; n != 0 && *l != '|'; ++l, --n) + id += *l; + + if (n == 0) + { + cerr << "error: paragraph begin '|' expected after id in '" + << string (ol, 0, on) << "'" << endl; + throw generation_failed (); + } + + ++l; --n; // Skip '|'. + } + + // Skip leading spaces after opening '|'. + // + if (k != block::text) + while (n != 0 && (*l == 0x20 || *l == 0x0D || *l == 0x09)) {++l; --n;} + + // Next figure out how many blocks we need to pop at the end of this + // paragraph. Things get a bit complicated since '|' could be escaped. + // + for (; n - pop > 0 && l[n - pop - 1] == '|'; ++pop) ; + if (pop != 0) + { + // To determine whether the first '|' is part of an escape sequence + // we have to find the first non-backslash character and then figure + // out who escapes whom. + // + size_t ec (0); // Escape count. + for (; n - pop - ec > 0 && l[n - pop - ec - 1] == '\\'; ++ec) ; + + // If we have an odd number of backslashes, then the last '|' is + // escaped. + // + if (ec % 2 != 0) + --pop; + + n -= pop; // Number of special '|' at the end. + + // Skip trailing spaces before closing '|'. + // + while (n != 0 && (l[n - 1] == 0x20 || + l[n - 1] == 0x0D || + l[n - 1] == 0x09)) n--; + } + } + + // Outer block kind. + // + block::kind_type ok (blocks.top ().kind); + + // Verify that this block type is valid in this context. Ignore + // empty text blocks (can happen if we just have '|'). + // + if (k != block::text || n != 0) + { + bool good (true); + + switch (ok) + { + case block::h: good = false; break; + case block::ul: + case block::ol: + case block::dl: good = (k == block::li); break; + case block::li: good = (k == block::note || + k == block::text || + k == block::pre ); break; + case block::note: good = (k == block::text || + k == block::pre || + (ot == ot_html && (k == block::ul || + k == block::ol || + k == block::dl))); break; + case block::text: good = (k != block::li); break; + case block::pre: assert (false); + } + + if (!good) + { + cerr << "error: " << k << " inside " << ok << " " + << "in documentation string '" << s << "'" << endl; + throw generation_failed (); + } + } + + // Check id for duplicates. Do it only on the non-TOC pass. + // + if (!toc && !id.empty ()) + { + if (!id_set.insert (id).second) + { + cerr << "error: duplicate id '" << id << "' in documentation " + << "string '" << s << "'" << endl; + throw generation_failed (); + } + } + + // Verify the block itself. + // + switch (k) + { + case block::h: + { + // \h blocks are only valid if we are required to start a new + // paragraph (first_para is true). + // + if (!para) + { + cerr << "error: paragraph '" << string (ol, 0, on) << "' " + << "not allowed in '" << s << "'" << endl; + throw generation_failed (); + } + + // \h must be single-paragraph. + // + if (pop == 0) + { + cerr << "error: '|' expected at the end of paragraph '" + << string (ol, 0, on) << "'" << endl; + throw generation_failed (); + } + + // \h must not be empty. + // + if (n == 0) + { + cerr << "error: empty paragraph '" << string (ol, 0, on) << "' " + << "in documentation string '" << s << "'" << endl; + throw generation_failed (); + } + + break; + } + case block::ul: + case block::ol: + case block::dl: + { + if (pop != 0) + { + cerr << "error: empty list '" << string (ol, 0, on) << "' " + << "in documentation string '" << s << "'" << endl; + throw generation_failed (); + } + + if (n != 0) + { + cerr << "error: unexpected text after " << k << "| " + << "in paragraph '" << string (ol, 0, on) << "'" << endl; + throw generation_failed (); + } + + break; + } + case block::li: + { + if (ok == block::dl) + { + if (n == 0) + { + cerr << "error: term text missing in paragraph '" + << string (ol, 0, on) << "'" << endl; + throw generation_failed (); + } + } + + break; + } + case block::note: + case block::text: + case block::pre: + break; + } + + // Push the block into the stack. + // + switch (k) + { + case block::h: blocks.push (block (k, false, id, header)); break; + case block::ul: + case block::ol: ol_count = 0; // Fall through. + case block::dl: blocks.push (block (k, true, id)); break; + case block::li: + { + switch (blocks.top ().kind) + { + case block::ol: + { + ostringstream os; + os << ++ol_count; + header = os.str (); + break; + } + case block::dl: + { + format_line (ot, header, l, n); + n = 0; + break; + } + default: + break; + } + + blocks.push (block (k, false, id, header)); + break; + } + case block::note: + { + blocks.push (block (k, true, id, header)); + break; + } + case block::text: break; // No push. + case block::pre: break; // No push. + } + + // Output paragraph text. If we are in TOC mode and this is a top-level + // block, then pretend we don't have anything to write. For non-top-level + // blocks, we handle this below, when we pop them. + // + if (toc && blocks.size () == 1) + n = 0; + + if (n != 0) + { + block& b (blocks.top ()); + string& v (b.value); + bool first (v.empty ()); + + switch (ot) + { + case ot_plain: + { + // Separate paragraphs with a blank line. + // + if (!first) + v += "\n\n"; + + if (k == block::pre) + v.append (l, n); + else + { + /* + // Strip a single "\n" at the beginning of the paragraph since we + // already wrote one newline as part of starting the paragraph. An + // example where this might be useful: + // + // li| + // + // \ + // ... + // \ + // + // \n| + // + if (n >= 2 && l[0] == '\\' && l[1] == 'n') + { + l += 2; + n -= 2; + } + + if (n != 0) + */ + + format_line (ot, v, l, n); + } + + break; + } + case ot_html: + { + // Separate paragraphs with a blank line. + // + if (!first) + v += "\n\n"; + + if (k == block::pre) + { + v += "
";
+            v.append (l, n);
+            v += "
"; + } + else + { + if (!first || b.para) + { + string t; + format_line (ot, t, l, n); + string s (html_margin (t)); + + // If the entire paragraph is wrapped in then signal this + // fact via the "code" class. This can be used, for example, to + // change alignment from "justify" to "left" in order to support + // custom alignments (in combination with non-ignorable space + // escaping; think multi-line synopsis in man pages). + // + // While at it also cleanup nested fragments (we + // may end up with quite a few of them due to the + // translation). + // + string c; + { + size_t n (t.size ()); + + if (n >= 13 && + t.compare (0, 6, "") == 0 && + t.compare (n - 7, 7, "") == 0) + { + string tt; + + // Make sure this is one contigous . + // + size_t b (1); // / balance. + for (size_t i (6), j (0); + i != n && (i = t.find ("code>", i)) != string::npos; + j = i = i + 5) + { + if (t[i - 1] == '<') b++; + else if (t[i - 1] == '/' && t[i - 2] == '<') b--; + else continue; + + // Append previous chunk. + // + tt.append (t, j, i - j - (t[i - 1] == '<' ? 1 : 2)); + + if (b == 0) + { + if (i + 5 == n) + { + tt.append (t, i - 2, 7); // Closing . + t = move (tt); + c = "code"; + } + + break; + } + } + } + } + + v += "= blocks.size ()) // >= to account for top-level. + { + cerr << "error: extraneous '|' at the end of paragraph '" + << string (ol, 0, on) << "'" << endl; + throw generation_failed (); + } + + for (; pop != 0; --pop) + { + block pb (blocks.top ()); // move + string& pi (pb.id); + string& ph (pb.header); + string& pv (pb.value); + + blocks.pop (); + + block& b (blocks.top ()); + string& v (b.value); + + // Handle poping into top-level block in TOC mode. + // + if (toc && blocks.size () == 1) + { + if (pb.kind == block::h) + { + switch (ot) + { + case ot_plain: break; + case ot_html: + { + char t (ph[0]); + + if (pi.empty ()) + { + cerr << "error: TOC heading '" << pv << "' has no id" << endl; + throw generation_failed (); + } + + pv = "" + pv + ""; + + // Unwind heading levels that are deeper ("more sub") than us. + // + for (; tocs.size () != 1; tocs.pop_back ()) + { + toc_entry const& e (tocs.back ()); + + bool pop (true); + + switch (t) + { + case '0': + case 'H': + case '1': break; // Always unwind. + case 'h': + { + pop = html_h == '1' || e.type == 'h' || e.type == '2'; + break; + } + case '2': pop = e.type == '2'; break; + } + + if (!pop) + break; + + // If we have sub-headings, then we need to close the table. + // + if (e.count != 0) + { + size_t l (tocs.size ()); + + v += string (l * 4 - 2, ' ') + "\n"; + v += string (l * 4 - 4, ' ') + "\n"; + } + else + // Otherwise it is inline. + // + v += "\n"; + } + + size_t l (tocs.size ()); + toc_entry& e (tocs.back ()); + + // If this is a first sub-entry, then we need to open the + // sub-table. + // + string in ((l * 4) - 2, ' '); + + if (l > 1 && e.count == 0) + { + v += "\n"; + v += in + "\n"; + } + + in += " "; + + switch (t) + { + case 'H': + { + v += in + "\n"; + break; + } + case '0': + case '1': + case 'h': + case '2': + { + // Calculate Chapter(X)/Section(X.Y)/Subsection(X.Y.Z) unless + // it is the preface (or a subsection thereof). + // + string n; + if (t != '0') + { + ++e.count; + + for (toc_stack::const_iterator i (tocs.begin ()); + i != tocs.end (); + ++i) + { + if (i->type == '0') + { + n.clear (); + break; + } + + if (!n.empty ()) + n += '.'; + + ostringstream os; + os << i->count; + n += os.str (); + } + } + + v += in + ""; + + if (!n.empty ()) + { + v += "\n"; + } + else + // Otherwise it is inline. + // + v += "\n"; + } + + v += "
" + pv + "
" + n + ""; + heading_map[pi] = n; // Save it for later. + } + else + v += ""; + + v += pv; // No newline + tocs.push_back (toc_entry (t)); + break; + } + } + + // Same as in non-TOC mode below. + // + // @@ This only works for a single string fragment. + // + if (t == '0' || t == '1') + html_h = '2'; + + break; + } + case ot_man: break; + } + } + + continue; + } + + switch (ot) + { + case ot_plain: + { + const char* sn (v.empty () ? "" : "\n"); + const char* dn (v.empty () ? "" : "\n\n"); + + switch (pb.kind) + { + case block::h: + { + v += dn; + + if (options.ansi_color ()) + v += "\033[1m"; // Bold. + + v += pv; + + if (options.ansi_color ()) + v += "\033[0m"; + + break; + } + case block::ul: + case block::ol: + case block::dl: v += dn + pv; break; + case block::li: + { + v += sn; + size_t ind (0), vn (pv.size ()); + + switch (b.kind) + { + case block::ul: + v += "* "; + ind = 2; + break; + case block::ol: + v += ph + ". "; + ind = ph.size () + 2; + break; + case block::dl: + v += ph + "\n" + (vn != 0 && pv[0] != '\n' ? " " : ""); + ind = 4; + break; + default: + break; + } + + // Add value with indentation for subsequent paragraphs. + // + char c, p ('\0'); // Current and previous characters. + for (size_t i (0); i != vn; p = c, ++i) + { + c = pv[i]; + + if (p == '\n' && c != '\n') // Don't indent blanks. + v += string (ind, ' '); + + v += c; + } + + break; + } + case block::note: + { + v += dn; + + // Add a special character (bell) plus space in front of every + // line (including blanks). The bell is recognized and + // translated by txt_wrap_lines() to '|'. + // + char p ('\n'); // Previous character. + for (size_t i (0), n (pv.size ()); i != n; ++i) + { + char c (pv[i]); + + if (p == '\n') + v += (c != '\n' ? "\x07 " : "\x07"); + + v += c; + p = c; + } + + break; + } + case block::text: + case block::pre: assert (false); + } + + break; + } + case ot_html: + { + if (!v.empty ()) + v += "\n\n"; + + switch (pb.kind) + { + case block::h: + { + char t (ph[0]); + + string h; + string c; + + typedef map map; + const map& hm (options.html_heading_map ()); + map::const_iterator mi (hm.find (t)); + + if (mi == hm.end ()) + { + switch (t) + { + case '0': h = "h1"; c = "preface"; break; + case 'H': h = "h1"; c = "part"; break; + case '1': h = "h1"; break; + case 'h': h = html_h == '1' ? "h1" : "h2"; break; + case '2': h = "h3"; break; + } + } + else + h = mi->second; + + v += '<' + h; + if (!pi.empty ()) v += " id=\"" + pi + '"'; + if (!c.empty ()) v += " class=\"" + c + '"'; + v += '>'; + + // See if we have a heading number (assigned by TOC). + // + if (!pi.empty ()) + { + heading_map_type::const_iterator i (heading_map.find (pi)); + + if (i != heading_map.end ()) + v += i->second + ' '; + } + + v += pv; + + v += "'; + + // @@ This only works for a single string fragment. + // + if (t == '0' || t == '1') + html_h = '2'; + + break; + } + case block::ul: v += "
    \n" + pv + "\n
"; break; + case block::ol: v += "
    \n" + pv + "\n
"; break; + case block::dl: v += "
\n" + pv + "\n
"; break; + case block::li: + { + string pvs (html_margin (pv)); + + if (b.kind == block::dl) + { + string phs (html_margin (ph)); + + v += (phs.empty () ? "
" : "
") + + ph + "
\n"; + + v += (pvs.empty () ? "
" : "
") + + pv + "
"; + } + else + v += (pvs.empty () ? "
  • " : "
  • ") + + pv + "
  • "; + + break; + } + case block::note: + { + v += "
    \n"; + v += pv; + v += "\n
    "; + + break; + } + case block::text: + case block::pre: assert (false); + } + + break; + } + case ot_man: + { + // Seeing that we always write a macro, one newline is enough. + // + if (!v.empty ()) + v += "\n"; + + switch (pb.kind) + { + case block::h: v += ".SH \"" + pv + "\""; break; + case block::ul: + case block::ol: + case block::dl: + { + if (!b.para) // First list inside .IP. + { + // .IP within .IP? Just shoot me in the head already! We + // have to manually indent it with .RS/.RE *and* everything + // that comes after it (since .PP resets the indent). Why + // not just indent the whole list content? Because then the + // first line will never start on the same line as the term. + // + v += ".RS\n"; + b.trailer = "\n.RE"; + b.para = true; // Start emitting .PP from now on. + } + + v += pv; + break; + } + case block::li: + { + man_margin (ph); // Strip leading/trailing .br. + + switch (b.kind) + { + case block::ul: v += ".IP \\(bu 2em\n" + pv; break; + case block::ol: v += ".IP " + ph + ". 4em\n" + pv; break; + // + // Add .br to force the definition to start on the new line. + // + case block::dl: v += ".IP \"" + ph + "\"\n.br\n" + pv; break; + default: break; + } + + break; + } + case block::note: + { + cerr << "error: " << pb.kind << "| in man output not yet " + << "supported" << endl; + throw generation_failed (); + } + case block::text: + case block::pre: assert (false); + } + + break; + } + } + } + } + + assert (!blocks.empty ()); // Should have top-level. + + if (blocks.size () > 1) + { + cerr << "error: unterminated paragraph " << blocks.top ().kind << " " + << "in documentation string '" << s << "'" << endl; + throw generation_failed (); + } + + block& b (blocks.top ()); + + switch (ot) + { + case ot_man: return b.value + b.trailer; + default: return b.value; + } +} + +string context:: +start_toc () +{ + switch (ot) + { + case ot_plain: break; + case ot_html: + { + tocs.push_back (toc_entry ('\0')); + return " "; + } + case ot_man: break; + } + + return string (); +} + +string context:: +end_toc () +{ + switch (ot) + { + case ot_plain: break; + case ot_html: + { + string v; + + // Unwind the TOC stack until we reach top level. Same code as in + // format(). + // + for (; tocs.size () != 1; tocs.pop_back ()) + { + toc_entry const& e (tocs.back ()); + + // If we have sub-headings, then we need to close the table. + // + if (e.count != 0) + { + size_t l (tocs.size ()); + + v += string (l * 4 - 2, ' ') + "
    \n"; + v += string (l * 4 - 4, ' ') + "
    "; + return v; + } + case ot_man: break; + } + + return string (); +} + +void context:: +verify_id_ref () +{ + if (!options.omit_link_check ()) + { + bool f (false); + + for (id_set_type::const_iterator i (ref_set.begin ()); + i != ref_set.end (); + ++i) + { + if (id_set.find (*i) == id_set.end ()) + { + cerr << "error: no id for fragment link '#" << *i << "'" << endl; + f = true; + } + } + + if (f) + throw generation_failed (); + } + + id_set.clear (); + ref_set.clear (); + heading_map.clear (); +} + +string context:: +substitute (const string& s, const path* d) +{ + string r; + + // Scan the string looking for variables ($var$) or paths. + // + size_t b (0), e (b); + for (size_t n (s.size ()); e != n; ++e) + { + if (s[e] == '$' && e + 1 != n) + { + if (s[e + 1] == '$') // Escape. + { + r.append (s, b, ++e - b); // Keep one, skip the other. + b = e + 1; + continue; + } + + bool file (false); + + // Scan for as long as it is a C identifier or a file. + // + size_t p (e + 1); // Position of the second '$'. + for (; p != n; ++p) + { + char c (s[p]); + bool f (p == e + 1); // First. + + if (d != 0 && f && c == '.' && p + 1 != n && + (s[p + 1] == '/' || // "./" + (s[p + 1] == '.' && p + 2 != n && s[p + 2] == '/'))) // "../" + file = true; + + if (file) + { + if (c == '$') + break; + } + else + { + if (!(c == '_' || + ('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + (!f && '0' <= c && c <= '9'))) + break; + } + } + + // Note: check that the second '$' is not escaped. + // + if (p != n && s[p] == '$' && (p + 1 == n || s[p + 1] != '$')) + { + r.append (s, b, e - b); // Save what came before the expansion. + + // Var/file name. + // + ++e; + string v (s, e, p - e); + + if (file) + { + path p (*d / path (v)); + p.normalize (); // Apply '.' and '..'. + + ifstream ifs (p.string ().c_str (), ifstream::in | ifstream::binary); + if (!ifs.is_open ()) + { + cerr << p << ": error: unable to open in read mode" << endl; + throw generation_failed (); + } + + // We don't expect our file to contain '\0' so use that as the + // delimiter to read the entire file with getline(). + // + string s; + getline (ifs, s, '\0'); + r += s; + } + else + { + // Handle special variables. + // + if (v == "TOC") + { + if (!r.empty () || p + 1 != n) + { + cerr << "error: TOC variable should be on its own line" << endl; + throw generation_failed (); + } + + // Invert the TOC mode. + // + if ((toc = toc ? 0 : 1)) + r = start_toc (); + else + r = end_toc (); + + return r; + } + + // Lookup and substiute the variable. + // + using semantics::doc; + + if (doc* d = unit.lookup ("", "var: " + v)) + r += d->front (); + else + { + cerr << "error: undefined variable '" << v << "' in '" << s << "'" + << endl; + throw generation_failed (); + } + } + + e = p; + b = e + 1; + } + } + } + + r.append (s, b, e - b); // Last chunk. + return r; +} + +bool context:: +substitute (semantics::scope& scope, const char* s, size_t n, string& result) +{ + bool sub (false); + result.clear (); + + // Scan the string looking for variables (\$var$). + // + size_t b (0), e (b); + for (char p ('\0'); e != n; ++e) + { + char c (s[e]); + + if (p == '\\') + { + if (c == '\\') // Escape sequence. + { + p = '\0'; + continue; + } + + if (c == '$') + { + // Variable expansion. + // + sub = true; + + // Find the closing '$'. + // + size_t p (e + 1); // Position of the second '$'. + for (; p != n && s[p] != '$'; ++p) ; + + if (p == n) + { + cerr << "error: missing closing '$' in '" << string (s, n) << "'" + << endl; + throw generation_failed (); + } + + result.append (s, b, e - b - 1); // Save what came before. + + // Var name. + // + ++e; + string v (s, e, p - e); + + // Handle special variables. + // + if (v == "TOC") + { + if (!result.empty () || p + 1 != n) + { + cerr << "error: TOC variable should be its own paragraph" << endl; + throw generation_failed (); + } + + // Invert the TOC mode. + // + if ((toc = toc ? 0 : 1)) + result = start_toc (); + else + result = end_toc (); + + return true; + } + + // Lookup and substiute. + // + using semantics::doc; + + if (doc* d = unit.lookup (scope.fq_name (), "var: " + v)) + result += d->front (); + else + { + cerr << "error: undefined variable '" << v << "' in '" + << string (s, n) << "'" << endl; + throw generation_failed (); + } + + e = p; + b = e + 1; + } + } + + p = s[e]; + } + + if (sub) + result.append (s, b, e - b); // Last chunk. + + return sub; +} + +string context:: +fq_name (semantics::nameable& n, bool cxx_name) +{ + using namespace semantics; + + string r; + + if (dynamic_cast (&n)) + { + return ""; // Map to global namespace. + } + else + { + r = fq_name (n.scope ()); + r += "::"; + r += cxx_name ? escape (n.name ()) : n.name (); + } + + return r; +} + +string context:: +ns_open (const string& qn, bool last) +{ + string::size_type b (0), e; + + do + { + e = qn.find ("::", b); + string n (qn, b, e == string::npos ? e : e - b); + + if (!n.empty () && (last || e != string::npos)) + os << "namespace " << n << "{"; + + b = e; + + if (b == string::npos) + return n; + + b += 2; + + } while (true); +} + +void context:: +ns_close (const string& qn, bool last) +{ + string::size_type b (0), e; + + do + { + e = qn.find ("::", b); + string n (qn, b, e == string::npos ? e : e - b); + + if (!n.empty () && (last || e != string::npos)) + os << "}"; + + b = e; + + if (b == string::npos) + return; + + b += 2; + + } while (true); +} + +class_doc context:: +class_doc (semantics::class_& c) +{ + typedef map map; + const map& m (options.class_doc ()); + + string n (c.fq_name ()); + + map::const_iterator i (m.find (n)); + + if (i == m.end ()) + { + n = string (n, 2, string::npos); // Try without leading '::'. + i = m.find (n); + } + + if (i == m.end ()) + return cd_default; + + const string& k (i->second); + + if (k == "exclude") + return cd_exclude; + if (k == "exclude-base") + return cd_exclude_base; + else if (k == "short") + return cd_short; + else if (k == "long") + return cd_long; + else + { + cerr << "error: unknown --class-doc kind value '" << k << "'" << endl; + throw generation_failed (); + } +} + +string context:: +first_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); +} + +// namespace +// + +void namespace_:: +pre (type& ns) +{ + string name (ns.name ()); + + if (!name.empty ()) + os << "namespace " << escape (name) + << "{"; +} + +void namespace_:: +post (type& ns) +{ + if (!ns.name ().empty ()) + os << "}"; +} diff --git a/cli/cli/context.hxx b/cli/cli/context.hxx new file mode 100644 index 0000000..633b8ad --- /dev/null +++ b/cli/cli/context.hxx @@ -0,0 +1,295 @@ +// file : cli/context.hxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#ifndef CLI_CONTEXT_HXX +#define CLI_CONTEXT_HXX + +#include +#include +#include +#include +#include +#include // std::size_t + +#include +#include +#include + +#include +#include +#include + +using std::endl; + +class generation_failed {}; + +enum usage +{ + ut_none, + ut_short, + ut_long, + ut_both +}; + +enum class_doc +{ + cd_default, + cd_exclude, + cd_exclude_base, + cd_short, + cd_long +}; + +class context +{ +public: + typedef std::size_t size_t; + typedef std::string string; + + typedef cutl::fs::path path; + typedef cutl::fs::invalid_path invalid_path; + + typedef ::options options_type; + typedef ::usage usage_type; + typedef ::class_doc class_doc_type; + + // Regex. + // + typedef cutl::re::regex regex; + typedef cutl::re::regexsub regexsub; + typedef cutl::re::format regex_format; + + typedef std::vector regex_mapping; + +private: + struct data; + cutl::shared_ptr data_; + +public: + std::ostream& os; + semantics::cli_unit& unit; + options_type const& options; + + // Documentation output type. + // + enum output_type + { + ot_plain, + ot_html, + ot_man + }; + + output_type ot; + + bool gen_modifier; + bool gen_specifier; + bool gen_parse; + bool gen_merge; + usage_type gen_usage; + + string const& inl; + string const& opt_prefix; + string const& opt_sep; + string const& cli; + + typedef std::map reserved_name_map_type; + reserved_name_map_type const& reserved_name_map; + + typedef std::set keyword_set_type; + keyword_set_type const& keyword_set; + + regex_mapping const& link_regex; + + typedef std::set id_set_type; + id_set_type& id_set; + id_set_type& ref_set; + + // Map of heading ids to heading number (assigned during TOC generation) + // + typedef std::map heading_map_type; + heading_map_type& heading_map; + + // TOC phase. + // + // 0 - non-TOC + // 1 - generating TOC after seeing expansion but before restart + // 2 - generating TOC after restart + // 0 - non-TOC after seeing expansion after restart + // + unsigned short& toc; + + struct toc_entry + { + toc_entry (char t = '\0', size_t c = 0): type (t), count (c) {} + + char type; // Entry type (output type-specific, usually X from \hX). + size_t count; // Number of sub-entries so far. + }; + + typedef std::vector toc_stack; + toc_stack& tocs; + +private: + struct data + { + string inl_; + string cli_; + keyword_set_type keyword_set_; + regex_mapping link_regex_; + id_set_type id_set_; + id_set_type ref_set_; + heading_map_type heading_map_; + unsigned short toc_; + toc_stack tocs_; + }; + +public: + // Escape C++ keywords, reserved names, and illegal characters. + // + string + escape (string const&) const; + + string + process_link_target (const string&); + + // Translate and format the documentation string. Translate converts + // the -style constructs to \i{arg}. Format converts the string + // to the output format. + // + static string + translate_arg (string const&, std::set&); + + static string + translate (string const&, std::set const&); + + // If para is true, start a new paragraph. + // + string + format (semantics::scope&, string const&, bool para); + + void + format_line (output_type, string&, const char*, size_t); + + // Called when we switch to the TOC mode and when we exit it, + // respectively. + // + string + start_toc (); + + string + end_toc (); + + // Make sure each local fragment reference has the corresponding id. Issue + // diagnostics and throw generation_failed if fails. Otherwise clear the + // id and ref sets. + // + void + verify_id_ref (); + + // Substitute doc variable expansions ($var$). Var must be a C identifier. + // If the path is not NULL, then also recognize names that start with either + // ./ or ../ and treat them as files relative to path. Such file expansions + // are substituted with the file contents. + // + string + substitute (const string&, const path* = 0); + + // Substitute doc variable expansions (\$var$). Note that it leaves escapes + // (\\$) as is. Return true if any substitutions have been made, in which + // case result will contain the expansion result. + // + bool + substitute (semantics::scope&, const char* s, size_t n, string& result); + +public: + static string const& + ename (semantics::nameable& n) + { + return n.context ().get ("name"); + } + + static string const& + especifier (semantics::nameable& n) + { + return n.context ().get ("specifier"); + } + + static string const& + emember (semantics::nameable& n) + { + return n.context ().get ("member"); + } + + static string const& + especifier_member (semantics::nameable& n) + { + return n.context ().get ("specifier-member"); + } + +public: + // Return fully-qualified C++ or CLI name. + // + string + fq_name (semantics::nameable& n, bool cxx_name = true); + + // Open/close namespace. If last is false, then the last name + // component is not treated as a namespace. The open function + // also returns the last name component. + // +public: + string + ns_open (const string& name, bool last = true); + + void + ns_close (const string& name, bool last = true); + +public: + class_doc_type + class_doc (semantics::class_&); + +public: + string + first_sentence (string const&); + +public: + context (std::ostream&, + output_type, + semantics::cli_unit&, + options_type const&); + + context (context&); + +private: + context& + operator= (context const&); +}; + +// Checks if scope Y names any of X. +// +template +bool +has (Y& y) +{ + for (semantics::scope::names_iterator i (y.names_begin ()), + e (y.names_end ()); i != e; ++i) + if (i->named (). template is_a ()) + return true; + + return false; +} + +// Standard namespace traverser. +// +struct namespace_: traversal::namespace_, context +{ + namespace_ (context& c) : context (c) {} + + virtual void + pre (type&); + + virtual void + post (type&); +}; + +#endif // CLI_CONTEXT_HXX diff --git a/cli/cli/generator.cxx b/cli/cli/generator.cxx new file mode 100644 index 0000000..df1b99e --- /dev/null +++ b/cli/cli/generator.cxx @@ -0,0 +1,584 @@ +// file : cli/generator.cxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#include // std::toupper, std::is{alpha,upper,lower} +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace cutl; + +using semantics::path; + +namespace +{ + static char const cxx_header[] = + "// -*- C++ -*-\n" + "//\n" + "// This file was generated by CLI, a command line interface\n" + "// compiler for C++.\n" + "//\n\n"; + + string + make_guard (string const& file, context& ctx) + { + string g (file); + + // Split words, e.g., "FooBar" to "Foo_Bar" and convert everything + // to upper case. + // + string r; + for (string::size_type i (0), n (g.size ()); i < n - 1; ++i) + { + char c1 (g[i]); + char c2 (g[i + 1]); + + r += toupper (c1); + + if (isalpha (c1) && isalpha (c2) && islower (c1) && isupper (c2)) + r += "_"; + } + r += toupper (g[g.size () - 1]); + + return ctx.escape (r); + } + + void + open (ifstream& ifs, string const& path) + { + ifs.open (path.c_str (), ios_base::in | ios_base::binary); + + if (!ifs.is_open ()) + { + cerr << path << ": error: unable to open in read mode" << endl; + throw generator::failed (); + } + } + + void + append (context& ctx, string const& s, const path* d = 0) + { + // Detect the switch to/from TOC mode. + // + unsigned short t (ctx.toc); + string const& r (ctx.substitute (s, d)); + + if (t != ctx.toc) + { + if (!r.empty ()) // TOC prologue/epilogue (returned by start/end_toc()). + ctx.os << r << endl; + } + // Skip it if we are in the TOC mode. + // + else if (!t) + ctx.os << r << endl; + } + + void + append (context& ctx, vector const& text, string const& file) + { + for (vector::const_iterator i (text.begin ()); + i != text.end (); ++i) + { + append (ctx, *i); + } + + if (!file.empty ()) + { + ifstream ifs; + open (ifs, file); + + path d (path (file).directory ()); + + // getline() will set the failbit if it failed to extract anything, + // not even the delimiter and eofbit if it reached eof before seeing + // the delimiter. + // + for (string s; getline (ifs, s); ) + append (ctx, s, &d); + } + } +} + +generator:: +generator () +{ +} + +void generator:: +generate (options& ops, semantics::cli_unit& unit, path const& p) +{ + if (ops.generate_group_scanner ()) + ops.generate_vector_scanner (true); + + try + { + path file (p.leaf ()); + string base (file.base ().string ()); + + const string& pfx (ops.output_prefix ()); + const string& sfx (ops.output_suffix ()); + + bool gen_cxx (ops.generate_cxx ()); + bool gen_man (ops.generate_man ()); + bool gen_html (ops.generate_html ()); + bool gen_txt (ops.generate_txt ()); + + if (!gen_cxx && !gen_man && !gen_html && !gen_txt) + gen_cxx = true; + + if (ops.stdout_ ()) + { + if (gen_cxx) + { + cerr << "error: --stdout cannot be used with C++ output" << endl; + throw failed (); + } + + if ((gen_man && gen_html) || + (gen_man && gen_txt) || + (gen_html && gen_txt)) + { + cerr << "error: --stdout cannot only be used with one output format" + << endl; + throw failed (); + } + } + + fs::auto_removes auto_rm; + + // C++ output. + // + if (gen_cxx) + { + bool inl (!ops.suppress_inline ()); + + string hxx_name (pfx + base + sfx + ops.hxx_suffix ()); + string ixx_name (pfx + base + sfx + ops.ixx_suffix ()); + string cxx_name (pfx + base + sfx + ops.cxx_suffix ()); + + path hxx_path (hxx_name); + path ixx_path (ixx_name); + path cxx_path (cxx_name); + + if (!ops.output_dir ().empty ()) + { + path dir (ops.output_dir ()); + + hxx_path = dir / hxx_path; + ixx_path = dir / ixx_path; + cxx_path = dir / cxx_path; + } + + // Process names. + // + { + context ctx (cerr, context::ot_plain, unit, ops); + process_names (ctx); + } + + // Check if we need to generate the runtime code. If we include + // another options file, then we assume the runtime is generated + // there. However, to reduce the number of standard headers we + // have to include in the generated header file, we will still + // need to generate some template code in the source file. + // + bool runtime (!ops.suppress_cli ()); + + if (runtime) + { + for (semantics::cli_unit::includes_iterator i (unit.includes_begin ()); + i != unit.includes_end (); + ++i) + { + if (i->is_a ()) + { + runtime = false; + break; + } + } + } + + // + // + ofstream hxx (hxx_path.string ().c_str ()); + + if (!hxx.is_open ()) + { + cerr << "error: unable to open '" << hxx_path << "' in write mode" + << endl; + throw failed (); + } + + auto_rm.add (hxx_path); + + // + // + ofstream ixx; + + if (inl) + { + ixx.open (ixx_path.string ().c_str (), ios_base::out); + + if (!ixx.is_open ()) + { + cerr << "error: unable to open '" << ixx_path << "' in write mode" + << endl; + throw failed (); + } + + auto_rm.add (ixx_path); + } + + // + // + ofstream cxx (cxx_path.string ().c_str ()); + + if (!cxx.is_open ()) + { + cerr << "error: unable to open '" << cxx_path << "' in write mode" + << endl; + throw failed (); + } + + auto_rm.add (cxx_path); + + // Print headers. + // + hxx << cxx_header; + if (inl) + ixx << cxx_header; + cxx << cxx_header; + + typedef + compiler::ostream_filter + cxx_filter; + + // Include settings. + // + bool br (ops.include_with_brackets ()); + string ip (ops.include_prefix ()); + string gp (ops.guard_prefix ()); + + if (!ip.empty () && ip[ip.size () - 1] != '/') + ip.append ("/"); + + if (!gp.empty () && gp[gp.size () - 1] != '_') + gp.append ("_"); + + // HXX + // + { + context ctx (hxx, context::ot_plain, unit, ops); + + string guard (make_guard (gp + hxx_name, ctx)); + + hxx << "#ifndef " << guard << endl + << "#define " << guard << endl + << endl; + + // Copy prologue. + // + hxx << "// Begin prologue." << endl + << "//" << endl; + append (ctx, ops.hxx_prologue (), ops.hxx_prologue_file ()); + hxx << "//" << endl + << "// End prologue." << endl + << endl; + + { + // We don't want to indent prologues/epilogues. + // + cxx_filter filt (ctx.os); + + if (runtime) + generate_runtime_header (ctx); + + generate_header (ctx); + } + + if (inl) + { + hxx << "#include " << (br ? '<' : '"') << ip << ixx_name << + (br ? '>' : '"') << endl + << endl; + } + + // Copy epilogue. + // + hxx << "// Begin epilogue." << endl + << "//" << endl; + append (ctx, ops.hxx_epilogue (), ops.hxx_epilogue_file ()); + hxx << "//" << endl + << "// End epilogue." << endl + << endl; + + hxx << "#endif // " << guard << endl; + } + + // IXX + // + if (inl) + { + context ctx (ixx, context::ot_plain, unit, ops); + + // Copy prologue. + // + ixx << "// Begin prologue." << endl + << "//" << endl; + append (ctx, ops.ixx_prologue (), ops.ixx_prologue_file ()); + ixx << "//" << endl + << "// End prologue." << endl + << endl; + + { + // We don't want to indent prologues/epilogues. + // + cxx_filter filt (ctx.os); + + if (runtime) + generate_runtime_inline (ctx); + + generate_inline (ctx); + } + + // Copy epilogue. + // + ixx << "// Begin epilogue." << endl + << "//" << endl; + append (ctx, ops.ixx_epilogue (), ops.ixx_epilogue_file ()); + ixx << "//" << endl + << "// End epilogue." << endl; + } + + // CXX + // + { + context ctx (cxx, context::ot_plain, unit, ops); + + // Copy prologue. + // + cxx << "// Begin prologue." << endl + << "//" << endl; + append (ctx, ops.cxx_prologue (), ops.cxx_prologue_file ()); + cxx << "//" << endl + << "// End prologue." << endl + << endl; + + cxx << "#include " << (br ? '<' : '"') << ip << hxx_name << + (br ? '>' : '"') << endl + << endl; + + { + // We don't want to indent prologues/epilogues. + // + cxx_filter filt (ctx.os); + + if (runtime && !inl) + generate_runtime_inline (ctx); + + if (!ops.suppress_cli ()) + generate_runtime_source (ctx, runtime); + + if (!inl) + generate_inline (ctx); + + generate_source (ctx); + } + + // Copy epilogue. + // + cxx << "// Begin epilogue." << endl + << "//" << endl; + append (ctx, ops.cxx_epilogue (), ops.cxx_epilogue_file ()); + cxx << "//" << endl + << "// End epilogue." << endl + << endl; + } + } + + // man output + // + if (gen_man) + { + ofstream man; + + if (!ops.stdout_ ()) + { + path man_path (pfx + base + sfx + ops.man_suffix ()); + + if (!ops.output_dir ().empty ()) + man_path = path (ops.output_dir ()) / man_path; + + man.open (man_path.string ().c_str ()); + + if (!man.is_open ()) + { + cerr << "error: unable to open '" << man_path << "' in write mode" + << endl; + throw failed (); + } + + auto_rm.add (man_path); + } + + // The explicit cast helps VC++ 8.0 overcome its issues. + // + ostream& os (ops.stdout_ () ? cout : static_cast (man)); + context ctx (os, context::ot_man, unit, ops); + + for (bool first (true); first || ctx.toc; first = false) + { + append (ctx, ops.man_prologue (), ops.man_prologue_file ()); + generate_man (ctx); + append (ctx, ops.man_epilogue (), ops.man_epilogue_file ()); + + if (ctx.toc) + { + assert (first); // Second run should end in non-TOC mode. + ctx.toc++; // TOC phase after restart. + } + } + + ctx.verify_id_ref (); + } + + // HTML output + // + if (gen_html) + { + ofstream html; + + if (!ops.stdout_ ()) + { + // May have to update link derivation in format_line() if changing + // this. + // + path html_path (pfx + base + sfx + ops.html_suffix ()); + + if (!ops.output_dir ().empty ()) + html_path = path (ops.output_dir ()) / html_path; + + html.open (html_path.string ().c_str ()); + + if (!html.is_open ()) + { + cerr << "error: unable to open '" << html_path << "' in write mode" + << endl; + throw failed (); + } + + auto_rm.add (html_path); + } + + // The explicit cast helps VC++ 8.0 overcome its issues. + // + ostream& os (ops.stdout_ () ? cout : static_cast (html)); + context ctx (os, context::ot_html, unit, ops); + + for (bool first (true); first || ctx.toc; first = false) + { + append (ctx, ops.html_prologue (), ops.html_prologue_file ()); + generate_html (ctx); + append (ctx, ops.html_epilogue (), ops.html_epilogue_file ()); + + if (ctx.toc) + { + assert (first); // Second run should end in non-TOC mode. + ctx.toc++; // TOC phase after restart. + } + } + + ctx.verify_id_ref (); + } + + // txt output + // + if (gen_txt) + { + ofstream txt; + + if (!ops.stdout_ ()) + { + path txt_path (pfx + base + sfx + ops.txt_suffix ()); + + if (!ops.output_dir ().empty ()) + txt_path = path (ops.output_dir ()) / txt_path; + + txt.open (txt_path.string ().c_str ()); + + if (!txt.is_open ()) + { + cerr << "error: unable to open '" << txt_path << "' in write mode" + << endl; + throw failed (); + } + + auto_rm.add (txt_path); + } + + // The explicit cast helps VC++ 8.0 overcome its issues. + // + ostream& os (ops.stdout_ () ? cout : static_cast (txt)); + context ctx (os, context::ot_plain, unit, ops); + + for (bool first (true); first || ctx.toc; first = false) + { + append (ctx, ops.txt_prologue (), ops.txt_prologue_file ()); + generate_txt (ctx); + append (ctx, ops.txt_epilogue (), ops.txt_epilogue_file ()); + + if (ctx.toc) + { + assert (first); // Second run should end in non-TOC mode. + ctx.toc++; // TOC phase after restart. + } + } + + ctx.verify_id_ref (); + } + + 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" + << endl; + throw failed (); + } + catch (fs::error const&) + { + // Auto-removal of generated files failed. Ignore it. + // + throw failed (); + } +} diff --git a/cli/cli/generator.hxx b/cli/cli/generator.hxx new file mode 100644 index 0000000..f567528 --- /dev/null +++ b/cli/cli/generator.hxx @@ -0,0 +1,28 @@ +// file : cli/generator.hxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#ifndef CLI_GENERATOR_HXX +#define CLI_GENERATOR_HXX + +#include +#include + +class generator +{ +public: + generator (); + + class failed {}; + + void + generate (options&, semantics::cli_unit&, semantics::path const&); + +private: + generator (generator const&); + + generator& + operator= (generator const&); +}; + +#endif // CLI_GENERATOR_HXX diff --git a/cli/cli/header.cxx b/cli/cli/header.cxx new file mode 100644 index 0000000..a2a3ccd --- /dev/null +++ b/cli/cli/header.cxx @@ -0,0 +1,383 @@ +// file : cli/header.cxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#include + +using namespace std; + +namespace +{ + // + // + struct option: traversal::option, context + { + option (context& c) : context (c) {} + + virtual void + traverse (type& o) + { + string name (ename (o)); + string type (o.type ().name ()); + + os << "const " << type << "&" << endl + << name << " () const;" + << endl; + + if (gen_modifier) + { + os << type << "&" << endl + << name << " ();" + << endl; + + os << "void" << endl + << name << " (const " << type << "&);" + << endl; + } + + if (gen_specifier && type != "bool") + { + string spec (especifier (o)); + + os << "bool" << endl + << spec << " () const;" + << endl; + + if (gen_modifier) + os << "void" << endl + << spec << " (bool);" + << endl; + } + } + }; + + // + // + struct option_data: traversal::option, context + { + option_data (context& c) : context (c) {} + + virtual void + traverse (type& o) + { + string member (emember (o)); + string type (o.type ().name ()); + + os << type << " " << member << ";"; + + if (gen_specifier && type != "bool") + os << "bool " << especifier_member (o) << ";"; + } + }; + + // + // + 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) + : context (c), + option_ (c), + option_data_ (c) + { + names_option_ >> option_; + names_option_data_ >> option_data_; + } + + virtual void + traverse (type& c) + { + bool abst (c.abstract ()); + string name (escape (c.name ())); + string um (cli + "::unknown_mode"); + + os << "class " << name; + + { + base b (*this); + traversal::inherits i (b); + inherits (c, i); + } + + os << "{" + << "public:" << endl; + + // c-tors + // + if (!abst) + { + os << name << " ();" + << endl; + + // Are we generating parsing constructors or parse() functions? + // + string n; + if (gen_parse) + { + os << "// Return true if anything has been parsed." << endl + << "//" << endl; + + n = string ("bool\n") + (name != "parse" ? "parse" : "parse_"); + } + else + n = name; + + os << n << " (int& argc," << endl + << "char** argv," << endl + << "bool erase = false," << endl + << um << " option = " << um << "::fail," << endl + << um << " argument = " << um << "::stop);" + << endl; + + os << n << " (int start," << endl + << "int& argc," << endl + << "char** argv," << endl + << "bool erase = false," << endl + << um << " option = " << um << "::fail," << endl + << um << " argument = " << um << "::stop);" + << endl; + + os << n << " (int& argc," << endl + << "char** argv," << endl + << "int& end," << endl + << "bool erase = false," << endl + << um << " option = " << um << "::fail," << endl + << um << " argument = " << um << "::stop);" + << endl; + + os << n << " (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 << n << " (" << cli << "::scanner&," << endl + << um << " option = " << um << "::fail," << endl + << um << " argument = " << um << "::stop);" + << endl; + } + + + // Note that we are generating public merge() function even for abstract + // classes; theoretically, one may want to merge options only starting + // form a specific point in the inheritance hierarchy (e.g., only common + // options or some such). + // + if (gen_merge) + os << "// Merge options from the specified instance appending/overriding" << endl + << "// them as if they appeared after options in this instance." << endl + << "//" << endl + << "void" << endl + << "merge (const " << name << "&);" + << endl; + + // + // + os << "// Option accessors" << (gen_modifier ? " and modifiers." : ".") << endl + << "//" << endl; + + names (c, names_option_); + + // Usage. + // + if (gen_usage != ut_none) + { + string up (cli + "::usage_para"); + string const& ost (options.ostream_type ()); + + os << "// Print usage information." << endl + << "//" << endl; + + os << "static " << up << endl + << "print_usage (" << ost << "&," << endl + << up << " = " << up << "::none);" + << endl; + + if (gen_usage == ut_both) + os << "static " << up << endl + << "print_long_usage (" << ost << "&," << endl + << up << " = " << up << "::none);" + << endl; + } + + // Description. + // + if (options.generate_description ()) + { + os << "// Option description." << endl + << "//" << endl + << "static const " << cli << "::options&" << endl + << "description ();" + << endl; + } + + os << "// Implementation details." << endl + << "//" << endl + << "protected:" << endl; + + // default c-tor (abstract) + // + if (abst) + os << name << " ();" + << endl; + + // fill () + // + if (options.generate_description ()) + os << "friend struct _cli_" + name + "_desc_type;" + << endl + << "static void" << endl + << "fill (" << cli << "::options&);" + << endl; + + // _parse () + // + os << "bool" << endl + << "_parse (const char*, " << cli << "::scanner&);" + << endl; + + // _parse () + // + if (!abst) + os << "private:" << endl + << "bool" << endl + << "_parse (" << cli << "::scanner&," << endl + << um << " option," << endl + << um << " argument);" + << endl; + + // Data members. + // + os << "public:" << endl; //@@ tmp + + names (c, names_option_data_); + + os << "};"; + } + + private: + option option_; + traversal::names names_option_; + + option_data option_data_; + traversal::names names_option_data_; + }; + + // + // + struct includes_: traversal::cxx_includes, + traversal::cli_includes, + context + { + includes_ (context& c) : context (c) {} + + virtual void + traverse (semantics::cxx_includes& i) + { + generate (i.kind (), i.file ().string ()); + } + + virtual void + traverse (semantics::cli_includes& i) + { + generate (i.kind (), + (options.output_prefix () + + i.file ().base ().string () + + options.output_suffix () + + options.hxx_suffix ())); + } + + void + generate (semantics::includes::kind_type k, string const& f) + { + char b, e; + if (k == semantics::includes::quote) + b = e = '"'; + else + { + b = '<'; + e = '>'; + } + + os << "#include " << b << f << e << endl + << endl; + } + }; +} + +void +generate_header (context& ctx) +{ + ostream& os (ctx.os); + + traversal::cli_unit unit; + includes_ includes (ctx); + traversal::names unit_names; + namespace_ ns (ctx); + class_ cl (ctx); + + unit >> includes; + unit >> unit_names >> ns; + unit_names >> cl; + + traversal::names ns_names; + + ns >> ns_names >> ns; + ns_names >> cl; + + unit.dispatch (ctx.unit); + + // Entire page usage. + // + if (ctx.gen_usage != ut_none && ctx.options.page_usage_specified ()) + { + os << "// Print page usage information." << endl + << "//" << endl; + + const string& qn (ctx.options.page_usage ()); + string n (ctx.escape (ctx.substitute (ctx.ns_open (qn, false)))); + + string up (ctx.cli + "::usage_para"); + string const& ost (ctx.options.ostream_type ()); + + os << up << endl + << n << "usage (" << ost << "&," << endl + << up << " = " << up << "::none);" + << endl; + + if (ctx.gen_usage == ut_both) + os << up << endl + << n << "long_usage (" << ost << "&," << endl + << up << " = " << up << "::none);" + << endl; + + ctx.ns_close (qn, false); + } +} diff --git a/cli/cli/header.hxx b/cli/cli/header.hxx new file mode 100644 index 0000000..e6e68ee --- /dev/null +++ b/cli/cli/header.hxx @@ -0,0 +1,13 @@ +// file : cli/header.hxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#ifndef CLI_HEADER_HXX +#define CLI_HEADER_HXX + +#include + +void +generate_header (context&); + +#endif // CLI_HEADER_HXX diff --git a/cli/cli/html.cxx b/cli/cli/html.cxx new file mode 100644 index 0000000..b374b91 --- /dev/null +++ b/cli/cli/html.cxx @@ -0,0 +1,348 @@ +// file : cli/html.cxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#include +#include + +#include + +using namespace std; + +namespace +{ + static string + escape_html (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; + } + + static void + wrap_lines (ostream& os, const string& d, size_t indent) + { + size_t lim (78 - indent); + string ind (indent, ' '); + + bool nl (true); // True if last written to os character is a newline. + + size_t b (0), e (0), i (0); + for (size_t n (d.size ()); i < n; ++i) + { + // First handle
    .
    +      //
    +      if (d.compare (i, 5, "
    ") == 0)
    +      {
    +        // Write what might have already accumulated.
    +        //
    +        if (b != i)
    +        {
    +          if (nl)
    +            os << ind;
    +
    +          os << string (d, b, i - b);
    +          nl = false;
    +        }
    +
    +        // Output everything until (and including) closing 
    as is. + // + e = d.find ("
    ", i + 5); + assert (e != string::npos); + e += 6; // Now points past '>'. + + if (nl) + os << ind; + + os << string (d, i, e - i); + + b = e; + i = e - 1; // For ++i in loop header. + nl = false; + continue; + } + + if (d[i] == ' ' || d[i] == '\n') + e = i; + + if (d[i] == '\n' || (i - b >= lim && e != b)) + { + if (nl && b != e) + os << ind; + + os << string (d, b, e - b) << endl; + + b = e = e + 1; + nl = true; + } + } + + // Write the last line. + // + if (b != i) + { + if (nl) + os << ind; + + os << string (d, b, i - b); + } + } + + struct doc: traversal::doc, context + { + doc (context& c, class_doc_type cd, bool& l) + : context (c), cd_ (cd), list_ (l) {} + + virtual void + traverse (type& ds) + { + if (ds.name ().compare (0, 3, "doc") != 0) // Ignore doc variables. + return; + + // n = 1 - common doc string + // n = 2 - arg string, common doc string + // n > 2 - arg string, short string, long string + // + size_t n (ds.size ()); + const string& d ( + n == 1 + ? (cd_ == cd_short ? first_sentence (ds[0]) : ds[0]) + : (n == 2 + ? (cd_ == cd_short ? first_sentence (ds[1]) : ds[1]) + : ds[cd_ == cd_short ? 1 : 2])); + + std::set arg_set; + if (n > 1) + translate_arg (ds[0], arg_set); + + unsigned short t (toc); // Detect the switch to/from TOC mode. + + string s ( + format (ds.scope (), escape_html (translate (d, arg_set)), true)); + + if (s.empty ()) + return; + + if (list_) + { + os << " " << endl + << endl; + list_ = false; + } + + wrap_lines (os, s, (t || toc) ? 0 : 2); // TOC mode does its own thing. + + if (!toc) // TOC mode does its own thing. + os << endl + << endl; + } + + private: + class_doc_type cd_; + bool& list_; // True if we are currently in
    . + }; + + struct option: traversal::option, context + { + option (context& c, class_doc_type cd, bool& l) + : context (c), cd_ (cd), list_ (l) {} + + virtual void + traverse (type& o) + { + using semantics::names; + + semantics::doc_strings const& doc (o.doc ()); + + if (options.suppress_undocumented () && doc.empty ()) + return; + + if (toc) + return; // No option documentation in the TOC mode. + + if (!list_) + { + os << "
    " << endl; + list_ = true; + } + else + os << endl; // Separate from the previous
    . + + names& n (o.named ()); + + os << "
    "; + + for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i) + { + if (i != n.name_begin ()) + os << "|"; + + os << escape_html (*i); + } + + os << ""; + + string type (o.type ().name ()); + + std::set arg_set; + + if (type != "bool" || doc.size () >= 3) + { + string s ( + translate_arg ( + doc.size () > 0 ? doc[0] : string (""), arg_set)); + + os << ' ' << format (o.scope (), escape_html (s), false); + } + + os << "
    " << endl; + + string d; + if (type == "bool" && doc.size () < 3) + { + if (doc.size () > 1) + d = doc[cd_ == cd_short ? 0 : 1]; + else if (doc.size () > 0) + d = (cd_ == cd_short ? first_sentence (doc[0]) : doc[0]); + } + else + { + if (doc.size () > 2) + d = doc[cd_ == cd_short ? 1 : 2]; + else if (doc.size () > 1) + d = (cd_ == cd_short ? first_sentence (doc[1]) : doc[1]); + } + + // Format the documentation string. + // + d = format (o.scope (), escape_html (translate (d, arg_set)), false); + + wrap_lines (os, "
    " + d + "
    ", 4); + os << endl; + } + + private: + class_doc_type cd_; + bool& list_; // True if we are currently in
    . + }; + + // + // + struct class_: traversal::class_, context + { + class_ (context& c, bool& l): context (c), list_ (l), base_ (false) + { + *this >> inherits_ >> *this; + } + + virtual void + traverse (type& c) + { + class_doc_type cd (class_doc (c)); + + if (cd == cd_exclude || (base_ && cd == cd_exclude_base)) + return; + + if (!options.exclude_base () && !options.include_base_last ()) + { + bool ob (base_); + base_ = true; + inherits (c); + base_ = ob; + } + + doc dc (*this, cd, list_); + option op (*this, cd, list_); + traversal::names n; + n >> dc; + n >> op; + names (c, n); + + if (!options.exclude_base () && options.include_base_last ()) + { + bool ob (base_); + base_ = true; + inherits (c); + base_ = ob; + } + } + + private: + bool& list_; + bool base_; + traversal::inherits inherits_; + }; +} + +void +generate_html (context& ctx) +{ + bool list (false); + + traversal::cli_unit unit; + traversal::names unit_names; + traversal::namespace_ ns; + doc dc (ctx, cd_default, list); + class_ cl (ctx, list); + unit >> unit_names; + unit_names >> dc; + unit_names >> ns; + unit_names >> cl; + + traversal::names ns_names; + ns >> ns_names; + ns_names >> dc; + ns_names >> ns; + ns_names >> cl; + + if (ctx.options.class_ ().empty ()) + unit.dispatch (ctx.unit); + else + { + for (vector::const_iterator i (ctx.options.class_ ().begin ()); + i != ctx.options.class_ ().end (); ++i) + { + string n (*i); + + // Strip leading :: if present. + // + if (n.size () > 2 && n[0] == ':' && n[1] == ':') + n = string (n, 2, string::npos); + + if (semantics::class_* c = ctx.unit.lookup ("", n)) + cl.traverse (*c); + else + { + cerr << "error: class '" << *i << "' not found" << endl; + throw generation_failed (); + } + } + } + + if (list) + ctx.os << "
    " << endl + << endl; +} diff --git a/cli/cli/html.hxx b/cli/cli/html.hxx new file mode 100644 index 0000000..4ba5a41 --- /dev/null +++ b/cli/cli/html.hxx @@ -0,0 +1,13 @@ +// file : cli/html.hxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#ifndef CLI_HTML_HXX +#define CLI_HTML_HXX + +#include + +void +generate_html (context&); + +#endif // CLI_HTML_HXX diff --git a/cli/cli/inline.cxx b/cli/cli/inline.cxx new file mode 100644 index 0000000..05b83db --- /dev/null +++ b/cli/cli/inline.cxx @@ -0,0 +1,108 @@ +// file : cli/inline.cxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#include + +namespace +{ + // + // + struct option: traversal::option, context + { + option (context& c) : context (c) {} + + virtual void + traverse (type& o) + { + string name (ename (o)); + string type (o.type ().name ()); + string scope (escape (o.scope ().name ())); + + os << inl << "const " << type << "& " << scope << "::" << endl + << name << " () const" + << "{" + << "return this->" << emember (o) << ";" + << "}"; + + if (gen_modifier) + { + os << inl << type << "& " << scope << "::" << endl + << name << " ()" + << "{" + << "return this->" << emember (o) << ";" + << "}"; + + os << inl << "void " << scope << "::" << endl + << name << "(const " << type << "& x)" + << "{" + << "this->" << emember (o) << " = x;" + << "}"; + } + + if (gen_specifier && type != "bool") + { + string spec (especifier (o)); + + os << inl << "bool " << scope << "::" << endl + << spec << " () const" + << "{" + << "return this->" << especifier_member (o) << ";" + << "}"; + + if (gen_modifier) + os << inl << "void " << scope << "::" << endl + << spec << "(bool x)" + << "{" + << "this->" << especifier_member (o) << " = x;" + << "}"; + } + } + }; + + // + // + struct class_: traversal::class_, context + { + class_ (context& c) + : context (c), option_ (c) + { + names_option_ >> option_; + } + + virtual void + traverse (type& c) + { + string name (escape (c.name ())); + + os << "// " << name << endl + << "//" << endl + << endl; + + names (c, names_option_); + } + + private: + option option_; + traversal::names names_option_; + }; +} + +void +generate_inline (context& ctx) +{ + traversal::cli_unit unit; + traversal::names unit_names; + namespace_ ns (ctx); + class_ cl (ctx); + + unit >> unit_names >> ns; + unit_names >> cl; + + traversal::names ns_names; + + ns >> ns_names >> ns; + ns_names >> cl; + + unit.dispatch (ctx.unit); +} diff --git a/cli/cli/inline.hxx b/cli/cli/inline.hxx new file mode 100644 index 0000000..38e7768 --- /dev/null +++ b/cli/cli/inline.hxx @@ -0,0 +1,13 @@ +// file : cli/inline.hxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#ifndef CLI_INLINE_HXX +#define CLI_INLINE_HXX + +#include + +void +generate_inline (context&); + +#endif // CLI_INLINE_HXX diff --git a/cli/cli/lexer.cxx b/cli/cli/lexer.cxx new file mode 100644 index 0000000..573c76b --- /dev/null +++ b/cli/cli/lexer.cxx @@ -0,0 +1,604 @@ +// file : cli/lexer.cxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#include + +#include + +using namespace std; + +lexer:: +lexer (istream& is, string const& id) + : loc_ ("C"), + is_ (is), + id_ (id), + l_ (1), + c_(1), + eos_ (false), + include_ (false), + valid_ (true), + buf_ (0, 0, 0), + unget_ (false) +{ + keyword_map_["source"] = token::k_source; + keyword_map_["include"] = token::k_include; + keyword_map_["namespace"] = token::k_namespace; + keyword_map_["class"] = token::k_class; + keyword_map_["signed"] = token::k_signed; + keyword_map_["unsigned"] = token::k_unsigned; + keyword_map_["bool"] = token::k_bool; + keyword_map_["char"] = token::k_char; + keyword_map_["wchar_t"] = token::k_wchar; + keyword_map_["short"] = token::k_short; + keyword_map_["int"] = token::k_int; + keyword_map_["long"] = token::k_long; + keyword_map_["float"] = token::k_float; + keyword_map_["double"] = token::k_double; +} + +lexer::xchar lexer:: +peek () +{ + if (unget_) + return buf_; + else + { + if (eos_) + return xchar (xchar::traits_type::eof (), l_, c_); + else + { + xchar::int_type i (is_.peek ()); + + if (i == xchar::traits_type::eof ()) + eos_ = true; + + return xchar (i, l_, c_); + } + } +} + +lexer::xchar lexer:: +get () +{ + if (unget_) + { + unget_ = false; + return buf_; + } + else + { + // When is_.get () returns eof, the failbit is also set (stupid, + // isn't?) which may trigger an exception. To work around this + // we will call peek() first and only call get() if it is not + // eof. But we can only call peek() on eof once; any subsequent + // calls will spoil the failbit (even more stupid). + // + xchar c (peek ()); + + if (!is_eos (c)) + { + is_.get (); + + if (c == '\n') + { + l_++; + c_ = 1; + } + else + c_++; + } + + return c; + } +} + +void lexer:: +unget (xchar c) +{ + // Because iostream::unget cannot work once eos is reached, + // we have to provide our own implementation. + // + buf_ = c; + unget_ = true; +} + +token lexer:: +next () +{ + while (true) // Recovery loop. + { + bool include (include_); + include_ = false; + + skip_spaces (); + + xchar c (get ()); + + if (is_eos (c)) + return token (c.line (), c.column ()); + + try + { + switch (c) + { + case '\'': + { + return char_literal (c); + } + case '\"': + { + if (include) + return path_literal (c); + else + return string_literal (c); + } + case '<': + { + if (include) + return path_literal (c); + else + return template_expression (c); + } + case ';': + { + return token (token::p_semi, c.line (), c.column ()); + } + case ',': + { + return token (token::p_comma, c.line (), c.column ()); + } + case ':': + { + if (peek () == ':') + { + get (); + return token (token::p_dcolon, c.line (), c.column ()); + } + + return token (token::p_colon, c.line (), c.column ()); + } + case '{': + { + return token (token::p_lcbrace, c.line (), c.column ()); + } + case '}': + { + return token (token::p_rcbrace, c.line (), c.column ()); + } + case '(': + { + return call_expression (c); + } + case '=': + { + return token (token::p_eq, c.line (), c.column ()); + } + case '|': + { + return token (token::p_or, c.line (), c.column ()); + } + case '-': + { + // This can be a beginning of an identifier or a an integer + // literal. Figure out which one it is. + // + xchar p (peek ()); + + if (is_dec_digit (p)) + return int_literal (get (), true, c.line (), c.column ()); + else if (is_space (p)) + { + skip_spaces (); + p = peek (); + + if (is_dec_digit (p)) + return int_literal (get (), true, c.line (), c.column ()); + + // Stray '-'. + // + cerr << id_ << ':' << c.line () << ':' << c.column () + << ": error: unexpected character '-'" << endl; + throw invalid_input (); + } + + break; + } + } + + if (is_alpha (c) || c == '_' || c == '-' || c == '/') + { + return identifier (c); + } + + if (is_dec_digit (c)) + { + return int_literal (c); + } + + cerr << id_ << ':' << c.line () << ':' << c.column () + << ": error: unexpected character '" << c << "'" << endl; + throw invalid_input (); + } + catch (invalid_input const&) + { + valid_ = false; + } + + // Try to recover. + // + do + { + c = get (); + + if (is_eos (c)) + return token (c.line (), c.column ()); + } while (c != ';'); + } +} + +void lexer:: +skip_spaces () +{ + for (xchar c (peek ());; c = peek ()) + { + if (is_eos (c)) + break; + + if (c == '/') + { + c = get (); + xchar p (peek ()); + + if (p == '/') + { + get (); + + // C++ comment. Read until newline or eos. + // + for (c = get (); !is_eos (c) && c != '\n'; c = get ()) ; + continue; + } + else if (p == '*') + { + get (); + + // C comment. + // + for (c = get ();; c = get ()) + { + if (is_eos (c)) + { + cerr << id_ << ':' << c.line () << ':' << c.column () + << ": error: end of stream reached while reading " + << "C-style comment" << endl; + throw invalid_input (); + } + + if (c == '*') + { + c = peek (); + if (c == '/') + { + get (); + break; + } + } + } + continue; + } + else + { + unget (c); + break; + } + } + + if (!is_space (c)) + break; + + get (); + } +} + +token lexer:: +identifier (xchar c) +{ + size_t ln (c.line ()), cl (c.column ()); + string lexeme; + lexeme += c; + + bool check (c == '-' || c == '/'); + + for (c = peek (); + !is_eos (c) && (is_alnum (c) || c == '_' || c == '-'); + c = peek ()) + { + get (); + lexeme += c; + } + + // Check for invalid identifiers. + // + if (check) + { + size_t i (1); + + for (; i < lexeme.size (); ++i) + if (is_alnum (lexeme[i]) || lexeme[i] == '_') + break; + + if (i == lexeme.size ()) + { + cerr << id_ << ':' << c.line () << ':' << c.column () << ": error: " + << "invalid character sequence '" << lexeme << "'" << endl; + throw invalid_input (); + } + } + + keyword_map::const_iterator i (keyword_map_.find (lexeme)); + + if (i != keyword_map_.end ()) + { + if (i->second == token::k_include || i->second == token::k_source) + include_ = true; + + return token (i->second, ln, cl); + } + + if (lexeme == "true" || lexeme == "false") + return token (token::t_bool_lit, lexeme, ln, cl); + + return token (token::t_identifier, lexeme, ln, cl); +} + +token lexer:: +int_literal (xchar c, bool neg, size_t ml, size_t mc) +{ + size_t ln (neg ? ml : c.line ()), cl (neg ? mc : c.column ()); + string lexeme; + + if (neg) + lexeme += '-'; + + lexeme += c; + + for (c = peek (); !is_eos (c) && is_dec_digit (c); c = peek ()) + { + get (); + lexeme += c; + } + + return token (token::t_int_lit, lexeme, ln, cl); +} + +token lexer:: +char_literal (xchar c) +{ + size_t ln (c.line ()), cl (c.column ()); + string lexeme; + lexeme += c; + + char p (c); + + while (true) + { + c = get (); + + if (is_eos (c)) + { + cerr << id_ << ':' << c.line () << ':' << c.column () << ": error: " + << "end of stream reached while reading character literal" << endl; + throw invalid_input (); + } + + lexeme += c; + + if (c == '\'' && p != '\\') + break; + + // We need to keep track of \\ escapings so we don't confuse + // them with \', as in '\\'. + // + if (c == '\\' && p == '\\') + p = '\0'; + else + p = c; + } + + return token (token::t_char_lit, lexeme, ln, cl); +} + +token lexer:: +string_literal (xchar c) +{ + size_t ln (c.line ()), cl (c.column ()); + string lexeme; + lexeme += c; + + while (true) + { + lexeme += string_literal_trailer (); + + // Check if there are more strings. + // + skip_spaces (); + + c = peek (); + + if (is_eos (c) || c != '"') + break; + + get (); + lexeme += "\""; + } + + return token (token::t_string_lit, lexeme, ln, cl); +} + +string lexer:: +string_literal_trailer () +{ + string r; + char p ('\0'); + + while (true) + { + xchar c = get (); + + if (is_eos (c)) + { + cerr << id_ << ':' << c.line () << ':' << c.column () << ": error: " + << "end of stream reached while reading string literal" << endl; + throw invalid_input (); + } + + r += c; + + if (c == '"' && p != '\\') + break; + + // We need to keep track of \\ escapings so we don't confuse + // them with \", as in "\\". + // + if (c == '\\' && p == '\\') + p = '\0'; + else + p = c; + } + + return r; +} + +token lexer:: +path_literal (xchar c) +{ + size_t ln (c.line ()), cl (c.column ()); + string lexeme; + lexeme += c; + + char end (c == '<' ? '>' : '"'); + + while (true) + { + c = get (); + + if (is_eos (c)) + { + cerr << id_ << ':' << c.line () << ':' << c.column () << ": error: " + << "end of stream reached while reading path literal" << endl; + throw invalid_input (); + } + + lexeme += c; + + if (c == end) + break; + } + + token::token_type tt; + + if (lexeme.compare (1, 4, "c++:") == 0) + { + tt = token::t_cxx_path_lit; + lexeme = lexeme[0] + string (lexeme, 5, string::npos); + } + else if (lexeme.compare (1, 4, "cli:") == 0) + { + tt = token::t_cli_path_lit; + lexeme = lexeme[0] + string (lexeme, 5, string::npos); + } + else + { + // See if the path ends with .cli. If not, then we assume this is + // a C++ inclusion. + // + size_t n (lexeme.size ()); + + if (n > 5 && lexeme.compare (n - 5, 4, ".cli") == 0) + tt = token::t_cli_path_lit; + else + tt = token::t_cxx_path_lit; + } + + return token (tt, lexeme, ln, cl); +} + +token lexer:: +call_expression (xchar c) +{ + size_t ln (c.line ()), cl (c.column ()); + string lexeme; + lexeme += c; + size_t balance (1); + + while (balance != 0) + { + c = get (); + + if (is_eos (c)) + { + cerr << id_ << ':' << c.line () << ':' << c.column () << ": error: " + << "end of stream reached while reading call expression" << endl; + throw invalid_input (); + } + + lexeme += c; + + switch (c) + { + case '(': + { + balance++; + break; + } + case ')': + { + balance--; + break; + } + } + } + + return token (token::t_call_expr, lexeme, ln, cl); +} + +token lexer:: +template_expression (xchar c) +{ + size_t ln (c.line ()), cl (c.column ()); + string lexeme; + lexeme += c; + size_t balance (1); + + while (balance != 0) + { + c = get (); + + if (is_eos (c)) + { + cerr << id_ << ':' << c.line () << ':' << c.column () << ": error: " + << "end of stream reached while reading template expression" + << endl; + throw invalid_input (); + } + + lexeme += c; + + switch (c) + { + case '<': + { + balance++; + break; + } + case '>': + { + balance--; + break; + } + } + } + + return token (token::t_template_expr, lexeme, ln, cl); +} diff --git a/cli/cli/lexer.hxx b/cli/cli/lexer.hxx new file mode 100644 index 0000000..bd7b0c9 --- /dev/null +++ b/cli/cli/lexer.hxx @@ -0,0 +1,142 @@ +// file : cli/lexer.hxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#ifndef CLI_LEXER_HXX +#define CLI_LEXER_HXX + +#include +#include +#include +#include // std::size_t +#include + +#include + +class lexer +{ +public: + lexer (std::istream& is, std::string const& id); + + token + next (); + + bool + valid () const; + +protected: + class xchar + { + public: + typedef std::char_traits traits_type; + typedef traits_type::int_type int_type; + typedef traits_type::char_type char_type; + + xchar (int_type v, std::size_t l, std::size_t c); + + operator char_type () const; + + int_type + value () const; + + std::size_t + line () const; + + std::size_t + column () const; + + private: + int_type v_; + std::size_t l_; + std::size_t c_; + }; + + xchar + peek (); + + xchar + get (); + + void + unget (xchar); + +protected: + class invalid_input {}; + + void + skip_spaces (); + + token + identifier (xchar); + + token + int_literal (xchar, + bool neg = false, + std::size_t ml = 0, + std::size_t mc = 0); + + token + char_literal (xchar); + + token + string_literal (xchar); + + std::string + string_literal_trailer (); + + token + path_literal (xchar); + + token + call_expression (xchar); + + token + template_expression (xchar); + +protected: + bool + is_alpha (char c) const; + + bool + is_oct_digit (char c) const; + + bool + is_dec_digit (char c) const; + + bool + is_hex_digit (char c) const; + + bool + is_alnum (char c) const; + + bool + is_space (char c) const; + + bool + is_eos (xchar const& c) const; + + char + to_upper (char c) const; + +private: + typedef std::map keyword_map; + + std::locale loc_; + std::istream& is_; + std::string id_; + std::size_t l_; + std::size_t c_; + + keyword_map keyword_map_; + + bool eos_; + bool include_; // Literal in include or source. + bool valid_; + + xchar buf_; + bool unget_; +}; + +#include + +#endif // CLI_LEXER_HXX diff --git a/cli/cli/lexer.ixx b/cli/cli/lexer.ixx new file mode 100644 index 0000000..1c4c42e --- /dev/null +++ b/cli/cli/lexer.ixx @@ -0,0 +1,91 @@ +// file : cli/lexer.ixx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +// lexer::xchar +// +inline lexer::xchar:: +xchar (int_type v, std::size_t l, std::size_t c) + : v_ (v), l_ (l), c_ (c) +{ +} + +inline lexer::xchar:: +operator char_type () const +{ + return traits_type::to_char_type (v_); +} + +inline lexer::xchar::int_type lexer::xchar:: +value () const +{ + return v_; +} + +inline std::size_t lexer::xchar:: +line () const +{ + return l_; +} + +inline std::size_t lexer::xchar:: +column () const +{ + return c_; +} + +// lexer +// +inline bool lexer:: +valid () const +{ + return valid_; +} + +inline bool lexer:: +is_alpha (char c) const +{ + return std::isalpha (c, loc_); +} + +inline bool lexer:: +is_oct_digit (char c) const +{ + return std::isdigit (c, loc_) && c != '8' && c != '9'; +} + +inline bool lexer:: +is_dec_digit (char c) const +{ + return std::isdigit (c, loc_); +} + +inline bool lexer:: +is_hex_digit (char c) const +{ + return std::isxdigit (c, loc_); +} + +inline bool lexer:: +is_alnum (char c) const +{ + return std::isalnum (c, loc_); +} + +inline bool lexer:: +is_space (char c) const +{ + return std::isspace (c, loc_); +} + +inline bool lexer:: +is_eos (xchar const& c) const +{ + return c.value () == xchar::traits_type::eof (); +} + +inline char lexer:: +to_upper (char c) const +{ + return std::toupper (c, loc_); +} diff --git a/cli/cli/lexer.test.cxx b/cli/cli/lexer.test.cxx new file mode 100644 index 0000000..0eb4dcb --- /dev/null +++ b/cli/cli/lexer.test.cxx @@ -0,0 +1,122 @@ +// file : cli/lexer.test.cxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#include +#include + +#include +#include + +using namespace std; + +const char* keywords[] = +{ + "source", + "include", + "namespace", + "class", + "signed", + "unsigned", + "bool", + "char", + "wchar_t", + "short", + "int", + "long", + "float", + "double" +}; + +const char* punctuation[] = { + ";", ",", ":", "::", "{", "}", /*"(", ")",*/ "=", "|"}; + +int +main (int argc, char* argv[]) +{ + if (argc != 2) + { + cerr << "usage: " << argv[0] << " file.cli" << endl; + return 1; + } + + ifstream ifs; + ifs.exceptions (ifstream::failbit | ifstream::badbit); + ifs.open (argv[1]); + + lexer l (ifs, argv[1]); + + while (true) + { + token t (l.next ()); + + switch (t.type ()) + { + case token::t_eos: + { + cout << "" << endl; + return 0; + } + case token::t_keyword: + { + cout << "keyword: " << keywords[t.keyword ()] << endl; + break; + } + case token::t_identifier: + { + cout << "identifier: " << t.identifier () << endl; + break; + } + case token::t_punctuation: + { + cout << punctuation[t.punctuation ()] << endl; + break; + } + case token::t_cxx_path_lit: + { + cout << "c++ path: " << t.literal () << endl; + break; + } + case token::t_cli_path_lit: + { + cout << "cli path: " << t.literal () << endl; + break; + } + case token::t_string_lit: + { + cout << t.literal () << endl; + break; + } + case token::t_char_lit: + { + cout << t.literal () << endl; + break; + } + case token::t_bool_lit: + { + cout << t.literal () << endl; + break; + } + case token::t_int_lit: + { + cout << t.literal () << endl; + break; + } + case token::t_float_lit: + { + cout << t.literal () << endl; + break; + } + case token::t_call_expr: + { + cout << t.expression () << endl; + break; + } + case token::t_template_expr: + { + cout << t.expression () << endl; + break; + } + } + } +} diff --git a/cli/cli/lexer.test.testscript b/cli/cli/lexer.test.testscript new file mode 100644 index 0000000..e2df5f6 --- /dev/null +++ b/cli/cli/lexer.test.testscript @@ -0,0 +1,191 @@ +# file : cli/lexer.test.testscript +# license : MIT; see accompanying LICENSE file + +# @@ Give tests some meaningfull descriptions. +# + +: 000 +: +cat <=test.cli; +help +help-me +-h +--help +--help-me +--help-me- +/h +/help-me +/help/me +--_ + +EOI +$* test.cli >>EOO +identifier: help +identifier: help-me +identifier: -h +identifier: --help +identifier: --help-me +identifier: --help-me- +identifier: /h +identifier: /help-me +identifier: /help +identifier: /me +identifier: --_ + +EOO + +: 001 +: +cat <=test.cli; +5 +123456 +-12345 +- 1 +- +123 +EOI +$* test.cli >>EOO +5 +123456 +-12345 +-1 +-123 + +EOO + +: 002 +: +cat <=test.cli; +'a' +'\n' +'\\' +'\0' +'\'' +'\xaf' +'\111' +EOI +$* test.cli >>EOO +'a' +'\n' +'\\' +'\0' +'\'' +'\xaf' +'\111' + +EOO + +: 003 +: +cat <=test.cli; +"abc"; +"a\nb"; +"abc\\"; +"aaa\0"; +"\""; +"a\xaf"; +"a\111"; +"abc""def"; +"abc" "def"; +"abc + def + + xyz"; +EOI +$* test.cli >>EOO +"abc" +; +"a\nb" +; +"abc\\" +; +"aaa\0" +; +"\"" +; +"a\xaf" +; +"a\111" +; +"abc""def" +; +"abc""def" +; +"abc + def + + xyz" +; + +EOO + +: 004 +: +cat <=test.cli; +include "foo/abc.hxx"; +include ; +include "c++:map"; +include ; +include "map.cli" +EOI +$* test.cli >>EOO +keyword: include +c++ path: "foo/abc.hxx" +; +keyword: include +c++ path: +; +keyword: include +c++ path: "map" +; +keyword: include +cli path: +; +keyword: include +cli path: "map.cli" + +EOO + +: 005 +: +cat <=test.cli; +(abc, 123 - 345, 12.34) + +EOI +$* test.cli >>EOO +(abc, 123 - 345, 12.34) + + +EOO + +: 006 +: +cat <=test.cli; +// c++ comment ; +/* c comment ; */ +; +"a" // foo +"b" +"a" /* foo +bar +baz */ "b"; +- // aaa +5; +- /* a +a +a*/ 5 +// eos +: +:: +EOI +$* test.cli >>EOO +; +"a""b""a""b" +; +-5 +; +-5 +: +:: + +EOO diff --git a/cli/cli/man.cxx b/cli/cli/man.cxx new file mode 100644 index 0000000..df703e8 --- /dev/null +++ b/cli/cli/man.cxx @@ -0,0 +1,278 @@ +// file : cli/man.cxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#include +#include + +#include + +using namespace std; + +namespace +{ + // According to groff_mdoc(7), groff may have issues with any of the + // following characters at the beginning of the line: + // + // {}+-/*%<>=,&`'" + // + // Plus, escaping leading '.' with '\' is not sufficient. + // + static const string escape ("{}+-/*%<>=,&`'\""); + + static string + escape_line (const string& s, size_t b, size_t e) + { + string r; + size_t n (e - b); + + + if (escape.find (s[b]) != string::npos || + (n > 1 && s[b] == '\\' && s[b + 1] == '.')) + r = "\\&"; + + r.append (s, b, n); + return r; + } + + static void + wrap_lines (ostream& os, const string& d) + { + size_t b (0), e (0), i (0); + for (size_t n (d.size ()); i < n; ++i) + { + // First handle preformatted text (.nf/.fi). + // + if (d.compare (i, 4, ".nf\n") == 0 && (i == 0 || d[i - 1] == '\n')) + { + assert (b == i); // We should have nothing accumulated. + + // Output everything until (and including) closing .fi as is. + // + e = d.find ("\n.fi", i + 4); + assert (e != string::npos); + e += 4; // Now points past 'i'. + + os << string (d, i, e - i); + + b = e; + i = e - 1; // For ++i in loop header. + continue; + } + + if (d[i] == ' ' || d[i] == '\n') + e = i; + + if (d[i] == '\n' || (i - b >= 78 && e != b)) + { + os << escape_line (d, b, e) << endl; + b = e = e + 1; + } + } + + // Flush the last line. + // + if (b != i) + os << escape_line (d, b, i); + } + + struct doc: traversal::doc, context + { + doc (context& c, class_doc_type cd): context (c), cd_ (cd) {} + + virtual void + traverse (type& ds) + { + if (ds.name ().compare (0, 3, "doc") != 0) // Ignore doc variables. + return; + + // n = 1 - common doc string + // n = 2 - arg string, common doc string + // n > 2 - arg string, short string, long string + // + size_t n (ds.size ()); + const string& d ( + n == 1 + ? (cd_ == cd_short ? first_sentence (ds[0]) : ds[0]) + : (n == 2 + ? (cd_ == cd_short ? first_sentence (ds[1]) : ds[1]) + : ds[cd_ == cd_short ? 1 : 2])); + + std::set arg_set; + if (n > 1) + translate_arg (ds[0], arg_set); + + string s (format (ds.scope (), translate (d, arg_set), true)); + + if (s.empty ()) + return; + + wrap_lines (os, s); + os << endl; + } + + private: + class_doc_type cd_; + }; + + struct option: traversal::option, context + { + option (context& c, class_doc_type cd) : context (c), cd_ (cd) {} + + virtual void + traverse (type& o) + { + using semantics::names; + + semantics::doc_strings const& doc (o.doc ()); + + if (options.suppress_undocumented () && doc.empty ()) + return; + + names& n (o.named ()); + + os << ".IP \"\\fB"; + + for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i) + { + if (i != n.name_begin ()) + os << "\\fR|\\fB"; + + os << *i; + } + + os << "\\fR"; + + string type (o.type ().name ()); + + std::set arg_set; + + if (type != "bool" || doc.size () >= 3) + { + string s ( + translate_arg ( + doc.size () > 0 ? doc[0] : string (""), arg_set)); + + os << ' ' << format (o.scope (), s, false); + } + + os << "\"" << endl; + + string d; + if (type == "bool" && doc.size () < 3) + { + if (doc.size () > 1) + d = doc[cd_ == cd_short ? 0 : 1]; + else if (doc.size () > 0) + d = (cd_ == cd_short ? first_sentence (doc[0]) : doc[0]); + } + else + { + if (doc.size () > 2) + d = doc[cd_ == cd_short ? 1 : 2]; + else if (doc.size () > 1) + d = (cd_ == cd_short ? first_sentence (doc[1]) : doc[1]); + } + + // Format the documentation string. + // + d = format (o.scope (), translate (d, arg_set), false); + + wrap_lines (os, d); + os << endl; + } + + private: + class_doc_type cd_; + }; + + // + // + struct class_: traversal::class_, context + { + class_ (context& c): context (c), base_ (false) + { + *this >> inherits_ >> *this; + } + + virtual void + traverse (type& c) + { + class_doc_type cd (class_doc (c)); + + if (cd == cd_exclude || (base_ && cd == cd_exclude_base)) + return; + + if (!options.exclude_base () && !options.include_base_last ()) + { + bool ob (base_); + base_ = true; + inherits (c); + base_ = ob; + } + + doc dc (*this, cd); + option op (*this, cd); + traversal::names n; + n >> dc; + n >> op; + names (c, n); + + if (!options.exclude_base () && options.include_base_last ()) + { + bool ob (base_); + base_ = true; + inherits (c); + base_ = ob; + } + } + + private: + bool base_; + traversal::inherits inherits_; + }; +} + +void +generate_man (context& ctx) +{ + traversal::cli_unit unit; + traversal::names unit_names; + traversal::namespace_ ns; + doc dc (ctx, cd_default); + class_ cl (ctx); + unit >> unit_names; + unit_names >> ns; + unit_names >> dc; + unit_names >> cl; + + traversal::names ns_names; + ns >> ns_names; + ns_names >> ns; + ns_names >> dc; + ns_names >> cl; + + if (ctx.options.class_ ().empty ()) + unit.dispatch (ctx.unit); + else + { + for (vector::const_iterator i (ctx.options.class_ ().begin ()); + i != ctx.options.class_ ().end (); ++i) + { + string n (*i); + + // Strip leading :: if present. + // + if (n.size () > 2 && n[0] == ':' && n[1] == ':') + n = string (n, 2, string::npos); + + if (semantics::class_* c = ctx.unit.lookup ("", n)) + cl.traverse (*c); + else + { + cerr << "error: class '" << *i << "' not found" << endl; + throw generation_failed (); + } + } + } +} diff --git a/cli/cli/man.hxx b/cli/cli/man.hxx new file mode 100644 index 0000000..0825305 --- /dev/null +++ b/cli/cli/man.hxx @@ -0,0 +1,13 @@ +// file : cli/man.hxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#ifndef CLI_MAN_HXX +#define CLI_MAN_HXX + +#include + +void +generate_man (context&); + +#endif // CLI_MAN_HXX diff --git a/cli/cli/name-processor.cxx b/cli/cli/name-processor.cxx new file mode 100644 index 0000000..ab125bc --- /dev/null +++ b/cli/cli/name-processor.cxx @@ -0,0 +1,193 @@ +// file : cli/name-processor.cxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#include +#include + +#include +#include + +using namespace std; + +namespace +{ + typedef set name_set; + typedef ::context context_base; + + struct context: context_base + { + context (context_base& c): context_base (c) {} + context (context& c): context_base (c) {} + + public: + string + find_name (string const& n, string const& suffix, name_set& set) + { + string name (escape (n + suffix)); + + for (size_t i (1); set.find (name) != set.end (); ++i) + { + ostringstream os; + os << i; + name = escape (n + os.str () + suffix); + } + + set.insert (name); + return name; + } + + string + find_name (string const& n, name_set& set) + { + return find_name (n, "", set); + } + }; + + struct primary_option: traversal::option, context + { + primary_option (context& c, name_set& set) + : context (c), set_ (set) + { + } + + virtual void + traverse (type& o) + { + string n (o.name ()), name; + + // Get rid of leading special characters, e.f., -, --, /, etc. + // + for (size_t i (0); i < n.size (); ++i) + { + if (isalpha (n[i]) || n[i] == '_') + { + name.assign (n.c_str (), i, n.size () - i); + break; + } + } + + o.context ().set ("name", find_name (name, set_)); + } + + private: + name_set& set_; + }; + + struct intermediate_option: traversal::option, context + { + intermediate_option (context& c, name_set& set) + : context (c), set_ (set) + { + } + + virtual void + traverse (type& o) + { + if (gen_specifier && o.type ().name () != "bool") + { + semantics::context& oc (o.context ()); + string const& base (oc.get ("name")); + oc.set ("specifier", find_name (base + "_specified", set_)); + } + } + + private: + name_set& set_; + }; + + struct secondary_option: traversal::option, context + { + secondary_option (context& c, name_set& set) + : context (c), set_ (set) + { + } + + virtual void + traverse (type& o) + { + semantics::context& oc (o.context ()); + string const& base (oc.get ("name")); + oc.set ("member", find_name (base + "_", set_)); + + if (gen_specifier && o.type ().name () != "bool") + { + string const& base (oc.get ("specifier")); + oc.set ("specifier-member", find_name (base + "_", set_)); + } + } + + private: + name_set& set_; + }; + + struct class_: traversal::class_, context + { + class_ (context& c) : context (c) {} + + virtual void + traverse (type& c) + { + semantics::context& cc (c.context ()); + + cc.set ("member-name-set", name_set ()); + name_set& member_set (cc.get ("member-name-set")); + + member_set.insert (escape (c.name ())); + + // First assign primary names. + // + { + primary_option option (*this, member_set); + traversal::names names (option); + + class_::names (c, names); + } + + // Then assign intermediate names. + // + { + intermediate_option option (*this, member_set); + traversal::names names (option); + + class_::names (c, names); + } + + // Finally assign secondary names. + // + { + secondary_option option (*this, member_set); + traversal::names names (option); + + class_::names (c, names); + } + } + }; + + void + process_names_ (context_base& c) + { + context ctx (c); + + traversal::cli_unit unit; + traversal::names unit_names; + traversal::namespace_ ns; + class_ cl (ctx); + + unit >> unit_names >> ns; + unit_names >> cl; + + traversal::names ns_names; + + ns >> ns_names >> ns; + ns_names >> cl; + + unit.dispatch (ctx.unit); + } +} + +void +process_names (context_base& c) +{ + process_names_ (c); +} diff --git a/cli/cli/name-processor.hxx b/cli/cli/name-processor.hxx new file mode 100644 index 0000000..c21561b --- /dev/null +++ b/cli/cli/name-processor.hxx @@ -0,0 +1,13 @@ +// file : cli/name-processor.hxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#ifndef CLI_NAME_PROCESSOR_HXX +#define CLI_NAME_PROCESSOR_HXX + +#include + +void +process_names (context&); + +#endif // CLI_NAME_PROCESSOR_HXX diff --git a/cli/cli/option-types.cxx b/cli/cli/option-types.cxx new file mode 100644 index 0000000..da1f434 --- /dev/null +++ b/cli/cli/option-types.cxx @@ -0,0 +1,43 @@ +// file : cli/option-types.cxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#include + +#include + +using namespace std; + +static const char* cxx_version_[] = +{ + "c++98", + "c++11" + "c++14" +}; + +string cxx_version:: +string () const +{ + return cxx_version_[v_]; +} + +istream& +operator>> (istream& is, cxx_version& v) +{ + string s; + is >> s; + + if (!is.fail ()) + { + if (s == "c++98") + v = cxx_version::cxx98; + else if (s == "c++11") + v = cxx_version::cxx11; + else if (s == "c++14") + v = cxx_version::cxx14; + else + is.setstate (istream::failbit); + } + + return is; +} diff --git a/cli/cli/option-types.hxx b/cli/cli/option-types.hxx new file mode 100644 index 0000000..02d9ede --- /dev/null +++ b/cli/cli/option-types.hxx @@ -0,0 +1,33 @@ +// file : cli/option-types.hxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#ifndef CLI_OPTION_TYPES_HXX +#define CLI_OPTION_TYPES_HXX + +#include +#include + +struct cxx_version +{ + enum value + { + cxx98, + cxx11, + cxx14 + }; + + cxx_version (value v = value (0)) : v_ (v) {} + operator value () const {return v_;} + + std::string + string () const; + +private: + value v_; +}; + +std::istream& +operator>> (std::istream&, cxx_version&); + +#endif // CLI_OPTION_TYPES_HXX diff --git a/cli/cli/options.cli b/cli/cli/options.cli new file mode 100644 index 0000000..e564bc4 --- /dev/null +++ b/cli/cli/options.cli @@ -0,0 +1,662 @@ +// file : cli/options.cli +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +// NOTE: Make sure you have a working CLI compiler around before +// modifying this file. +// + +include ; +include ; +include ; +include ; + +include ; + +class options +{ + bool --build2-metadata; // Leave undocumented/hidden. + + bool --help {"Print usage information and exit."}; + bool --version {"Print version and exit."}; + + std::vector --include-path | -I + { + "", + "Search for bracket-included (\cb{<>}) options files." + }; + + std::string --output-dir | -o + { + "", + "Write the generated files to instead of the current directory." + }; + + cxx_version --std = cxx_version::cxx98 + { + "", + "Specify the C++ standard that should be used during compilation. + Valid values are \cb{c++98} (default), \cb{c++11}, and \cb{c++14}." + }; + + bool --generate-modifier + { + "Generate option value modifiers in addition to accessors." + }; + + bool --generate-specifier + { + "Generate functions for determining whether the option was specified + on the command line." + }; + + bool --generate-parse + { + "Generate \cb{parse()} functions instead of parsing constructors. This is + primarily useful for being able to parse into an already initialized + options class instance, for example, to implement option + appending/overriding." + }; + + bool --generate-merge + { + "Generate \cb{merge()} functions. This is primarily useful for being able + to merge several already parsed options class instances, for example, to + implement option appending/overriding. Note that this option forces + \cb{--generate-specifier}." + }; + + bool --generate-description + { + "Generate the option description list that can be examined at runtime." + }; + + bool --generate-file-scanner + { + "Generate the \cb{argv_file_scanner} implementation. This scanner is + capable of reading command line arguments from the \cb{argv} array as + well as files specified with command line options." + }; + + bool --generate-vector-scanner + { + "Generate the \cb{vector_scanner} implementation. This scanner is capable + of reading command line arguments from \cb{vector}." + }; + + bool --generate-group-scanner + { + "Generate the \cb{group_scanner} implementation. This scanner supports + grouping of arguments (usually options) to apply only to a certain + argument. + + Groups can be specified before (leading) and/or after (trailing) the + argument they apply to. A leading group starts with '\cb{{}' and ends + with '\cb{\}+}' while a trailing group starts with '\cb{+{}' and ends + with '\cb{\}}'. For example: + + \ + { --foo --bar }+ arg # 'arg' with '--foo' '--bar' + arg +{ fox=1 baz=2 } # 'arg' with 'fox=1' 'baz=2' + \ + + Multiple leading and/or trailing groups can be specified for the + same argument. For example: + + \ + { -f }+ { -b }+ arg +{ f=1 } +{ b=2 } # 'arg' with '-f' 'b' 'f=1' 'b=2' + \ + + Note that the group applies to a single argument only. For example: + + \ + { --foo }+ arg1 arg2 +{ --bar } # 'arg1' with '--foo' and + # 'arg2' with '--bar' + \ + + The group separators ('\cb{{}', '\cb{\}+'}, etc) must be separate command + line arguments. In particular, they must not be adjacent either to the + arguments inside the group nor to the argument they apply to. All such + cases will be treated as ordinary arguments. For example: + + \ + {--foo}+ arg # '{--foo}+' ... + arg+{ --foo } # 'arg+{' ... + \ + + If one of the group separators needs to be specified as an argument + verbatim, then it must be escaped with '\cb{\\}'. For example: + + \ + } # error: unexpected group separator + }x # '}x' + \} # '}' + { \}+ }+ arg # 'arg' with '}+' + \ + " + }; + + bool --suppress-inline + { + "Generate all functions non-inline. By default simple functions are + made inline. This option suppresses creation of the inline file." + }; + + bool --suppress-cli + { + "Do not generate the CLI support types (scanners, parser, etc). Normally, + the support types are generated unless another \cb{.cli} was included, + in which case the support types are expected to be provided by its + generated code." + }; + + std::string --cli-namespace = "::cli" + { + "", + "Generate the CLI support types in the namespace (\cb{cli} by + default). The namespace can be nested, for example \cb{details::cli}. + If the namespace is empty, then the support types are generated in + the global namespace." + }; + + std::string --ostream-type = "::std::ostream" + { + "", + "Output stream type instead of the default \cb{std::ostream} that + should be used to print usage and exception information." + }; + + bool --generate-cxx + { + "Generate C++ code. If neither \cb{--generate-man}, \cb{--generate-html}, + nor \cb{--generate-txt} is specified, this mode is assumed by default." + }; + + bool --generate-man + { + "Generate documentation in the man page format." + }; + + bool --generate-html + { + "Generate documentation in the HTML format." + }; + + bool --generate-txt + { + "Generate documentation in the plain text format, similar to usage." + }; + + bool --stdout + { + "Write output to STDOUT instead of a file. This option is not valid + when generating C++ code and is normally used to combine generated + documentation for several option classes in a single file." + }; + + bool --suppress-undocumented + { + "Suppress the generation of documentation entries for undocumented + options." + }; + + bool --suppress-usage + { + "Suppress the generation of the usage printing code." + }; + + bool --long-usage + { + "If no short documentation string is provided, use the complete + long documentation string in usage. By default, in this situation + only the first sentence from the long string is used." + }; + + bool --short-usage + { + "If specified together with \cb{--long-usage}, generate both short + and long usage versions. In this mode, the long usage printing function + is called \cb{print_long_usage()} and in its implementation the long + documentation string is always used, even if the short version is + provided." + }; + + std::string --page-usage + { + "", + "Generate the combined usage printing code for the entire page. + Specifically, this will include all the namespace-level documentation as + well as usage for all the options classes printed in the order they are + defined in the main translation unit (documentation/classes from included + units are ignored except for base classes). + + The argument is used as a prefix to form the name of the usage + printing function. It can include the namespace qualification as well + as documentation variable expansion, for example: + + \ + --page-usage print_ # print_usage() in global namespace + --page-usage app::print_ # print_usage() in app namespace + --page-usage print_$name$_ # print_foo_usage() if name is foo + \ + + If both \cb{--long-usage} and \cb{--short-usage} options are specified, + then the long usage function has the \cb{*long_usage()} suffix." + }; + + std::size_t --option-length = 0 + { + "", + "Indent option descriptions 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." + }; + + bool --ansi-color + { + "Use ANSI color escape sequences when printing usage. By \"color\" we + really only mean the bold and underline modifiers. Note that Windows + console does not recognize ANSI escape sequences and will display + them as garbage. However, if you pipe such output through \cb{less(1)}, + it will display them correctly." + }; + + bool --exclude-base + { + "Exclude base class information from usage and documentation." + }; + + bool --include-base-last + { + "Include base class information after derived for usage and documentation. + By default, base classes are included first." + }; + + std::map --class-doc + { + "=", + "Specify the documentation that should be used for the options class + . The value should be a fully-qualified class name, for + example, \cb{app::options}. The value can be \cb{short}, + \cb{long}, \cb{exclude}, or \cb{exclude-base}. If the value is + \cb{exclude}, then the class documentation is excluded from usage and + man/HTML/text output. If it is \cb{exclude-base}, then it is only + excluded when used as a base. For usage, the \cb{short} and \cb{long} + values determine which usage function will be called when the class is + used as base or as part of the page usage (see \cb{--page-usage}). For + man/HTML/text, these values determine which documentation strings are + used in the output." + }; + + std::vector --class + { + "", + "Generate the man page, HTML, or text documentation only for the options + class . The value should be a fully-qualified options class + name, for example, \cb{app::options}. To generate documentation for + multiple classes, repeat this option and the documentation will be + produced in the order specified. This functionality is useful if you need + to assemble documentation from multiple classes in a specific order or to + insert custom documentation between options belonging to different + classes." + }; + + std::map --docvar|-v + { + "=", + "Set documentation variable to the value . Documentation + variables can be substituted in prologues and epilogues (see + \cb{--*-prologue*} and \cb{--*-epilogue*} options) using the + \cb{$}\cb{$} expansion syntax (use \cb{$$} to escape expansion). + They can also be defined in \cb{.cli} files using the + \c{\"\\=\"} syntax." + }; + + std::vector --link-regex + { + "", + "Add to the list of regular expressions used to transform link + targets in the generated documentation. The argument to this option + is a Perl-like regular expression in the form + \c{\b{/}\i{pattern}\b{/}\i{replacement}\b{/}}. Any character can be + used as a delimiter instead of '\cb{/}' and the delimiter can be escaped + inside \ci{pattern} and \ci{replacement} with a backslash (\cb{\\}). + You can specify multiple regular expressions by repeating this option. + All the regular expressions are tried in the order specified and the + first expression that matches is used. Use the \cb{--link-regex-trace} + option to debug link transformation." + }; + + bool --link-regex-trace + { + "Trace the process of applying regular expressions specified with the + \cb{--link-regex} option. Use this option to find out why your regular + expressions don't do what you expected them to do." + }; + + std::map --html-heading-map + { + "=", + "Map CLI heading (valid values: '\cb{H}', '\cb{0}', '\cb{1}', + '\cb{h}', and '\cb{2}') to HTML heading (for example, '\cb{h1}', + '\cb{h2}', etc)." + }; + + bool --omit-link-check + { + "Don't check that local fragment link references (\\l{#ref ...}) resolve + to ids." + }; + + // Prologues. + // + std::vector --hxx-prologue + { + "", + "Insert at the beginning of the generated C++ header file." + }; + + std::vector --ixx-prologue + { + "", + "Insert at the beginning of the generated C++ inline file." + }; + + std::vector --cxx-prologue + { + "", + "Insert at the beginning of the generated C++ source file." + }; + + std::vector --man-prologue + { + "", + "Insert at the beginning of the generated man page file." + }; + + std::vector --html-prologue + { + "", + "Insert at the beginning of the generated HTML file." + }; + + std::vector --txt-prologue + { + "", + "Insert at the beginning of the generated text file." + }; + + // Epilogues. + // + std::vector --hxx-epilogue + { + "", + "Insert at the end of the generated C++ header file." + }; + + std::vector --ixx-epilogue + { + "", + "Insert at the end of the generated C++ inline file." + }; + + std::vector --cxx-epilogue + { + "", + "Insert at the end of the generated C++ source file." + }; + + std::vector --man-epilogue + { + "", + "Insert at the end of the generated man page file." + }; + + std::vector --html-epilogue + { + "", + "Insert at the end of the generated HTML file." + }; + + std::vector --txt-epilogue + { + "", + "Insert at the end of the generated text file." + }; + + // Prologue files. + // + std::string --hxx-prologue-file + { + "", + "Insert the content of at the beginning of the generated C++ + header file." + }; + + std::string --ixx-prologue-file + { + "", + "Insert the content of at the beginning of the generated C++ + inline file." + }; + + std::string --cxx-prologue-file + { + "", + "Insert the content of at the beginning of the generated C++ + source file." + }; + + std::string --man-prologue-file + { + "", + "Insert the content of at the beginning of the generated man + page file." + }; + + std::string --html-prologue-file + { + "", + "Insert the content of at the beginning of the generated HTML + file." + }; + + std::string --txt-prologue-file + { + "", + "Insert the content of at the beginning of the generated text + file." + }; + + // Epilogue files. + // + std::string --hxx-epilogue-file + { + "", + "Insert the content of at the end of the generated C++ header + file." + }; + + std::string --ixx-epilogue-file + { + "", + "Insert the content of at the end of the generated C++ inline + file." + }; + + std::string --cxx-epilogue-file + { + "", + "Insert the content of at the end of the generated C++ source + file." + }; + + std::string --man-epilogue-file + { + "", + "Insert the content of at the end of the generated man page file." + }; + + std::string --html-epilogue-file + { + "", + "Insert the content of at the end of the generated HTML file." + }; + + std::string --txt-epilogue-file + { + "", + "Insert the content of at the end of the generated text file." + }; + + // Output. + // + std::string --output-prefix + { + "", + "Add at the beginning of the generated output file name(s)." + }; + + std::string --output-suffix + { + "", + "Add at the end of the generated output file name(s). Note that + it is added before any file type-specific suffixes; see \cb{--*-suffix} + below." + }; + + std::string --hxx-suffix = ".hxx" + { + "", + "Use instead of the default \cb{.hxx} to construct the name of + the generated header file." + }; + + std::string --ixx-suffix = ".ixx" + { + "", + "Use instead of the default \cb{.ixx} to construct the name of + the generated inline file." + }; + + std::string --cxx-suffix = ".cxx" + { + "", + "Use instead of the default \cb{.cxx} to construct the name of + the generated source file." + }; + + std::string --man-suffix = ".1" + { + "", + "Use instead of the default \cb{.1} to construct the name of + the generated man page file." + }; + + std::string --html-suffix = ".html" + { + "", + "Use instead of the default \cb{.html} to construct the name + of the generated HTML file." + }; + + std::string --txt-suffix = ".txt" + { + "", + "Use instead of the default \cb{.txt} to construct the name of + the generated text file." + }; + + std::string --option-prefix = "-" + { + "", + "Use instead of the default '\cb{-}' as an option prefix. Unknown + command line arguments that start with this prefix are treated as unknown + options. If you set the option prefix to the empty value, then all the + unknown command line arguments will be treated as program arguments." + }; + + std::string --option-separator = "--" + { + "", + "Use instead of the default '\cb{--}' as an optional separator + between options and arguments. All the command line arguments that are + parsed after this separator are treated as program arguments. Set the + option separator to the empty value if you don't want this functionality." + }; + + bool --keep-separator + { + "Leave the option separator in the scanner. This is primarily useful for + incremental option parsing." + }; + + bool --no-combined-flags + { + "Disable support for combining multiple single-character flags into a + single argument (the \cb{-xyz} form that is equivalent to \cb{-x} \cb{-y} + \cb{-z}). An argument is considered a combination of flags if it starts + with a single option prefix (\cb{--option-prefix}) and only contains + letters and digits. Note that an option with a value may not be part of + such a combination, not even if it is specified last." + } + + bool --no-combined-values + { + "Disable support for combining an option and its value into a single + argument with the assignment sign (the \c{\i{option}\b{=}\i{value}} + form). This functionality requires a non-empty option prefix + (\cb{--option-prefix})." + } + + bool --include-with-brackets + { + "Use angle brackets (\cb{<>}) instead of quotes (\cb{\"\"}) in the + generated \cb{#include} directives." + }; + + std::string --include-prefix + { + "", + "Add to the generated \cb{#include} directive paths." + }; + + std::string --guard-prefix + { + "", + "Add to the generated header inclusion guards. The prefix is + transformed to upper case and characters that are illegal in a + preprocessor macro name are replaced with underscores." + }; + + std::map --reserved-name + { + "=", + "Add with an optional replacement to the list of names + that should not be used as identifiers. If provided, the replacement + name is used instead. All C++ keywords are already in this list." + }; + + // This is a "fake" option in that it is actually handled by + // argv_file_scanner. We have it here to get the documentation. + // + std::string --options-file + { + "", + "Read additional options from . Each option should appear on a + separate line optionally followed by space or equal sign (\cb{=}) and an + option value. Empty lines and lines starting with \cb{#} are ignored. + Option values can be enclosed in double (\cb{\"}) or single (\cb{'}) + quotes to preserve leading and trailing whitespaces as well as to specify + empty values. If the value itself contains trailing or leading quotes, + enclose it with an extra pair of quotes, for example \cb{'\"x\"'}. + Non-leading and non-trailing quotes are interpreted as being part of the + option value. + + The semantics of providing options in a file is equivalent to providing + the same set of options in the same order on the command line at the + point where the \cb{--options-file} option is specified except that + the shell escaping and quoting is not required. Repeat this option + to specify more than one options file." + }; +}; diff --git a/cli/cli/options.cxx b/cli/cli/options.cxx new file mode 100644 index 0000000..cc22a35 --- /dev/null +++ b/cli/cli/options.cxx @@ -0,0 +1,2146 @@ +// -*- C++ -*- +// +// This file was generated by CLI, a command line interface +// compiler for C++. +// + +// Begin prologue. +// +// +// End prologue. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cli +{ + // unknown_option + // + unknown_option:: + ~unknown_option () throw () + { + } + + void unknown_option:: + print (::std::ostream& os) const + { + os << "unknown option '" << option ().c_str () << "'"; + } + + const char* unknown_option:: + what () const throw () + { + return "unknown option"; + } + + // unknown_argument + // + unknown_argument:: + ~unknown_argument () throw () + { + } + + void unknown_argument:: + print (::std::ostream& os) const + { + os << "unknown argument '" << argument ().c_str () << "'"; + } + + const char* unknown_argument:: + what () const throw () + { + return "unknown argument"; + } + + // missing_value + // + missing_value:: + ~missing_value () throw () + { + } + + void missing_value:: + print (::std::ostream& os) const + { + os << "missing value for option '" << option ().c_str () << "'"; + } + + const char* missing_value:: + what () const throw () + { + return "missing option value"; + } + + // invalid_value + // + invalid_value:: + ~invalid_value () throw () + { + } + + void invalid_value:: + print (::std::ostream& os) const + { + os << "invalid value '" << value ().c_str () << "' for option '" + << option ().c_str () << "'"; + + if (!message ().empty ()) + os << ": " << message ().c_str (); + } + + const char* invalid_value:: + what () const throw () + { + return "invalid option value"; + } + + // eos_reached + // + void eos_reached:: + print (::std::ostream& os) const + { + os << what (); + } + + const char* eos_reached:: + what () const throw () + { + return "end of argument stream reached"; + } + + // file_io_failure + // + file_io_failure:: + ~file_io_failure () throw () + { + } + + void file_io_failure:: + print (::std::ostream& os) const + { + os << "unable to open file '" << file ().c_str () << "' or read failure"; + } + + const char* file_io_failure:: + what () const throw () + { + return "unable to open file or read failure"; + } + + // unmatched_quote + // + unmatched_quote:: + ~unmatched_quote () throw () + { + } + + void unmatched_quote:: + print (::std::ostream& os) const + { + os << "unmatched quote in argument '" << argument ().c_str () << "'"; + } + + const char* unmatched_quote:: + what () const throw () + { + return "unmatched quote"; + } + + // scanner + // + scanner:: + ~scanner () + { + } + + // argv_scanner + // + bool argv_scanner:: + more () + { + return i_ < argc_; + } + + const char* argv_scanner:: + peek () + { + if (i_ < argc_) + return argv_[i_]; + else + throw eos_reached (); + } + + const char* argv_scanner:: + next () + { + if (i_ < argc_) + { + const char* r (argv_[i_]); + + if (erase_) + { + for (int i (i_ + 1); i < argc_; ++i) + argv_[i - 1] = argv_[i]; + + --argc_; + argv_[argc_] = 0; + } + else + ++i_; + + return r; + } + else + throw eos_reached (); + } + + void argv_scanner:: + skip () + { + if (i_ < argc_) + ++i_; + else + throw eos_reached (); + } + + // argv_file_scanner + // + int argv_file_scanner::zero_argc_ = 0; + std::string argv_file_scanner::empty_string_; + + bool argv_file_scanner:: + more () + { + if (!args_.empty ()) + return true; + + while (base::more ()) + { + // See if the next argument is the file option. + // + const char* a (base::peek ()); + const option_info* oi = 0; + const char* ov = 0; + + if (!skip_) + { + if ((oi = find (a)) != 0) + { + base::next (); + + if (!base::more ()) + throw missing_value (a); + + ov = base::next (); + } + else if (std::strncmp (a, "-", 1) == 0) + { + if ((ov = std::strchr (a, '=')) != 0) + { + std::string o (a, 0, ov - a); + if ((oi = find (o.c_str ())) != 0) + { + base::next (); + ++ov; + } + } + } + } + + if (oi != 0) + { + if (oi->search_func != 0) + { + std::string f (oi->search_func (ov, oi->arg)); + + if (!f.empty ()) + load (f); + } + else + load (ov); + + if (!args_.empty ()) + return true; + } + else + { + if (!skip_) + skip_ = (std::strcmp (a, "--") == 0); + + return true; + } + } + + return false; + } + + const char* argv_file_scanner:: + peek () + { + if (!more ()) + throw eos_reached (); + + return args_.empty () ? base::peek () : args_.front ().value.c_str (); + } + + const std::string& argv_file_scanner:: + peek_file () + { + if (!more ()) + throw eos_reached (); + + return args_.empty () ? empty_string_ : *args_.front ().file; + } + + std::size_t argv_file_scanner:: + peek_line () + { + if (!more ()) + throw eos_reached (); + + return args_.empty () ? 0 : args_.front ().line; + } + + const char* argv_file_scanner:: + next () + { + if (!more ()) + throw eos_reached (); + + if (args_.empty ()) + return base::next (); + else + { + hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value); + args_.pop_front (); + return hold_[i_].c_str (); + } + } + + void argv_file_scanner:: + skip () + { + if (!more ()) + throw eos_reached (); + + if (args_.empty ()) + return base::skip (); + else + 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 std::string& file) + { + using namespace std; + + ifstream is (file.c_str ()); + + if (!is.is_open ()) + throw file_io_failure (file); + + files_.push_back (file); + + arg a; + a.file = &*files_.rbegin (); + + for (a.line = 1; !is.eof (); ++a.line) + { + string line; + getline (is, line); + + if (is.fail () && !is.eof ()) + throw file_io_failure (file); + + string::size_type n (line.size ()); + + // Trim the line from leading and trailing whitespaces. + // + if (n != 0) + { + const char* f (line.c_str ()); + const char* l (f + n); + + const char* of (f); + while (f < l && (*f == ' ' || *f == '\t' || *f == '\r')) + ++f; + + --l; + + const char* ol (l); + while (l > f && (*l == ' ' || *l == '\t' || *l == '\r')) + --l; + + if (f != of || l != ol) + line = f <= l ? string (f, l - f + 1) : string (); + } + + // Ignore empty lines, those that start with #. + // + if (line.empty () || line[0] == '#') + continue; + + string::size_type p (string::npos); + if (line.compare (0, 1, "-") == 0) + { + p = line.find (' '); + + string::size_type q (line.find ('=')); + if (q != string::npos && q < p) + p = q; + } + + string s1; + if (p != string::npos) + { + s1.assign (line, 0, p); + + // Skip leading whitespaces in the argument. + // + if (line[p] == '=') + ++p; + else + { + n = line.size (); + for (++p; p < n; ++p) + { + char c (line[p]); + if (c != ' ' && c != '\t' && c != '\r') + break; + } + } + } + else if (!skip_) + skip_ = (line == "--"); + + string s2 (line, p != string::npos ? p : 0); + + // If the string (which is an option value or argument) is + // wrapped in quotes, remove them. + // + n = s2.size (); + char cf (s2[0]), cl (s2[n - 1]); + + if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'') + { + if (n == 1 || cf != cl) + throw unmatched_quote (s2); + + s2 = string (s2, 1, n - 2); + } + + if (!s1.empty ()) + { + // See if this is another file option. + // + 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); + + continue; + } + + a.value = s1; + args_.push_back (a); + } + + a.value = s2; + args_.push_back (a); + } + } + + template + struct parser + { + static void + parse (X& x, bool& xs, scanner& s) + { + using namespace std; + + const char* o (s.next ()); + if (s.more ()) + { + string v (s.next ()); + istringstream is (v); + if (!(is >> x && is.peek () == istringstream::traits_type::eof ())) + throw invalid_value (o, v); + } + else + throw missing_value (o); + + xs = true; + } + }; + + template <> + struct parser + { + static void + parse (bool& x, scanner& s) + { + s.next (); + x = true; + } + }; + + template <> + struct parser + { + static void + parse (std::string& x, bool& xs, scanner& s) + { + const char* o (s.next ()); + + if (s.more ()) + x = s.next (); + else + throw missing_value (o); + + xs = true; + } + }; + + template + struct parser > + { + static void + parse (std::vector& c, bool& xs, scanner& s) + { + X x; + bool dummy; + parser::parse (x, dummy, s); + c.push_back (x); + xs = true; + } + }; + + template + struct parser > + { + static void + parse (std::set& c, bool& xs, scanner& s) + { + X x; + bool dummy; + parser::parse (x, dummy, s); + c.insert (x); + xs = true; + } + }; + + template + struct parser > + { + static void + parse (std::map& m, bool& xs, scanner& s) + { + const char* o (s.next ()); + + if (s.more ()) + { + std::string ov (s.next ()); + std::string::size_type p = ov.find ('='); + + K k = K (); + V v = V (); + std::string kstr (ov, 0, p); + std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ())); + + int ac (2); + char* av[] = + { + const_cast (o), 0 + }; + + bool dummy; + if (!kstr.empty ()) + { + av[1] = const_cast (kstr.c_str ()); + argv_scanner s (0, ac, av); + parser::parse (k, dummy, s); + } + + if (!vstr.empty ()) + { + av[1] = const_cast (vstr.c_str ()); + argv_scanner s (0, ac, av); + parser::parse (v, dummy, s); + } + + m[k] = v; + } + else + throw missing_value (o); + + xs = true; + } + }; + + template + void + thunk (X& x, scanner& s) + { + parser::parse (x.*M, s); + } + + template + void + thunk (X& x, scanner& s) + { + parser::parse (x.*M, x.*S, s); + } +} + +#include +#include + +// options +// + +options:: +options () +: build2_metadata_ (), + help_ (), + version_ (), + include_path_ (), + include_path_specified_ (false), + output_dir_ (), + output_dir_specified_ (false), + std_ (cxx_version::cxx98), + std_specified_ (false), + generate_modifier_ (), + generate_specifier_ (), + generate_parse_ (), + generate_merge_ (), + generate_description_ (), + generate_file_scanner_ (), + generate_vector_scanner_ (), + generate_group_scanner_ (), + suppress_inline_ (), + suppress_cli_ (), + cli_namespace_ ("::cli"), + cli_namespace_specified_ (false), + ostream_type_ ("::std::ostream"), + ostream_type_specified_ (false), + generate_cxx_ (), + generate_man_ (), + generate_html_ (), + generate_txt_ (), + stdout__ (), + suppress_undocumented_ (), + suppress_usage_ (), + long_usage_ (), + short_usage_ (), + page_usage_ (), + page_usage_specified_ (false), + option_length_ (0), + option_length_specified_ (false), + ansi_color_ (), + exclude_base_ (), + include_base_last_ (), + class_doc_ (), + class_doc_specified_ (false), + class__ (), + class__specified_ (false), + docvar_ (), + docvar_specified_ (false), + link_regex_ (), + link_regex_specified_ (false), + link_regex_trace_ (), + html_heading_map_ (), + html_heading_map_specified_ (false), + omit_link_check_ (), + hxx_prologue_ (), + hxx_prologue_specified_ (false), + ixx_prologue_ (), + ixx_prologue_specified_ (false), + cxx_prologue_ (), + cxx_prologue_specified_ (false), + man_prologue_ (), + man_prologue_specified_ (false), + html_prologue_ (), + html_prologue_specified_ (false), + txt_prologue_ (), + txt_prologue_specified_ (false), + hxx_epilogue_ (), + hxx_epilogue_specified_ (false), + ixx_epilogue_ (), + ixx_epilogue_specified_ (false), + cxx_epilogue_ (), + cxx_epilogue_specified_ (false), + man_epilogue_ (), + man_epilogue_specified_ (false), + html_epilogue_ (), + html_epilogue_specified_ (false), + txt_epilogue_ (), + txt_epilogue_specified_ (false), + hxx_prologue_file_ (), + hxx_prologue_file_specified_ (false), + ixx_prologue_file_ (), + ixx_prologue_file_specified_ (false), + cxx_prologue_file_ (), + cxx_prologue_file_specified_ (false), + man_prologue_file_ (), + man_prologue_file_specified_ (false), + html_prologue_file_ (), + html_prologue_file_specified_ (false), + txt_prologue_file_ (), + txt_prologue_file_specified_ (false), + hxx_epilogue_file_ (), + hxx_epilogue_file_specified_ (false), + ixx_epilogue_file_ (), + ixx_epilogue_file_specified_ (false), + cxx_epilogue_file_ (), + cxx_epilogue_file_specified_ (false), + man_epilogue_file_ (), + man_epilogue_file_specified_ (false), + html_epilogue_file_ (), + html_epilogue_file_specified_ (false), + txt_epilogue_file_ (), + txt_epilogue_file_specified_ (false), + output_prefix_ (), + output_prefix_specified_ (false), + output_suffix_ (), + output_suffix_specified_ (false), + hxx_suffix_ (".hxx"), + hxx_suffix_specified_ (false), + ixx_suffix_ (".ixx"), + ixx_suffix_specified_ (false), + cxx_suffix_ (".cxx"), + cxx_suffix_specified_ (false), + man_suffix_ (".1"), + man_suffix_specified_ (false), + html_suffix_ (".html"), + html_suffix_specified_ (false), + txt_suffix_ (".txt"), + txt_suffix_specified_ (false), + option_prefix_ ("-"), + option_prefix_specified_ (false), + option_separator_ ("--"), + option_separator_specified_ (false), + keep_separator_ (), + no_combined_flags_ (), + no_combined_values_ (), + include_with_brackets_ (), + include_prefix_ (), + include_prefix_specified_ (false), + guard_prefix_ (), + guard_prefix_specified_ (false), + reserved_name_ (), + reserved_name_specified_ (false), + options_file_ (), + options_file_specified_ (false) +{ +} + +options:: +options (int& argc, + char** argv, + bool erase, + ::cli::unknown_mode opt, + ::cli::unknown_mode arg) +: build2_metadata_ (), + help_ (), + version_ (), + include_path_ (), + include_path_specified_ (false), + output_dir_ (), + output_dir_specified_ (false), + std_ (cxx_version::cxx98), + std_specified_ (false), + generate_modifier_ (), + generate_specifier_ (), + generate_parse_ (), + generate_merge_ (), + generate_description_ (), + generate_file_scanner_ (), + generate_vector_scanner_ (), + generate_group_scanner_ (), + suppress_inline_ (), + suppress_cli_ (), + cli_namespace_ ("::cli"), + cli_namespace_specified_ (false), + ostream_type_ ("::std::ostream"), + ostream_type_specified_ (false), + generate_cxx_ (), + generate_man_ (), + generate_html_ (), + generate_txt_ (), + stdout__ (), + suppress_undocumented_ (), + suppress_usage_ (), + long_usage_ (), + short_usage_ (), + page_usage_ (), + page_usage_specified_ (false), + option_length_ (0), + option_length_specified_ (false), + ansi_color_ (), + exclude_base_ (), + include_base_last_ (), + class_doc_ (), + class_doc_specified_ (false), + class__ (), + class__specified_ (false), + docvar_ (), + docvar_specified_ (false), + link_regex_ (), + link_regex_specified_ (false), + link_regex_trace_ (), + html_heading_map_ (), + html_heading_map_specified_ (false), + omit_link_check_ (), + hxx_prologue_ (), + hxx_prologue_specified_ (false), + ixx_prologue_ (), + ixx_prologue_specified_ (false), + cxx_prologue_ (), + cxx_prologue_specified_ (false), + man_prologue_ (), + man_prologue_specified_ (false), + html_prologue_ (), + html_prologue_specified_ (false), + txt_prologue_ (), + txt_prologue_specified_ (false), + hxx_epilogue_ (), + hxx_epilogue_specified_ (false), + ixx_epilogue_ (), + ixx_epilogue_specified_ (false), + cxx_epilogue_ (), + cxx_epilogue_specified_ (false), + man_epilogue_ (), + man_epilogue_specified_ (false), + html_epilogue_ (), + html_epilogue_specified_ (false), + txt_epilogue_ (), + txt_epilogue_specified_ (false), + hxx_prologue_file_ (), + hxx_prologue_file_specified_ (false), + ixx_prologue_file_ (), + ixx_prologue_file_specified_ (false), + cxx_prologue_file_ (), + cxx_prologue_file_specified_ (false), + man_prologue_file_ (), + man_prologue_file_specified_ (false), + html_prologue_file_ (), + html_prologue_file_specified_ (false), + txt_prologue_file_ (), + txt_prologue_file_specified_ (false), + hxx_epilogue_file_ (), + hxx_epilogue_file_specified_ (false), + ixx_epilogue_file_ (), + ixx_epilogue_file_specified_ (false), + cxx_epilogue_file_ (), + cxx_epilogue_file_specified_ (false), + man_epilogue_file_ (), + man_epilogue_file_specified_ (false), + html_epilogue_file_ (), + html_epilogue_file_specified_ (false), + txt_epilogue_file_ (), + txt_epilogue_file_specified_ (false), + output_prefix_ (), + output_prefix_specified_ (false), + output_suffix_ (), + output_suffix_specified_ (false), + hxx_suffix_ (".hxx"), + hxx_suffix_specified_ (false), + ixx_suffix_ (".ixx"), + ixx_suffix_specified_ (false), + cxx_suffix_ (".cxx"), + cxx_suffix_specified_ (false), + man_suffix_ (".1"), + man_suffix_specified_ (false), + html_suffix_ (".html"), + html_suffix_specified_ (false), + txt_suffix_ (".txt"), + txt_suffix_specified_ (false), + option_prefix_ ("-"), + option_prefix_specified_ (false), + option_separator_ ("--"), + option_separator_specified_ (false), + keep_separator_ (), + no_combined_flags_ (), + no_combined_values_ (), + include_with_brackets_ (), + include_prefix_ (), + include_prefix_specified_ (false), + guard_prefix_ (), + guard_prefix_specified_ (false), + reserved_name_ (), + reserved_name_specified_ (false), + options_file_ (), + options_file_specified_ (false) +{ + ::cli::argv_scanner s (argc, argv, erase); + _parse (s, opt, arg); +} + +options:: +options (int start, + int& argc, + char** argv, + bool erase, + ::cli::unknown_mode opt, + ::cli::unknown_mode arg) +: build2_metadata_ (), + help_ (), + version_ (), + include_path_ (), + include_path_specified_ (false), + output_dir_ (), + output_dir_specified_ (false), + std_ (cxx_version::cxx98), + std_specified_ (false), + generate_modifier_ (), + generate_specifier_ (), + generate_parse_ (), + generate_merge_ (), + generate_description_ (), + generate_file_scanner_ (), + generate_vector_scanner_ (), + generate_group_scanner_ (), + suppress_inline_ (), + suppress_cli_ (), + cli_namespace_ ("::cli"), + cli_namespace_specified_ (false), + ostream_type_ ("::std::ostream"), + ostream_type_specified_ (false), + generate_cxx_ (), + generate_man_ (), + generate_html_ (), + generate_txt_ (), + stdout__ (), + suppress_undocumented_ (), + suppress_usage_ (), + long_usage_ (), + short_usage_ (), + page_usage_ (), + page_usage_specified_ (false), + option_length_ (0), + option_length_specified_ (false), + ansi_color_ (), + exclude_base_ (), + include_base_last_ (), + class_doc_ (), + class_doc_specified_ (false), + class__ (), + class__specified_ (false), + docvar_ (), + docvar_specified_ (false), + link_regex_ (), + link_regex_specified_ (false), + link_regex_trace_ (), + html_heading_map_ (), + html_heading_map_specified_ (false), + omit_link_check_ (), + hxx_prologue_ (), + hxx_prologue_specified_ (false), + ixx_prologue_ (), + ixx_prologue_specified_ (false), + cxx_prologue_ (), + cxx_prologue_specified_ (false), + man_prologue_ (), + man_prologue_specified_ (false), + html_prologue_ (), + html_prologue_specified_ (false), + txt_prologue_ (), + txt_prologue_specified_ (false), + hxx_epilogue_ (), + hxx_epilogue_specified_ (false), + ixx_epilogue_ (), + ixx_epilogue_specified_ (false), + cxx_epilogue_ (), + cxx_epilogue_specified_ (false), + man_epilogue_ (), + man_epilogue_specified_ (false), + html_epilogue_ (), + html_epilogue_specified_ (false), + txt_epilogue_ (), + txt_epilogue_specified_ (false), + hxx_prologue_file_ (), + hxx_prologue_file_specified_ (false), + ixx_prologue_file_ (), + ixx_prologue_file_specified_ (false), + cxx_prologue_file_ (), + cxx_prologue_file_specified_ (false), + man_prologue_file_ (), + man_prologue_file_specified_ (false), + html_prologue_file_ (), + html_prologue_file_specified_ (false), + txt_prologue_file_ (), + txt_prologue_file_specified_ (false), + hxx_epilogue_file_ (), + hxx_epilogue_file_specified_ (false), + ixx_epilogue_file_ (), + ixx_epilogue_file_specified_ (false), + cxx_epilogue_file_ (), + cxx_epilogue_file_specified_ (false), + man_epilogue_file_ (), + man_epilogue_file_specified_ (false), + html_epilogue_file_ (), + html_epilogue_file_specified_ (false), + txt_epilogue_file_ (), + txt_epilogue_file_specified_ (false), + output_prefix_ (), + output_prefix_specified_ (false), + output_suffix_ (), + output_suffix_specified_ (false), + hxx_suffix_ (".hxx"), + hxx_suffix_specified_ (false), + ixx_suffix_ (".ixx"), + ixx_suffix_specified_ (false), + cxx_suffix_ (".cxx"), + cxx_suffix_specified_ (false), + man_suffix_ (".1"), + man_suffix_specified_ (false), + html_suffix_ (".html"), + html_suffix_specified_ (false), + txt_suffix_ (".txt"), + txt_suffix_specified_ (false), + option_prefix_ ("-"), + option_prefix_specified_ (false), + option_separator_ ("--"), + option_separator_specified_ (false), + keep_separator_ (), + no_combined_flags_ (), + no_combined_values_ (), + include_with_brackets_ (), + include_prefix_ (), + include_prefix_specified_ (false), + guard_prefix_ (), + guard_prefix_specified_ (false), + reserved_name_ (), + reserved_name_specified_ (false), + options_file_ (), + options_file_specified_ (false) +{ + ::cli::argv_scanner s (start, argc, argv, erase); + _parse (s, opt, arg); +} + +options:: +options (int& argc, + char** argv, + int& end, + bool erase, + ::cli::unknown_mode opt, + ::cli::unknown_mode arg) +: build2_metadata_ (), + help_ (), + version_ (), + include_path_ (), + include_path_specified_ (false), + output_dir_ (), + output_dir_specified_ (false), + std_ (cxx_version::cxx98), + std_specified_ (false), + generate_modifier_ (), + generate_specifier_ (), + generate_parse_ (), + generate_merge_ (), + generate_description_ (), + generate_file_scanner_ (), + generate_vector_scanner_ (), + generate_group_scanner_ (), + suppress_inline_ (), + suppress_cli_ (), + cli_namespace_ ("::cli"), + cli_namespace_specified_ (false), + ostream_type_ ("::std::ostream"), + ostream_type_specified_ (false), + generate_cxx_ (), + generate_man_ (), + generate_html_ (), + generate_txt_ (), + stdout__ (), + suppress_undocumented_ (), + suppress_usage_ (), + long_usage_ (), + short_usage_ (), + page_usage_ (), + page_usage_specified_ (false), + option_length_ (0), + option_length_specified_ (false), + ansi_color_ (), + exclude_base_ (), + include_base_last_ (), + class_doc_ (), + class_doc_specified_ (false), + class__ (), + class__specified_ (false), + docvar_ (), + docvar_specified_ (false), + link_regex_ (), + link_regex_specified_ (false), + link_regex_trace_ (), + html_heading_map_ (), + html_heading_map_specified_ (false), + omit_link_check_ (), + hxx_prologue_ (), + hxx_prologue_specified_ (false), + ixx_prologue_ (), + ixx_prologue_specified_ (false), + cxx_prologue_ (), + cxx_prologue_specified_ (false), + man_prologue_ (), + man_prologue_specified_ (false), + html_prologue_ (), + html_prologue_specified_ (false), + txt_prologue_ (), + txt_prologue_specified_ (false), + hxx_epilogue_ (), + hxx_epilogue_specified_ (false), + ixx_epilogue_ (), + ixx_epilogue_specified_ (false), + cxx_epilogue_ (), + cxx_epilogue_specified_ (false), + man_epilogue_ (), + man_epilogue_specified_ (false), + html_epilogue_ (), + html_epilogue_specified_ (false), + txt_epilogue_ (), + txt_epilogue_specified_ (false), + hxx_prologue_file_ (), + hxx_prologue_file_specified_ (false), + ixx_prologue_file_ (), + ixx_prologue_file_specified_ (false), + cxx_prologue_file_ (), + cxx_prologue_file_specified_ (false), + man_prologue_file_ (), + man_prologue_file_specified_ (false), + html_prologue_file_ (), + html_prologue_file_specified_ (false), + txt_prologue_file_ (), + txt_prologue_file_specified_ (false), + hxx_epilogue_file_ (), + hxx_epilogue_file_specified_ (false), + ixx_epilogue_file_ (), + ixx_epilogue_file_specified_ (false), + cxx_epilogue_file_ (), + cxx_epilogue_file_specified_ (false), + man_epilogue_file_ (), + man_epilogue_file_specified_ (false), + html_epilogue_file_ (), + html_epilogue_file_specified_ (false), + txt_epilogue_file_ (), + txt_epilogue_file_specified_ (false), + output_prefix_ (), + output_prefix_specified_ (false), + output_suffix_ (), + output_suffix_specified_ (false), + hxx_suffix_ (".hxx"), + hxx_suffix_specified_ (false), + ixx_suffix_ (".ixx"), + ixx_suffix_specified_ (false), + cxx_suffix_ (".cxx"), + cxx_suffix_specified_ (false), + man_suffix_ (".1"), + man_suffix_specified_ (false), + html_suffix_ (".html"), + html_suffix_specified_ (false), + txt_suffix_ (".txt"), + txt_suffix_specified_ (false), + option_prefix_ ("-"), + option_prefix_specified_ (false), + option_separator_ ("--"), + option_separator_specified_ (false), + keep_separator_ (), + no_combined_flags_ (), + no_combined_values_ (), + include_with_brackets_ (), + include_prefix_ (), + include_prefix_specified_ (false), + guard_prefix_ (), + guard_prefix_specified_ (false), + reserved_name_ (), + reserved_name_specified_ (false), + options_file_ (), + options_file_specified_ (false) +{ + ::cli::argv_scanner s (argc, argv, erase); + _parse (s, opt, arg); + end = s.end (); +} + +options:: +options (int start, + int& argc, + char** argv, + int& end, + bool erase, + ::cli::unknown_mode opt, + ::cli::unknown_mode arg) +: build2_metadata_ (), + help_ (), + version_ (), + include_path_ (), + include_path_specified_ (false), + output_dir_ (), + output_dir_specified_ (false), + std_ (cxx_version::cxx98), + std_specified_ (false), + generate_modifier_ (), + generate_specifier_ (), + generate_parse_ (), + generate_merge_ (), + generate_description_ (), + generate_file_scanner_ (), + generate_vector_scanner_ (), + generate_group_scanner_ (), + suppress_inline_ (), + suppress_cli_ (), + cli_namespace_ ("::cli"), + cli_namespace_specified_ (false), + ostream_type_ ("::std::ostream"), + ostream_type_specified_ (false), + generate_cxx_ (), + generate_man_ (), + generate_html_ (), + generate_txt_ (), + stdout__ (), + suppress_undocumented_ (), + suppress_usage_ (), + long_usage_ (), + short_usage_ (), + page_usage_ (), + page_usage_specified_ (false), + option_length_ (0), + option_length_specified_ (false), + ansi_color_ (), + exclude_base_ (), + include_base_last_ (), + class_doc_ (), + class_doc_specified_ (false), + class__ (), + class__specified_ (false), + docvar_ (), + docvar_specified_ (false), + link_regex_ (), + link_regex_specified_ (false), + link_regex_trace_ (), + html_heading_map_ (), + html_heading_map_specified_ (false), + omit_link_check_ (), + hxx_prologue_ (), + hxx_prologue_specified_ (false), + ixx_prologue_ (), + ixx_prologue_specified_ (false), + cxx_prologue_ (), + cxx_prologue_specified_ (false), + man_prologue_ (), + man_prologue_specified_ (false), + html_prologue_ (), + html_prologue_specified_ (false), + txt_prologue_ (), + txt_prologue_specified_ (false), + hxx_epilogue_ (), + hxx_epilogue_specified_ (false), + ixx_epilogue_ (), + ixx_epilogue_specified_ (false), + cxx_epilogue_ (), + cxx_epilogue_specified_ (false), + man_epilogue_ (), + man_epilogue_specified_ (false), + html_epilogue_ (), + html_epilogue_specified_ (false), + txt_epilogue_ (), + txt_epilogue_specified_ (false), + hxx_prologue_file_ (), + hxx_prologue_file_specified_ (false), + ixx_prologue_file_ (), + ixx_prologue_file_specified_ (false), + cxx_prologue_file_ (), + cxx_prologue_file_specified_ (false), + man_prologue_file_ (), + man_prologue_file_specified_ (false), + html_prologue_file_ (), + html_prologue_file_specified_ (false), + txt_prologue_file_ (), + txt_prologue_file_specified_ (false), + hxx_epilogue_file_ (), + hxx_epilogue_file_specified_ (false), + ixx_epilogue_file_ (), + ixx_epilogue_file_specified_ (false), + cxx_epilogue_file_ (), + cxx_epilogue_file_specified_ (false), + man_epilogue_file_ (), + man_epilogue_file_specified_ (false), + html_epilogue_file_ (), + html_epilogue_file_specified_ (false), + txt_epilogue_file_ (), + txt_epilogue_file_specified_ (false), + output_prefix_ (), + output_prefix_specified_ (false), + output_suffix_ (), + output_suffix_specified_ (false), + hxx_suffix_ (".hxx"), + hxx_suffix_specified_ (false), + ixx_suffix_ (".ixx"), + ixx_suffix_specified_ (false), + cxx_suffix_ (".cxx"), + cxx_suffix_specified_ (false), + man_suffix_ (".1"), + man_suffix_specified_ (false), + html_suffix_ (".html"), + html_suffix_specified_ (false), + txt_suffix_ (".txt"), + txt_suffix_specified_ (false), + option_prefix_ ("-"), + option_prefix_specified_ (false), + option_separator_ ("--"), + option_separator_specified_ (false), + keep_separator_ (), + no_combined_flags_ (), + no_combined_values_ (), + include_with_brackets_ (), + include_prefix_ (), + include_prefix_specified_ (false), + guard_prefix_ (), + guard_prefix_specified_ (false), + reserved_name_ (), + reserved_name_specified_ (false), + options_file_ (), + options_file_specified_ (false) +{ + ::cli::argv_scanner s (start, argc, argv, erase); + _parse (s, opt, arg); + end = s.end (); +} + +options:: +options (::cli::scanner& s, + ::cli::unknown_mode opt, + ::cli::unknown_mode arg) +: build2_metadata_ (), + help_ (), + version_ (), + include_path_ (), + include_path_specified_ (false), + output_dir_ (), + output_dir_specified_ (false), + std_ (cxx_version::cxx98), + std_specified_ (false), + generate_modifier_ (), + generate_specifier_ (), + generate_parse_ (), + generate_merge_ (), + generate_description_ (), + generate_file_scanner_ (), + generate_vector_scanner_ (), + generate_group_scanner_ (), + suppress_inline_ (), + suppress_cli_ (), + cli_namespace_ ("::cli"), + cli_namespace_specified_ (false), + ostream_type_ ("::std::ostream"), + ostream_type_specified_ (false), + generate_cxx_ (), + generate_man_ (), + generate_html_ (), + generate_txt_ (), + stdout__ (), + suppress_undocumented_ (), + suppress_usage_ (), + long_usage_ (), + short_usage_ (), + page_usage_ (), + page_usage_specified_ (false), + option_length_ (0), + option_length_specified_ (false), + ansi_color_ (), + exclude_base_ (), + include_base_last_ (), + class_doc_ (), + class_doc_specified_ (false), + class__ (), + class__specified_ (false), + docvar_ (), + docvar_specified_ (false), + link_regex_ (), + link_regex_specified_ (false), + link_regex_trace_ (), + html_heading_map_ (), + html_heading_map_specified_ (false), + omit_link_check_ (), + hxx_prologue_ (), + hxx_prologue_specified_ (false), + ixx_prologue_ (), + ixx_prologue_specified_ (false), + cxx_prologue_ (), + cxx_prologue_specified_ (false), + man_prologue_ (), + man_prologue_specified_ (false), + html_prologue_ (), + html_prologue_specified_ (false), + txt_prologue_ (), + txt_prologue_specified_ (false), + hxx_epilogue_ (), + hxx_epilogue_specified_ (false), + ixx_epilogue_ (), + ixx_epilogue_specified_ (false), + cxx_epilogue_ (), + cxx_epilogue_specified_ (false), + man_epilogue_ (), + man_epilogue_specified_ (false), + html_epilogue_ (), + html_epilogue_specified_ (false), + txt_epilogue_ (), + txt_epilogue_specified_ (false), + hxx_prologue_file_ (), + hxx_prologue_file_specified_ (false), + ixx_prologue_file_ (), + ixx_prologue_file_specified_ (false), + cxx_prologue_file_ (), + cxx_prologue_file_specified_ (false), + man_prologue_file_ (), + man_prologue_file_specified_ (false), + html_prologue_file_ (), + html_prologue_file_specified_ (false), + txt_prologue_file_ (), + txt_prologue_file_specified_ (false), + hxx_epilogue_file_ (), + hxx_epilogue_file_specified_ (false), + ixx_epilogue_file_ (), + ixx_epilogue_file_specified_ (false), + cxx_epilogue_file_ (), + cxx_epilogue_file_specified_ (false), + man_epilogue_file_ (), + man_epilogue_file_specified_ (false), + html_epilogue_file_ (), + html_epilogue_file_specified_ (false), + txt_epilogue_file_ (), + txt_epilogue_file_specified_ (false), + output_prefix_ (), + output_prefix_specified_ (false), + output_suffix_ (), + output_suffix_specified_ (false), + hxx_suffix_ (".hxx"), + hxx_suffix_specified_ (false), + ixx_suffix_ (".ixx"), + ixx_suffix_specified_ (false), + cxx_suffix_ (".cxx"), + cxx_suffix_specified_ (false), + man_suffix_ (".1"), + man_suffix_specified_ (false), + html_suffix_ (".html"), + html_suffix_specified_ (false), + txt_suffix_ (".txt"), + txt_suffix_specified_ (false), + option_prefix_ ("-"), + option_prefix_specified_ (false), + option_separator_ ("--"), + option_separator_specified_ (false), + keep_separator_ (), + no_combined_flags_ (), + no_combined_values_ (), + include_with_brackets_ (), + include_prefix_ (), + include_prefix_specified_ (false), + guard_prefix_ (), + guard_prefix_specified_ (false), + reserved_name_ (), + reserved_name_specified_ (false), + options_file_ (), + options_file_specified_ (false) +{ + _parse (s, opt, arg); +} + +::cli::usage_para options:: +print_usage (::std::ostream& os, ::cli::usage_para p) +{ + CLI_POTENTIALLY_UNUSED (os); + + if (p == ::cli::usage_para::text) + os << ::std::endl; + + os << "--build2-metadata" << std::endl; + + os << "--help Print usage information and exit." << ::std::endl; + + os << "--version Print version and exit." << ::std::endl; + + os << "--include-path|-I Search for bracket-included (<>) options" << ::std::endl + << " files." << ::std::endl; + + os << "--output-dir|-o Write the generated files to instead of the" << ::std::endl + << " current directory." << ::std::endl; + + os << "--std Specify the C++ standard that should be used" << ::std::endl + << " during compilation." << ::std::endl; + + os << "--generate-modifier Generate option value modifiers in addition to" << ::std::endl + << " accessors." << ::std::endl; + + os << "--generate-specifier Generate functions for determining whether the" << ::std::endl + << " option was specified on the command line." << ::std::endl; + + os << "--generate-parse Generate parse() functions instead of parsing" << ::std::endl + << " constructors." << ::std::endl; + + os << "--generate-merge Generate merge() functions." << ::std::endl; + + os << "--generate-description Generate the option description list that can be" << ::std::endl + << " examined at runtime." << ::std::endl; + + os << "--generate-file-scanner Generate the argv_file_scanner implementation." << ::std::endl; + + os << "--generate-vector-scanner Generate the vector_scanner implementation." << ::std::endl; + + os << "--generate-group-scanner Generate the group_scanner implementation." << ::std::endl; + + os << "--suppress-inline Generate all functions non-inline." << ::std::endl; + + os << "--suppress-cli Do not generate the CLI support types (scanners," << ::std::endl + << " parser, etc)." << ::std::endl; + + os << "--cli-namespace Generate the CLI support types in the " << ::std::endl + << " namespace (cli by default)." << ::std::endl; + + os << "--ostream-type Output stream type instead of the default" << ::std::endl + << " std::ostream that should be used to print usage" << ::std::endl + << " and exception information." << ::std::endl; + + os << "--generate-cxx Generate C++ code." << ::std::endl; + + os << "--generate-man Generate documentation in the man page format." << ::std::endl; + + os << "--generate-html Generate documentation in the HTML format." << ::std::endl; + + os << "--generate-txt Generate documentation in the plain text format," << ::std::endl + << " similar to usage." << ::std::endl; + + os << "--stdout Write output to STDOUT instead of a file." << ::std::endl; + + 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" << ::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; + + os << "--short-usage If specified together with --long-usage, generate" << ::std::endl + << " both short and long usage versions." << ::std::endl; + + os << "--page-usage Generate the combined usage printing code for the" << ::std::endl + << " entire page." << ::std::endl; + + os << "--option-length Indent option descriptions characters when" << ::std::endl + << " printing usage." << ::std::endl; + + os << "--ansi-color Use ANSI color escape sequences when printing" << ::std::endl + << " usage." << ::std::endl; + + os << "--exclude-base Exclude base class information from usage and" << ::std::endl + << " documentation." << ::std::endl; + + os << "--include-base-last Include base class information after derived for" << ::std::endl + << " usage and documentation." << ::std::endl; + + os << "--class-doc = Specify the documentation that should be" << ::std::endl + << " used for the options class ." << ::std::endl; + + os << "--class Generate the man page, HTML, or text documentation" << ::std::endl + << " only for the options class ." << ::std::endl; + + os << "--docvar|-v = Set documentation variable to the value" << ::std::endl + << " ." << ::std::endl; + + os << "--link-regex Add to the list of regular expressions" << ::std::endl + << " used to transform link targets in the generated" << ::std::endl + << " documentation." << ::std::endl; + + os << "--link-regex-trace Trace the process of applying regular expressions" << ::std::endl + << " specified with the --link-regex option." << ::std::endl; + + os << "--html-heading-map = Map CLI heading (valid values: 'H', '0', '1'," << ::std::endl + << " 'h', and '2') to HTML heading (for example," << ::std::endl + << " 'h1', 'h2', etc)." << ::std::endl; + + os << "--omit-link-check Don't check that local fragment link references" << ::std::endl + << " (\\l{#ref ...}) resolve to ids." << ::std::endl; + + os << "--hxx-prologue Insert at the beginning of the generated" << ::std::endl + << " C++ header file." << ::std::endl; + + os << "--ixx-prologue Insert at the beginning of the generated" << ::std::endl + << " C++ inline file." << ::std::endl; + + os << "--cxx-prologue Insert at the beginning of the generated" << ::std::endl + << " C++ source file." << ::std::endl; + + os << "--man-prologue Insert at the beginning of the generated" << ::std::endl + << " man page file." << ::std::endl; + + os << "--html-prologue Insert at the beginning of the generated" << ::std::endl + << " HTML file." << ::std::endl; + + os << "--txt-prologue Insert at the beginning of the generated" << ::std::endl + << " text file." << ::std::endl; + + os << "--hxx-epilogue Insert at the end of the generated C++" << ::std::endl + << " header file." << ::std::endl; + + os << "--ixx-epilogue Insert at the end of the generated C++" << ::std::endl + << " inline file." << ::std::endl; + + os << "--cxx-epilogue Insert at the end of the generated C++" << ::std::endl + << " source file." << ::std::endl; + + os << "--man-epilogue Insert at the end of the generated man page" << ::std::endl + << " file." << ::std::endl; + + os << "--html-epilogue Insert at the end of the generated HTML" << ::std::endl + << " file." << ::std::endl; + + os << "--txt-epilogue Insert at the end of the generated text" << ::std::endl + << " file." << ::std::endl; + + os << "--hxx-prologue-file Insert the content of at the beginning of" << ::std::endl + << " the generated C++ header file." << ::std::endl; + + os << "--ixx-prologue-file Insert the content of at the beginning of" << ::std::endl + << " the generated C++ inline file." << ::std::endl; + + os << "--cxx-prologue-file Insert the content of at the beginning of" << ::std::endl + << " the generated C++ source file." << ::std::endl; + + os << "--man-prologue-file Insert the content of at the beginning of" << ::std::endl + << " the generated man page file." << ::std::endl; + + os << "--html-prologue-file Insert the content of at the beginning of" << ::std::endl + << " the generated HTML file." << ::std::endl; + + os << "--txt-prologue-file Insert the content of at the beginning of" << ::std::endl + << " the generated text file." << ::std::endl; + + os << "--hxx-epilogue-file Insert the content of at the end of the" << ::std::endl + << " generated C++ header file." << ::std::endl; + + os << "--ixx-epilogue-file Insert the content of at the end of the" << ::std::endl + << " generated C++ inline file." << ::std::endl; + + os << "--cxx-epilogue-file Insert the content of at the end of the" << ::std::endl + << " generated C++ source file." << ::std::endl; + + os << "--man-epilogue-file Insert the content of at the end of the" << ::std::endl + << " generated man page file." << ::std::endl; + + os << "--html-epilogue-file Insert the content of at the end of the" << ::std::endl + << " generated HTML file." << ::std::endl; + + os << "--txt-epilogue-file Insert the content of at the end of the" << ::std::endl + << " generated text file." << ::std::endl; + + os << "--output-prefix Add at the beginning of the generated" << ::std::endl + << " output file name(s)." << ::std::endl; + + os << "--output-suffix Add at the end of the generated output" << ::std::endl + << " file name(s)." << ::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 << "--man-suffix Use instead of the default .1 to" << ::std::endl + << " construct the name of the generated man page file." << ::std::endl; + + os << "--html-suffix Use instead of the default .html to" << ::std::endl + << " construct the name of the generated HTML file." << ::std::endl; + + os << "--txt-suffix Use instead of the default .txt to" << ::std::endl + << " construct the name of the generated text 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 << "--keep-separator Leave the option separator in the scanner." << ::std::endl; + + os << "--no-combined-flags Disable support for combining multiple" << ::std::endl + << " single-character flags into a single argument (the" << ::std::endl + << " -xyz form that is equivalent to -x -y -z)." << ::std::endl; + + os << "--no-combined-values Disable support for combining an option and its" << ::std::endl + << " value into a single argument with the assignment" << ::std::endl + << " sign (the option=value form)." << ::std::endl; + + os << "--include-with-brackets Use angle brackets (<>) instead of quotes (\"\") in" << ::std::endl + << " the generated #include directives." << ::std::endl; + + os << "--include-prefix Add to the generated #include directive" << ::std::endl + << " paths." << ::std::endl; + + os << "--guard-prefix Add to the generated header inclusion" << ::std::endl + << " guards." << ::std::endl; + + os << "--reserved-name = Add with an optional replacement to" << ::std::endl + << " the list of names that should not be used as" << ::std::endl + << " identifiers." << ::std::endl; + + os << "--options-file Read additional options from ." << ::std::endl; + + p = ::cli::usage_para::option; + + return p; +} + +typedef +std::map +_cli_options_map; + +static _cli_options_map _cli_options_map_; + +struct _cli_options_map_init +{ + _cli_options_map_init () + { + _cli_options_map_["--build2-metadata"] = + &::cli::thunk< options, bool, &options::build2_metadata_ >; + _cli_options_map_["--help"] = + &::cli::thunk< options, bool, &options::help_ >; + _cli_options_map_["--version"] = + &::cli::thunk< options, bool, &options::version_ >; + _cli_options_map_["--include-path"] = + &::cli::thunk< options, std::vector, &options::include_path_, + &options::include_path_specified_ >; + _cli_options_map_["-I"] = + &::cli::thunk< options, std::vector, &options::include_path_, + &options::include_path_specified_ >; + _cli_options_map_["--output-dir"] = + &::cli::thunk< options, std::string, &options::output_dir_, + &options::output_dir_specified_ >; + _cli_options_map_["-o"] = + &::cli::thunk< options, std::string, &options::output_dir_, + &options::output_dir_specified_ >; + _cli_options_map_["--std"] = + &::cli::thunk< options, cxx_version, &options::std_, + &options::std_specified_ >; + _cli_options_map_["--generate-modifier"] = + &::cli::thunk< options, bool, &options::generate_modifier_ >; + _cli_options_map_["--generate-specifier"] = + &::cli::thunk< options, bool, &options::generate_specifier_ >; + _cli_options_map_["--generate-parse"] = + &::cli::thunk< options, bool, &options::generate_parse_ >; + _cli_options_map_["--generate-merge"] = + &::cli::thunk< options, bool, &options::generate_merge_ >; + _cli_options_map_["--generate-description"] = + &::cli::thunk< options, bool, &options::generate_description_ >; + _cli_options_map_["--generate-file-scanner"] = + &::cli::thunk< options, bool, &options::generate_file_scanner_ >; + _cli_options_map_["--generate-vector-scanner"] = + &::cli::thunk< options, bool, &options::generate_vector_scanner_ >; + _cli_options_map_["--generate-group-scanner"] = + &::cli::thunk< options, bool, &options::generate_group_scanner_ >; + _cli_options_map_["--suppress-inline"] = + &::cli::thunk< options, bool, &options::suppress_inline_ >; + _cli_options_map_["--suppress-cli"] = + &::cli::thunk< options, bool, &options::suppress_cli_ >; + _cli_options_map_["--cli-namespace"] = + &::cli::thunk< options, std::string, &options::cli_namespace_, + &options::cli_namespace_specified_ >; + _cli_options_map_["--ostream-type"] = + &::cli::thunk< options, std::string, &options::ostream_type_, + &options::ostream_type_specified_ >; + _cli_options_map_["--generate-cxx"] = + &::cli::thunk< options, bool, &options::generate_cxx_ >; + _cli_options_map_["--generate-man"] = + &::cli::thunk< options, bool, &options::generate_man_ >; + _cli_options_map_["--generate-html"] = + &::cli::thunk< options, bool, &options::generate_html_ >; + _cli_options_map_["--generate-txt"] = + &::cli::thunk< options, bool, &options::generate_txt_ >; + _cli_options_map_["--stdout"] = + &::cli::thunk< options, bool, &options::stdout__ >; + _cli_options_map_["--suppress-undocumented"] = + &::cli::thunk< options, bool, &options::suppress_undocumented_ >; + _cli_options_map_["--suppress-usage"] = + &::cli::thunk< options, bool, &options::suppress_usage_ >; + _cli_options_map_["--long-usage"] = + &::cli::thunk< options, bool, &options::long_usage_ >; + _cli_options_map_["--short-usage"] = + &::cli::thunk< options, bool, &options::short_usage_ >; + _cli_options_map_["--page-usage"] = + &::cli::thunk< options, std::string, &options::page_usage_, + &options::page_usage_specified_ >; + _cli_options_map_["--option-length"] = + &::cli::thunk< options, std::size_t, &options::option_length_, + &options::option_length_specified_ >; + _cli_options_map_["--ansi-color"] = + &::cli::thunk< options, bool, &options::ansi_color_ >; + _cli_options_map_["--exclude-base"] = + &::cli::thunk< options, bool, &options::exclude_base_ >; + _cli_options_map_["--include-base-last"] = + &::cli::thunk< options, bool, &options::include_base_last_ >; + _cli_options_map_["--class-doc"] = + &::cli::thunk< options, std::map, &options::class_doc_, + &options::class_doc_specified_ >; + _cli_options_map_["--class"] = + &::cli::thunk< options, std::vector, &options::class__, + &options::class__specified_ >; + _cli_options_map_["--docvar"] = + &::cli::thunk< options, std::map, &options::docvar_, + &options::docvar_specified_ >; + _cli_options_map_["-v"] = + &::cli::thunk< options, std::map, &options::docvar_, + &options::docvar_specified_ >; + _cli_options_map_["--link-regex"] = + &::cli::thunk< options, std::vector, &options::link_regex_, + &options::link_regex_specified_ >; + _cli_options_map_["--link-regex-trace"] = + &::cli::thunk< options, bool, &options::link_regex_trace_ >; + _cli_options_map_["--html-heading-map"] = + &::cli::thunk< options, std::map, &options::html_heading_map_, + &options::html_heading_map_specified_ >; + _cli_options_map_["--omit-link-check"] = + &::cli::thunk< options, bool, &options::omit_link_check_ >; + _cli_options_map_["--hxx-prologue"] = + &::cli::thunk< options, std::vector, &options::hxx_prologue_, + &options::hxx_prologue_specified_ >; + _cli_options_map_["--ixx-prologue"] = + &::cli::thunk< options, std::vector, &options::ixx_prologue_, + &options::ixx_prologue_specified_ >; + _cli_options_map_["--cxx-prologue"] = + &::cli::thunk< options, std::vector, &options::cxx_prologue_, + &options::cxx_prologue_specified_ >; + _cli_options_map_["--man-prologue"] = + &::cli::thunk< options, std::vector, &options::man_prologue_, + &options::man_prologue_specified_ >; + _cli_options_map_["--html-prologue"] = + &::cli::thunk< options, std::vector, &options::html_prologue_, + &options::html_prologue_specified_ >; + _cli_options_map_["--txt-prologue"] = + &::cli::thunk< options, std::vector, &options::txt_prologue_, + &options::txt_prologue_specified_ >; + _cli_options_map_["--hxx-epilogue"] = + &::cli::thunk< options, std::vector, &options::hxx_epilogue_, + &options::hxx_epilogue_specified_ >; + _cli_options_map_["--ixx-epilogue"] = + &::cli::thunk< options, std::vector, &options::ixx_epilogue_, + &options::ixx_epilogue_specified_ >; + _cli_options_map_["--cxx-epilogue"] = + &::cli::thunk< options, std::vector, &options::cxx_epilogue_, + &options::cxx_epilogue_specified_ >; + _cli_options_map_["--man-epilogue"] = + &::cli::thunk< options, std::vector, &options::man_epilogue_, + &options::man_epilogue_specified_ >; + _cli_options_map_["--html-epilogue"] = + &::cli::thunk< options, std::vector, &options::html_epilogue_, + &options::html_epilogue_specified_ >; + _cli_options_map_["--txt-epilogue"] = + &::cli::thunk< options, std::vector, &options::txt_epilogue_, + &options::txt_epilogue_specified_ >; + _cli_options_map_["--hxx-prologue-file"] = + &::cli::thunk< options, std::string, &options::hxx_prologue_file_, + &options::hxx_prologue_file_specified_ >; + _cli_options_map_["--ixx-prologue-file"] = + &::cli::thunk< options, std::string, &options::ixx_prologue_file_, + &options::ixx_prologue_file_specified_ >; + _cli_options_map_["--cxx-prologue-file"] = + &::cli::thunk< options, std::string, &options::cxx_prologue_file_, + &options::cxx_prologue_file_specified_ >; + _cli_options_map_["--man-prologue-file"] = + &::cli::thunk< options, std::string, &options::man_prologue_file_, + &options::man_prologue_file_specified_ >; + _cli_options_map_["--html-prologue-file"] = + &::cli::thunk< options, std::string, &options::html_prologue_file_, + &options::html_prologue_file_specified_ >; + _cli_options_map_["--txt-prologue-file"] = + &::cli::thunk< options, std::string, &options::txt_prologue_file_, + &options::txt_prologue_file_specified_ >; + _cli_options_map_["--hxx-epilogue-file"] = + &::cli::thunk< options, std::string, &options::hxx_epilogue_file_, + &options::hxx_epilogue_file_specified_ >; + _cli_options_map_["--ixx-epilogue-file"] = + &::cli::thunk< options, std::string, &options::ixx_epilogue_file_, + &options::ixx_epilogue_file_specified_ >; + _cli_options_map_["--cxx-epilogue-file"] = + &::cli::thunk< options, std::string, &options::cxx_epilogue_file_, + &options::cxx_epilogue_file_specified_ >; + _cli_options_map_["--man-epilogue-file"] = + &::cli::thunk< options, std::string, &options::man_epilogue_file_, + &options::man_epilogue_file_specified_ >; + _cli_options_map_["--html-epilogue-file"] = + &::cli::thunk< options, std::string, &options::html_epilogue_file_, + &options::html_epilogue_file_specified_ >; + _cli_options_map_["--txt-epilogue-file"] = + &::cli::thunk< options, std::string, &options::txt_epilogue_file_, + &options::txt_epilogue_file_specified_ >; + _cli_options_map_["--output-prefix"] = + &::cli::thunk< options, std::string, &options::output_prefix_, + &options::output_prefix_specified_ >; + _cli_options_map_["--output-suffix"] = + &::cli::thunk< options, std::string, &options::output_suffix_, + &options::output_suffix_specified_ >; + _cli_options_map_["--hxx-suffix"] = + &::cli::thunk< options, std::string, &options::hxx_suffix_, + &options::hxx_suffix_specified_ >; + _cli_options_map_["--ixx-suffix"] = + &::cli::thunk< options, std::string, &options::ixx_suffix_, + &options::ixx_suffix_specified_ >; + _cli_options_map_["--cxx-suffix"] = + &::cli::thunk< options, std::string, &options::cxx_suffix_, + &options::cxx_suffix_specified_ >; + _cli_options_map_["--man-suffix"] = + &::cli::thunk< options, std::string, &options::man_suffix_, + &options::man_suffix_specified_ >; + _cli_options_map_["--html-suffix"] = + &::cli::thunk< options, std::string, &options::html_suffix_, + &options::html_suffix_specified_ >; + _cli_options_map_["--txt-suffix"] = + &::cli::thunk< options, std::string, &options::txt_suffix_, + &options::txt_suffix_specified_ >; + _cli_options_map_["--option-prefix"] = + &::cli::thunk< options, std::string, &options::option_prefix_, + &options::option_prefix_specified_ >; + _cli_options_map_["--option-separator"] = + &::cli::thunk< options, std::string, &options::option_separator_, + &options::option_separator_specified_ >; + _cli_options_map_["--keep-separator"] = + &::cli::thunk< options, bool, &options::keep_separator_ >; + _cli_options_map_["--no-combined-flags"] = + &::cli::thunk< options, bool, &options::no_combined_flags_ >; + _cli_options_map_["--no-combined-values"] = + &::cli::thunk< options, bool, &options::no_combined_values_ >; + _cli_options_map_["--include-with-brackets"] = + &::cli::thunk< options, bool, &options::include_with_brackets_ >; + _cli_options_map_["--include-prefix"] = + &::cli::thunk< options, std::string, &options::include_prefix_, + &options::include_prefix_specified_ >; + _cli_options_map_["--guard-prefix"] = + &::cli::thunk< options, std::string, &options::guard_prefix_, + &options::guard_prefix_specified_ >; + _cli_options_map_["--reserved-name"] = + &::cli::thunk< options, std::map, &options::reserved_name_, + &options::reserved_name_specified_ >; + _cli_options_map_["--options-file"] = + &::cli::thunk< options, std::string, &options::options_file_, + &options::options_file_specified_ >; + } +}; + +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; +} + +bool options:: +_parse (::cli::scanner& s, + ::cli::unknown_mode opt_mode, + ::cli::unknown_mode arg_mode) +{ + // Can't skip combined flags (--no-combined-flags). + // + assert (opt_mode != ::cli::unknown_mode::skip); + + bool r = false; + bool opt = true; + + while (s.more ()) + { + const char* o = s.peek (); + + if (std::strcmp (o, "--") == 0) + { + opt = false; + s.skip (); + r = true; + continue; + } + + if (opt) + { + if (_parse (o, s)) + { + r = true; + continue; + } + + if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') + { + // Handle combined option values. + // + std::string co; + if (const char* v = std::strchr (o, '=')) + { + co.assign (o, 0, v - o); + ++v; + + int ac (2); + char* av[] = + { + const_cast (co.c_str ()), + const_cast (v) + }; + + ::cli::argv_scanner ns (0, ac, av); + + if (_parse (co.c_str (), ns)) + { + // Parsed the option but not its value? + // + if (ns.end () != 2) + throw ::cli::invalid_value (co, v); + + s.next (); + r = true; + continue; + } + else + { + // Set the unknown option and fall through. + // + o = co.c_str (); + } + } + + // Handle combined flags. + // + char cf[3]; + { + const char* p = o + 1; + for (; *p != '\0'; ++p) + { + if (!((*p >= 'a' && *p <= 'z') || + (*p >= 'A' && *p <= 'Z') || + (*p >= '0' && *p <= '9'))) + break; + } + + if (*p == '\0') + { + for (p = o + 1; *p != '\0'; ++p) + { + std::strcpy (cf, "-"); + cf[1] = *p; + cf[2] = '\0'; + + int ac (1); + char* av[] = + { + cf + }; + + ::cli::argv_scanner ns (0, ac, av); + + if (!_parse (cf, ns)) + break; + } + + if (*p == '\0') + { + // All handled. + // + s.next (); + r = true; + continue; + } + else + { + // Set the unknown option and fall through. + // + o = cf; + } + } + } + + switch (opt_mode) + { + case ::cli::unknown_mode::skip: + { + s.skip (); + r = true; + continue; + } + case ::cli::unknown_mode::stop: + { + break; + } + case ::cli::unknown_mode::fail: + { + throw ::cli::unknown_option (o); + } + } + + break; + } + } + + switch (arg_mode) + { + case ::cli::unknown_mode::skip: + { + s.skip (); + r = true; + continue; + } + case ::cli::unknown_mode::stop: + { + break; + } + case ::cli::unknown_mode::fail: + { + throw ::cli::unknown_argument (o); + } + } + + break; + } + + return r; +} + +// Begin epilogue. +// +// +// End epilogue. + diff --git a/cli/cli/options.hxx b/cli/cli/options.hxx new file mode 100644 index 0000000..b253092 --- /dev/null +++ b/cli/cli/options.hxx @@ -0,0 +1,1632 @@ +// -*- C++ -*- +// +// This file was generated by CLI, a command line interface +// compiler for C++. +// + +#ifndef CLI_OPTIONS_HXX +#define CLI_OPTIONS_HXX + +// Begin prologue. +// +// +// End prologue. + +#include +#include +#include +#include +#include +#include + +#ifndef CLI_POTENTIALLY_UNUSED +# if defined(_MSC_VER) || defined(__xlC__) +# define CLI_POTENTIALLY_UNUSED(x) (void*)&x +# else +# define CLI_POTENTIALLY_UNUSED(x) (void)x +# endif +#endif + +namespace cli +{ + class usage_para + { + public: + enum value + { + none, + text, + option + }; + + usage_para (value); + + operator value () const + { + return v_; + } + + private: + value v_; + }; + + class unknown_mode + { + public: + enum value + { + skip, + stop, + fail + }; + + unknown_mode (value); + + operator value () const + { + return v_; + } + + private: + value v_; + }; + + // Exceptions. + // + + class exception: public std::exception + { + public: + virtual void + print (::std::ostream&) const = 0; + }; + + ::std::ostream& + operator<< (::std::ostream&, const exception&); + + class unknown_option: public exception + { + public: + virtual + ~unknown_option () throw (); + + unknown_option (const std::string& option); + + const std::string& + option () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string option_; + }; + + class unknown_argument: public exception + { + public: + virtual + ~unknown_argument () throw (); + + unknown_argument (const std::string& argument); + + const std::string& + argument () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string argument_; + }; + + class missing_value: public exception + { + public: + virtual + ~missing_value () throw (); + + missing_value (const std::string& option); + + const std::string& + option () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string option_; + }; + + class invalid_value: public exception + { + public: + virtual + ~invalid_value () throw (); + + invalid_value (const std::string& option, + const std::string& value, + const std::string& message = std::string ()); + + const std::string& + option () const; + + const std::string& + value () const; + + const std::string& + message () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string option_; + std::string value_; + std::string message_; + }; + + class eos_reached: public exception + { + public: + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + }; + + class file_io_failure: public exception + { + public: + virtual + ~file_io_failure () throw (); + + file_io_failure (const std::string& file); + + const std::string& + file () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string file_; + }; + + class unmatched_quote: public exception + { + public: + virtual + ~unmatched_quote () throw (); + + unmatched_quote (const std::string& argument); + + const std::string& + argument () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string argument_; + }; + + // Command line argument scanner interface. + // + // The values returned by next() are guaranteed to be valid + // for the two previous arguments up until a call to a third + // peek() or next(). + // + class scanner + { + public: + virtual + ~scanner (); + + virtual bool + more () = 0; + + virtual const char* + peek () = 0; + + virtual const char* + next () = 0; + + virtual void + skip () = 0; + }; + + class argv_scanner: public scanner + { + public: + argv_scanner (int& argc, char** argv, bool erase = false); + argv_scanner (int start, int& argc, char** argv, bool erase = false); + + int + end () const; + + virtual bool + more (); + + virtual const char* + peek (); + + virtual const char* + next (); + + virtual void + skip (); + + private: + int i_; + int& argc_; + char** argv_; + bool erase_; + }; + + class argv_file_scanner: public argv_scanner + { + public: + argv_file_scanner (int& argc, + char** argv, + const std::string& option, + bool erase = false); + + argv_file_scanner (int start, + int& argc, + char** argv, + const std::string& option, + bool erase = false); + + argv_file_scanner (const std::string& file, + const std::string& option); + + 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 option_info* options, + std::size_t options_count, + bool erase = false); + + argv_file_scanner (const std::string& file, + const option_info* options = 0, + std::size_t options_count = 0); + + virtual bool + more (); + + virtual const char* + peek (); + + virtual const char* + next (); + + virtual void + skip (); + + // Return the file path if the peeked at argument came from a file and + // the empty string otherwise. The reference is guaranteed to be valid + // till the end of the scanner lifetime. + // + const std::string& + peek_file (); + + // Return the 1-based line number if the peeked at argument came from + // a file and zero otherwise. + // + std::size_t + peek_line (); + + private: + const option_info* + find (const char*) const; + + void + 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_; + + struct arg + { + std::string value; + const std::string* file; + std::size_t line; + }; + + std::deque args_; + std::list files_; + + // Circular buffer of two arguments. + // + std::string hold_[2]; + std::size_t i_; + + bool skip_; + + static int zero_argc_; + static std::string empty_string_; + }; + + template + struct parser; +} + +#include + +#include + +#include + +#include + +#include + +class options +{ + public: + options (); + + options (int& argc, + char** argv, + bool erase = false, + ::cli::unknown_mode option = ::cli::unknown_mode::fail, + ::cli::unknown_mode argument = ::cli::unknown_mode::stop); + + options (int start, + int& argc, + char** argv, + bool erase = false, + ::cli::unknown_mode option = ::cli::unknown_mode::fail, + ::cli::unknown_mode argument = ::cli::unknown_mode::stop); + + options (int& argc, + char** argv, + int& end, + bool erase = false, + ::cli::unknown_mode option = ::cli::unknown_mode::fail, + ::cli::unknown_mode argument = ::cli::unknown_mode::stop); + + options (int start, + int& argc, + char** argv, + int& end, + bool erase = false, + ::cli::unknown_mode option = ::cli::unknown_mode::fail, + ::cli::unknown_mode argument = ::cli::unknown_mode::stop); + + options (::cli::scanner&, + ::cli::unknown_mode option = ::cli::unknown_mode::fail, + ::cli::unknown_mode argument = ::cli::unknown_mode::stop); + + // Option accessors and modifiers. + // + const bool& + build2_metadata () const; + + bool& + build2_metadata (); + + void + build2_metadata (const bool&); + + const bool& + help () const; + + bool& + help (); + + void + help (const bool&); + + const bool& + version () const; + + bool& + version (); + + void + version (const bool&); + + const std::vector& + include_path () const; + + std::vector& + include_path (); + + void + include_path (const std::vector&); + + bool + include_path_specified () const; + + void + include_path_specified (bool); + + const std::string& + output_dir () const; + + std::string& + output_dir (); + + void + output_dir (const std::string&); + + bool + output_dir_specified () const; + + void + output_dir_specified (bool); + + const cxx_version& + std () const; + + cxx_version& + std (); + + void + std (const cxx_version&); + + bool + std_specified () const; + + void + std_specified (bool); + + const bool& + generate_modifier () const; + + bool& + generate_modifier (); + + void + generate_modifier (const bool&); + + const bool& + generate_specifier () const; + + bool& + generate_specifier (); + + void + generate_specifier (const bool&); + + const bool& + generate_parse () const; + + bool& + generate_parse (); + + void + generate_parse (const bool&); + + const bool& + generate_merge () const; + + bool& + generate_merge (); + + void + generate_merge (const bool&); + + const bool& + generate_description () const; + + bool& + generate_description (); + + void + generate_description (const bool&); + + const bool& + generate_file_scanner () const; + + bool& + generate_file_scanner (); + + void + generate_file_scanner (const bool&); + + const bool& + generate_vector_scanner () const; + + bool& + generate_vector_scanner (); + + void + generate_vector_scanner (const bool&); + + const bool& + generate_group_scanner () const; + + bool& + generate_group_scanner (); + + void + generate_group_scanner (const bool&); + + const bool& + suppress_inline () const; + + bool& + suppress_inline (); + + void + suppress_inline (const bool&); + + const bool& + suppress_cli () const; + + bool& + suppress_cli (); + + void + suppress_cli (const bool&); + + const std::string& + cli_namespace () const; + + std::string& + cli_namespace (); + + void + cli_namespace (const std::string&); + + bool + cli_namespace_specified () const; + + void + cli_namespace_specified (bool); + + const std::string& + ostream_type () const; + + std::string& + ostream_type (); + + void + ostream_type (const std::string&); + + bool + ostream_type_specified () const; + + void + ostream_type_specified (bool); + + const bool& + generate_cxx () const; + + bool& + generate_cxx (); + + void + generate_cxx (const bool&); + + const bool& + generate_man () const; + + bool& + generate_man (); + + void + generate_man (const bool&); + + const bool& + generate_html () const; + + bool& + generate_html (); + + void + generate_html (const bool&); + + const bool& + generate_txt () const; + + bool& + generate_txt (); + + void + generate_txt (const bool&); + + const bool& + stdout_ () const; + + bool& + stdout_ (); + + void + stdout_ (const bool&); + + const bool& + suppress_undocumented () const; + + bool& + suppress_undocumented (); + + void + suppress_undocumented (const bool&); + + const bool& + suppress_usage () const; + + bool& + suppress_usage (); + + void + suppress_usage (const bool&); + + const bool& + long_usage () const; + + bool& + long_usage (); + + void + long_usage (const bool&); + + const bool& + short_usage () const; + + bool& + short_usage (); + + void + short_usage (const bool&); + + const std::string& + page_usage () const; + + std::string& + page_usage (); + + void + page_usage (const std::string&); + + bool + page_usage_specified () const; + + void + page_usage_specified (bool); + + const std::size_t& + option_length () const; + + std::size_t& + option_length (); + + void + option_length (const std::size_t&); + + bool + option_length_specified () const; + + void + option_length_specified (bool); + + const bool& + ansi_color () const; + + bool& + ansi_color (); + + void + ansi_color (const bool&); + + const bool& + exclude_base () const; + + bool& + exclude_base (); + + void + exclude_base (const bool&); + + const bool& + include_base_last () const; + + bool& + include_base_last (); + + void + include_base_last (const bool&); + + const std::map& + class_doc () const; + + std::map& + class_doc (); + + void + class_doc (const std::map&); + + bool + class_doc_specified () const; + + void + class_doc_specified (bool); + + const std::vector& + class_ () const; + + std::vector& + class_ (); + + void + class_ (const std::vector&); + + bool + class__specified () const; + + void + class__specified (bool); + + const std::map& + docvar () const; + + std::map& + docvar (); + + void + docvar (const std::map&); + + bool + docvar_specified () const; + + void + docvar_specified (bool); + + const std::vector& + link_regex () const; + + std::vector& + link_regex (); + + void + link_regex (const std::vector&); + + bool + link_regex_specified () const; + + void + link_regex_specified (bool); + + const bool& + link_regex_trace () const; + + bool& + link_regex_trace (); + + void + link_regex_trace (const bool&); + + const std::map& + html_heading_map () const; + + std::map& + html_heading_map (); + + void + html_heading_map (const std::map&); + + bool + html_heading_map_specified () const; + + void + html_heading_map_specified (bool); + + const bool& + omit_link_check () const; + + bool& + omit_link_check (); + + void + omit_link_check (const bool&); + + const std::vector& + hxx_prologue () const; + + std::vector& + hxx_prologue (); + + void + hxx_prologue (const std::vector&); + + bool + hxx_prologue_specified () const; + + void + hxx_prologue_specified (bool); + + const std::vector& + ixx_prologue () const; + + std::vector& + ixx_prologue (); + + void + ixx_prologue (const std::vector&); + + bool + ixx_prologue_specified () const; + + void + ixx_prologue_specified (bool); + + const std::vector& + cxx_prologue () const; + + std::vector& + cxx_prologue (); + + void + cxx_prologue (const std::vector&); + + bool + cxx_prologue_specified () const; + + void + cxx_prologue_specified (bool); + + const std::vector& + man_prologue () const; + + std::vector& + man_prologue (); + + void + man_prologue (const std::vector&); + + bool + man_prologue_specified () const; + + void + man_prologue_specified (bool); + + const std::vector& + html_prologue () const; + + std::vector& + html_prologue (); + + void + html_prologue (const std::vector&); + + bool + html_prologue_specified () const; + + void + html_prologue_specified (bool); + + const std::vector& + txt_prologue () const; + + std::vector& + txt_prologue (); + + void + txt_prologue (const std::vector&); + + bool + txt_prologue_specified () const; + + void + txt_prologue_specified (bool); + + const std::vector& + hxx_epilogue () const; + + std::vector& + hxx_epilogue (); + + void + hxx_epilogue (const std::vector&); + + bool + hxx_epilogue_specified () const; + + void + hxx_epilogue_specified (bool); + + const std::vector& + ixx_epilogue () const; + + std::vector& + ixx_epilogue (); + + void + ixx_epilogue (const std::vector&); + + bool + ixx_epilogue_specified () const; + + void + ixx_epilogue_specified (bool); + + const std::vector& + cxx_epilogue () const; + + std::vector& + cxx_epilogue (); + + void + cxx_epilogue (const std::vector&); + + bool + cxx_epilogue_specified () const; + + void + cxx_epilogue_specified (bool); + + const std::vector& + man_epilogue () const; + + std::vector& + man_epilogue (); + + void + man_epilogue (const std::vector&); + + bool + man_epilogue_specified () const; + + void + man_epilogue_specified (bool); + + const std::vector& + html_epilogue () const; + + std::vector& + html_epilogue (); + + void + html_epilogue (const std::vector&); + + bool + html_epilogue_specified () const; + + void + html_epilogue_specified (bool); + + const std::vector& + txt_epilogue () const; + + std::vector& + txt_epilogue (); + + void + txt_epilogue (const std::vector&); + + bool + txt_epilogue_specified () const; + + void + txt_epilogue_specified (bool); + + const std::string& + hxx_prologue_file () const; + + std::string& + hxx_prologue_file (); + + void + hxx_prologue_file (const std::string&); + + bool + hxx_prologue_file_specified () const; + + void + hxx_prologue_file_specified (bool); + + const std::string& + ixx_prologue_file () const; + + std::string& + ixx_prologue_file (); + + void + ixx_prologue_file (const std::string&); + + bool + ixx_prologue_file_specified () const; + + void + ixx_prologue_file_specified (bool); + + const std::string& + cxx_prologue_file () const; + + std::string& + cxx_prologue_file (); + + void + cxx_prologue_file (const std::string&); + + bool + cxx_prologue_file_specified () const; + + void + cxx_prologue_file_specified (bool); + + const std::string& + man_prologue_file () const; + + std::string& + man_prologue_file (); + + void + man_prologue_file (const std::string&); + + bool + man_prologue_file_specified () const; + + void + man_prologue_file_specified (bool); + + const std::string& + html_prologue_file () const; + + std::string& + html_prologue_file (); + + void + html_prologue_file (const std::string&); + + bool + html_prologue_file_specified () const; + + void + html_prologue_file_specified (bool); + + const std::string& + txt_prologue_file () const; + + std::string& + txt_prologue_file (); + + void + txt_prologue_file (const std::string&); + + bool + txt_prologue_file_specified () const; + + void + txt_prologue_file_specified (bool); + + const std::string& + hxx_epilogue_file () const; + + std::string& + hxx_epilogue_file (); + + void + hxx_epilogue_file (const std::string&); + + bool + hxx_epilogue_file_specified () const; + + void + hxx_epilogue_file_specified (bool); + + const std::string& + ixx_epilogue_file () const; + + std::string& + ixx_epilogue_file (); + + void + ixx_epilogue_file (const std::string&); + + bool + ixx_epilogue_file_specified () const; + + void + ixx_epilogue_file_specified (bool); + + const std::string& + cxx_epilogue_file () const; + + std::string& + cxx_epilogue_file (); + + void + cxx_epilogue_file (const std::string&); + + bool + cxx_epilogue_file_specified () const; + + void + cxx_epilogue_file_specified (bool); + + const std::string& + man_epilogue_file () const; + + std::string& + man_epilogue_file (); + + void + man_epilogue_file (const std::string&); + + bool + man_epilogue_file_specified () const; + + void + man_epilogue_file_specified (bool); + + const std::string& + html_epilogue_file () const; + + std::string& + html_epilogue_file (); + + void + html_epilogue_file (const std::string&); + + bool + html_epilogue_file_specified () const; + + void + html_epilogue_file_specified (bool); + + const std::string& + txt_epilogue_file () const; + + std::string& + txt_epilogue_file (); + + void + txt_epilogue_file (const std::string&); + + bool + txt_epilogue_file_specified () const; + + void + txt_epilogue_file_specified (bool); + + const std::string& + output_prefix () const; + + std::string& + output_prefix (); + + void + output_prefix (const std::string&); + + bool + output_prefix_specified () const; + + void + output_prefix_specified (bool); + + const std::string& + output_suffix () const; + + std::string& + output_suffix (); + + void + output_suffix (const std::string&); + + bool + output_suffix_specified () const; + + void + output_suffix_specified (bool); + + const std::string& + hxx_suffix () const; + + std::string& + hxx_suffix (); + + void + hxx_suffix (const std::string&); + + bool + hxx_suffix_specified () const; + + void + hxx_suffix_specified (bool); + + const std::string& + ixx_suffix () const; + + std::string& + ixx_suffix (); + + void + ixx_suffix (const std::string&); + + bool + ixx_suffix_specified () const; + + void + ixx_suffix_specified (bool); + + const std::string& + cxx_suffix () const; + + std::string& + cxx_suffix (); + + void + cxx_suffix (const std::string&); + + bool + cxx_suffix_specified () const; + + void + cxx_suffix_specified (bool); + + const std::string& + man_suffix () const; + + std::string& + man_suffix (); + + void + man_suffix (const std::string&); + + bool + man_suffix_specified () const; + + void + man_suffix_specified (bool); + + const std::string& + html_suffix () const; + + std::string& + html_suffix (); + + void + html_suffix (const std::string&); + + bool + html_suffix_specified () const; + + void + html_suffix_specified (bool); + + const std::string& + txt_suffix () const; + + std::string& + txt_suffix (); + + void + txt_suffix (const std::string&); + + bool + txt_suffix_specified () const; + + void + txt_suffix_specified (bool); + + const std::string& + option_prefix () const; + + std::string& + option_prefix (); + + void + option_prefix (const std::string&); + + bool + option_prefix_specified () const; + + void + option_prefix_specified (bool); + + const std::string& + option_separator () const; + + std::string& + option_separator (); + + void + option_separator (const std::string&); + + bool + option_separator_specified () const; + + void + option_separator_specified (bool); + + const bool& + keep_separator () const; + + bool& + keep_separator (); + + void + keep_separator (const bool&); + + const bool& + no_combined_flags () const; + + bool& + no_combined_flags (); + + void + no_combined_flags (const bool&); + + const bool& + no_combined_values () const; + + bool& + no_combined_values (); + + void + no_combined_values (const bool&); + + const bool& + include_with_brackets () const; + + bool& + include_with_brackets (); + + void + include_with_brackets (const bool&); + + const std::string& + include_prefix () const; + + std::string& + include_prefix (); + + void + include_prefix (const std::string&); + + bool + include_prefix_specified () const; + + void + include_prefix_specified (bool); + + const std::string& + guard_prefix () const; + + std::string& + guard_prefix (); + + void + guard_prefix (const std::string&); + + bool + guard_prefix_specified () const; + + void + guard_prefix_specified (bool); + + const std::map& + reserved_name () const; + + std::map& + reserved_name (); + + void + reserved_name (const std::map&); + + bool + reserved_name_specified () const; + + void + reserved_name_specified (bool); + + const std::string& + options_file () const; + + std::string& + options_file (); + + void + options_file (const std::string&); + + bool + options_file_specified () const; + + void + options_file_specified (bool); + + // Print usage information. + // + static ::cli::usage_para + print_usage (::std::ostream&, + ::cli::usage_para = ::cli::usage_para::none); + + // Implementation details. + // + protected: + bool + _parse (const char*, ::cli::scanner&); + + private: + bool + _parse (::cli::scanner&, + ::cli::unknown_mode option, + ::cli::unknown_mode argument); + + public: + bool build2_metadata_; + bool help_; + bool version_; + std::vector include_path_; + bool include_path_specified_; + std::string output_dir_; + bool output_dir_specified_; + cxx_version std_; + bool std_specified_; + bool generate_modifier_; + bool generate_specifier_; + bool generate_parse_; + bool generate_merge_; + bool generate_description_; + bool generate_file_scanner_; + bool generate_vector_scanner_; + bool generate_group_scanner_; + bool suppress_inline_; + bool suppress_cli_; + std::string cli_namespace_; + bool cli_namespace_specified_; + std::string ostream_type_; + bool ostream_type_specified_; + bool generate_cxx_; + bool generate_man_; + bool generate_html_; + bool generate_txt_; + bool stdout__; + bool suppress_undocumented_; + bool suppress_usage_; + bool long_usage_; + bool short_usage_; + std::string page_usage_; + bool page_usage_specified_; + std::size_t option_length_; + bool option_length_specified_; + bool ansi_color_; + bool exclude_base_; + bool include_base_last_; + std::map class_doc_; + bool class_doc_specified_; + std::vector class__; + bool class__specified_; + std::map docvar_; + bool docvar_specified_; + std::vector link_regex_; + bool link_regex_specified_; + bool link_regex_trace_; + std::map html_heading_map_; + bool html_heading_map_specified_; + bool omit_link_check_; + std::vector hxx_prologue_; + bool hxx_prologue_specified_; + std::vector ixx_prologue_; + bool ixx_prologue_specified_; + std::vector cxx_prologue_; + bool cxx_prologue_specified_; + std::vector man_prologue_; + bool man_prologue_specified_; + std::vector html_prologue_; + bool html_prologue_specified_; + std::vector txt_prologue_; + bool txt_prologue_specified_; + std::vector hxx_epilogue_; + bool hxx_epilogue_specified_; + std::vector ixx_epilogue_; + bool ixx_epilogue_specified_; + std::vector cxx_epilogue_; + bool cxx_epilogue_specified_; + std::vector man_epilogue_; + bool man_epilogue_specified_; + std::vector html_epilogue_; + bool html_epilogue_specified_; + std::vector txt_epilogue_; + bool txt_epilogue_specified_; + std::string hxx_prologue_file_; + bool hxx_prologue_file_specified_; + std::string ixx_prologue_file_; + bool ixx_prologue_file_specified_; + std::string cxx_prologue_file_; + bool cxx_prologue_file_specified_; + std::string man_prologue_file_; + bool man_prologue_file_specified_; + std::string html_prologue_file_; + bool html_prologue_file_specified_; + std::string txt_prologue_file_; + bool txt_prologue_file_specified_; + std::string hxx_epilogue_file_; + bool hxx_epilogue_file_specified_; + std::string ixx_epilogue_file_; + bool ixx_epilogue_file_specified_; + std::string cxx_epilogue_file_; + bool cxx_epilogue_file_specified_; + std::string man_epilogue_file_; + bool man_epilogue_file_specified_; + std::string html_epilogue_file_; + bool html_epilogue_file_specified_; + std::string txt_epilogue_file_; + bool txt_epilogue_file_specified_; + std::string output_prefix_; + bool output_prefix_specified_; + std::string output_suffix_; + bool output_suffix_specified_; + std::string hxx_suffix_; + bool hxx_suffix_specified_; + std::string ixx_suffix_; + bool ixx_suffix_specified_; + std::string cxx_suffix_; + bool cxx_suffix_specified_; + std::string man_suffix_; + bool man_suffix_specified_; + std::string html_suffix_; + bool html_suffix_specified_; + std::string txt_suffix_; + bool txt_suffix_specified_; + std::string option_prefix_; + bool option_prefix_specified_; + std::string option_separator_; + bool option_separator_specified_; + bool keep_separator_; + bool no_combined_flags_; + bool no_combined_values_; + bool include_with_brackets_; + std::string include_prefix_; + bool include_prefix_specified_; + std::string guard_prefix_; + bool guard_prefix_specified_; + std::map reserved_name_; + bool reserved_name_specified_; + std::string options_file_; + bool options_file_specified_; +}; + +#include + +// Begin epilogue. +// +// +// End epilogue. + +#endif // CLI_OPTIONS_HXX diff --git a/cli/cli/options.ixx b/cli/cli/options.ixx new file mode 100644 index 0000000..12d40db --- /dev/null +++ b/cli/cli/options.ixx @@ -0,0 +1,2319 @@ +// -*- C++ -*- +// +// This file was generated by CLI, a command line interface +// compiler for C++. +// + +// Begin prologue. +// +// +// End prologue. + +#include + +namespace cli +{ + // usage_para + // + inline usage_para:: + usage_para (value v) + : v_ (v) + { + } + + // unknown_mode + // + inline unknown_mode:: + unknown_mode (value v) + : v_ (v) + { + } + + // exception + // + inline ::std::ostream& + operator<< (::std::ostream& os, const exception& e) + { + e.print (os); + return os; + } + + // unknown_option + // + inline unknown_option:: + unknown_option (const std::string& option) + : option_ (option) + { + } + + inline const std::string& unknown_option:: + option () const + { + return option_; + } + + // unknown_argument + // + inline unknown_argument:: + unknown_argument (const std::string& argument) + : argument_ (argument) + { + } + + inline const std::string& unknown_argument:: + argument () const + { + return argument_; + } + + // missing_value + // + inline missing_value:: + missing_value (const std::string& option) + : option_ (option) + { + } + + inline const std::string& missing_value:: + option () const + { + return option_; + } + + // invalid_value + // + inline invalid_value:: + invalid_value (const std::string& option, + const std::string& value, + const std::string& message) + : option_ (option), + value_ (value), + message_ (message) + { + } + + inline const std::string& invalid_value:: + option () const + { + return option_; + } + + inline const std::string& invalid_value:: + value () const + { + return value_; + } + + inline const std::string& invalid_value:: + message () const + { + return message_; + } + + // file_io_failure + // + inline file_io_failure:: + file_io_failure (const std::string& file) + : file_ (file) + { + } + + inline const std::string& file_io_failure:: + file () const + { + return file_; + } + + // unmatched_quote + // + inline unmatched_quote:: + unmatched_quote (const std::string& argument) + : argument_ (argument) + { + } + + inline const std::string& unmatched_quote:: + argument () const + { + return argument_; + } + + // argv_scanner + // + inline argv_scanner:: + argv_scanner (int& argc, char** argv, bool erase) + : i_ (1), argc_ (argc), argv_ (argv), erase_ (erase) + { + } + + inline argv_scanner:: + argv_scanner (int start, int& argc, char** argv, bool erase) + : i_ (start), argc_ (argc), argv_ (argv), erase_ (erase) + { + } + + inline int argv_scanner:: + end () const + { + return i_; + } + + // argv_file_scanner + // + inline argv_file_scanner:: + argv_file_scanner (int& argc, + char** argv, + const std::string& option, + bool erase) + : argv_scanner (argc, argv, erase), + option_ (option), + options_ (&option_info_), + options_count_ (1), + i_ (1), + skip_ (false) + { + option_info_.option = option_.c_str (); + option_info_.search_func = 0; + } + + inline argv_file_scanner:: + argv_file_scanner (int start, + int& argc, + char** argv, + const std::string& option, + bool erase) + : argv_scanner (start, argc, argv, erase), + option_ (option), + options_ (&option_info_), + options_count_ (1), + i_ (1), + skip_ (false) + { + option_info_.option = option_.c_str (); + option_info_.search_func = 0; + } + + inline argv_file_scanner:: + argv_file_scanner (const std::string& file, + const std::string& option) + : argv_scanner (0, zero_argc_, 0), + option_ (option), + options_ (&option_info_), + options_count_ (1), + i_ (1), + skip_ (false) + { + option_info_.option = option_.c_str (); + option_info_.search_func = 0; + + load (file); + } + + 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), + i_ (1), + 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), + i_ (1), + skip_ (false) + { + } + + inline argv_file_scanner:: + argv_file_scanner (const std::string& file, + const option_info* options, + std::size_t options_count) + : argv_scanner (0, zero_argc_, 0), + options_ (options), + options_count_ (options_count), + i_ (1), + skip_ (false) + { + load (file); + } +} + +// options +// + +inline const bool& options:: +build2_metadata () const +{ + return this->build2_metadata_; +} + +inline bool& options:: +build2_metadata () +{ + return this->build2_metadata_; +} + +inline void options:: +build2_metadata(const bool& x) +{ + this->build2_metadata_ = x; +} + +inline const bool& options:: +help () const +{ + return this->help_; +} + +inline bool& options:: +help () +{ + return this->help_; +} + +inline void options:: +help(const bool& x) +{ + this->help_ = x; +} + +inline const bool& options:: +version () const +{ + return this->version_; +} + +inline bool& options:: +version () +{ + return this->version_; +} + +inline void options:: +version(const bool& x) +{ + this->version_ = x; +} + +inline const std::vector& options:: +include_path () const +{ + return this->include_path_; +} + +inline std::vector& options:: +include_path () +{ + return this->include_path_; +} + +inline void options:: +include_path(const std::vector& x) +{ + this->include_path_ = x; +} + +inline bool options:: +include_path_specified () const +{ + return this->include_path_specified_; +} + +inline void options:: +include_path_specified(bool x) +{ + this->include_path_specified_ = x; +} + +inline const std::string& options:: +output_dir () const +{ + return this->output_dir_; +} + +inline std::string& options:: +output_dir () +{ + return this->output_dir_; +} + +inline void options:: +output_dir(const std::string& x) +{ + this->output_dir_ = x; +} + +inline bool options:: +output_dir_specified () const +{ + return this->output_dir_specified_; +} + +inline void options:: +output_dir_specified(bool x) +{ + this->output_dir_specified_ = x; +} + +inline const cxx_version& options:: +std () const +{ + return this->std_; +} + +inline cxx_version& options:: +std () +{ + return this->std_; +} + +inline void options:: +std(const cxx_version& x) +{ + this->std_ = x; +} + +inline bool options:: +std_specified () const +{ + return this->std_specified_; +} + +inline void options:: +std_specified(bool x) +{ + this->std_specified_ = x; +} + +inline const bool& options:: +generate_modifier () const +{ + return this->generate_modifier_; +} + +inline bool& options:: +generate_modifier () +{ + return this->generate_modifier_; +} + +inline void options:: +generate_modifier(const bool& x) +{ + this->generate_modifier_ = x; +} + +inline const bool& options:: +generate_specifier () const +{ + return this->generate_specifier_; +} + +inline bool& options:: +generate_specifier () +{ + return this->generate_specifier_; +} + +inline void options:: +generate_specifier(const bool& x) +{ + this->generate_specifier_ = x; +} + +inline const bool& options:: +generate_parse () const +{ + return this->generate_parse_; +} + +inline bool& options:: +generate_parse () +{ + return this->generate_parse_; +} + +inline void options:: +generate_parse(const bool& x) +{ + this->generate_parse_ = x; +} + +inline const bool& options:: +generate_merge () const +{ + return this->generate_merge_; +} + +inline bool& options:: +generate_merge () +{ + return this->generate_merge_; +} + +inline void options:: +generate_merge(const bool& x) +{ + this->generate_merge_ = x; +} + +inline const bool& options:: +generate_description () const +{ + return this->generate_description_; +} + +inline bool& options:: +generate_description () +{ + return this->generate_description_; +} + +inline void options:: +generate_description(const bool& x) +{ + this->generate_description_ = x; +} + +inline const bool& options:: +generate_file_scanner () const +{ + return this->generate_file_scanner_; +} + +inline bool& options:: +generate_file_scanner () +{ + return this->generate_file_scanner_; +} + +inline void options:: +generate_file_scanner(const bool& x) +{ + this->generate_file_scanner_ = x; +} + +inline const bool& options:: +generate_vector_scanner () const +{ + return this->generate_vector_scanner_; +} + +inline bool& options:: +generate_vector_scanner () +{ + return this->generate_vector_scanner_; +} + +inline void options:: +generate_vector_scanner(const bool& x) +{ + this->generate_vector_scanner_ = x; +} + +inline const bool& options:: +generate_group_scanner () const +{ + return this->generate_group_scanner_; +} + +inline bool& options:: +generate_group_scanner () +{ + return this->generate_group_scanner_; +} + +inline void options:: +generate_group_scanner(const bool& x) +{ + this->generate_group_scanner_ = x; +} + +inline const bool& options:: +suppress_inline () const +{ + return this->suppress_inline_; +} + +inline bool& options:: +suppress_inline () +{ + return this->suppress_inline_; +} + +inline void options:: +suppress_inline(const bool& x) +{ + this->suppress_inline_ = x; +} + +inline const bool& options:: +suppress_cli () const +{ + return this->suppress_cli_; +} + +inline bool& options:: +suppress_cli () +{ + return this->suppress_cli_; +} + +inline void options:: +suppress_cli(const bool& x) +{ + this->suppress_cli_ = x; +} + +inline const std::string& options:: +cli_namespace () const +{ + return this->cli_namespace_; +} + +inline std::string& options:: +cli_namespace () +{ + return this->cli_namespace_; +} + +inline void options:: +cli_namespace(const std::string& x) +{ + this->cli_namespace_ = x; +} + +inline bool options:: +cli_namespace_specified () const +{ + return this->cli_namespace_specified_; +} + +inline void options:: +cli_namespace_specified(bool x) +{ + this->cli_namespace_specified_ = x; +} + +inline const std::string& options:: +ostream_type () const +{ + return this->ostream_type_; +} + +inline std::string& options:: +ostream_type () +{ + return this->ostream_type_; +} + +inline void options:: +ostream_type(const std::string& x) +{ + this->ostream_type_ = x; +} + +inline bool options:: +ostream_type_specified () const +{ + return this->ostream_type_specified_; +} + +inline void options:: +ostream_type_specified(bool x) +{ + this->ostream_type_specified_ = x; +} + +inline const bool& options:: +generate_cxx () const +{ + return this->generate_cxx_; +} + +inline bool& options:: +generate_cxx () +{ + return this->generate_cxx_; +} + +inline void options:: +generate_cxx(const bool& x) +{ + this->generate_cxx_ = x; +} + +inline const bool& options:: +generate_man () const +{ + return this->generate_man_; +} + +inline bool& options:: +generate_man () +{ + return this->generate_man_; +} + +inline void options:: +generate_man(const bool& x) +{ + this->generate_man_ = x; +} + +inline const bool& options:: +generate_html () const +{ + return this->generate_html_; +} + +inline bool& options:: +generate_html () +{ + return this->generate_html_; +} + +inline void options:: +generate_html(const bool& x) +{ + this->generate_html_ = x; +} + +inline const bool& options:: +generate_txt () const +{ + return this->generate_txt_; +} + +inline bool& options:: +generate_txt () +{ + return this->generate_txt_; +} + +inline void options:: +generate_txt(const bool& x) +{ + this->generate_txt_ = x; +} + +inline const bool& options:: +stdout_ () const +{ + return this->stdout__; +} + +inline bool& options:: +stdout_ () +{ + return this->stdout__; +} + +inline void options:: +stdout_(const bool& x) +{ + this->stdout__ = x; +} + +inline const bool& options:: +suppress_undocumented () const +{ + return this->suppress_undocumented_; +} + +inline bool& options:: +suppress_undocumented () +{ + return this->suppress_undocumented_; +} + +inline void options:: +suppress_undocumented(const bool& x) +{ + this->suppress_undocumented_ = x; +} + +inline const bool& options:: +suppress_usage () const +{ + return this->suppress_usage_; +} + +inline bool& options:: +suppress_usage () +{ + return this->suppress_usage_; +} + +inline void options:: +suppress_usage(const bool& x) +{ + this->suppress_usage_ = x; +} + +inline const bool& options:: +long_usage () const +{ + return this->long_usage_; +} + +inline bool& options:: +long_usage () +{ + return this->long_usage_; +} + +inline void options:: +long_usage(const bool& x) +{ + this->long_usage_ = x; +} + +inline const bool& options:: +short_usage () const +{ + return this->short_usage_; +} + +inline bool& options:: +short_usage () +{ + return this->short_usage_; +} + +inline void options:: +short_usage(const bool& x) +{ + this->short_usage_ = x; +} + +inline const std::string& options:: +page_usage () const +{ + return this->page_usage_; +} + +inline std::string& options:: +page_usage () +{ + return this->page_usage_; +} + +inline void options:: +page_usage(const std::string& x) +{ + this->page_usage_ = x; +} + +inline bool options:: +page_usage_specified () const +{ + return this->page_usage_specified_; +} + +inline void options:: +page_usage_specified(bool x) +{ + this->page_usage_specified_ = x; +} + +inline const std::size_t& options:: +option_length () const +{ + return this->option_length_; +} + +inline std::size_t& options:: +option_length () +{ + return this->option_length_; +} + +inline void options:: +option_length(const std::size_t& x) +{ + this->option_length_ = x; +} + +inline bool options:: +option_length_specified () const +{ + return this->option_length_specified_; +} + +inline void options:: +option_length_specified(bool x) +{ + this->option_length_specified_ = x; +} + +inline const bool& options:: +ansi_color () const +{ + return this->ansi_color_; +} + +inline bool& options:: +ansi_color () +{ + return this->ansi_color_; +} + +inline void options:: +ansi_color(const bool& x) +{ + this->ansi_color_ = x; +} + +inline const bool& options:: +exclude_base () const +{ + return this->exclude_base_; +} + +inline bool& options:: +exclude_base () +{ + return this->exclude_base_; +} + +inline void options:: +exclude_base(const bool& x) +{ + this->exclude_base_ = x; +} + +inline const bool& options:: +include_base_last () const +{ + return this->include_base_last_; +} + +inline bool& options:: +include_base_last () +{ + return this->include_base_last_; +} + +inline void options:: +include_base_last(const bool& x) +{ + this->include_base_last_ = x; +} + +inline const std::map& options:: +class_doc () const +{ + return this->class_doc_; +} + +inline std::map& options:: +class_doc () +{ + return this->class_doc_; +} + +inline void options:: +class_doc(const std::map& x) +{ + this->class_doc_ = x; +} + +inline bool options:: +class_doc_specified () const +{ + return this->class_doc_specified_; +} + +inline void options:: +class_doc_specified(bool x) +{ + this->class_doc_specified_ = x; +} + +inline const std::vector& options:: +class_ () const +{ + return this->class__; +} + +inline std::vector& options:: +class_ () +{ + return this->class__; +} + +inline void options:: +class_(const std::vector& x) +{ + this->class__ = x; +} + +inline bool options:: +class__specified () const +{ + return this->class__specified_; +} + +inline void options:: +class__specified(bool x) +{ + this->class__specified_ = x; +} + +inline const std::map& options:: +docvar () const +{ + return this->docvar_; +} + +inline std::map& options:: +docvar () +{ + return this->docvar_; +} + +inline void options:: +docvar(const std::map& x) +{ + this->docvar_ = x; +} + +inline bool options:: +docvar_specified () const +{ + return this->docvar_specified_; +} + +inline void options:: +docvar_specified(bool x) +{ + this->docvar_specified_ = x; +} + +inline const std::vector& options:: +link_regex () const +{ + return this->link_regex_; +} + +inline std::vector& options:: +link_regex () +{ + return this->link_regex_; +} + +inline void options:: +link_regex(const std::vector& x) +{ + this->link_regex_ = x; +} + +inline bool options:: +link_regex_specified () const +{ + return this->link_regex_specified_; +} + +inline void options:: +link_regex_specified(bool x) +{ + this->link_regex_specified_ = x; +} + +inline const bool& options:: +link_regex_trace () const +{ + return this->link_regex_trace_; +} + +inline bool& options:: +link_regex_trace () +{ + return this->link_regex_trace_; +} + +inline void options:: +link_regex_trace(const bool& x) +{ + this->link_regex_trace_ = x; +} + +inline const std::map& options:: +html_heading_map () const +{ + return this->html_heading_map_; +} + +inline std::map& options:: +html_heading_map () +{ + return this->html_heading_map_; +} + +inline void options:: +html_heading_map(const std::map& x) +{ + this->html_heading_map_ = x; +} + +inline bool options:: +html_heading_map_specified () const +{ + return this->html_heading_map_specified_; +} + +inline void options:: +html_heading_map_specified(bool x) +{ + this->html_heading_map_specified_ = x; +} + +inline const bool& options:: +omit_link_check () const +{ + return this->omit_link_check_; +} + +inline bool& options:: +omit_link_check () +{ + return this->omit_link_check_; +} + +inline void options:: +omit_link_check(const bool& x) +{ + this->omit_link_check_ = x; +} + +inline const std::vector& options:: +hxx_prologue () const +{ + return this->hxx_prologue_; +} + +inline std::vector& options:: +hxx_prologue () +{ + return this->hxx_prologue_; +} + +inline void options:: +hxx_prologue(const std::vector& x) +{ + this->hxx_prologue_ = x; +} + +inline bool options:: +hxx_prologue_specified () const +{ + return this->hxx_prologue_specified_; +} + +inline void options:: +hxx_prologue_specified(bool x) +{ + this->hxx_prologue_specified_ = x; +} + +inline const std::vector& options:: +ixx_prologue () const +{ + return this->ixx_prologue_; +} + +inline std::vector& options:: +ixx_prologue () +{ + return this->ixx_prologue_; +} + +inline void options:: +ixx_prologue(const std::vector& x) +{ + this->ixx_prologue_ = x; +} + +inline bool options:: +ixx_prologue_specified () const +{ + return this->ixx_prologue_specified_; +} + +inline void options:: +ixx_prologue_specified(bool x) +{ + this->ixx_prologue_specified_ = x; +} + +inline const std::vector& options:: +cxx_prologue () const +{ + return this->cxx_prologue_; +} + +inline std::vector& options:: +cxx_prologue () +{ + return this->cxx_prologue_; +} + +inline void options:: +cxx_prologue(const std::vector& x) +{ + this->cxx_prologue_ = x; +} + +inline bool options:: +cxx_prologue_specified () const +{ + return this->cxx_prologue_specified_; +} + +inline void options:: +cxx_prologue_specified(bool x) +{ + this->cxx_prologue_specified_ = x; +} + +inline const std::vector& options:: +man_prologue () const +{ + return this->man_prologue_; +} + +inline std::vector& options:: +man_prologue () +{ + return this->man_prologue_; +} + +inline void options:: +man_prologue(const std::vector& x) +{ + this->man_prologue_ = x; +} + +inline bool options:: +man_prologue_specified () const +{ + return this->man_prologue_specified_; +} + +inline void options:: +man_prologue_specified(bool x) +{ + this->man_prologue_specified_ = x; +} + +inline const std::vector& options:: +html_prologue () const +{ + return this->html_prologue_; +} + +inline std::vector& options:: +html_prologue () +{ + return this->html_prologue_; +} + +inline void options:: +html_prologue(const std::vector& x) +{ + this->html_prologue_ = x; +} + +inline bool options:: +html_prologue_specified () const +{ + return this->html_prologue_specified_; +} + +inline void options:: +html_prologue_specified(bool x) +{ + this->html_prologue_specified_ = x; +} + +inline const std::vector& options:: +txt_prologue () const +{ + return this->txt_prologue_; +} + +inline std::vector& options:: +txt_prologue () +{ + return this->txt_prologue_; +} + +inline void options:: +txt_prologue(const std::vector& x) +{ + this->txt_prologue_ = x; +} + +inline bool options:: +txt_prologue_specified () const +{ + return this->txt_prologue_specified_; +} + +inline void options:: +txt_prologue_specified(bool x) +{ + this->txt_prologue_specified_ = x; +} + +inline const std::vector& options:: +hxx_epilogue () const +{ + return this->hxx_epilogue_; +} + +inline std::vector& options:: +hxx_epilogue () +{ + return this->hxx_epilogue_; +} + +inline void options:: +hxx_epilogue(const std::vector& x) +{ + this->hxx_epilogue_ = x; +} + +inline bool options:: +hxx_epilogue_specified () const +{ + return this->hxx_epilogue_specified_; +} + +inline void options:: +hxx_epilogue_specified(bool x) +{ + this->hxx_epilogue_specified_ = x; +} + +inline const std::vector& options:: +ixx_epilogue () const +{ + return this->ixx_epilogue_; +} + +inline std::vector& options:: +ixx_epilogue () +{ + return this->ixx_epilogue_; +} + +inline void options:: +ixx_epilogue(const std::vector& x) +{ + this->ixx_epilogue_ = x; +} + +inline bool options:: +ixx_epilogue_specified () const +{ + return this->ixx_epilogue_specified_; +} + +inline void options:: +ixx_epilogue_specified(bool x) +{ + this->ixx_epilogue_specified_ = x; +} + +inline const std::vector& options:: +cxx_epilogue () const +{ + return this->cxx_epilogue_; +} + +inline std::vector& options:: +cxx_epilogue () +{ + return this->cxx_epilogue_; +} + +inline void options:: +cxx_epilogue(const std::vector& x) +{ + this->cxx_epilogue_ = x; +} + +inline bool options:: +cxx_epilogue_specified () const +{ + return this->cxx_epilogue_specified_; +} + +inline void options:: +cxx_epilogue_specified(bool x) +{ + this->cxx_epilogue_specified_ = x; +} + +inline const std::vector& options:: +man_epilogue () const +{ + return this->man_epilogue_; +} + +inline std::vector& options:: +man_epilogue () +{ + return this->man_epilogue_; +} + +inline void options:: +man_epilogue(const std::vector& x) +{ + this->man_epilogue_ = x; +} + +inline bool options:: +man_epilogue_specified () const +{ + return this->man_epilogue_specified_; +} + +inline void options:: +man_epilogue_specified(bool x) +{ + this->man_epilogue_specified_ = x; +} + +inline const std::vector& options:: +html_epilogue () const +{ + return this->html_epilogue_; +} + +inline std::vector& options:: +html_epilogue () +{ + return this->html_epilogue_; +} + +inline void options:: +html_epilogue(const std::vector& x) +{ + this->html_epilogue_ = x; +} + +inline bool options:: +html_epilogue_specified () const +{ + return this->html_epilogue_specified_; +} + +inline void options:: +html_epilogue_specified(bool x) +{ + this->html_epilogue_specified_ = x; +} + +inline const std::vector& options:: +txt_epilogue () const +{ + return this->txt_epilogue_; +} + +inline std::vector& options:: +txt_epilogue () +{ + return this->txt_epilogue_; +} + +inline void options:: +txt_epilogue(const std::vector& x) +{ + this->txt_epilogue_ = x; +} + +inline bool options:: +txt_epilogue_specified () const +{ + return this->txt_epilogue_specified_; +} + +inline void options:: +txt_epilogue_specified(bool x) +{ + this->txt_epilogue_specified_ = x; +} + +inline const std::string& options:: +hxx_prologue_file () const +{ + return this->hxx_prologue_file_; +} + +inline std::string& options:: +hxx_prologue_file () +{ + return this->hxx_prologue_file_; +} + +inline void options:: +hxx_prologue_file(const std::string& x) +{ + this->hxx_prologue_file_ = x; +} + +inline bool options:: +hxx_prologue_file_specified () const +{ + return this->hxx_prologue_file_specified_; +} + +inline void options:: +hxx_prologue_file_specified(bool x) +{ + this->hxx_prologue_file_specified_ = x; +} + +inline const std::string& options:: +ixx_prologue_file () const +{ + return this->ixx_prologue_file_; +} + +inline std::string& options:: +ixx_prologue_file () +{ + return this->ixx_prologue_file_; +} + +inline void options:: +ixx_prologue_file(const std::string& x) +{ + this->ixx_prologue_file_ = x; +} + +inline bool options:: +ixx_prologue_file_specified () const +{ + return this->ixx_prologue_file_specified_; +} + +inline void options:: +ixx_prologue_file_specified(bool x) +{ + this->ixx_prologue_file_specified_ = x; +} + +inline const std::string& options:: +cxx_prologue_file () const +{ + return this->cxx_prologue_file_; +} + +inline std::string& options:: +cxx_prologue_file () +{ + return this->cxx_prologue_file_; +} + +inline void options:: +cxx_prologue_file(const std::string& x) +{ + this->cxx_prologue_file_ = x; +} + +inline bool options:: +cxx_prologue_file_specified () const +{ + return this->cxx_prologue_file_specified_; +} + +inline void options:: +cxx_prologue_file_specified(bool x) +{ + this->cxx_prologue_file_specified_ = x; +} + +inline const std::string& options:: +man_prologue_file () const +{ + return this->man_prologue_file_; +} + +inline std::string& options:: +man_prologue_file () +{ + return this->man_prologue_file_; +} + +inline void options:: +man_prologue_file(const std::string& x) +{ + this->man_prologue_file_ = x; +} + +inline bool options:: +man_prologue_file_specified () const +{ + return this->man_prologue_file_specified_; +} + +inline void options:: +man_prologue_file_specified(bool x) +{ + this->man_prologue_file_specified_ = x; +} + +inline const std::string& options:: +html_prologue_file () const +{ + return this->html_prologue_file_; +} + +inline std::string& options:: +html_prologue_file () +{ + return this->html_prologue_file_; +} + +inline void options:: +html_prologue_file(const std::string& x) +{ + this->html_prologue_file_ = x; +} + +inline bool options:: +html_prologue_file_specified () const +{ + return this->html_prologue_file_specified_; +} + +inline void options:: +html_prologue_file_specified(bool x) +{ + this->html_prologue_file_specified_ = x; +} + +inline const std::string& options:: +txt_prologue_file () const +{ + return this->txt_prologue_file_; +} + +inline std::string& options:: +txt_prologue_file () +{ + return this->txt_prologue_file_; +} + +inline void options:: +txt_prologue_file(const std::string& x) +{ + this->txt_prologue_file_ = x; +} + +inline bool options:: +txt_prologue_file_specified () const +{ + return this->txt_prologue_file_specified_; +} + +inline void options:: +txt_prologue_file_specified(bool x) +{ + this->txt_prologue_file_specified_ = x; +} + +inline const std::string& options:: +hxx_epilogue_file () const +{ + return this->hxx_epilogue_file_; +} + +inline std::string& options:: +hxx_epilogue_file () +{ + return this->hxx_epilogue_file_; +} + +inline void options:: +hxx_epilogue_file(const std::string& x) +{ + this->hxx_epilogue_file_ = x; +} + +inline bool options:: +hxx_epilogue_file_specified () const +{ + return this->hxx_epilogue_file_specified_; +} + +inline void options:: +hxx_epilogue_file_specified(bool x) +{ + this->hxx_epilogue_file_specified_ = x; +} + +inline const std::string& options:: +ixx_epilogue_file () const +{ + return this->ixx_epilogue_file_; +} + +inline std::string& options:: +ixx_epilogue_file () +{ + return this->ixx_epilogue_file_; +} + +inline void options:: +ixx_epilogue_file(const std::string& x) +{ + this->ixx_epilogue_file_ = x; +} + +inline bool options:: +ixx_epilogue_file_specified () const +{ + return this->ixx_epilogue_file_specified_; +} + +inline void options:: +ixx_epilogue_file_specified(bool x) +{ + this->ixx_epilogue_file_specified_ = x; +} + +inline const std::string& options:: +cxx_epilogue_file () const +{ + return this->cxx_epilogue_file_; +} + +inline std::string& options:: +cxx_epilogue_file () +{ + return this->cxx_epilogue_file_; +} + +inline void options:: +cxx_epilogue_file(const std::string& x) +{ + this->cxx_epilogue_file_ = x; +} + +inline bool options:: +cxx_epilogue_file_specified () const +{ + return this->cxx_epilogue_file_specified_; +} + +inline void options:: +cxx_epilogue_file_specified(bool x) +{ + this->cxx_epilogue_file_specified_ = x; +} + +inline const std::string& options:: +man_epilogue_file () const +{ + return this->man_epilogue_file_; +} + +inline std::string& options:: +man_epilogue_file () +{ + return this->man_epilogue_file_; +} + +inline void options:: +man_epilogue_file(const std::string& x) +{ + this->man_epilogue_file_ = x; +} + +inline bool options:: +man_epilogue_file_specified () const +{ + return this->man_epilogue_file_specified_; +} + +inline void options:: +man_epilogue_file_specified(bool x) +{ + this->man_epilogue_file_specified_ = x; +} + +inline const std::string& options:: +html_epilogue_file () const +{ + return this->html_epilogue_file_; +} + +inline std::string& options:: +html_epilogue_file () +{ + return this->html_epilogue_file_; +} + +inline void options:: +html_epilogue_file(const std::string& x) +{ + this->html_epilogue_file_ = x; +} + +inline bool options:: +html_epilogue_file_specified () const +{ + return this->html_epilogue_file_specified_; +} + +inline void options:: +html_epilogue_file_specified(bool x) +{ + this->html_epilogue_file_specified_ = x; +} + +inline const std::string& options:: +txt_epilogue_file () const +{ + return this->txt_epilogue_file_; +} + +inline std::string& options:: +txt_epilogue_file () +{ + return this->txt_epilogue_file_; +} + +inline void options:: +txt_epilogue_file(const std::string& x) +{ + this->txt_epilogue_file_ = x; +} + +inline bool options:: +txt_epilogue_file_specified () const +{ + return this->txt_epilogue_file_specified_; +} + +inline void options:: +txt_epilogue_file_specified(bool x) +{ + this->txt_epilogue_file_specified_ = x; +} + +inline const std::string& options:: +output_prefix () const +{ + return this->output_prefix_; +} + +inline std::string& options:: +output_prefix () +{ + return this->output_prefix_; +} + +inline void options:: +output_prefix(const std::string& x) +{ + this->output_prefix_ = x; +} + +inline bool options:: +output_prefix_specified () const +{ + return this->output_prefix_specified_; +} + +inline void options:: +output_prefix_specified(bool x) +{ + this->output_prefix_specified_ = x; +} + +inline const std::string& options:: +output_suffix () const +{ + return this->output_suffix_; +} + +inline std::string& options:: +output_suffix () +{ + return this->output_suffix_; +} + +inline void options:: +output_suffix(const std::string& x) +{ + this->output_suffix_ = x; +} + +inline bool options:: +output_suffix_specified () const +{ + return this->output_suffix_specified_; +} + +inline void options:: +output_suffix_specified(bool x) +{ + this->output_suffix_specified_ = x; +} + +inline const std::string& options:: +hxx_suffix () const +{ + return this->hxx_suffix_; +} + +inline std::string& options:: +hxx_suffix () +{ + return this->hxx_suffix_; +} + +inline void options:: +hxx_suffix(const std::string& x) +{ + this->hxx_suffix_ = x; +} + +inline bool options:: +hxx_suffix_specified () const +{ + return this->hxx_suffix_specified_; +} + +inline void options:: +hxx_suffix_specified(bool x) +{ + this->hxx_suffix_specified_ = x; +} + +inline const std::string& options:: +ixx_suffix () const +{ + return this->ixx_suffix_; +} + +inline std::string& options:: +ixx_suffix () +{ + return this->ixx_suffix_; +} + +inline void options:: +ixx_suffix(const std::string& x) +{ + this->ixx_suffix_ = x; +} + +inline bool options:: +ixx_suffix_specified () const +{ + return this->ixx_suffix_specified_; +} + +inline void options:: +ixx_suffix_specified(bool x) +{ + this->ixx_suffix_specified_ = x; +} + +inline const std::string& options:: +cxx_suffix () const +{ + return this->cxx_suffix_; +} + +inline std::string& options:: +cxx_suffix () +{ + return this->cxx_suffix_; +} + +inline void options:: +cxx_suffix(const std::string& x) +{ + this->cxx_suffix_ = x; +} + +inline bool options:: +cxx_suffix_specified () const +{ + return this->cxx_suffix_specified_; +} + +inline void options:: +cxx_suffix_specified(bool x) +{ + this->cxx_suffix_specified_ = x; +} + +inline const std::string& options:: +man_suffix () const +{ + return this->man_suffix_; +} + +inline std::string& options:: +man_suffix () +{ + return this->man_suffix_; +} + +inline void options:: +man_suffix(const std::string& x) +{ + this->man_suffix_ = x; +} + +inline bool options:: +man_suffix_specified () const +{ + return this->man_suffix_specified_; +} + +inline void options:: +man_suffix_specified(bool x) +{ + this->man_suffix_specified_ = x; +} + +inline const std::string& options:: +html_suffix () const +{ + return this->html_suffix_; +} + +inline std::string& options:: +html_suffix () +{ + return this->html_suffix_; +} + +inline void options:: +html_suffix(const std::string& x) +{ + this->html_suffix_ = x; +} + +inline bool options:: +html_suffix_specified () const +{ + return this->html_suffix_specified_; +} + +inline void options:: +html_suffix_specified(bool x) +{ + this->html_suffix_specified_ = x; +} + +inline const std::string& options:: +txt_suffix () const +{ + return this->txt_suffix_; +} + +inline std::string& options:: +txt_suffix () +{ + return this->txt_suffix_; +} + +inline void options:: +txt_suffix(const std::string& x) +{ + this->txt_suffix_ = x; +} + +inline bool options:: +txt_suffix_specified () const +{ + return this->txt_suffix_specified_; +} + +inline void options:: +txt_suffix_specified(bool x) +{ + this->txt_suffix_specified_ = x; +} + +inline const std::string& options:: +option_prefix () const +{ + return this->option_prefix_; +} + +inline std::string& options:: +option_prefix () +{ + return this->option_prefix_; +} + +inline void options:: +option_prefix(const std::string& x) +{ + this->option_prefix_ = x; +} + +inline bool options:: +option_prefix_specified () const +{ + return this->option_prefix_specified_; +} + +inline void options:: +option_prefix_specified(bool x) +{ + this->option_prefix_specified_ = x; +} + +inline const std::string& options:: +option_separator () const +{ + return this->option_separator_; +} + +inline std::string& options:: +option_separator () +{ + return this->option_separator_; +} + +inline void options:: +option_separator(const std::string& x) +{ + this->option_separator_ = x; +} + +inline bool options:: +option_separator_specified () const +{ + return this->option_separator_specified_; +} + +inline void options:: +option_separator_specified(bool x) +{ + this->option_separator_specified_ = x; +} + +inline const bool& options:: +keep_separator () const +{ + return this->keep_separator_; +} + +inline bool& options:: +keep_separator () +{ + return this->keep_separator_; +} + +inline void options:: +keep_separator(const bool& x) +{ + this->keep_separator_ = x; +} + +inline const bool& options:: +no_combined_flags () const +{ + return this->no_combined_flags_; +} + +inline bool& options:: +no_combined_flags () +{ + return this->no_combined_flags_; +} + +inline void options:: +no_combined_flags(const bool& x) +{ + this->no_combined_flags_ = x; +} + +inline const bool& options:: +no_combined_values () const +{ + return this->no_combined_values_; +} + +inline bool& options:: +no_combined_values () +{ + return this->no_combined_values_; +} + +inline void options:: +no_combined_values(const bool& x) +{ + this->no_combined_values_ = x; +} + +inline const bool& options:: +include_with_brackets () const +{ + return this->include_with_brackets_; +} + +inline bool& options:: +include_with_brackets () +{ + return this->include_with_brackets_; +} + +inline void options:: +include_with_brackets(const bool& x) +{ + this->include_with_brackets_ = x; +} + +inline const std::string& options:: +include_prefix () const +{ + return this->include_prefix_; +} + +inline std::string& options:: +include_prefix () +{ + return this->include_prefix_; +} + +inline void options:: +include_prefix(const std::string& x) +{ + this->include_prefix_ = x; +} + +inline bool options:: +include_prefix_specified () const +{ + return this->include_prefix_specified_; +} + +inline void options:: +include_prefix_specified(bool x) +{ + this->include_prefix_specified_ = x; +} + +inline const std::string& options:: +guard_prefix () const +{ + return this->guard_prefix_; +} + +inline std::string& options:: +guard_prefix () +{ + return this->guard_prefix_; +} + +inline void options:: +guard_prefix(const std::string& x) +{ + this->guard_prefix_ = x; +} + +inline bool options:: +guard_prefix_specified () const +{ + return this->guard_prefix_specified_; +} + +inline void options:: +guard_prefix_specified(bool x) +{ + this->guard_prefix_specified_ = x; +} + +inline const std::map& options:: +reserved_name () const +{ + return this->reserved_name_; +} + +inline std::map& options:: +reserved_name () +{ + return this->reserved_name_; +} + +inline void options:: +reserved_name(const std::map& x) +{ + this->reserved_name_ = x; +} + +inline bool options:: +reserved_name_specified () const +{ + return this->reserved_name_specified_; +} + +inline void options:: +reserved_name_specified(bool x) +{ + this->reserved_name_specified_ = x; +} + +inline const std::string& options:: +options_file () const +{ + return this->options_file_; +} + +inline std::string& options:: +options_file () +{ + return this->options_file_; +} + +inline void options:: +options_file(const std::string& x) +{ + this->options_file_ = x; +} + +inline bool options:: +options_file_specified () const +{ + return this->options_file_specified_; +} + +inline void options:: +options_file_specified(bool x) +{ + this->options_file_specified_ = x; +} + +// Begin epilogue. +// +// +// End epilogue. diff --git a/cli/cli/parser.cxx b/cli/cli/parser.cxx new file mode 100644 index 0000000..4685edc --- /dev/null +++ b/cli/cli/parser.cxx @@ -0,0 +1,1728 @@ +// file : cli/parser.cxx +// author : Boris Kolpackov +// license : MIT; see accompanying LICENSE file + +#ifndef _WIN32 +# include // stat +# include // stat +# include // stat +#else +# include // _stat +# include // _stat(), S_I* + +# ifdef _MSC_VER // Unlikely to be fixed in newer versions. +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +# endif +#endif + +#include +#include +#include + +#include +#include +#include + +#include + +using namespace std; +using namespace semantics; + +// Check that the file exist without checking for permissions, etc. +// +inline static bool +file_exists (const path& p) +{ +#ifndef _WIN32 + struct stat s; + int r (stat (p.string ().c_str (), &s)); +#else + struct _stat s; + int r (_stat (p.string ().c_str (), &s)); +#endif + + return r == 0 && S_ISREG (s.st_mode); +} + +const char* keywords[] = +{ + "include", + "namespace", + "class", + "signed", + "unsigned", + "bool", + "char", + "wchar_t", + "short", + "int", + "long", + "float", + "double" +}; + +const char* punctuation[] = { + ";", ",", ":", "::", "{", "}", /*"(", ")",*/ "=", "|"}; + +// Output the token type and value in a format suitable for diagnostics. +// +std::ostream& +operator<< (std::ostream& os, token const& t) +{ + switch (t.type ()) + { + case token::t_eos: + { + os << "end-of-stream"; + break; + } + case token::t_keyword: + { + os << "keyword '" << keywords[t.keyword ()] << "'"; + break; + } + case token::t_identifier: + { + os << "identifier '" << t.identifier () << "'"; + break; + } + case token::t_punctuation: + { + os << "'" << punctuation[t.punctuation ()] << "'"; + break; + } + case token::t_cxx_path_lit: + { + os << "c++ path literal"; + break; + } + case token::t_cli_path_lit: + { + os << "cli path literal"; + break; + } + case token::t_string_lit: + { + os << "string literal"; + break; + } + case token::t_char_lit: + { + os << "char literal"; + break; + } + case token::t_bool_lit: + { + os << "bool literal"; + break; + } + case token::t_int_lit: + { + os << "integer literal"; + break; + } + case token::t_float_lit: + { + os << "floating point literal"; + break; + } + case token::t_call_expr: + { + os << "call expression"; + break; + } + case token::t_template_expr: + { + os << "template expression"; + break; + } + } + + return os; +} + +// RAII-style set new value on construction, restore old one on destruction. +// +template +struct auto_restore +{ + auto_restore (T*& var, T* new_val = 0) + : var_ (var), old_val_ (var_) + { + if (new_val != 0) + var_ = new_val; + } + + void + set (T* new_val) {var_ = new_val;} + + ~auto_restore () {var_ = old_val_;} + +private: + T*& var_; + T* old_val_; +}; + + +void parser:: +recover (token& t) +{ + // Recover by skipping past next ';' or '}'. + // + for (;; t = lexer_->next ()) + { + if (t.type () == token::t_eos) + break; + + token::punctuation_type p (t.punctuation ()); + + if (p == token::p_semi || p == token::p_rcbrace) + { + t = lexer_->next (); + break; + } + } +} + +unique_ptr parser:: +parse (std::istream& is, path const& p) +{ + unique_ptr unit (new cli_unit (p, 1, 1)); + + { + path ap (p); + ap.absolute (); + ap.normalize (); + include_map_[ap] = unit.get (); + } + + root_ = cur_ = unit.get (); + + lexer l (is, p.string ()); + lexer_ = &l; + + doc_count_ = 0; + + path_ = &p; + valid_ = true; + + def_unit (); + + if (!valid_ || !l.valid ()) + throw invalid_input (); + + return unit; +} + +void parser:: +def_unit () +{ + token t (lexer_->next ()); + + // include-decl-seq + // + for (token::keyword_type k (t.keyword ()); + k == token::k_include || k == token::k_source; + k = t.keyword ()) + { + try + { + if (k == token::k_include) + include_decl (); + else + source_decl (); + + t = lexer_->next (); + } + catch (error const&) + { + valid_ = false; + recover (t); + } + } + + auto_restore new_scope (scope_, cur_); + + // decl-seq + // + while (t.type () != token::t_eos) + { + try + { + if (t.keyword () == token::k_source) + { + try + { + source_decl (); + t = lexer_->next (); + } + catch (error const&) + { + valid_ = false; + recover (t); + } + + continue; + } + + if (decl (t)) + { + t = lexer_->next (); + continue; + } + + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected namespace, class, or documentation instead of " + << t << endl; + throw error (); + } + catch (error const&) + { + valid_ = false; + break; // Non-recoverable error. + } + } +} + +void parser:: +source_decl () +{ + token t (lexer_->next ()); + + if (t.type () != token::t_cli_path_lit) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected cli path literal instead of " << t << endl; + throw error (); + } + + string const& l (t.literal ()); + bool q (l[0] == '"'); // Quote or braket include? + + path f; + try + { + f = path (string (l, 1, l.size () - 2)); + } + catch (const invalid_path& e) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "'" << e.path () << "' is not a valid filesystem path" << endl; + valid_ = false; + } + + if (valid_) + { + path p; + + // If this is a quote include, then include relative to the current + // file. + // + if (q) + { + p = path_->directory () / f; + p.normalize (); + } + // Otherwise search the include directories (-I). + // + else + { + for (paths::const_iterator i (include_paths_.begin ()); + i != include_paths_.end (); ++i) + { + p = *i / f; + p.normalize (); + + if (file_exists (p)) + break; + + p.clear (); + } + + if (p.empty ()) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": " + << "error: file '" << f << "' not found in any of the " + << "include search directories (-I)" << endl; + valid_ = false; + } + } + + if (valid_) + { + auto_restore new_path (path_, &p); + + ifstream ifs (p.string ().c_str ()); + if (ifs.is_open ()) + { + ifs.exceptions (ifstream::failbit | ifstream::badbit); + + try + { + lexer l (ifs, p.string ()); + auto_restore new_lexer (lexer_, &l); + + def_unit (); + + if (!l.valid ()) + valid_ = false; + } + catch (std::ios_base::failure const&) + { + cerr << p << ": error: read failure" << endl; + valid_ = false; + } + } + else + { + cerr << p << ": error: unable to open in read mode" << endl; + valid_ = false; + } + } + } + + t = lexer_->next (); + + if (t.punctuation () != token::p_semi) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected ';' instead of " << t << endl; + throw error (); + } +} + +void parser:: +include_decl () +{ + token t (lexer_->next ()); + token::token_type tt (t.type ()); + + if (tt != token::t_cxx_path_lit && tt != token::t_cli_path_lit) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected path literal instead of " << t << endl; + throw error (); + } + + string const& l (t.literal ()); + includes::kind_type ik (l[0] == '<' ? includes::bracket : includes::quote); + + path f; + try + { + f = path (string (l, 1, l.size () - 2)); + } + catch (const invalid_path& e) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "'" << e.path () << "' is not a valid filesystem path" << endl; + valid_ = false; + } + + if (valid_) + { + if (tt == token::t_cxx_path_lit) + { + cxx_unit& n ( + root_->new_node (*path_, t.line (), t.column ())); + root_->new_edge (*cur_, n, ik, f); + } + else + { + path p; + // If this is a quote include, then include relative to the current + // file. + // + if (ik == includes::quote) + { + p = path_->directory () / f; + p.normalize (); + } + // Otherwise search the include directories (-I). + // + else + { + for (paths::const_iterator i (include_paths_.begin ()); + i != include_paths_.end (); ++i) + { + p = *i / f; + p.normalize (); + + if (file_exists (p)) + break; + + p.clear (); + } + + if (p.empty ()) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": " + << "error: file '" << f << "' not found in any of the " + << "include search directories (-I)" << endl; + valid_ = false; + } + } + + if (valid_) + { + // Detect and ignore multiple inclusions. + // + path ap (p); + ap.absolute (); + ap.normalize (); + + include_map::iterator it (include_map_.find (ap)); + if (it == include_map_.end ()) + { + cli_unit& n (root_->new_node (p, 1, 1)); + root_->new_edge (*cur_, n, ik, f); + include_map_[ap] = &n; + + auto_restore new_cur (cur_, &n); + auto_restore new_path (path_, &p); + + ifstream ifs (p.string ().c_str ()); + if (ifs.is_open ()) + { + ifs.exceptions (ifstream::failbit | ifstream::badbit); + + try + { + lexer l (ifs, p.string ()); + auto_restore new_lexer (lexer_, &l); + + def_unit (); + + if (!l.valid ()) + valid_ = false; + } + catch (std::ios_base::failure const&) + { + cerr << p << ": error: read failure" << endl; + valid_ = false; + } + } + else + { + cerr << p << ": error: unable to open in read mode" << endl; + valid_ = false; + } + } + else + root_->new_edge (*cur_, *it->second, ik, f); + } + } + } + + t = lexer_->next (); + + if (t.punctuation () != token::p_semi) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected ';' instead of " << t << endl; + throw error (); + } +} + +bool parser:: +decl (token& t) +{ + switch (t.type ()) + { + case token::t_keyword: + { + switch (t.keyword ()) + { + case token::k_namespace: + { + namespace_def (); + return true; + } + case token::k_class: + { + class_def (); + return true; + } + default: + break; + } + + break; + } + case token::t_punctuation: + { + if (t.punctuation () != token::p_lcbrace) + break; + } + // Fall through. + case token::t_string_lit: + { + scope_doc (t); + return true; + } + default: + break; + } + + return false; +} + +void parser:: +scope_doc (token& t) +{ + size_t ln (t.line ()), cl (t.column ()); + + // Use a counter to give scope-level docs unique names. We use a + // single counter throughout all units/scope because we could be + // reopening namespaces. + // + if (t.type () == token::t_string_lit) + { + // string-literal + // + if (valid_) + { + // Enter each ""-enclosed string as a separate documentation + // entry, handle documentation variables. + // + const string& l (t.literal ()); + + char p ('\0'); + for (size_t b (0), e (1); e < l.size (); ++e) + { + if (l[e] == '"' && p != '\\') + { + string s (doc_string (l.c_str () + b, e - b + 1)); + + if (!s.empty ()) + { + doc& d (root_->new_node (*path_, ln, cl)); + + // See if this is a variable assignment: "\=". + // + size_t p (0); // '=' position. + if (s.size () >= 3 && s[0] == '\\' && s[1] != '\\') + { + for (p = 1; p != s.size (); ++p) + { + char c (s[p]); + + // Variable name should be a C identifier. + // + if (!(c == '_' || + ('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + (p != 1 && '0' <= c && c <= '9'))) + break; + } + + if (p == s.size () || s[p] != '=' || p == 1) // Not a variable. + p = 0; + } + + if (p != 0) + { + root_->new_edge ( + *scope_, d, "var: " + string (s, 1, p - 1)); + s = string (s, p + 1); + } + else + { + ostringstream os; + os << "doc: " << doc_count_++; + root_->new_edge (*scope_, d, os.str ()); + } + + d.push_back (s); // move(). + } + + // If we have more, then make b point to the opening '"'. Second + // ++e in for() above will make e point to the character after it. + // + b = ++e; + continue; + } + + // We need to keep track of \\ escapings so we don't confuse + // them with \", as in \\". + // + if (l[e] == '\\' && p == '\\') + p = '\0'; + else + p = l[e]; + } + } + } + else + { + // doc-string-seq + // + assert (t.punctuation () == token::p_lcbrace); + + doc* d (0); + if (valid_) + { + ostringstream os; + os << "doc: " << doc_count_++; + + d = &root_->new_node (*path_, ln, cl); + root_->new_edge (*scope_, *d, os.str ()); + } + + for (t = lexer_->next ();; t = lexer_->next ()) + { + if (t.type () != token::t_string_lit) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected documentation string instead of " << t << endl; + throw error (); + } + + if (valid_) + d->push_back (doc_string (t.literal ().c_str (), + t.literal ().size ())); + + t = lexer_->next (); + + if (t.punctuation () != token::p_comma) + break; + } + + if (t.punctuation () != token::p_rcbrace) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected '}' instead of " << t << endl; + throw error (); + } + } +} + +void parser:: +namespace_def () +{ + token t (lexer_->next ()); + + if (t.type () != token::t_identifier) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected identifier instead of " << t << endl; + throw error (); + } + + auto_restore new_scope (scope_); + + if (valid_) + { + namespace_& n ( + root_->new_node (*path_, t.line (), t.column ())); + root_->new_edge (*scope_, n, t.identifier ()); + new_scope.set (&n); + } + + t = lexer_->next (); + + if (t.punctuation () != token::p_lcbrace) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected '{' instead of " << t << endl; + throw error (); + } + + // decl-seq + // + t = lexer_->next (); + + while (decl (t)) + t = lexer_->next (); + + if (t.punctuation () != token::p_rcbrace) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected namespace, class, documentation, or '}' instead of " + << t << endl; + throw error (); + } +} + +void parser:: +class_def () +{ + token t (lexer_->next ()); + + if (t.type () != token::t_identifier) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected identifier instead of " << t << endl; + throw error (); + } + + class_* n (0); + if (valid_) + { + 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 = cur_->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: " + << "expected '{' instead of " << t << endl; + throw error (); + } + + auto_restore new_scope (scope_, n); + + // class-decl-seq + // + t = lexer_->next (); + + for (;;) + { + try + { + if (t.type () == token::t_string_lit || + t.punctuation () == token::p_lcbrace) + { + scope_doc (t); + t = lexer_->next (); + } + else + { + if (!option_def (t)) + break; + } + } + catch (error const&) + { + valid_ = false; + recover (t); + } + } + + if (t.punctuation () != token::p_rcbrace) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected option, documentation, or '}' instead of " << t << endl; + throw error (); + } + + t = lexer_->next (); + + if (t.punctuation () != token::p_semi) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected ';' instead of " << t << endl; + throw error (); + } +} + +bool parser:: +option_def (token& t) +{ + size_t l (t.line ()), c (t.column ()); + + // type-spec + // + // These two functions set t to the next token if they return + // true. + // + string type_name; + + if (!qualified_name (t, type_name) && !fundamental_type (t, type_name)) + return false; + + option* o (0); + + if (valid_) + { + o = &root_->new_node