summaryrefslogtreecommitdiff
path: root/cli/cli
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2020-04-08 14:51:57 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2020-04-27 11:38:53 +0300
commit720c5a33b6a49cf328fdd7611f49153cf8f60247 (patch)
tree9725f3d1f42ec90fde84520f49647edea013ce5e /cli/cli
parent3183f3bb927a90783ae0aeaf190a0919377aabe4 (diff)
Separate tests and examples into individual packages
Also make cli module to be explicitly enabled via the config.cli configuration variable.
Diffstat (limited to 'cli/cli')
-rw-r--r--cli/cli/.gitignore7
-rw-r--r--cli/cli/buildfile75
-rw-r--r--cli/cli/cli.cxx165
-rw-r--r--cli/cli/context.cxx2742
-rw-r--r--cli/cli/context.hxx295
-rw-r--r--cli/cli/generator.cxx584
-rw-r--r--cli/cli/generator.hxx28
-rw-r--r--cli/cli/header.cxx383
-rw-r--r--cli/cli/header.hxx13
-rw-r--r--cli/cli/html.cxx348
-rw-r--r--cli/cli/html.hxx13
-rw-r--r--cli/cli/inline.cxx108
-rw-r--r--cli/cli/inline.hxx13
-rw-r--r--cli/cli/lexer.cxx604
-rw-r--r--cli/cli/lexer.hxx142
-rw-r--r--cli/cli/lexer.ixx91
-rw-r--r--cli/cli/lexer.test.cxx122
-rw-r--r--cli/cli/lexer.test.testscript191
-rw-r--r--cli/cli/man.cxx278
-rw-r--r--cli/cli/man.hxx13
-rw-r--r--cli/cli/name-processor.cxx193
-rw-r--r--cli/cli/name-processor.hxx13
-rw-r--r--cli/cli/option-types.cxx43
-rw-r--r--cli/cli/option-types.hxx33
-rw-r--r--cli/cli/options.cli662
-rw-r--r--cli/cli/options.cxx2146
-rw-r--r--cli/cli/options.hxx1632
-rw-r--r--cli/cli/options.ixx2319
-rw-r--r--cli/cli/parser.cxx1728
-rw-r--r--cli/cli/parser.hxx91
-rw-r--r--cli/cli/parser.test.cxx45
-rw-r--r--cli/cli/parser.test.testscript242
-rw-r--r--cli/cli/runtime-header.cxx647
-rw-r--r--cli/cli/runtime-header.hxx13
-rw-r--r--cli/cli/runtime-inline.cxx508
-rw-r--r--cli/cli/runtime-inline.hxx13
-rw-r--r--cli/cli/runtime-source.cxx1052
-rw-r--r--cli/cli/runtime-source.hxx13
-rw-r--r--cli/cli/semantics.hxx16
-rw-r--r--cli/cli/semantics/class.cxx39
-rw-r--r--cli/cli/semantics/class.hxx106
-rw-r--r--cli/cli/semantics/doc.cxx27
-rw-r--r--cli/cli/semantics/doc.hxx22
-rw-r--r--cli/cli/semantics/elements.cxx129
-rw-r--r--cli/cli/semantics/elements.hxx407
-rw-r--r--cli/cli/semantics/expression.cxx27
-rw-r--r--cli/cli/semantics/expression.hxx76
-rw-r--r--cli/cli/semantics/namespace.cxx27
-rw-r--r--cli/cli/semantics/namespace.hxx26
-rw-r--r--cli/cli/semantics/option.cxx47
-rw-r--r--cli/cli/semantics/option.hxx189
-rw-r--r--cli/cli/semantics/unit.cxx63
-rw-r--r--cli/cli/semantics/unit.hxx310
-rw-r--r--cli/cli/semantics/unit.txx108
-rw-r--r--cli/cli/source.cxx1374
-rw-r--r--cli/cli/source.hxx13
-rw-r--r--cli/cli/token.hxx135
-rw-r--r--cli/cli/token.ixx88
-rw-r--r--cli/cli/traversal.hxx16
-rw-r--r--cli/cli/traversal/class.cxx49
-rw-r--r--cli/cli/traversal/class.hxx41
-rw-r--r--cli/cli/traversal/doc.hxx16
-rw-r--r--cli/cli/traversal/elements.cxx14
-rw-r--r--cli/cli/traversal/elements.hxx142
-rw-r--r--cli/cli/traversal/expression.hxx16
-rw-r--r--cli/cli/traversal/namespace.cxx26
-rw-r--r--cli/cli/traversal/namespace.hxx26
-rw-r--r--cli/cli/traversal/option.cxx59
-rw-r--r--cli/cli/traversal/option.hxx74
-rw-r--r--cli/cli/traversal/unit.cxx46
-rw-r--r--cli/cli/traversal/unit.hxx58
-rw-r--r--cli/cli/txt.cxx301
-rw-r--r--cli/cli/txt.hxx46
-rw-r--r--cli/cli/version.hxx.in45
74 files changed, 21812 insertions, 0 deletions
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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <vector>
+#include <string>
+#include <memory> // unique_ptr
+#include <fstream>
+#include <iostream>
+
+#include <cutl/compiler/code-stream.hxx>
+
+#include <cli/semantics/doc.hxx>
+
+#include <cli/options.hxx>
+#include <cli/parser.hxx>
+#include <cli/generator.hxx>
+
+#include <cli/version.hxx>
+
+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<string>::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<semantics::cli_unit> unit (p.parse (ifs, path));
+
+ // Merge documentation variables from the command line.
+ //
+ for (map<string, string>::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<doc> ("", "var: " + i->first) != 0)
+ continue;
+
+ doc& d (unit->new_node<doc> (semantics::path ("<command line>"), 0, 0));
+ unit->new_edge<semantics::names> (*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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <stack>
+#include <vector>
+#include <cstring> // strncmp()
+#include <fstream>
+#include <sstream>
+#include <iostream>
+
+#include <cli/context.hxx>
+
+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<string>::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<string>& 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<string> 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<span> 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 += "&#160;";
+ 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 += "&#8211;";
+ 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 += "<br/>";
+ 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<span>::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 += "<span class=\"note\">";
+ }
+ else if (s & link)
+ {
+ r += "<a href=\"";
+
+ // It might be useful to include the man section into the regex
+ // somehow.
+ //
+ string t (link_section.empty ()
+ ? link_target
+ : link_target + options.html_suffix ());
+
+ string pt (process_link_target (t));
+
+ if (pt.empty ())
+ {
+ cerr << "error: link '" << t << "' became empty" << endl;
+ throw generation_failed ();
+ }
+
+ r += pt;
+ r += "\">";
+ }
+ else
+ {
+ if (s & code)
+ r += "<code>";
+
+ if (s & itlc)
+ r += "<i>";
+
+ if (s & bold)
+ r += "<b>";
+ }
+
+ 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<span>::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 += "</span>";
+ }
+ else if ((s & link) != 0)
+ {
+ if (link_empty)
+ {
+ if (link_section.empty ())
+ r += link_target;
+ else
+ {
+ r += "<code><b>";
+ r += link_target + "(" + link_section + ")";
+ r += "</b></code>";
+ }
+ }
+
+ r += "</a>";
+ }
+ else
+ {
+ if (s & bold)
+ r += "</b>";
+
+ if (s & itlc)
+ r += "</i>";
+
+ if (s & code)
+ r += "</code>";
+ }
+
+ 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 <h1 class="preface">
+ // \H - part <h1 class="part">
+ // \h1 - chapter <h1>
+ // \h - section <h1> or <h2>
+ // \h2 - sub-section <h3>
+ //
+ // In HTML, if \h0 or \h1 was seen, then \h is automatically mappend to
+ // <h2>. Otherwise it is <h1>.
+ //
+ // 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 <br/>
+// 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, "<br/>", 5) == 0; ++top)
+ {
+ b += 5;
+
+ if (b != e && *b == '\n') // Remove following newline, if any.
+ ++b;
+ }
+
+ for (; e - b >= 5 && strncmp (e - 5, "<br/>", 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<block> 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 <h1> until we encounter
+ // \h0 or \h1 at which point we change it to <h2>.
+ //
+ 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 += "<pre>";
+ v.append (l, n);
+ v += "</pre>";
+ }
+ 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 <code> 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 <code></code> fragments (we
+ // may end up with quite a few of them due to the <arg>
+ // translation).
+ //
+ string c;
+ {
+ size_t n (t.size ());
+
+ if (n >= 13 &&
+ t.compare (0, 6, "<code>") == 0 &&
+ t.compare (n - 7, 7, "</code>") == 0)
+ {
+ string tt;
+
+ // Make sure this is one contigous <code>.
+ //
+ size_t b (1); // <code>/</code> 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 </code>.
+ t = move (tt);
+ c = "code";
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ v += "<p";
+ if (!c.empty ()) v += " class=\"" + c + "\"";
+ if (!s.empty ()) v += " style=\"" + s + "\"";
+ v += ">";
+ v += t;
+ v += "</p>";
+ }
+ else
+ format_line (ot, v, l, n);
+ }
+
+ break;
+ }
+ case ot_man:
+ {
+ if (b.para)
+ {
+ if (!first)
+ v += "\n";
+
+ v += ".PP\n";
+ }
+ else
+ {
+ if (!first)
+ v += "\n\n";
+ }
+
+ if (k == block::pre)
+ {
+ v += ".nf\n";
+
+ // Note that if we have several consecutive blank lines, they
+ // will be collapsed into a single one. No, .sp doesn't work.
+ //
+ char c, p ('\n'); // Current and previous characters.
+ for (size_t i (0); i != n; p = c, ++i)
+ {
+ switch (c = l[i])
+ {
+ case '\\': v += '\\'; break;
+ case '.': v += p != '\n' ? "\\" : "\\&\\"; break;
+ }
+
+ v += c;
+ }
+
+ v += "\n.fi";
+ }
+ else
+ {
+ format_line (ot, v, l, n);
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Pop paragraph blocks.
+ //
+ if (pop >= 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 = "<a href=\"#" + pi + "\">" + pv + "</a>";
+
+ // 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, ' ') + "</table>\n";
+ v += string (l * 4 - 4, ' ') + "</td></tr>\n";
+ }
+ else
+ // Otherwise it is inline.
+ //
+ v += "</td></tr>\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 + "<table class=\"toc\">\n";
+ }
+
+ in += " ";
+
+ switch (t)
+ {
+ case 'H':
+ {
+ v += in + "<tr><th colspan=\"2\">" + pv + "</th></tr>\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 + "<tr>";
+
+ if (!n.empty ())
+ {
+ v += "<th>" + n + "</th><td>";
+ heading_map[pi] = n; // Save it for later.
+ }
+ else
+ v += "<td class=\"preface\" colspan=\"2\">";
+
+ 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<char, string> 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 += "</" + h + '>';
+
+ // @@ This only works for a single string fragment.
+ //
+ if (t == '0' || t == '1')
+ html_h = '2';
+
+ break;
+ }
+ case block::ul: v += "<ul>\n" + pv + "\n</ul>"; break;
+ case block::ol: v += "<ol>\n" + pv + "\n</ol>"; break;
+ case block::dl: v += "<dl>\n" + pv + "\n</dl>"; break;
+ case block::li:
+ {
+ string pvs (html_margin (pv));
+
+ if (b.kind == block::dl)
+ {
+ string phs (html_margin (ph));
+
+ v += (phs.empty () ? "<dt>" : "<dt style=\"" + phs + "\">")
+ + ph + "</dt>\n";
+
+ v += (pvs.empty () ? "<dd>" : "<dd style=\"" + pvs + "\">")
+ + pv + "</dd>";
+ }
+ else
+ v += (pvs.empty () ? "<li>" : "<li style=\"" + pvs + "\">")
+ + pv + "</li>";
+
+ break;
+ }
+ case block::note:
+ {
+ v += "<div class=\"note\">\n";
+ v += pv;
+ v += "\n</div>";
+
+ 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 " <table class=\"toc\">";
+ }
+ 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, ' ') + "</table>\n";
+ v += string (l * 4 - 4, ' ') + "</td></tr>\n";
+ }
+ else
+ // Otherwise it is inline.
+ //
+ v += "</td></tr>\n";
+ }
+
+ v += " </table>";
+ 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<doc> ("", "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<doc> (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<cli_unit*> (&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<string, string> 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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_CONTEXT_HXX
+#define CLI_CONTEXT_HXX
+
+#include <set>
+#include <map>
+#include <string>
+#include <vector>
+#include <ostream>
+#include <cstddef> // std::size_t
+
+#include <cutl/re.hxx>
+#include <cutl/shared-ptr.hxx>
+#include <cutl/fs/path.hxx>
+
+#include <cli/options.hxx>
+#include <cli/semantics.hxx>
+#include <cli/traversal.hxx>
+
+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<regexsub> regex_mapping;
+
+private:
+ struct data;
+ cutl::shared_ptr<data> 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<string, string> reserved_name_map_type;
+ reserved_name_map_type const& reserved_name_map;
+
+ typedef std::set<string> keyword_set_type;
+ keyword_set_type const& keyword_set;
+
+ regex_mapping const& link_regex;
+
+ typedef std::set<string> 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<string, string> 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_entry> 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 <arg>-style constructs to \i{arg}. Format converts the string
+ // to the output format.
+ //
+ static string
+ translate_arg (string const&, std::set<string>&);
+
+ static string
+ translate (string const&, std::set<string> 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<string> ("name");
+ }
+
+ static string const&
+ especifier (semantics::nameable& n)
+ {
+ return n.context ().get<string> ("specifier");
+ }
+
+ static string const&
+ emember (semantics::nameable& n)
+ {
+ return n.context ().get<string> ("member");
+ }
+
+ static string const&
+ especifier_member (semantics::nameable& n)
+ {
+ return n.context ().get<string> ("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 <typename X, typename Y>
+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<X> ())
+ 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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cctype> // std::toupper, std::is{alpha,upper,lower}
+#include <string>
+#include <fstream>
+#include <iostream>
+
+#include <cutl/fs/auto-remove.hxx>
+
+#include <cutl/compiler/code-stream.hxx>
+#include <cutl/compiler/cxx-indenter.hxx>
+
+#include <cli/header.hxx>
+#include <cli/inline.hxx>
+#include <cli/source.hxx>
+
+#include <cli/runtime-header.hxx>
+#include <cli/runtime-inline.hxx>
+#include <cli/runtime-source.hxx>
+
+#include <cli/man.hxx>
+#include <cli/html.hxx>
+#include <cli/txt.hxx>
+
+#include <cli/context.hxx>
+#include <cli/generator.hxx>
+#include <cli/name-processor.hxx>
+
+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<string> const& text, string const& file)
+ {
+ for (vector<string>::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<semantics::cli_includes> ())
+ {
+ 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<compiler::cxx_indenter, char>
+ 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<ostream&> (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<ostream&> (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<ostream&> (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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_GENERATOR_HXX
+#define CLI_GENERATOR_HXX
+
+#include <cli/options.hxx>
+#include <cli/semantics/unit.hxx>
+
+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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cli/header.hxx>
+
+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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_HEADER_HXX
+#define CLI_HEADER_HXX
+
+#include <cli/context.hxx>
+
+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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <vector>
+#include <iostream>
+
+#include <cli/html.hxx>
+
+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 += "&lt;";
+ break;
+ }
+ case '&':
+ {
+ r += "&amp;";
+ 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 <pre>.
+ //
+ if (d.compare (i, 5, "<pre>") == 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 </pre> as is.
+ //
+ e = d.find ("</pre>", 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<string> 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 << " </dl>" << 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 <dl>.
+ };
+
+ 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 << " <dl class=\"options\">" << endl;
+ list_ = true;
+ }
+ else
+ os << endl; // Separate from the previous <dl>.
+
+ names& n (o.named ());
+
+ os << " <dt><code><b>";
+
+ for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i)
+ {
+ if (i != n.name_begin ())
+ os << "</b></code>|<code><b>";
+
+ os << escape_html (*i);
+ }
+
+ os << "</b></code>";
+
+ string type (o.type ().name ());
+
+ std::set<string> arg_set;
+
+ if (type != "bool" || doc.size () >= 3)
+ {
+ string s (
+ translate_arg (
+ doc.size () > 0 ? doc[0] : string ("<arg>"), arg_set));
+
+ os << ' ' << format (o.scope (), escape_html (s), false);
+ }
+
+ os << "</dt>" << 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, "<dd>" + d + "</dd>", 4);
+ os << endl;
+ }
+
+ private:
+ class_doc_type cd_;
+ bool& list_; // True if we are currently in <dl>.
+ };
+
+ //
+ //
+ 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<string>::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<semantics::class_> ("", n))
+ cl.traverse (*c);
+ else
+ {
+ cerr << "error: class '" << *i << "' not found" << endl;
+ throw generation_failed ();
+ }
+ }
+ }
+
+ if (list)
+ ctx.os << " </dl>" << 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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_HTML_HXX
+#define CLI_HTML_HXX
+
+#include <cli/context.hxx>
+
+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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cli/inline.hxx>
+
+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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_INLINE_HXX
+#define CLI_INLINE_HXX
+
+#include <cli/context.hxx>
+
+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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <iostream>
+
+#include <cli/lexer.hxx>
+
+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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_LEXER_HXX
+#define CLI_LEXER_HXX
+
+#include <map>
+#include <string>
+#include <locale>
+#include <cstddef> // std::size_t
+#include <istream>
+
+#include <cli/token.hxx>
+
+class lexer
+{
+public:
+ lexer (std::istream& is, std::string const& id);
+
+ token
+ next ();
+
+ bool
+ valid () const;
+
+protected:
+ class xchar
+ {
+ public:
+ typedef std::char_traits<char> 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<std::string, token::keyword_type> 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 <cli/lexer.ixx>
+
+#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 <boris@codesynthesis.com>
+// 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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <fstream>
+#include <iostream>
+
+#include <cli/token.hxx>
+#include <cli/lexer.hxx>
+
+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 << "<EOS>" << 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 <<EOI >=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: --_
+<EOS>
+EOO
+
+: 001
+:
+cat <<EOI >=test.cli;
+5
+123456
+-12345
+- 1
+-
+123
+EOI
+$* test.cli >>EOO
+5
+123456
+-12345
+-1
+-123
+<EOS>
+EOO
+
+: 002
+:
+cat <<EOI >=test.cli;
+'a'
+'\n'
+'\\'
+'\0'
+'\''
+'\xaf'
+'\111'
+EOI
+$* test.cli >>EOO
+'a'
+'\n'
+'\\'
+'\0'
+'\''
+'\xaf'
+'\111'
+<EOS>
+EOO
+
+: 003
+:
+cat <<EOI >=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"
+;
+<EOS>
+EOO
+
+: 004
+:
+cat <<EOI >=test.cli;
+include "foo/abc.hxx";
+include <vector>;
+include "c++:map";
+include <cli:map>;
+include "map.cli"
+EOI
+$* test.cli >>EOO
+keyword: include
+c++ path: "foo/abc.hxx"
+;
+keyword: include
+c++ path: <vector>
+;
+keyword: include
+c++ path: "map"
+;
+keyword: include
+cli path: <map>
+;
+keyword: include
+cli path: "map.cli"
+<EOS>
+EOO
+
+: 005
+:
+cat <<EOI >=test.cli;
+(abc, 123 - 345, 12.34)
+<foo, bar::baz, 123*345>
+EOI
+$* test.cli >>EOO
+(abc, 123 - 345, 12.34)
+<foo, bar::baz, 123*345>
+<EOS>
+EOO
+
+: 006
+:
+cat <<EOI >=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
+:
+::
+<EOS>
+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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <vector>
+#include <iostream>
+
+#include <cli/man.hxx>
+
+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<string> 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<string> arg_set;
+
+ if (type != "bool" || doc.size () >= 3)
+ {
+ string s (
+ translate_arg (
+ doc.size () > 0 ? doc[0] : string ("<arg>"), 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<string>::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<semantics::class_> ("", 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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_MAN_HXX
+#define CLI_MAN_HXX
+
+#include <cli/context.hxx>
+
+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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <set>
+#include <sstream>
+
+#include <cli/context.hxx>
+#include <cli/name-processor.hxx>
+
+using namespace std;
+
+namespace
+{
+ typedef set<string> 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<string> ("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<string> ("name"));
+ oc.set ("member", find_name (base + "_", set_));
+
+ if (gen_specifier && o.type ().name () != "bool")
+ {
+ string const& base (oc.get<string> ("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<name_set> ("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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_NAME_PROCESSOR_HXX
+#define CLI_NAME_PROCESSOR_HXX
+
+#include <cli/context.hxx>
+
+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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <istream>
+
+#include <cli/option-types.hxx>
+
+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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_OPTION_TYPES_HXX
+#define CLI_OPTION_TYPES_HXX
+
+#include <iosfwd>
+#include <string>
+
+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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+// NOTE: Make sure you have a working CLI compiler around before
+// modifying this file.
+//
+
+include <map>;
+include <string>;
+include <vector>;
+include <cstddef>;
+
+include <cli/option-types.hxx>;
+
+class options
+{
+ bool --build2-metadata; // Leave undocumented/hidden.
+
+ bool --help {"Print usage information and exit."};
+ bool --version {"Print version and exit."};
+
+ std::vector<std::string> --include-path | -I
+ {
+ "<dir>",
+ "Search <dir> for bracket-included (\cb{<>}) options files."
+ };
+
+ std::string --output-dir | -o
+ {
+ "<dir>",
+ "Write the generated files to <dir> instead of the current directory."
+ };
+
+ cxx_version --std = cxx_version::cxx98
+ {
+ "<version>",
+ "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<string>}."
+ };
+
+ 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"
+ {
+ "<ns>",
+ "Generate the CLI support types in the <ns> 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"
+ {
+ "<type>",
+ "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
+ {
+ "<name>",
+ "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 <name> 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
+ {
+ "<len>",
+ "Indent option descriptions <len> 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<std::string, std::string> --class-doc
+ {
+ "<name>=<kind>",
+ "Specify the documentation <kind> that should be used for the options class
+ <name>. The <name> value should be a fully-qualified class name, for
+ example, \cb{app::options}. The <kind> 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<std::string> --class
+ {
+ "<name>",
+ "Generate the man page, HTML, or text documentation only for the options
+ class <name>. The <name> 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<std::string, std::string> --docvar|-v
+ {
+ "<name>=<val>",
+ "Set documentation variable <name> to the value <val>. Documentation
+ variables can be substituted in prologues and epilogues (see
+ \cb{--*-prologue*} and \cb{--*-epilogue*} options) using the
+ \cb{$}<name>\cb{$} expansion syntax (use \cb{$$} to escape expansion).
+ They can also be defined in \cb{.cli} files using the
+ \c{\"\\<name>=<val>\"} syntax."
+ };
+
+ std::vector<std::string> --link-regex
+ {
+ "<regex>",
+ "Add <regex> 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<char, std::string> --html-heading-map
+ {
+ "<c>=<h>",
+ "Map CLI heading <c> (valid values: '\cb{H}', '\cb{0}', '\cb{1}',
+ '\cb{h}', and '\cb{2}') to HTML heading <h> (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<std::string> --hxx-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated C++ header file."
+ };
+
+ std::vector<std::string> --ixx-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated C++ inline file."
+ };
+
+ std::vector<std::string> --cxx-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated C++ source file."
+ };
+
+ std::vector<std::string> --man-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated man page file."
+ };
+
+ std::vector<std::string> --html-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated HTML file."
+ };
+
+ std::vector<std::string> --txt-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated text file."
+ };
+
+ // Epilogues.
+ //
+ std::vector<std::string> --hxx-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated C++ header file."
+ };
+
+ std::vector<std::string> --ixx-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated C++ inline file."
+ };
+
+ std::vector<std::string> --cxx-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated C++ source file."
+ };
+
+ std::vector<std::string> --man-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated man page file."
+ };
+
+ std::vector<std::string> --html-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated HTML file."
+ };
+
+ std::vector<std::string> --txt-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated text file."
+ };
+
+ // Prologue files.
+ //
+ std::string --hxx-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated C++
+ header file."
+ };
+
+ std::string --ixx-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated C++
+ inline file."
+ };
+
+ std::string --cxx-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated C++
+ source file."
+ };
+
+ std::string --man-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated man
+ page file."
+ };
+
+ std::string --html-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated HTML
+ file."
+ };
+
+ std::string --txt-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated text
+ file."
+ };
+
+ // Epilogue files.
+ //
+ std::string --hxx-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated C++ header
+ file."
+ };
+
+ std::string --ixx-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated C++ inline
+ file."
+ };
+
+ std::string --cxx-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated C++ source
+ file."
+ };
+
+ std::string --man-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated man page file."
+ };
+
+ std::string --html-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated HTML file."
+ };
+
+ std::string --txt-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated text file."
+ };
+
+ // Output.
+ //
+ std::string --output-prefix
+ {
+ "<prefix>",
+ "Add <prefix> at the beginning of the generated output file name(s)."
+ };
+
+ std::string --output-suffix
+ {
+ "<suffix>",
+ "Add <suffix> 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"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.hxx} to construct the name of
+ the generated header file."
+ };
+
+ std::string --ixx-suffix = ".ixx"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.ixx} to construct the name of
+ the generated inline file."
+ };
+
+ std::string --cxx-suffix = ".cxx"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.cxx} to construct the name of
+ the generated source file."
+ };
+
+ std::string --man-suffix = ".1"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.1} to construct the name of
+ the generated man page file."
+ };
+
+ std::string --html-suffix = ".html"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.html} to construct the name
+ of the generated HTML file."
+ };
+
+ std::string --txt-suffix = ".txt"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.txt} to construct the name of
+ the generated text file."
+ };
+
+ std::string --option-prefix = "-"
+ {
+ "<prefix>",
+ "Use <prefix> 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 = "--"
+ {
+ "<sep>",
+ "Use <sep> 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
+ {
+ "<prefix>",
+ "Add <prefix> to the generated \cb{#include} directive paths."
+ };
+
+ std::string --guard-prefix
+ {
+ "<prefix>",
+ "Add <prefix> 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<std::string, std::string> --reserved-name
+ {
+ "<name>=<rep>",
+ "Add <name> with an optional <rep> 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
+ {
+ "<file>",
+ "Read additional options from <file>. 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 <cli/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+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 <typename X>
+ 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<bool>
+ {
+ static void
+ parse (bool& x, scanner& s)
+ {
+ s.next ();
+ x = true;
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ 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 <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.push_back (x);
+ xs = true;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::set<X> >
+ {
+ static void
+ parse (std::set<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.insert (x);
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V>
+ struct parser<std::map<K, V> >
+ {
+ static void
+ parse (std::map<K, V>& 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<char*> (o), 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, typename T, T X::*M, bool X::*S>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, x.*S, s);
+ }
+}
+
+#include <map>
+#include <cstring>
+
+// 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 <dir> Search <dir> for bracket-included (<>) options" << ::std::endl
+ << " files." << ::std::endl;
+
+ os << "--output-dir|-o <dir> Write the generated files to <dir> instead of the" << ::std::endl
+ << " current directory." << ::std::endl;
+
+ os << "--std <version> 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 <ns> Generate the CLI support types in the <ns>" << ::std::endl
+ << " namespace (cli by default)." << ::std::endl;
+
+ os << "--ostream-type <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 <name> Generate the combined usage printing code for the" << ::std::endl
+ << " entire page." << ::std::endl;
+
+ os << "--option-length <len> Indent option descriptions <len> 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 <name>=<kind> Specify the documentation <kind> that should be" << ::std::endl
+ << " used for the options class <name>." << ::std::endl;
+
+ os << "--class <name> Generate the man page, HTML, or text documentation" << ::std::endl
+ << " only for the options class <name>." << ::std::endl;
+
+ os << "--docvar|-v <name>=<val> Set documentation variable <name> to the value" << ::std::endl
+ << " <val>." << ::std::endl;
+
+ os << "--link-regex <regex> Add <regex> 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 <c>=<h> Map CLI heading <c> (valid values: 'H', '0', '1'," << ::std::endl
+ << " 'h', and '2') to HTML heading <h> (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 <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " C++ header file." << ::std::endl;
+
+ os << "--ixx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " C++ inline file." << ::std::endl;
+
+ os << "--cxx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " C++ source file." << ::std::endl;
+
+ os << "--man-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " man page file." << ::std::endl;
+
+ os << "--html-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " HTML file." << ::std::endl;
+
+ os << "--txt-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " text file." << ::std::endl;
+
+ os << "--hxx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
+ << " header file." << ::std::endl;
+
+ os << "--ixx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
+ << " inline file." << ::std::endl;
+
+ os << "--cxx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
+ << " source file." << ::std::endl;
+
+ os << "--man-epilogue <text> Insert <text> at the end of the generated man page" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--html-epilogue <text> Insert <text> at the end of the generated HTML" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--txt-epilogue <text> Insert <text> at the end of the generated text" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--hxx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated C++ header file." << ::std::endl;
+
+ os << "--ixx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated C++ inline file." << ::std::endl;
+
+ os << "--cxx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated C++ source file." << ::std::endl;
+
+ os << "--man-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated man page file." << ::std::endl;
+
+ os << "--html-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated HTML file." << ::std::endl;
+
+ os << "--txt-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated text file." << ::std::endl;
+
+ os << "--hxx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated C++ header file." << ::std::endl;
+
+ os << "--ixx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated C++ inline file." << ::std::endl;
+
+ os << "--cxx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated C++ source file." << ::std::endl;
+
+ os << "--man-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated man page file." << ::std::endl;
+
+ os << "--html-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated HTML file." << ::std::endl;
+
+ os << "--txt-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated text file." << ::std::endl;
+
+ os << "--output-prefix <prefix> Add <prefix> at the beginning of the generated" << ::std::endl
+ << " output file name(s)." << ::std::endl;
+
+ os << "--output-suffix <suffix> Add <suffix> at the end of the generated output" << ::std::endl
+ << " file name(s)." << ::std::endl;
+
+ os << "--hxx-suffix <suffix> Use <suffix> instead of the default .hxx to" << ::std::endl
+ << " construct the name of the generated header file." << ::std::endl;
+
+ os << "--ixx-suffix <suffix> Use <suffix> instead of the default .ixx to" << ::std::endl
+ << " construct the name of the generated inline file." << ::std::endl;
+
+ os << "--cxx-suffix <suffix> Use <suffix> instead of the default .cxx to" << ::std::endl
+ << " construct the name of the generated source file." << ::std::endl;
+
+ os << "--man-suffix <suffix> Use <suffix> instead of the default .1 to" << ::std::endl
+ << " construct the name of the generated man page file." << ::std::endl;
+
+ os << "--html-suffix <suffix> Use <suffix> instead of the default .html to" << ::std::endl
+ << " construct the name of the generated HTML file." << ::std::endl;
+
+ os << "--txt-suffix <suffix> Use <suffix> instead of the default .txt to" << ::std::endl
+ << " construct the name of the generated text file." << ::std::endl;
+
+ os << "--option-prefix <prefix> Use <prefix> instead of the default '-' as an" << ::std::endl
+ << " option prefix." << ::std::endl;
+
+ os << "--option-separator <sep> Use <sep> 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 <prefix> Add <prefix> to the generated #include directive" << ::std::endl
+ << " paths." << ::std::endl;
+
+ os << "--guard-prefix <prefix> Add <prefix> to the generated header inclusion" << ::std::endl
+ << " guards." << ::std::endl;
+
+ os << "--reserved-name <name>=<rep> Add <name> with an optional <rep> replacement to" << ::std::endl
+ << " the list of names that should not be used as" << ::std::endl
+ << " identifiers." << ::std::endl;
+
+ os << "--options-file <file> Read additional options from <file>." << ::std::endl;
+
+ p = ::cli::usage_para::option;
+
+ return p;
+}
+
+typedef
+std::map<std::string, void (*) (options&, ::cli::scanner&)>
+_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<std::string>, &options::include_path_,
+ &options::include_path_specified_ >;
+ _cli_options_map_["-I"] =
+ &::cli::thunk< options, std::vector<std::string>, &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<std::string, std::string>, &options::class_doc_,
+ &options::class_doc_specified_ >;
+ _cli_options_map_["--class"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::class__,
+ &options::class__specified_ >;
+ _cli_options_map_["--docvar"] =
+ &::cli::thunk< options, std::map<std::string, std::string>, &options::docvar_,
+ &options::docvar_specified_ >;
+ _cli_options_map_["-v"] =
+ &::cli::thunk< options, std::map<std::string, std::string>, &options::docvar_,
+ &options::docvar_specified_ >;
+ _cli_options_map_["--link-regex"] =
+ &::cli::thunk< options, std::vector<std::string>, &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<char, std::string>, &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<std::string>, &options::hxx_prologue_,
+ &options::hxx_prologue_specified_ >;
+ _cli_options_map_["--ixx-prologue"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::ixx_prologue_,
+ &options::ixx_prologue_specified_ >;
+ _cli_options_map_["--cxx-prologue"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::cxx_prologue_,
+ &options::cxx_prologue_specified_ >;
+ _cli_options_map_["--man-prologue"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::man_prologue_,
+ &options::man_prologue_specified_ >;
+ _cli_options_map_["--html-prologue"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::html_prologue_,
+ &options::html_prologue_specified_ >;
+ _cli_options_map_["--txt-prologue"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::txt_prologue_,
+ &options::txt_prologue_specified_ >;
+ _cli_options_map_["--hxx-epilogue"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::hxx_epilogue_,
+ &options::hxx_epilogue_specified_ >;
+ _cli_options_map_["--ixx-epilogue"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::ixx_epilogue_,
+ &options::ixx_epilogue_specified_ >;
+ _cli_options_map_["--cxx-epilogue"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::cxx_epilogue_,
+ &options::cxx_epilogue_specified_ >;
+ _cli_options_map_["--man-epilogue"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::man_epilogue_,
+ &options::man_epilogue_specified_ >;
+ _cli_options_map_["--html-epilogue"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::html_epilogue_,
+ &options::html_epilogue_specified_ >;
+ _cli_options_map_["--txt-epilogue"] =
+ &::cli::thunk< options, std::vector<std::string>, &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<std::string, std::string>, &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<char*> (co.c_str ()),
+ const_cast<char*> (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 <list>
+#include <deque>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#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<arg> args_;
+ std::list<std::string> 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 <typename X>
+ struct parser;
+}
+
+#include <map>
+
+#include <string>
+
+#include <vector>
+
+#include <cstddef>
+
+#include <cli/option-types.hxx>
+
+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<std::string>&
+ include_path () const;
+
+ std::vector<std::string>&
+ include_path ();
+
+ void
+ include_path (const std::vector<std::string>&);
+
+ 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<std::string, std::string>&
+ class_doc () const;
+
+ std::map<std::string, std::string>&
+ class_doc ();
+
+ void
+ class_doc (const std::map<std::string, std::string>&);
+
+ bool
+ class_doc_specified () const;
+
+ void
+ class_doc_specified (bool);
+
+ const std::vector<std::string>&
+ class_ () const;
+
+ std::vector<std::string>&
+ class_ ();
+
+ void
+ class_ (const std::vector<std::string>&);
+
+ bool
+ class__specified () const;
+
+ void
+ class__specified (bool);
+
+ const std::map<std::string, std::string>&
+ docvar () const;
+
+ std::map<std::string, std::string>&
+ docvar ();
+
+ void
+ docvar (const std::map<std::string, std::string>&);
+
+ bool
+ docvar_specified () const;
+
+ void
+ docvar_specified (bool);
+
+ const std::vector<std::string>&
+ link_regex () const;
+
+ std::vector<std::string>&
+ link_regex ();
+
+ void
+ link_regex (const std::vector<std::string>&);
+
+ 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<char, std::string>&
+ html_heading_map () const;
+
+ std::map<char, std::string>&
+ html_heading_map ();
+
+ void
+ html_heading_map (const std::map<char, std::string>&);
+
+ 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<std::string>&
+ hxx_prologue () const;
+
+ std::vector<std::string>&
+ hxx_prologue ();
+
+ void
+ hxx_prologue (const std::vector<std::string>&);
+
+ bool
+ hxx_prologue_specified () const;
+
+ void
+ hxx_prologue_specified (bool);
+
+ const std::vector<std::string>&
+ ixx_prologue () const;
+
+ std::vector<std::string>&
+ ixx_prologue ();
+
+ void
+ ixx_prologue (const std::vector<std::string>&);
+
+ bool
+ ixx_prologue_specified () const;
+
+ void
+ ixx_prologue_specified (bool);
+
+ const std::vector<std::string>&
+ cxx_prologue () const;
+
+ std::vector<std::string>&
+ cxx_prologue ();
+
+ void
+ cxx_prologue (const std::vector<std::string>&);
+
+ bool
+ cxx_prologue_specified () const;
+
+ void
+ cxx_prologue_specified (bool);
+
+ const std::vector<std::string>&
+ man_prologue () const;
+
+ std::vector<std::string>&
+ man_prologue ();
+
+ void
+ man_prologue (const std::vector<std::string>&);
+
+ bool
+ man_prologue_specified () const;
+
+ void
+ man_prologue_specified (bool);
+
+ const std::vector<std::string>&
+ html_prologue () const;
+
+ std::vector<std::string>&
+ html_prologue ();
+
+ void
+ html_prologue (const std::vector<std::string>&);
+
+ bool
+ html_prologue_specified () const;
+
+ void
+ html_prologue_specified (bool);
+
+ const std::vector<std::string>&
+ txt_prologue () const;
+
+ std::vector<std::string>&
+ txt_prologue ();
+
+ void
+ txt_prologue (const std::vector<std::string>&);
+
+ bool
+ txt_prologue_specified () const;
+
+ void
+ txt_prologue_specified (bool);
+
+ const std::vector<std::string>&
+ hxx_epilogue () const;
+
+ std::vector<std::string>&
+ hxx_epilogue ();
+
+ void
+ hxx_epilogue (const std::vector<std::string>&);
+
+ bool
+ hxx_epilogue_specified () const;
+
+ void
+ hxx_epilogue_specified (bool);
+
+ const std::vector<std::string>&
+ ixx_epilogue () const;
+
+ std::vector<std::string>&
+ ixx_epilogue ();
+
+ void
+ ixx_epilogue (const std::vector<std::string>&);
+
+ bool
+ ixx_epilogue_specified () const;
+
+ void
+ ixx_epilogue_specified (bool);
+
+ const std::vector<std::string>&
+ cxx_epilogue () const;
+
+ std::vector<std::string>&
+ cxx_epilogue ();
+
+ void
+ cxx_epilogue (const std::vector<std::string>&);
+
+ bool
+ cxx_epilogue_specified () const;
+
+ void
+ cxx_epilogue_specified (bool);
+
+ const std::vector<std::string>&
+ man_epilogue () const;
+
+ std::vector<std::string>&
+ man_epilogue ();
+
+ void
+ man_epilogue (const std::vector<std::string>&);
+
+ bool
+ man_epilogue_specified () const;
+
+ void
+ man_epilogue_specified (bool);
+
+ const std::vector<std::string>&
+ html_epilogue () const;
+
+ std::vector<std::string>&
+ html_epilogue ();
+
+ void
+ html_epilogue (const std::vector<std::string>&);
+
+ bool
+ html_epilogue_specified () const;
+
+ void
+ html_epilogue_specified (bool);
+
+ const std::vector<std::string>&
+ txt_epilogue () const;
+
+ std::vector<std::string>&
+ txt_epilogue ();
+
+ void
+ txt_epilogue (const std::vector<std::string>&);
+
+ 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<std::string, std::string>&
+ reserved_name () const;
+
+ std::map<std::string, std::string>&
+ reserved_name ();
+
+ void
+ reserved_name (const std::map<std::string, std::string>&);
+
+ 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<std::string> 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<std::string, std::string> class_doc_;
+ bool class_doc_specified_;
+ std::vector<std::string> class__;
+ bool class__specified_;
+ std::map<std::string, std::string> docvar_;
+ bool docvar_specified_;
+ std::vector<std::string> link_regex_;
+ bool link_regex_specified_;
+ bool link_regex_trace_;
+ std::map<char, std::string> html_heading_map_;
+ bool html_heading_map_specified_;
+ bool omit_link_check_;
+ std::vector<std::string> hxx_prologue_;
+ bool hxx_prologue_specified_;
+ std::vector<std::string> ixx_prologue_;
+ bool ixx_prologue_specified_;
+ std::vector<std::string> cxx_prologue_;
+ bool cxx_prologue_specified_;
+ std::vector<std::string> man_prologue_;
+ bool man_prologue_specified_;
+ std::vector<std::string> html_prologue_;
+ bool html_prologue_specified_;
+ std::vector<std::string> txt_prologue_;
+ bool txt_prologue_specified_;
+ std::vector<std::string> hxx_epilogue_;
+ bool hxx_epilogue_specified_;
+ std::vector<std::string> ixx_epilogue_;
+ bool ixx_epilogue_specified_;
+ std::vector<std::string> cxx_epilogue_;
+ bool cxx_epilogue_specified_;
+ std::vector<std::string> man_epilogue_;
+ bool man_epilogue_specified_;
+ std::vector<std::string> html_epilogue_;
+ bool html_epilogue_specified_;
+ std::vector<std::string> 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<std::string, std::string> reserved_name_;
+ bool reserved_name_specified_;
+ std::string options_file_;
+ bool options_file_specified_;
+};
+
+#include <cli/options.ixx>
+
+// 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 <cassert>
+
+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<std::string>& options::
+include_path () const
+{
+ return this->include_path_;
+}
+
+inline std::vector<std::string>& options::
+include_path ()
+{
+ return this->include_path_;
+}
+
+inline void options::
+include_path(const std::vector<std::string>& 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<std::string, std::string>& options::
+class_doc () const
+{
+ return this->class_doc_;
+}
+
+inline std::map<std::string, std::string>& options::
+class_doc ()
+{
+ return this->class_doc_;
+}
+
+inline void options::
+class_doc(const std::map<std::string, std::string>& 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<std::string>& options::
+class_ () const
+{
+ return this->class__;
+}
+
+inline std::vector<std::string>& options::
+class_ ()
+{
+ return this->class__;
+}
+
+inline void options::
+class_(const std::vector<std::string>& 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<std::string, std::string>& options::
+docvar () const
+{
+ return this->docvar_;
+}
+
+inline std::map<std::string, std::string>& options::
+docvar ()
+{
+ return this->docvar_;
+}
+
+inline void options::
+docvar(const std::map<std::string, std::string>& 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<std::string>& options::
+link_regex () const
+{
+ return this->link_regex_;
+}
+
+inline std::vector<std::string>& options::
+link_regex ()
+{
+ return this->link_regex_;
+}
+
+inline void options::
+link_regex(const std::vector<std::string>& 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<char, std::string>& options::
+html_heading_map () const
+{
+ return this->html_heading_map_;
+}
+
+inline std::map<char, std::string>& options::
+html_heading_map ()
+{
+ return this->html_heading_map_;
+}
+
+inline void options::
+html_heading_map(const std::map<char, std::string>& 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<std::string>& options::
+hxx_prologue () const
+{
+ return this->hxx_prologue_;
+}
+
+inline std::vector<std::string>& options::
+hxx_prologue ()
+{
+ return this->hxx_prologue_;
+}
+
+inline void options::
+hxx_prologue(const std::vector<std::string>& 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<std::string>& options::
+ixx_prologue () const
+{
+ return this->ixx_prologue_;
+}
+
+inline std::vector<std::string>& options::
+ixx_prologue ()
+{
+ return this->ixx_prologue_;
+}
+
+inline void options::
+ixx_prologue(const std::vector<std::string>& 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<std::string>& options::
+cxx_prologue () const
+{
+ return this->cxx_prologue_;
+}
+
+inline std::vector<std::string>& options::
+cxx_prologue ()
+{
+ return this->cxx_prologue_;
+}
+
+inline void options::
+cxx_prologue(const std::vector<std::string>& 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<std::string>& options::
+man_prologue () const
+{
+ return this->man_prologue_;
+}
+
+inline std::vector<std::string>& options::
+man_prologue ()
+{
+ return this->man_prologue_;
+}
+
+inline void options::
+man_prologue(const std::vector<std::string>& 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<std::string>& options::
+html_prologue () const
+{
+ return this->html_prologue_;
+}
+
+inline std::vector<std::string>& options::
+html_prologue ()
+{
+ return this->html_prologue_;
+}
+
+inline void options::
+html_prologue(const std::vector<std::string>& 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<std::string>& options::
+txt_prologue () const
+{
+ return this->txt_prologue_;
+}
+
+inline std::vector<std::string>& options::
+txt_prologue ()
+{
+ return this->txt_prologue_;
+}
+
+inline void options::
+txt_prologue(const std::vector<std::string>& 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<std::string>& options::
+hxx_epilogue () const
+{
+ return this->hxx_epilogue_;
+}
+
+inline std::vector<std::string>& options::
+hxx_epilogue ()
+{
+ return this->hxx_epilogue_;
+}
+
+inline void options::
+hxx_epilogue(const std::vector<std::string>& 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<std::string>& options::
+ixx_epilogue () const
+{
+ return this->ixx_epilogue_;
+}
+
+inline std::vector<std::string>& options::
+ixx_epilogue ()
+{
+ return this->ixx_epilogue_;
+}
+
+inline void options::
+ixx_epilogue(const std::vector<std::string>& 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<std::string>& options::
+cxx_epilogue () const
+{
+ return this->cxx_epilogue_;
+}
+
+inline std::vector<std::string>& options::
+cxx_epilogue ()
+{
+ return this->cxx_epilogue_;
+}
+
+inline void options::
+cxx_epilogue(const std::vector<std::string>& 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<std::string>& options::
+man_epilogue () const
+{
+ return this->man_epilogue_;
+}
+
+inline std::vector<std::string>& options::
+man_epilogue ()
+{
+ return this->man_epilogue_;
+}
+
+inline void options::
+man_epilogue(const std::vector<std::string>& 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<std::string>& options::
+html_epilogue () const
+{
+ return this->html_epilogue_;
+}
+
+inline std::vector<std::string>& options::
+html_epilogue ()
+{
+ return this->html_epilogue_;
+}
+
+inline void options::
+html_epilogue(const std::vector<std::string>& 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<std::string>& options::
+txt_epilogue () const
+{
+ return this->txt_epilogue_;
+}
+
+inline std::vector<std::string>& options::
+txt_epilogue ()
+{
+ return this->txt_epilogue_;
+}
+
+inline void options::
+txt_epilogue(const std::vector<std::string>& 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<std::string, std::string>& options::
+reserved_name () const
+{
+ return this->reserved_name_;
+}
+
+inline std::map<std::string, std::string>& options::
+reserved_name ()
+{
+ return this->reserved_name_;
+}
+
+inline void options::
+reserved_name(const std::map<std::string, std::string>& 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 <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef _WIN32
+# include <unistd.h> // stat
+# include <sys/types.h> // stat
+# include <sys/stat.h> // stat
+#else
+# include <sys/types.h> // _stat
+# include <sys/stat.h> // _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 <fstream>
+#include <sstream>
+#include <iostream>
+
+#include <cli/token.hxx>
+#include <cli/lexer.hxx>
+#include <cli/parser.hxx>
+
+#include <cli/semantics.hxx>
+
+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 <typename T>
+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<cli_unit> parser::
+parse (std::istream& is, path const& p)
+{
+ unique_ptr<cli_unit> 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<scope> 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<path const> 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<lexer> 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<cxx_unit> (*path_, t.line (), t.column ()));
+ root_->new_edge<cxx_includes> (*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<cli_unit> (p, 1, 1));
+ root_->new_edge<cli_includes> (*cur_, n, ik, f);
+ include_map_[ap] = &n;
+
+ auto_restore<cli_unit> new_cur (cur_, &n);
+ auto_restore<path const> 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<lexer> 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<cli_includes> (*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<doc> (*path_, ln, cl));
+
+ // See if this is a variable assignment: "\<var>=<val>".
+ //
+ 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<names> (
+ *scope_, d, "var: " + string (s, 1, p - 1));
+ s = string (s, p + 1);
+ }
+ else
+ {
+ ostringstream os;
+ os << "doc: " << doc_count_++;
+ root_->new_edge<names> (*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<doc> (*path_, ln, cl);
+ root_->new_edge<names> (*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<scope> new_scope (scope_);
+
+ if (valid_)
+ {
+ namespace_& n (
+ root_->new_node<namespace_> (*path_, t.line (), t.column ()));
+ root_->new_edge<names> (*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<class_> (*path_, t.line (), t.column ());
+ root_->new_edge<names> (*scope_, *n, t.identifier ());
+ }
+
+ t = lexer_->next ();
+
+ // inheritance-spec
+ //
+ if (t.punctuation () == token::p_colon)
+ {
+ for (;;)
+ {
+ t = lexer_->next ();
+ size_t line (t.line ()), col (t.column ());
+
+ string name;
+ if (!qualified_name (t, name))
+ {
+ cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: "
+ << "expected qualified name instead of " << t << endl;
+ throw error ();
+ }
+
+ string ns;
+
+ // If it is a fully-qualifed name, then start from the global namespace.
+ // Otherwise, from the current scope.
+ //
+ if (name[0] == ':')
+ name = string (name, 2, string::npos);
+ else
+ ns = scope_->fq_name ();
+
+ if (class_* b = cur_->lookup<class_> (ns, name))
+ root_->new_edge<inherits> (*n, *b);
+ else
+ {
+ cerr << *path_ << ':' << line << ':' << col << ": error: "
+ << "unable to resolve base class '" << name << "'" << endl;
+ valid_ = false;
+ }
+
+ if (t.punctuation () != token::p_comma)
+ break;
+ }
+ }
+
+ // abstract-spec
+ //
+ if (t.punctuation () == token::p_eq)
+ {
+ t = lexer_->next ();
+
+ if (t.type () != token::t_int_lit || t.literal () != "0")
+ {
+ cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: "
+ << "expected '0' instead of " << t << endl;
+ throw error ();
+ }
+
+ if (n != 0)
+ n->abstract (true);
+
+ t = lexer_->next ();
+ }
+
+ if (t.punctuation () != token::p_lcbrace)
+ {
+ cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: "
+ << "expected '{' instead of " << t << endl;
+ throw error ();
+ }
+
+ auto_restore<scope> 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<option> (*path_, l, c);
+ type& t (root_->new_type (*path_, l, c, type_name));
+ root_->new_edge<belongs> (*o, t);
+ }
+
+ // option-name-seq
+ //
+ names::name_list nl;
+ for (;;)
+ {
+ switch (t.type ())
+ {
+ case token::t_identifier:
+ {
+ if (valid_)
+ nl.push_back (t.identifier ());
+
+ break;
+ }
+ case token::t_string_lit:
+ {
+ if (valid_)
+ {
+ // Get rid of '"'.
+ //
+ string r;
+ string const& l (t.literal ());
+ char p ('\0');
+
+ for (size_t i (0), n (l.size ()); i < n; ++i)
+ {
+ if (l[i] == '"' && p != '\\')
+ continue;
+
+ // We need to keep track of \\ escapings so we don't confuse
+ // them with \", as in "\\".
+ //
+ if (l[i] == '\\' && p == '\\')
+ p = '\0';
+ else
+ p = l[i];
+
+ r += l[i];
+ }
+
+ nl.push_back (r);
+ }
+
+ break;
+ }
+ default:
+ {
+ cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: "
+ << "option name expected instead of " << t << endl;
+ throw error ();
+ }
+ }
+
+ t = lexer_->next ();
+
+ if (t.punctuation () == token::p_or)
+ t = lexer_->next ();
+ else
+ break;
+ }
+
+ if (valid_)
+ root_->new_edge<names> (*scope_, *o, nl);
+
+ // initializer
+ //
+ std::string ev;
+ expression::expression_type et;
+
+ if (t.punctuation () == token::p_eq)
+ {
+ // assignment initiaizer
+ //
+ t = lexer_->next ();
+
+ l = t.line ();
+ c = t.column ();
+
+ if (qualified_name (t, ev))
+ {
+ et = expression::identifier;
+ }
+ else
+ {
+ switch (t.type ())
+ {
+ case token::t_string_lit:
+ {
+ ev = t.literal ();
+ et = expression::string_lit;
+ t = lexer_->next ();
+ break;
+ }
+ case token::t_char_lit:
+ {
+ ev = t.literal ();
+ et = expression::char_lit;
+ t = lexer_->next ();
+ break;
+ }
+ case token::t_bool_lit:
+ {
+ ev = t.literal ();
+ et = expression::bool_lit;
+ t = lexer_->next ();
+ break;
+ }
+ case token::t_int_lit:
+ {
+ ev = t.literal ();
+ et = expression::int_lit;
+ t = lexer_->next ();
+ break;
+ }
+ case token::t_float_lit:
+ {
+ ev = t.literal ();
+ et = expression::float_lit;
+ t = lexer_->next ();
+ break;
+ }
+ case token::t_call_expr:
+ {
+ ev = t.expression ();
+ et = expression::call_expr;
+ t = lexer_->next ();
+ break;
+ }
+ default:
+ {
+ cerr << *path_ << ':' << t.line () << ':' << t.column ()
+ << ": error: expected intializer instead of " << t << endl;
+ throw error ();
+ }
+ }
+ }
+ }
+ else if (t.type () == token::t_call_expr)
+ {
+ // c-tor initializer
+ //
+ l = t.line ();
+ c = t.column ();
+
+ ev = t.expression ();
+ et = expression::call_expr;
+ t = lexer_->next ();
+ }
+
+ if (valid_ && !ev.empty ())
+ {
+ expression& e (root_->new_node<expression> (*path_, l, c, et, ev));
+ root_->new_edge<initialized> (*o, e);
+ }
+
+ // option-def-trailer
+ //
+ if (t.punctuation () == token::p_lcbrace)
+ {
+ // doc-string-seq
+ //
+ 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_)
+ o->doc ().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 ();
+ }
+
+ t = lexer_->next ();
+
+ // Allow semicolon after option-doc for backwards compatibility.
+ //
+ if (t.punctuation () == token::p_semi)
+ t = lexer_->next ();
+ }
+ else
+ {
+ if (t.punctuation () != token::p_semi)
+ {
+ cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: "
+ << "expected ';' instead of " << t << endl;
+ throw error ();
+ }
+
+ t = lexer_->next ();
+ }
+
+ return true;
+}
+
+string parser::
+doc_string (const char* l, size_t n)
+{
+ // Pass 1: get rid of " (as in "foo""bar"), convert \" to just ".
+ //
+ string t1, t2, t3;
+ char p ('\0'); // Previous character.
+
+ for (size_t i (0); i < n; ++i)
+ {
+ char c (l[i]);
+
+ if (c == '"')
+ {
+ if (p == '\\')
+ {
+ t1[t1.size () - 1] = '"'; // Replace \ with ".
+ p = c;
+ }
+ continue;
+ }
+
+ // We need to keep track of \\ escapings so we don't confuse them with \",
+ // as in \\".
+ //
+ if (c == '\\' && p == '\\')
+ p = '\0';
+ else
+ p = c;
+
+ t1 += c;
+ }
+
+ // Pass two: get rid of leading and trailing spaces in each line. Also
+ // handle pre-formatted fragments.
+ //
+ if (t1.size () != 0)
+ {
+ bool more (true);
+ size_t b (0), e, p;
+
+ bool pre (false);
+ size_t m (0); // Number of leading spaces to remove in pre.
+
+ while (more)
+ {
+ p = e = t1.find ('\n', b);
+
+ if (p == string::npos)
+ {
+ e = t1.size ();
+ more = false;
+ }
+
+ if (b != e) // Unless this is just a single newline.
+ {
+ // In the pre mode we only remove up to m leading whitespaces.
+ //
+ {
+ size_t i (0);
+ while (b < e &&
+ (t1[b] == 0x20 || t1[b] == 0x0D || t1[b] == 0x09) &&
+ (!pre || i != m))
+ {
+ ++b;
+ ++i;
+ }
+
+ if (!pre)
+ m = i;
+ }
+
+ --e;
+ while (e > b && (t1[e] == 0x20 || t1[e] == 0x0D || t1[e] == 0x09))
+ --e;
+
+ // Pre-formatted fragment marker or its escape.
+ //
+ if (t1[b] == '\\' && (b == e || (b + 1 == e && t1[e] == '\\')))
+ {
+ // Use Start of Text (0x02) and End of Text (0x03) special
+ // characters as pre-formatted fragment markers.
+ //
+ if (b == e)
+ {
+ pre = !pre;
+ t2 += (pre ? 0x02 : 0x03);
+ }
+ else
+ t2 += "\\\\"; // Keep escaped.
+ }
+ else if (b <= e)
+ t2.append (t1, b, e - b + 1);
+ }
+
+ if (more)
+ {
+ t2 += '\n';
+ b = p + 1;
+ }
+ }
+
+ if (pre)
+ {
+ cerr << *path_ << ": error: missing pre-formatted fragment end marker "
+ << "in documentation string '" << t1 << "'" << endl;
+ throw error ();
+ }
+ }
+
+ // Pass 3: replace every single newline with single space and all multiple
+ // newlines (paragraph marker) with a single newline, unless we are in a
+ // pre-formatted fragment. Also process escapes in pre-formatted fragmens.
+ //
+ bool pre (false);
+ p = '\0'; // Previous character in pre-formatted fragment.
+ for (size_t i (0), n (t2.size ()); i < n; ++i)
+ {
+ char c (t2[i]);
+
+ if (c == '\n' && !pre)
+ {
+ size_t j (i);
+ for (; i + 1 < n && t2[i + 1] == '\n'; ++i) ;
+
+ if (j != 0 && i + 1 != n) // Strip leading and trailing newlines.
+ t3 += i != j ? '\n' : ' ';
+ }
+ else
+ {
+ if (c == (pre ? 0x03 : 0x02))
+ {
+ pre = !pre;
+
+ // Kill "inner" newlines (after opening and before closing '/'
+ // markers). Also check for "outer" newlines so that we always
+ // have paragraph separation.
+ //
+ size_t k (t3.size ());
+ if (pre)
+ {
+ if (k != 0 && t3[k - 1] != '\n') // Outer.
+ {
+ cerr << *path_ << ": error: missing empty line before pre-"
+ << "formatted fragment start marker in documentation "
+ << "string '" << t1 << "'" << endl;
+ throw error ();
+ }
+
+ ++i; // Skip inner.
+ }
+ else
+ {
+ if (t3[k - 1] == '\n') // Could be the same as opening if empty.
+ t3.resize (k - 1); // Pop inner.
+
+ if (i + 2 < n && (t2[i + 1] != '\n' || t2[i + 2] != '\n')) // Outer.
+ {
+ cerr << *path_ << ": error: missing empty line after pre-"
+ << "formatted fragment end marker in documentation "
+ << "string '" << t1 << "'" << endl;
+ throw error ();
+ }
+ }
+
+ t3 += c;
+ continue;
+ }
+
+ if (pre)
+ {
+ // In the pre-formatted fragments the only two escapes that we
+ // recognize are \" which was handled on pass 1 above and \\ which we
+ // handle here.
+ //
+ if (c == '\\' && p == '\\')
+ {
+ p = '\0'; // Keep the already added and clear.
+ continue;
+ }
+
+ p = c;
+ }
+
+ t3 += c;
+ }
+ }
+
+ return t3;
+}
+
+
+bool parser::
+qualified_name (token& t, string& r)
+{
+ if (t.type () != token::t_identifier && t.punctuation () != token::p_dcolon)
+ return false;
+
+ r.clear ();
+
+ if (t.punctuation () == token::p_dcolon)
+ {
+ r += "::";
+ t = lexer_->next ();
+ }
+
+ for (;;)
+ {
+ if (t.type () != token::t_identifier)
+ {
+ cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: "
+ << "expected identifier after '::'" << endl;
+ throw error ();
+ }
+
+ r += t.identifier ();
+ t = lexer_->next ();
+
+ if (t.type () == token::t_template_expr)
+ {
+ // Template-id.
+ //
+ r += t.expression ();
+ t = lexer_->next ();
+ }
+
+ if (t.punctuation () == token::p_dcolon)
+ {
+ r += "::";
+ t = lexer_->next ();
+ }
+ else
+ break;
+ }
+
+ return true;
+}
+
+bool parser::
+fundamental_type (token& t, string& r)
+{
+ r.clear ();
+
+ switch (t.keyword ())
+ {
+ case token::k_signed:
+ case token::k_unsigned:
+ {
+ r = t.keyword () == token::k_signed ? "signed" : "unsigned";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_short:
+ {
+ r += " short";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_int:
+ {
+ r += " int";
+ t = lexer_->next ();
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case token::k_long:
+ {
+ r += " long";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_int:
+ {
+ r += " int";
+ t = lexer_->next ();
+ break;
+ }
+ case token::k_long:
+ {
+ r += " long";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_int:
+ {
+ r += " int";
+ t = lexer_->next ();
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case token::k_int:
+ {
+ r += " int";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_short:
+ {
+ r += " short";
+ t = lexer_->next ();
+ break;
+ }
+ case token::k_long:
+ {
+ r += " long";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_long:
+ {
+ r += " long";
+ t = lexer_->next ();
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case token::k_char:
+ {
+ r += " char";
+ t = lexer_->next ();
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case token::k_short:
+ case token::k_long:
+ {
+ bool l (t.keyword () == token::k_long);
+ r = l ? "long" : "short";
+
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_signed:
+ case token::k_unsigned:
+ {
+ r += t.keyword () == token::k_signed ? " signed" : " unsigned";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_int:
+ {
+ r += " int";
+ t = lexer_->next ();
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case token::k_long:
+ {
+ r += " long";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_signed:
+ case token::k_unsigned:
+ {
+ r += t.keyword () == token::k_signed ? " signed" : " unsigned";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_int:
+ {
+ r += " int";
+ t = lexer_->next ();
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case token::k_int:
+ {
+ r += " int";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_signed:
+ {
+ r += " signed";
+ t = lexer_->next ();
+ break;
+ }
+ case token::k_unsigned:
+ {
+ r += " unsigned";
+ t = lexer_->next ();
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case token::k_int:
+ {
+ r += " int";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_signed:
+ {
+ r += " signed";
+ t = lexer_->next ();
+ break;
+ }
+ case token::k_unsigned:
+ {
+ r += " unsigned";
+ t = lexer_->next ();
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case token::k_double:
+ {
+ if (l)
+ {
+ r += " double";
+ t = lexer_->next ();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case token::k_int:
+ {
+ r = "int";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_signed:
+ case token::k_unsigned:
+ {
+ r += t.keyword () == token::k_signed ? " signed" : " unsigned";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_short:
+ {
+ r += " short";
+ t = lexer_->next ();
+ break;
+ }
+ case token::k_long:
+ {
+ r += " long";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_long:
+ {
+ r += " long";
+ t = lexer_->next ();
+ }
+ default:
+ break;
+ }
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case token::k_short:
+ {
+ r += " short";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_signed:
+ {
+ r += " signed";
+ t = lexer_->next ();
+ break;
+ }
+ case token::k_unsigned:
+ {
+ r += " unsigned";
+ t = lexer_->next ();
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case token::k_long:
+ {
+ r += " long";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_signed:
+ {
+ r += " signed";
+ t = lexer_->next ();
+ break;
+ }
+ case token::k_unsigned:
+ {
+ r += " unsigned";
+ t = lexer_->next ();
+ break;
+ }
+ case token::k_long:
+ {
+ r += " long";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_signed:
+ {
+ r += " signed";
+ t = lexer_->next ();
+ break;
+ }
+ case token::k_unsigned:
+ {
+ r += " unsigned";
+ t = lexer_->next ();
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case token::k_char:
+ {
+ r = "char";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_signed:
+ {
+ r += " signed";
+ t = lexer_->next ();
+ break;
+ }
+ case token::k_unsigned:
+ {
+ r += " unsigned";
+ t = lexer_->next ();
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case token::k_bool:
+ {
+ r = "bool";
+ t = lexer_->next ();
+ break;
+ }
+ case token::k_wchar:
+ {
+ r = "wchar_t";
+ t = lexer_->next ();
+ break;
+ }
+ case token::k_float:
+ {
+ r = "float";
+ t = lexer_->next ();
+ break;
+ }
+ case token::k_double:
+ {
+ r = "double";
+ switch ((t = lexer_->next ()).keyword ())
+ {
+ case token::k_long:
+ {
+ r += " long";
+ t = lexer_->next ();
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+
+ return true;
+}
diff --git a/cli/cli/parser.hxx b/cli/cli/parser.hxx
new file mode 100644
index 0000000..326768e
--- /dev/null
+++ b/cli/cli/parser.hxx
@@ -0,0 +1,91 @@
+// file : cli/parser.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_PARSER_HXX
+#define CLI_PARSER_HXX
+
+#include <map>
+#include <string>
+#include <vector>
+#include <memory> // unique_ptr
+#include <cstddef> // size_t
+#include <istream>
+
+#include <cli/semantics/elements.hxx>
+#include <cli/semantics/unit.hxx>
+
+class token;
+class lexer;
+
+class parser
+{
+public:
+ typedef std::vector<semantics::path> paths;
+
+ parser (paths const& include_paths): include_paths_ (include_paths) {}
+
+ struct invalid_input {};
+
+ std::unique_ptr<semantics::cli_unit>
+ parse (std::istream& is, semantics::path const& path);
+
+private:
+ struct error {};
+
+ void
+ def_unit ();
+
+ void
+ source_decl ();
+
+ void
+ include_decl ();
+
+ bool
+ decl (token&);
+
+ void
+ scope_doc (token&);
+
+ void
+ namespace_def ();
+
+ void
+ class_def ();
+
+ bool
+ option_def (token&);
+
+ std::string
+ doc_string (const char*, std::size_t);
+
+ bool
+ qualified_name (token&, std::string& name);
+
+ bool
+ fundamental_type (token&, std::string& name);
+
+private:
+ void
+ recover (token& t);
+
+private:
+ paths const include_paths_;
+
+ bool valid_;
+ semantics::path const* path_;
+
+ lexer* lexer_;
+
+ semantics::cli_unit* root_;
+ semantics::cli_unit* cur_;
+ semantics::scope* scope_;
+
+ std::size_t doc_count_; // Scope doc counter, see scope_doc() for details.
+
+ typedef std::map<semantics::path, semantics::cli_unit*> include_map;
+ include_map include_map_;
+};
+
+#endif // CLI_PARSER_HXX
diff --git a/cli/cli/parser.test.cxx b/cli/cli/parser.test.cxx
new file mode 100644
index 0000000..6ef0d11
--- /dev/null
+++ b/cli/cli/parser.test.cxx
@@ -0,0 +1,45 @@
+// file : cli/parser.test.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <fstream>
+#include <iostream>
+
+#include <cli/parser.hxx>
+#include <cli/semantics.hxx>
+#include <cli/traversal.hxx>
+
+using namespace std;
+
+int
+main (int argc, char* argv[])
+{
+ if (argc != 2)
+ {
+ cerr << "usage: " << argv[0] << " file.cli" << endl;
+ return 1;
+ }
+
+ try
+ {
+ semantics::path path (argv[1]);
+
+ ifstream ifs;
+ ifs.exceptions (ifstream::failbit | ifstream::badbit);
+ ifs.open (path.string ().c_str ());
+
+ parser::paths include_paths;
+ parser p (include_paths);
+ p.parse (ifs, path);
+ }
+ catch (semantics::invalid_path const& e)
+ {
+ cerr << "error: '" << e.path () << "' is not a valid filesystem path"
+ << endl;
+ return 1;
+ }
+ catch (parser::invalid_input const&)
+ {
+ return 1;
+ }
+}
diff --git a/cli/cli/parser.test.testscript b/cli/cli/parser.test.testscript
new file mode 100644
index 0000000..9522dbe
--- /dev/null
+++ b/cli/cli/parser.test.testscript
@@ -0,0 +1,242 @@
+# file : cli/parser.test.testscript
+# license : MIT; see accompanying LICENSE file
+
+# @@ Give tests some meaningfull descriptions. Probably move c++-style comments
+# from the test.cli files to test descriptions.
+#
+
+: 000
+:
+cat <<EOI >=test.cli;
+// def-unit
+//
+include <string>;
+namespace n {}
+class c {};
+EOI
+$* test.cli >:""
+
+: 001
+:
+cat <<EOI >=base.cli;
+EOI
+cat <<EOI >=common.cli;
+include "base.cli";
+EOI
+cat <<EOI >=test.cli;
+// include-decl-seq, include-decl
+//
+include <string>;
+include "types.hxx";
+include "common.cli";
+include "../001/base.cli";
+EOI
+$* test.cli >:""
+
+: 002
+:
+cat <<EOI >=test.cli;
+// namespace-def, namespace-def-body
+//
+namespace n1 {}
+
+namespace n1
+{
+ namespace n2 {}
+ class c {};
+ namespace n2
+ {
+ namespace n3 {}
+ }
+}
+
+namespace n4 {}
+EOI
+$* test.cli >:""
+
+: 003
+:
+cat <<EOI >=test.cli;
+// class-def, inheritance-spec, abstract-spec
+//
+class c1
+{
+};
+
+class c2 = 0
+{
+};
+
+class c3: c1, ::c2
+{
+};
+
+namespace n1
+{
+ class c {};
+}
+
+class c4: n1::c = 0
+{
+};
+
+EOI
+$* test.cli >:""
+
+: 004
+:
+cat <<EOI >=test.cli;
+// option-def-seq
+//
+class c
+{
+ bool -a;
+ int -b;
+ float -c;
+};
+EOI
+$* test.cli >:""
+
+: 005
+:
+cat <<EOI >=test.cli;
+// option-def, type-spec, fundamental-type-spec, option-name-seq,
+// option-name, initializer, initializer-expr
+//
+class c
+{
+ bool --bool;
+ char --char;
+
+ int -i1;
+ unsigned int -i2;
+ int unsigned -i3;
+ long -i4;
+ long int -i5;
+ int long -i6;
+ unsigned long -i7;
+ long unsigned -i8;
+
+ unsigned long int -i9;
+ long unsigned int -i10;
+ int long unsigned -i11;
+ unsigned int long -i12;
+
+ short -i13;
+ unsigned short -i14;
+ short unsigned -i15;
+
+ char -i16;
+ signed char -i17;
+ char signed -i18;
+ unsigned char -i19;
+ char unsigned -i20;
+
+ long long -ll1;
+ long long int -ll2;
+ long long unsigned -ll3;
+ int long long -ll4;
+ unsigned long long -ll5;
+ long long int unsigned -ll6;
+ long long unsigned int -ll7;
+ unsigned long long int -ll8;
+ unsigned int long long -ll9;
+ int long long unsigned -ll10;
+ int unsigned long long -ll11;
+
+ double -d1;
+ long double -d2;
+ double long -d3;
+
+ foo -o1;
+ ::foo -o2;
+ ::foo<bar> -o3;
+ foo::bar -o4;
+ ::foo::bar -o5;
+ ::foo<bar>::baz -o6;
+ ::foo<bar>::baz< ::fox<2> > -o7;
+
+ bool -n1|--name1|/name1;
+ bool "-n2"|"--name2";
+
+ string -init1 = "string";
+ char -init2 = 'c';
+ int -init3 = -5;
+ bool -inti4 = true;
+ long -init5 = (2 * 4 - 5);
+ type -init6 = type::default_value;
+ type -init7 (abc, 2 - 1);
+};
+EOI
+$* test.cli >:""
+
+: 006
+:
+cat <<EOI >=test.cli;
+// option-doc
+//
+class c
+{
+ bool --help | -h {"Help me"};
+
+ int --comp = 5
+ {
+ "<level>",
+ "Set compression level",
+ "Set compression level to <level>.
+
+ The minimum value for this options is 0 and
+ maximum is 9."
+ };
+};
+EOI
+$* test.cli >:""
+
+: 007
+:
+cat <<EOI >=base.cli;
+class b1 {};
+
+namespace n1
+{
+ class b2 {};
+
+ namespace i1
+ {
+ class b3 {};
+ }
+}
+EOI
+cat <<EOI >=test.cli;
+// base class lookup
+//
+
+include "base.cli";
+
+class c1 {};
+class c2: c1 {};
+class c3: ::c1 {};
+
+namespace n1
+{
+ class c4 {};
+ class c5: c4 {};
+ class c6: n1::c4 {};
+ class c7: ::n1::c4 {};
+
+ class c8: b2 {}; // From included.
+ class c9: i1::b3 {}; // From included.
+
+ namespace i1
+ {
+ class c10: c4 {}; // Outer scope.
+ class c11: b3 {}; // From included.
+ class c12: b2 {}; // Outer scope from included.
+ class c4: n1::c4 {}; // Qualified name from outer scope.
+ }
+}
+
+class c13: n1::c4 {};
+class c14: ::n1::c4 {};
+EOI
+$* test.cli >:""
diff --git a/cli/cli/runtime-header.cxx b/cli/cli/runtime-header.cxx
new file mode 100644
index 0000000..adf70dd
--- /dev/null
+++ b/cli/cli/runtime-header.cxx
@@ -0,0 +1,647 @@
+// file : cli/runtime-header.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cli/runtime-header.hxx>
+
+using namespace std;
+
+void
+generate_runtime_header (context& ctx)
+{
+ ostream& os (ctx.os);
+
+ if (ctx.options.generate_file_scanner ())
+ os << "#include <list>" << endl
+ << "#include <deque>" << endl;
+
+ if (ctx.options.generate_description ())
+ os << "#include <map>" << endl;
+
+ if (ctx.options.generate_description () ||
+ ctx.options.generate_vector_scanner ())
+ os << "#include <vector>" << endl;
+
+ os << "#include <iosfwd>" << endl
+ << "#include <string>" << endl
+ << "#include <cstddef>" << endl
+ << "#include <exception>" << endl
+ << endl;
+
+ // VC++ and xlC don't like the (void)x expression if x is a reference
+ // to an incomplete type. On the other hand, GCC warns that (void*)&x
+ // doesn't have any effect.
+ //
+ os << "#ifndef CLI_POTENTIALLY_UNUSED" << endl
+ << "# if defined(_MSC_VER) || defined(__xlC__)" << endl
+ << "# define CLI_POTENTIALLY_UNUSED(x) (void*)&x" << endl
+ << "# else" << endl
+ << "# define CLI_POTENTIALLY_UNUSED(x) (void)x" << endl
+ << "# endif" << endl
+ << "#endif" << endl
+ << endl;
+
+ ctx.ns_open (ctx.cli);
+
+ // usage_para
+ //
+ if (!ctx.options.suppress_usage ())
+ os << "class usage_para"
+ << "{"
+ << "public:" << endl
+ << "enum value"
+ << "{"
+ << "none," << endl
+ << "text," << endl
+ << "option" << endl
+ << "};"
+ << "usage_para (value);"
+ << endl
+ << "operator value () const {return v_;}" // Can't generate outside.
+ << "private:" << endl
+ << "value v_;"
+ << "};";
+
+ // unknown_mode
+ //
+ os << "class unknown_mode"
+ << "{"
+ << "public:" << endl
+ << "enum value"
+ << "{"
+ << "skip," << endl
+ << "stop," << endl
+ << "fail" << endl
+ << "};"
+ << "unknown_mode (value);"
+ << endl
+ << "operator value () const {return v_;}" // Can't generate outside.
+ << "private:" << endl
+ << "value v_;"
+ << "};";
+
+ // Exceptions.
+ //
+
+ string const& os_type (ctx.options.ostream_type ());
+
+ os << "// Exceptions." << endl
+ << "//" << endl
+ << endl;
+
+ os << "class exception: public std::exception"
+ << "{"
+ << "public:" << endl
+ << "virtual void" << endl
+ << "print (" << os_type << "&) const = 0;"
+ << "};";
+
+ os << os_type << "&" << endl
+ << "operator<< (" << os_type << "&, const exception&);"
+ << endl;
+
+ os << "class unknown_option: public exception"
+ << "{"
+ << "public:" << endl
+ << "virtual" << endl
+ << "~unknown_option () throw ();"
+ << endl
+ << "unknown_option (const std::string& option);"
+ << endl
+ << "const std::string&" << endl
+ << "option () const;"
+ << endl
+ << "virtual void" << endl
+ << "print (" << os_type << "&) const;"
+ << endl
+ << "virtual const char*" << endl
+ << "what () const throw ();"
+ << endl
+ << "private:" << endl
+ << "std::string option_;"
+ << "};";
+
+ os << "class unknown_argument: public exception"
+ << "{"
+ << "public:" << endl
+ << "virtual" << endl
+ << "~unknown_argument () throw ();"
+ << endl
+ << "unknown_argument (const std::string& argument);"
+ << endl
+ << "const std::string&" << endl
+ << "argument () const;"
+ << endl
+ << "virtual void" << endl
+ << "print (" << os_type << "&) const;"
+ << endl
+ << "virtual const char*" << endl
+ << "what () const throw ();"
+ << endl
+ << "private:" << endl
+ << "std::string argument_;"
+ << "};";
+
+ os << "class missing_value: public exception"
+ << "{"
+ << "public:" << endl
+ << "virtual" << endl
+ << "~missing_value () throw ();"
+ << endl
+ << "missing_value (const std::string& option);"
+ << endl
+ << "const std::string&" << endl
+ << "option () const;"
+ << endl
+ << "virtual void" << endl
+ << "print (" << os_type << "&) const;"
+ << endl
+ << "virtual const char*" << endl
+ << "what () const throw ();"
+ << endl
+ << "private:" << endl
+ << "std::string option_;"
+ << "};";
+
+ os << "class invalid_value: public exception"
+ << "{"
+ << "public:" << endl
+ << "virtual" << endl
+ << "~invalid_value () throw ();"
+ << endl
+ << "invalid_value (const std::string& option," << endl
+ << "const std::string& value," << endl
+ << "const std::string& message = std::string ());"
+ << endl
+ << "const std::string&" << endl
+ << "option () const;"
+ << endl
+ << "const std::string&" << endl
+ << "value () const;"
+ << endl
+ << "const std::string&" << endl
+ << "message () const;"
+ << endl
+ << "virtual void" << endl
+ << "print (" << os_type << "&) const;"
+ << endl
+ << "virtual const char*" << endl
+ << "what () const throw ();"
+ << endl
+ << "private:" << endl
+ << "std::string option_;"
+ << "std::string value_;"
+ << "std::string message_;"
+ << "};";
+
+ os << "class eos_reached: public exception"
+ << "{"
+ << "public:" << endl
+ << "virtual void" << endl
+ << "print (" << os_type << "&) const;"
+ << endl
+ << "virtual const char*" << endl
+ << "what () const throw ();"
+ << "};";
+
+ if (ctx.options.generate_file_scanner ())
+ {
+ os << "class file_io_failure: public exception"
+ << "{"
+ << "public:" << endl
+ << "virtual" << endl
+ << "~file_io_failure () throw ();"
+ << endl
+ << "file_io_failure (const std::string& file);"
+ << endl
+ << "const std::string&" << endl
+ << "file () const;"
+ << endl
+ << "virtual void" << endl
+ << "print (" << os_type << "&) const;"
+ << endl
+ << "virtual const char*" << endl
+ << "what () const throw ();"
+ << endl
+ << "private:" << endl
+ << "std::string file_;"
+ << "};";
+
+ os << "class unmatched_quote: public exception"
+ << "{"
+ << "public:" << endl
+ << "virtual" << endl
+ << "~unmatched_quote () throw ();"
+ << endl
+ << "unmatched_quote (const std::string& argument);"
+ << endl
+ << "const std::string&" << endl
+ << "argument () const;"
+ << endl
+ << "virtual void" << endl
+ << "print (" << os_type << "&) const;"
+ << endl
+ << "virtual const char*" << endl
+ << "what () const throw ();"
+ << endl
+ << "private:" << endl
+ << "std::string argument_;"
+ << "};";
+ }
+
+ if (ctx.options.generate_group_scanner ())
+ {
+ os << "class unexpected_group: public exception"
+ << "{"
+ << "public:" << endl
+ << "virtual" << endl
+ << "~unexpected_group () throw ();"
+ << endl
+ << "unexpected_group (const std::string& argument," << endl
+ << "const std::string& group);"
+ << endl
+ << "const std::string&" << endl
+ << "argument () const;"
+ << endl
+ << "const std::string&" << endl
+ << "group () const;"
+ << endl
+ << "virtual void" << endl
+ << "print (std::ostream&) const;"
+ << endl
+ << "virtual const char*" << endl
+ << "what () const throw ();"
+ << endl
+ << "private:" << endl
+ << "std::string argument_;"
+ << "std::string group_;"
+ << "};";
+
+ os << "class group_separator: public exception" << endl
+ << "{"
+ << "public:" << endl
+ << "virtual" << endl
+ << "~group_separator () throw ();"
+ << endl
+ << "// Note: either (but not both) can be empty." << endl
+ << "//" << endl
+ << "group_separator (const std::string& encountered," << endl
+ << "const std::string& expected);"
+ << endl
+ << "const std::string&" << endl
+ << "encountered () const;"
+ << endl
+ << "const std::string&" << endl
+ << "expected () const;"
+ << endl
+ << "virtual void" << endl
+ << "print (std::ostream&) const;"
+ << endl
+ << "virtual const char*" << endl
+ << "what () const throw ();"
+ << endl
+ << "private:" << endl
+ << "std::string encountered_;"
+ << "std::string expected_;"
+ << "};";
+ }
+
+ // scanner
+ //
+ os << "// Command line argument scanner interface." << endl
+ << "//" << endl
+ << "// The values returned by next() are guaranteed to be valid" << endl
+ << "// for the two previous arguments up until a call to a third" << endl
+ << "// peek() or next()." << endl
+ << "//" << endl
+ << "class scanner"
+ << "{"
+ << "public:" << endl
+ << "virtual" << endl
+ << "~scanner ();"
+ << endl
+ << "virtual bool" << endl
+ << "more () = 0;"
+ << endl
+ << "virtual const char*" << endl
+ << "peek () = 0;"
+ << endl
+ << "virtual const char*" << endl
+ << "next () = 0;"
+ << endl
+ << "virtual void" << endl
+ << "skip () = 0;"
+ << "};";
+
+ // argv_scanner
+ //
+ os << "class argv_scanner: public scanner"
+ << "{"
+ << "public:" << endl
+ << "argv_scanner (int& argc, char** argv, bool erase = false);"
+ << "argv_scanner (int start, int& argc, char** argv, bool erase = false);"
+ << endl
+ << "int" << endl
+ << "end () const;"
+ << endl
+ << "virtual bool" << endl
+ << "more ();"
+ << endl
+ << "virtual const char*" << endl
+ << "peek ();"
+ << endl
+ << "virtual const char*" << endl
+ << "next ();"
+ << endl
+ << "virtual void" << endl
+ << "skip ();"
+ << endl
+ << "private:" << endl
+ << "int i_;"
+ << "int& argc_;"
+ << "char** argv_;"
+ << "bool erase_;"
+ << "};";
+
+ // vector_scanner
+ //
+ if (ctx.options.generate_vector_scanner ())
+ {
+ os << "class vector_scanner: public scanner"
+ << "{"
+ << "public:" << endl
+ << "vector_scanner (const std::vector<std::string>&, " <<
+ "std::size_t start = 0);"
+ << endl
+ << "std::size_t" << endl
+ << "end () const;"
+ << endl
+ << "void" << endl
+ << "reset (std::size_t start = 0);"
+ << endl
+ << "virtual bool" << endl
+ << "more ();"
+ << endl
+ << "virtual const char*" << endl
+ << "peek ();"
+ << endl
+ << "virtual const char*" << endl
+ << "next ();"
+ << endl
+ << "virtual void" << endl
+ << "skip ();"
+ << endl
+ << "private:" << endl
+ << "const std::vector<std::string>& v_;"
+ << "std::size_t i_;"
+ << "};";
+ }
+
+ // argv_file_scanner
+ //
+ if (ctx.options.generate_file_scanner ())
+ {
+ os << "class argv_file_scanner: public argv_scanner"
+ << "{"
+ << "public:" << endl
+ << "argv_file_scanner (int& argc," << endl
+ << "char** argv," << endl
+ << "const std::string& option," << endl
+ << "bool erase = false);"
+ << endl
+ << "argv_file_scanner (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "const std::string& option," << endl
+ << "bool erase = false);"
+ << endl
+ << "argv_file_scanner (const std::string& file," << endl
+ << "const std::string& option);"
+ << endl
+ << "struct option_info"
+ << "{"
+ << "// If search_func is not NULL, it is called, with the arg" << endl
+ << "// value as the second argument, to locate the options file." << endl
+ << "// If it returns an empty string, then the file is ignored." << endl
+ << "//" << endl
+ << "const char* option;"
+ << "std::string (*search_func) (const char*, void* arg);"
+ << "void* arg;"
+ << "};"
+ << "argv_file_scanner (int& argc," << endl
+ << "char** argv," << endl
+ << "const option_info* options," << endl
+ << "std::size_t options_count," << endl
+ << "bool erase = false);"
+ << endl
+ << "argv_file_scanner (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "const option_info* options," << endl
+ << "std::size_t options_count," << endl
+ << "bool erase = false);"
+ << endl
+ << "argv_file_scanner (const std::string& file," << endl
+ << "const option_info* options = 0," << endl
+ << "std::size_t options_count = 0);"
+ << endl
+ << "virtual bool" << endl
+ << "more ();"
+ << endl
+ << "virtual const char*" << endl
+ << "peek ();"
+ << endl
+ << "virtual const char*" << endl
+ << "next ();"
+ << endl
+ << "virtual void" << endl
+ << "skip ();"
+ << endl
+ << "// Return the file path if the peeked at argument came from a file and" << endl
+ << "// the empty string otherwise. The reference is guaranteed to be valid" << endl
+ << "// till the end of the scanner lifetime." << endl
+ << "//" << endl
+ << "const std::string&" << endl
+ << "peek_file ();"
+ << endl
+ << "// Return the 1-based line number if the peeked at argument came from" << endl
+ << "// a file and zero otherwise." << endl
+ << "//" << endl
+ << "std::size_t" << endl
+ << "peek_line ();"
+ << endl
+ << "private:" << endl
+ << "const option_info*" << endl
+ << "find (const char*) const;"
+ << endl
+ << "void" << endl
+ << "load (const std::string& file);"
+ << endl
+ << "typedef argv_scanner base;"
+ << endl
+ << "const std::string option_;"
+ << "option_info option_info_;"
+ << "const option_info* options_;"
+ << "std::size_t options_count_;"
+ << endl
+ << "struct arg"
+ << "{"
+ << "std::string value;"
+ << "const std::string* file;"
+ << "std::size_t line;"
+ << "};"
+ << "std::deque<arg> args_;"
+ << "std::list<std::string> files_;"
+ << endl
+ << "// Circular buffer of two arguments." << endl
+ << "//" << endl
+ << "std::string hold_[2];"
+ << "std::size_t i_;";
+
+ if (!ctx.opt_sep.empty ())
+ os << endl
+ << "bool skip_;";
+
+ os << endl
+ << "static int zero_argc_;"
+ << "static std::string empty_string_;"
+ << "};";
+ }
+
+ // group_scanner
+ //
+ if (ctx.options.generate_group_scanner ())
+ {
+ os << "class group_scanner: public scanner"
+ << "{"
+ << "public:" << endl
+ << "group_scanner (scanner&);"
+ << endl
+ << "virtual bool" << endl
+ << "more ();"
+ << endl
+ << "virtual const char*" << endl
+ << "peek ();"
+ << endl
+ << "virtual const char*" << endl
+ << "next ();"
+ << endl
+ << "virtual void" << endl
+ << "skip ();"
+ << endl
+ << "// The group is only available after the call to next()" << endl
+ << "// (and skip() -- in case one needs to make sure the group" << endl
+ << "// was empty, or some such) and is only valid (and must be" << endl
+ << "// handled) until the next call to any of the scanner" << endl
+ << "// functions (including more())." << endl
+ << "//" << endl
+ << "scanner&" << endl
+ << "group ();"
+ << endl
+ << "// Escape an argument that is a group separator. Return the" << endl
+ << "// passed string if no escaping is required." << endl
+ << "//" << endl
+ << "static const char*" << endl
+ << "escape (const char*);"
+ << endl
+ << "private:" << endl
+ << "enum state"
+ << "{"
+ << "peeked, // Argument peeked at with peek()." << endl
+ << "scanned, // Argument scanned with next()." << endl
+ << "skipped, // Argument skipped with skip()/initial." << endl
+ << "};"
+ << "enum separator"
+ << "{"
+ << "none," << endl
+ << "open, // {" << endl
+ << "close, // }" << endl
+ << "open_plus, // +{" << endl
+ << "close_plus // }+" << endl
+ << "};"
+ << "static separator" << endl
+ << "sense (const char*);"
+ << endl
+ << "// If the state is scanned or skipped, then scan the" << endl
+ << "// leading groups and save the next (unescaped) argument in" << endl
+ << "// arg_. If the state is peeked, then scan the trailing" << endl
+ << "// groups. In both cases set the new state." << endl
+ << "//" << endl
+ << "void" << endl
+ << "scan_group (state);"
+ << endl
+ << "scanner& scan_;"
+ << "state state_;"
+ << endl
+ << "// Circular buffer of two arguments." << endl
+ << "//" << endl
+ << "std::string arg_[2];"
+ << "std::size_t i_;"
+ << endl
+ << "std::vector<std::string> group_;"
+ << "vector_scanner group_scan_;"
+ << "};";
+ }
+
+ // Option description.
+ //
+ if (ctx.options.generate_description ())
+ {
+ os << "typedef std::vector<std::string> option_names;"
+ << endl;
+
+ os << "class option"
+ << "{"
+ << "public:" << endl
+ << endl
+ << "const std::string&" << endl
+ << "name () const;"
+ << endl
+ << "const option_names&" << endl
+ << "aliases () const;"
+ << endl
+ << "bool" << endl
+ << "flag () const;"
+ << endl
+ << "const std::string&" << endl
+ << "default_value () const;"
+ << endl
+ << "public:"
+ << "option ();"
+ << "option (const std::string& name," << endl
+ << "const option_names& aliases," << endl
+ << "bool flag," << endl
+ << "const std::string& default_value);"
+ << endl
+ << "private:" << endl
+ << "std::string name_;"
+ << "option_names aliases_;"
+ << "bool flag_;"
+ << "std::string default_value_;"
+ << "};";
+
+ os << "class options: public std::vector<option>"
+ << "{"
+ << "public:" << endl
+ << "typedef std::vector<option> container_type;"
+ << endl
+ << "container_type::const_iterator" << endl
+ << "find (const std::string& name) const;"
+ << endl
+ << "void" << endl
+ << "push_back (const option&);"
+ << "private:" << endl
+ << "typedef std::map<std::string, container_type::size_type> map_type;"
+ << "map_type map_;"
+ << "};";
+ }
+
+ // Parser class template. Provide a forward declaration to allow
+ // custom specializations.
+ //
+ os << "template <typename X>" << endl
+ << "struct parser;"
+ << endl;
+
+ ctx.ns_close (ctx.cli);
+}
diff --git a/cli/cli/runtime-header.hxx b/cli/cli/runtime-header.hxx
new file mode 100644
index 0000000..f4bff7c
--- /dev/null
+++ b/cli/cli/runtime-header.hxx
@@ -0,0 +1,13 @@
+// file : cli/runtime-header.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_RUNTIME_HEADER_HXX
+#define CLI_RUNTIME_HEADER_HXX
+
+#include <cli/context.hxx>
+
+void
+generate_runtime_header (context&);
+
+#endif // CLI_RUNTIME_HEADER_HXX
diff --git a/cli/cli/runtime-inline.cxx b/cli/cli/runtime-inline.cxx
new file mode 100644
index 0000000..8f0e84c
--- /dev/null
+++ b/cli/cli/runtime-inline.cxx
@@ -0,0 +1,508 @@
+// file : cli/runtime-inline.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cli/runtime-inline.hxx>
+
+using namespace std;
+
+void
+generate_runtime_inline (context& ctx)
+{
+ ostream& os (ctx.os);
+
+ os << "#include <cassert>" << endl
+ << endl;
+
+ string const& inl (ctx.inl);
+ string const& os_type (ctx.options.ostream_type ());
+
+ ctx.ns_open (ctx.cli);
+
+ // usage_para
+ //
+ if (!ctx.options.suppress_usage ())
+ os << "// usage_para" << endl
+ << "//" << endl
+
+ << inl << "usage_para::" << endl
+ << "usage_para (value v)" << endl
+ << ": v_ (v)"
+ << "{"
+ << "}";
+
+ // unknown_mode
+ //
+ os << "// unknown_mode" << endl
+ << "//" << endl
+
+ << inl << "unknown_mode::" << endl
+ << "unknown_mode (value v)" << endl
+ << ": v_ (v)"
+ << "{"
+ << "}";
+
+ // exception
+ //
+ os << "// exception" << endl
+ << "//" << endl
+
+ << inl << os_type << "&" << endl
+ << "operator<< (" << os_type << "& os, const exception& e)"
+ << "{"
+ << "e.print (os);"
+ << "return os;"
+ << "}";
+
+ // unknown_option
+ //
+ os << "// unknown_option" << endl
+ << "//" << endl
+
+ << inl << "unknown_option::" << endl
+ << "unknown_option (const std::string& option)" << endl
+ << ": option_ (option)"
+ << "{"
+ << "}"
+
+ << inl << "const std::string& unknown_option::" << endl
+ << "option () const"
+ << "{"
+ << "return option_;"
+ << "}";
+
+ // unknown_argument
+ //
+ os << "// unknown_argument" << endl
+ << "//" << endl
+
+ << inl << "unknown_argument::" << endl
+ << "unknown_argument (const std::string& argument)" << endl
+ << ": argument_ (argument)"
+ << "{"
+ << "}"
+
+ << inl << "const std::string& unknown_argument::" << endl
+ << "argument () const"
+ << "{"
+ << "return argument_;"
+ << "}";
+
+ // missing_value
+ //
+ os << "// missing_value" << endl
+ << "//" << endl
+
+ << inl << "missing_value::" << endl
+ << "missing_value (const std::string& option)" << endl
+ << ": option_ (option)"
+ << "{"
+ << "}"
+
+ << inl << "const std::string& missing_value::" << endl
+ << "option () const"
+ << "{"
+ << "return option_;"
+ << "}";
+
+ // invalid_value
+ //
+ os << "// invalid_value" << endl
+ << "//" << endl
+
+ << inl << "invalid_value::" << endl
+ << "invalid_value (const std::string& option," << endl
+ << "const std::string& value," << endl
+ << "const std::string& message)" << endl
+ << ": option_ (option)," << endl
+ << " value_ (value)," << endl
+ << " message_ (message)"
+ << "{"
+ << "}"
+
+ << inl << "const std::string& invalid_value::" << endl
+ << "option () const"
+ << "{"
+ << "return option_;"
+ << "}"
+
+ << inl << "const std::string& invalid_value::" << endl
+ << "value () const"
+ << "{"
+ << "return value_;"
+ << "}"
+
+ << inl << "const std::string& invalid_value::" << endl
+ << "message () const"
+ << "{"
+ << "return message_;"
+ << "}";
+
+ if (ctx.options.generate_file_scanner ())
+ {
+ // file_io_failure
+ //
+ os << "// file_io_failure" << endl
+ << "//" << endl
+
+ << inl << "file_io_failure::" << endl
+ << "file_io_failure (const std::string& file)" << endl
+ << ": file_ (file)"
+ << "{"
+ << "}"
+
+ << inl << "const std::string& file_io_failure::" << endl
+ << "file () const"
+ << "{"
+ << "return file_;"
+ << "}";
+
+ // unmatched_option
+ //
+ os << "// unmatched_quote" << endl
+ << "//" << endl
+
+ << inl << "unmatched_quote::" << endl
+ << "unmatched_quote (const std::string& argument)" << endl
+ << ": argument_ (argument)"
+ << "{"
+ << "}"
+
+ << inl << "const std::string& unmatched_quote::" << endl
+ << "argument () const"
+ << "{"
+ << "return argument_;"
+ << "}";
+ }
+
+ if (ctx.options.generate_group_scanner ())
+ {
+ // unexpected_group
+ //
+ os << "// unexpected_group" << endl
+ << "//" << endl
+
+ << inl << "unexpected_group::" << endl
+ << "unexpected_group (const std::string& argument," << endl
+ << "const std::string& group)" << endl
+ << ": argument_ (argument), group_ (group)"
+ << "{"
+ << "}"
+
+ << inl << "const std::string& unexpected_group::" << endl
+ << "argument () const"
+ << "{"
+ << "return argument_;"
+ << "}"
+
+ << inl << "const std::string& unexpected_group::" << endl
+ << "group () const"
+ << "{"
+ << "return group_;"
+ << "}";
+
+ // group_separator
+ //
+ os << "// group_separator" << endl
+ << "//" << endl
+
+ << inl << "group_separator::" << endl
+ << "group_separator (const std::string& encountered," << endl
+ << "const std::string& expected)" << endl
+ << ": encountered_ (encountered), expected_ (expected)"
+ << "{"
+ << "}"
+
+ << inl << "const std::string& group_separator::" << endl
+ << "encountered () const"
+ << "{"
+ << "return encountered_;"
+ << "}"
+
+ << inl << "const std::string& group_separator::" << endl
+ << "expected () const"
+ << "{"
+ << "return expected_;"
+ << "}";
+ }
+
+ // argv_scanner
+ //
+ os << "// argv_scanner" << endl
+ << "//" << endl;
+
+ os << inl << "argv_scanner::" << endl
+ << "argv_scanner (int& argc, char** argv, bool erase)" << endl
+ << ": i_ (1), argc_ (argc), argv_ (argv), erase_ (erase)"
+ << "{"
+ << "}";
+
+ os << inl << "argv_scanner::" << endl
+ << "argv_scanner (int start, int& argc, char** argv, bool erase)" << endl
+ << ": i_ (start), argc_ (argc), argv_ (argv), erase_ (erase)"
+ << "{"
+ << "}";
+
+ os << inl << "int argv_scanner::" << endl
+ << "end () const"
+ << "{"
+ << "return i_;"
+ << "}";
+
+ // vector_scanner
+ //
+ if (ctx.options.generate_vector_scanner ())
+ {
+ os << "// vector_scanner" << endl
+ << "//" << endl;
+
+ os << inl << "vector_scanner::" << endl
+ << "vector_scanner (const std::vector<std::string>& v, " <<
+ "std::size_t i)" << endl
+ << ": v_ (v), i_ (i)"
+ << "{"
+ << "}";
+
+ os << inl << "std::size_t vector_scanner::" << endl
+ << "end () const"
+ << "{"
+ << "return i_;"
+ << "}";
+
+ os << inl << "void vector_scanner::" << endl
+ << "reset (std::size_t i)"
+ << "{"
+ << "i_ = i;"
+ << "}";
+ }
+
+ // argv_file_scanner
+ //
+ if (ctx.options.generate_file_scanner ())
+ {
+ bool sep (!ctx.opt_sep.empty ());
+
+ os << "// argv_file_scanner" << endl
+ << "//" << endl;
+
+ os << inl << "argv_file_scanner::" << endl
+ << "argv_file_scanner (int& argc," << endl
+ << "char** argv," << endl
+ << "const std::string& option," << endl
+ << "bool erase)" << endl
+ << ": argv_scanner (argc, argv, erase)," << endl
+ << " option_ (option)," << endl
+ << " options_ (&option_info_)," << endl
+ << " options_count_ (1)," << endl
+ << " i_ (1)";
+ if (sep)
+ os << "," << endl
+ << " skip_ (false)";
+ os << "{"
+ << "option_info_.option = option_.c_str ();"
+ << "option_info_.search_func = 0;"
+ << "}";
+
+ os << inl << "argv_file_scanner::" << endl
+ << "argv_file_scanner (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "const std::string& option," << endl
+ << "bool erase)" << endl
+ << ": argv_scanner (start, argc, argv, erase)," << endl
+ << " option_ (option)," << endl
+ << " options_ (&option_info_)," << endl
+ << " options_count_ (1)," << endl
+ << " i_ (1)";
+ if (sep)
+ os << "," << endl
+ << " skip_ (false)";
+ os << "{"
+ << "option_info_.option = option_.c_str ();"
+ << "option_info_.search_func = 0;"
+ << "}";
+
+ os << inl << "argv_file_scanner::" << endl
+ << "argv_file_scanner (const std::string& file," << endl
+ << "const std::string& option)" << endl
+ << ": argv_scanner (0, zero_argc_, 0)," << endl
+ << " option_ (option)," << endl
+ << " options_ (&option_info_)," << endl
+ << " options_count_ (1)," << endl
+ << " i_ (1)";
+ if (sep)
+ os << "," << endl
+ << " skip_ (false)";
+ os << "{"
+ << "option_info_.option = option_.c_str ();"
+ << "option_info_.search_func = 0;"
+ << endl
+ << "load (file);"
+ << "}";
+
+ os << inl << "argv_file_scanner::" << endl
+ << "argv_file_scanner (int& argc," << endl
+ << "char** argv," << endl
+ << "const option_info* options," << endl
+ << "std::size_t options_count," << endl
+ << "bool erase)" << endl
+ << ": argv_scanner (argc, argv, erase)," << endl
+ << " options_ (options)," << endl
+ << " options_count_ (options_count)," << endl
+ << " i_ (1)";
+ if (sep)
+ os << "," << endl
+ << " skip_ (false)";
+ os << "{"
+ << "}";
+
+ os << inl << "argv_file_scanner::" << endl
+ << "argv_file_scanner (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "const option_info* options," << endl
+ << "std::size_t options_count," << endl
+ << "bool erase)" << endl
+ << ": argv_scanner (start, argc, argv, erase)," << endl
+ << " options_ (options)," << endl
+ << " options_count_ (options_count)," << endl
+ << " i_ (1)";
+ if (sep)
+ os << "," << endl
+ << " skip_ (false)";
+ os << "{"
+ << "}";
+
+ os << inl << "argv_file_scanner::" << endl
+ << "argv_file_scanner (const std::string& file," << endl
+ << "const option_info* options," << endl
+ << "std::size_t options_count)" << endl
+ << ": argv_scanner (0, zero_argc_, 0)," << endl
+ << " options_ (options)," << endl
+ << " options_count_ (options_count)," << endl
+ << " i_ (1)";
+ if (sep)
+ os << "," << endl
+ << " skip_ (false)";
+ os << "{"
+ << "load (file);"
+ << "}";
+ }
+
+ // group_scanner
+ //
+ if (ctx.options.generate_group_scanner ())
+ {
+ os << "// group_scanner" << endl
+ << "//" << endl
+
+ << inl << "group_scanner::" << endl
+ << "group_scanner (scanner& s)" << endl
+ << ": scan_ (s), state_ (skipped), i_ (1), group_scan_ (group_)"
+ << "{"
+ << "}"
+
+ << inl << "scanner& group_scanner::" << endl
+ << "group ()"
+ << "{"
+ << "assert (state_ == scanned || state_ == skipped);"
+ << "return group_scan_;"
+ << "}"
+
+ << inl << "const char* group_scanner::" << endl
+ << "escape (const char* a)"
+ << "{"
+ << "switch (sense (a))"
+ << "{"
+ << "case separator::none: break;"
+ << "case separator::open: return \"\\\\{\";"
+ << "case separator::close: return \"\\\\}\";"
+ << "case separator::open_plus: return \"\\\\+{\";"
+ << "case separator::close_plus: return \"\\\\}+\";"
+ << "}"
+ << "return a;"
+ << "}"
+
+ << inl << "group_scanner::separator group_scanner::" << endl
+ << "sense (const char* s)"
+ << "{"
+ << "switch (s[0])"
+ << "{"
+ << "case '{': return s[1] == '\\0' ? open : none;"
+ << "case '}':"
+ << "{"
+ << "switch (s[1])"
+ << "{"
+ << "case '+': return s[2] == '\\0' ? close_plus : none;"
+ << "default: return s[1] == '\\0' ? close : none;"
+ << "}"
+ << "}"
+ << "case '+':"
+ << "{"
+ << "switch (s[1])"
+ << "{"
+ << "case '{': return s[2] == '\\0' ? open_plus : none;"
+ << "default: return none;"
+ << "}"
+ << "}"
+ << "}"
+ << "return none;"
+ << "}";
+ }
+
+ // Option description.
+ //
+ if (ctx.options.generate_description ())
+ {
+ // option
+ //
+ os << inl << "const std::string& option::" << endl
+ << "name () const"
+ << "{"
+ << "return name_;"
+ << "}";
+
+ os << inl << "const option_names& option::" << endl
+ << "aliases () const"
+ << "{"
+ << "return aliases_;"
+ << "}";
+
+ os << inl << "bool option::" << endl
+ << "flag () const"
+ << "{"
+ << "return flag_;"
+ << "}";
+
+ os << inl << "const std::string& option::" << endl
+ << "default_value () const"
+ << "{"
+ << "return default_value_;"
+ << "}";
+
+ os << inl << "option::" << endl
+ << "option ()"
+ << "{"
+ << "}";
+
+ os << inl << "option::" << endl
+ << "option (const std::string& n," << endl
+ << "const option_names& a," << endl
+ << "bool f," << endl
+ << "const std::string& dv)" << endl
+ << ": name_ (n), aliases_ (a), flag_ (f), default_value_ (dv)"
+ << "{"
+ << "}";
+
+ // options
+ //
+ os << inl << "options::container_type::const_iterator options::" << endl
+ << "find (const std::string& name) const"
+ << "{"
+ << "map_type::const_iterator i (map_.find (name));"
+ << "return i != map_.end () ? begin () + i->second : end ();"
+ << "}";
+ }
+
+ ctx.ns_close (ctx.cli);
+}
diff --git a/cli/cli/runtime-inline.hxx b/cli/cli/runtime-inline.hxx
new file mode 100644
index 0000000..045c060
--- /dev/null
+++ b/cli/cli/runtime-inline.hxx
@@ -0,0 +1,13 @@
+// file : cli/runtime-inline.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_RUNTIME_INLINE_HXX
+#define CLI_RUNTIME_INLINE_HXX
+
+#include <cli/context.hxx>
+
+void
+generate_runtime_inline (context&);
+
+#endif // CLI_RUNTIME_INLINE_HXX
diff --git a/cli/cli/runtime-source.cxx b/cli/cli/runtime-source.cxx
new file mode 100644
index 0000000..36d8968
--- /dev/null
+++ b/cli/cli/runtime-source.cxx
@@ -0,0 +1,1052 @@
+// file : cli/runtime-source.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cli/runtime-source.hxx>
+
+using namespace std;
+
+void
+generate_runtime_source (context& ctx, bool complete)
+{
+ ostream& os (ctx.os);
+
+ os << "#include <map>" << endl
+ << "#include <set>" << endl
+ << "#include <string>" << endl
+ << "#include <vector>" << endl
+ << "#include <ostream>" << endl
+ << "#include <sstream>" << endl;
+
+ if (complete && ctx.options.generate_file_scanner ())
+ os << "#include <cstring>" << endl
+ << "#include <fstream>" << endl;
+
+ os << endl;
+
+ ctx.ns_open (ctx.cli);
+
+ if (complete)
+ {
+ string const& os_type (ctx.options.ostream_type ());
+
+ // unknown_option
+ //
+ os << "// unknown_option" << endl
+ << "//" << endl
+ << "unknown_option::" << endl
+ << "~unknown_option () throw ()"
+ << "{"
+ << "}"
+
+ << "void unknown_option::" << endl
+ << "print (" << os_type << "& os) const"
+ << "{"
+ << "os << \"unknown option '\" << option ().c_str () << \"'\";"
+ << "}"
+
+ << "const char* unknown_option::" << endl
+ << "what () const throw ()"
+ << "{"
+ << "return \"unknown option\";"
+ << "}";
+
+ // unknown_argument
+ //
+ os << "// unknown_argument" << endl
+ << "//" << endl
+ << "unknown_argument::" << endl
+ << "~unknown_argument () throw ()"
+ << "{"
+ << "}"
+
+ << "void unknown_argument::" << endl
+ << "print (" << os_type << "& os) const"
+ << "{"
+ << "os << \"unknown argument '\" << argument ().c_str () << \"'\";"
+ << "}"
+
+ << "const char* unknown_argument::" << endl
+ << "what () const throw ()"
+ << "{"
+ << "return \"unknown argument\";"
+ << "}";
+
+ // missing_value
+ //
+ os << "// missing_value" << endl
+ << "//" << endl
+ << "missing_value::" << endl
+ << "~missing_value () throw ()"
+ << "{"
+ << "}"
+
+ << "void missing_value::" << endl
+ << "print (" << os_type << "& os) const"
+ << "{"
+ << "os << \"missing value for option '\" << option ().c_str () << \"'\";"
+ << "}"
+
+ << "const char* missing_value::" << endl
+ << "what () const throw ()"
+ << "{"
+ << "return \"missing option value\";"
+ << "}";
+
+ // invalid_value
+ //
+ os << "// invalid_value" << endl
+ << "//" << endl
+ << "invalid_value::" << endl
+ << "~invalid_value () throw ()"
+ << "{"
+ << "}"
+
+ << "void invalid_value::" << endl
+ << "print (" << os_type << "& os) const"
+ << "{"
+ << "os << \"invalid value '\" << value ().c_str () << \"' for " <<
+ "option '\"" << endl
+ << " << option ().c_str () << \"'\";"
+ << endl
+ << "if (!message ().empty ())" << endl
+ << "os << \": \" << message ().c_str ();"
+ << "}"
+
+ << "const char* invalid_value::" << endl
+ << "what () const throw ()"
+ << "{"
+ << "return \"invalid option value\";"
+ << "}";
+
+ // eos_reached
+ //
+ os << "// eos_reached" << endl
+ << "//" << endl
+ << "void eos_reached::" << endl
+ << "print (" << os_type << "& os) const"
+ << "{"
+ << "os << what ();"
+ << "}"
+
+ << "const char* eos_reached::" << endl
+ << "what () const throw ()"
+ << "{"
+ << "return \"end of argument stream reached\";"
+ << "}";
+
+ if (ctx.options.generate_file_scanner ())
+ {
+ // file_io_failure
+ //
+ os << "// file_io_failure" << endl
+ << "//" << endl
+ << "file_io_failure::" << endl
+ << "~file_io_failure () throw ()"
+ << "{"
+ << "}"
+
+ << "void file_io_failure::" << endl
+ << "print (" << os_type << "& os) const"
+ << "{"
+ << "os << \"unable to open file '\" << file ().c_str () << " <<
+ "\"' or read failure\";"
+ << "}"
+
+ << "const char* file_io_failure::" << endl
+ << "what () const throw ()"
+ << "{"
+ << "return \"unable to open file or read failure\";"
+ << "}";
+
+ // unmatched_argument
+ //
+ os << "// unmatched_quote" << endl
+ << "//" << endl
+ << "unmatched_quote::" << endl
+ << "~unmatched_quote () throw ()"
+ << "{"
+ << "}"
+
+ << "void unmatched_quote::" << endl
+ << "print (" << os_type << "& os) const"
+ << "{"
+ << "os << \"unmatched quote in argument '\" << " <<
+ "argument ().c_str () << \"'\";"
+ << "}"
+
+ << "const char* unmatched_quote::" << endl
+ << "what () const throw ()"
+ << "{"
+ << "return \"unmatched quote\";"
+ << "}";
+ }
+
+ if (ctx.options.generate_group_scanner ())
+ {
+ // unexpected_group
+ //
+ os << "// unexpected_group" << endl
+ << "//" << endl
+
+ << "unexpected_group::" << endl
+ << "~unexpected_group () throw ()"
+ << "{"
+ << "}"
+
+ << "void unexpected_group::" << endl
+ << "print (" << os_type << "& os) const"
+ << "{"
+ << "os << \"unexpected grouped argument '\" << group_ << \"' \"" << endl
+ << " << \"for argument '\" << argument_ << \"'\";"
+ << "}"
+
+ << "const char* unexpected_group::" << endl
+ << "what () const throw ()"
+ << "{"
+ << "return \"unexpected grouped argument\";"
+ << "}";
+
+ // group_separator
+ //
+ os << "// group_separator" << endl
+ << "//" << endl
+
+ << "group_separator::" << endl
+ << "~group_separator () throw ()"
+ << "{"
+ << "}"
+
+ << "void group_separator::" << endl
+ << "print (" << os_type << "& os) const"
+ << "{"
+ << "bool ex (!expected_.empty ());"
+ << "bool en (!encountered_.empty ());"
+ << endl
+ << "if (ex)"
+ << "{"
+ << "os << \"expected group separator '\" << expected_ << \"'\";"
+ << "if (en)" << endl
+ << "os << \" instead of '\" << encountered_ << \"'\";"
+ << "}"
+ << "else" << endl
+ << "os << \"unexpected group separator '\" << encountered_ << \"'\";"
+ << endl
+ << "if (en)" << endl
+ << "os << \", use '\\\\\" << encountered_ << \"' to escape\";"
+ << "}"
+
+ << "const char* group_separator::" << endl
+ << "what () const throw ()"
+ << "{"
+ << "bool ex (!expected_.empty ());"
+ << "bool en (!encountered_.empty ());"
+ << endl
+ << "return en" << endl
+ << " ? ex ? \"wrong group separator\" : \"unexpected group separator\"" << endl
+ << " : ex ? \"expected group separator\" : \"\";"
+ << "}";
+ }
+
+ // scanner
+ //
+ os << "// scanner" << endl
+ << "//" << endl
+ << "scanner::" << endl
+ << "~scanner ()"
+ << "{"
+ << "}";
+
+ // argv_scanner
+ //
+ os << "// argv_scanner" << endl
+ << "//" << endl
+
+ << "bool argv_scanner::" << endl
+ << "more ()"
+ << "{"
+ << "return i_ < argc_;"
+ << "}"
+
+ << "const char* argv_scanner::" << endl
+ << "peek ()"
+ << "{"
+ << "if (i_ < argc_)" << endl
+ << "return argv_[i_];"
+ << "else" << endl
+ << "throw eos_reached ();"
+ << "}"
+
+ << "const char* argv_scanner::" << endl
+ << "next ()"
+ << "{"
+ << "if (i_ < argc_)"
+ << "{"
+ << "const char* r (argv_[i_]);"
+ << endl
+ << "if (erase_)"
+ << "{"
+ << "for (int i (i_ + 1); i < argc_; ++i)" << endl
+ << "argv_[i - 1] = argv_[i];"
+ << endl
+ << "--argc_;"
+ << "argv_[argc_] = 0;"
+ << "}"
+ << "else" << endl
+ << "++i_;"
+ << endl
+ << "return r;"
+ << "}"
+ << "else" << endl
+ << "throw eos_reached ();"
+ << "}"
+
+ << "void argv_scanner::" << endl
+ << "skip ()"
+ << "{"
+ << "if (i_ < argc_)" << endl
+ << "++i_;"
+ << "else" << endl
+ << "throw eos_reached ();"
+ << "}";
+
+ // vector_scanner
+ //
+ if (ctx.options.generate_vector_scanner ())
+ {
+ os << "// vector_scanner" << endl
+ << "//" << endl
+
+ << "bool vector_scanner::" << endl
+ << "more ()"
+ << "{"
+ << "return i_ < v_.size ();"
+ << "}"
+
+ << "const char* vector_scanner::" << endl
+ << "peek ()"
+ << "{"
+ << "if (i_ < v_.size ())" << endl
+ << "return v_[i_].c_str ();"
+ << "else" << endl
+ << "throw eos_reached ();"
+ << "}"
+
+ << "const char* vector_scanner::" << endl
+ << "next ()"
+ << "{"
+ << "if (i_ < v_.size ())" << endl
+ << "return v_[i_++].c_str ();"
+ << "else" << endl
+ << "throw eos_reached ();"
+ << "}"
+
+ << "void vector_scanner::" << endl
+ << "skip ()"
+ << "{"
+ << "if (i_ < v_.size ())" << endl
+ << "++i_;"
+ << "else" << endl
+ << "throw eos_reached ();"
+ << "}";
+ }
+
+ // argv_file_scanner
+ //
+ if (ctx.options.generate_file_scanner ())
+ {
+ bool sep (!ctx.opt_sep.empty ());
+
+ const string& pfx (ctx.opt_prefix);
+ size_t pfx_n (pfx.size ());
+ bool comb_values (pfx_n != 0 && !ctx.options.no_combined_values ());
+
+ os << "// argv_file_scanner" << endl
+ << "//" << endl
+
+ << "int argv_file_scanner::zero_argc_ = 0;"
+ << "std::string argv_file_scanner::empty_string_;"
+ << endl
+
+ << "bool argv_file_scanner::" << endl
+ << "more ()"
+ << "{"
+ << "if (!args_.empty ())" << endl
+ << "return true;"
+ << endl
+ << "while (base::more ())"
+ << "{"
+ << "// See if the next argument is the file option." << endl
+ << "//" << endl
+ << "const char* a (base::peek ());"
+ << "const option_info* oi = 0;"
+ << "const char* ov = 0;"
+ << endl;
+
+ if (sep)
+ os << "if (!skip_)"
+ << "{";
+
+ os << "if ((oi = find (a)) != 0)"
+ << "{"
+ << "base::next ();"
+ << endl
+ << "if (!base::more ())" << endl
+ << "throw missing_value (a);"
+ << endl
+ << "ov = base::next ();"
+ << "}";
+
+ // Handle the combined option/value (--foo=bar). See the option parsing
+ // implementation for details.
+ //
+ if (comb_values)
+ {
+ os << "else if (std::strncmp (a, \"" << pfx << "\", " <<
+ pfx_n << ") == 0)" // It looks like an option.
+ << "{"
+ << "if ((ov = std::strchr (a, '=')) != 0)" // Has '='.
+ << "{"
+ << "std::string o (a, 0, ov - a);"
+ << "if ((oi = find (o.c_str ())) != 0)"
+ << "{"
+ << "base::next ();"
+ << "++ov;" // That's the value.
+ << "}"
+ << "}"
+ << "}";
+ }
+
+ if (sep)
+ os << "}";
+
+ os << "if (oi != 0)"
+ << "{"
+ << "if (oi->search_func != 0)"
+ << "{"
+ << "std::string f (oi->search_func (ov, oi->arg));"
+ << endl
+ << "if (!f.empty ())" << endl
+ << "load (f);"
+ << "}"
+ << "else" << endl
+ << "load (ov);"
+ << endl
+ << "if (!args_.empty ())" << endl
+ << "return true;"
+ << "}"
+ << "else"
+ << "{";
+ if (sep)
+ os << "if (!skip_)" << endl
+ << "skip_ = (std::strcmp (a, \"" << ctx.opt_sep << "\") == 0);"
+ << endl;
+ os << "return true;"
+ << "}"
+ << "}" // while
+ << "return false;"
+ << "}"
+
+ << "const char* argv_file_scanner::" << endl
+ << "peek ()"
+ << "{"
+ << "if (!more ())" << endl
+ << "throw eos_reached ();"
+ << endl
+ << "return args_.empty () ? base::peek () : args_.front ().value.c_str ();"
+ << "}"
+
+ << "const std::string& argv_file_scanner::" << endl
+ << "peek_file ()"
+ << "{"
+ << "if (!more ())" << endl
+ << "throw eos_reached ();"
+ << endl
+ << "return args_.empty () ? empty_string_ : *args_.front ().file;"
+ << "}"
+
+ << "std::size_t argv_file_scanner::" << endl
+ << "peek_line ()"
+ << "{"
+ << "if (!more ())" << endl
+ << "throw eos_reached ();"
+ << endl
+ << "return args_.empty () ? 0 : args_.front ().line;"
+ << "}"
+
+ << "const char* argv_file_scanner::" << endl
+ << "next ()"
+ << "{"
+ << "if (!more ())" << endl
+ << "throw eos_reached ();"
+ << endl
+ << "if (args_.empty ())" << endl
+ << "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::" << endl
+ << "skip ()"
+ << "{"
+ << "if (!more ())" << endl
+ << "throw eos_reached ();"
+ << endl
+ << "if (args_.empty ())" << endl
+ << "return base::skip ();"
+ << "else" << endl
+ << "args_.pop_front ();"
+ << "}"
+
+ << "const argv_file_scanner::option_info* argv_file_scanner::" << endl
+ << "find (const char* a) const"
+ << "{"
+ << "for (std::size_t i (0); i < options_count_; ++i)" << endl
+ << "if (std::strcmp (a, options_[i].option) == 0)" << endl
+ << "return &options_[i];"
+ << endl
+ << "return 0;"
+ << "}"
+
+ << "void argv_file_scanner::" << endl
+ << "load (const std::string& file)"
+ << "{"
+ << "using namespace std;"
+ << endl
+ << "ifstream is (file.c_str ());"
+ << endl
+ << "if (!is.is_open ())" << endl
+ << "throw file_io_failure (file);"
+ << endl
+ << "files_.push_back (file);"
+ << endl
+ << "arg a;"
+ << "a.file = &*files_.rbegin ();"
+ << endl
+ << "for (a.line = 1; !is.eof (); ++a.line)"
+ << "{"
+ << "string line;"
+ << "getline (is, line);"
+ << endl
+ << "if (is.fail () && !is.eof ())" << endl
+ << "throw file_io_failure (file);"
+ << endl
+ << "string::size_type n (line.size ());"
+ << endl
+ << "// Trim the line from leading and trailing whitespaces." << endl
+ << "//" << endl
+ << "if (n != 0)"
+ << "{"
+ << "const char* f (line.c_str ());"
+ << "const char* l (f + n);"
+ << endl
+ << "const char* of (f);"
+ << "while (f < l && (*f == ' ' || *f == '\\t' || *f == '\\r'))" << endl
+ << "++f;"
+ << endl
+ << "--l;"
+ << endl
+ << "const char* ol (l);"
+ << "while (l > f && (*l == ' ' || *l == '\\t' || *l == '\\r'))" << endl
+ << "--l;"
+ << endl
+ << "if (f != of || l != ol)" << endl
+ << "line = f <= l ? string (f, l - f + 1) : string ();"
+ << "}"
+ << "// Ignore empty lines, those that start with #." << endl
+ << "//" << endl
+ << "if (line.empty () || line[0] == '#')" << endl
+ << "continue;"
+ << endl
+ << "string::size_type p (string::npos);";
+
+ // If we have the option prefix, then only consider lines that start
+ // with that as options.
+ //
+ if (pfx_n != 0)
+ os << "if (line.compare (0, " << pfx_n << ", \"" << pfx << "\") == 0)"
+ << "{";
+
+ os << "p = line.find (' ');";
+
+ // Handle the combined option/value (--foo=bar). This is a bit tricky
+ // since the equal sign can be part of the value (--foo bar=baz).
+ //
+ // Note that we cannot just pass it along as combined because of
+ // quoting (--foo="'bar baz'").
+ //
+ if (comb_values)
+ {
+ os << endl
+ << "string::size_type q (line.find ('='));"
+ << "if (q != string::npos && q < p)" << endl
+ << "p = q;";
+ }
+
+ if (pfx_n != 0)
+ os << "}";
+ else
+ os << endl;
+
+ os << "string s1;"
+ << "if (p != string::npos)"
+ << "{"
+ << "s1.assign (line, 0, p);"
+ << endl
+ << "// Skip leading whitespaces in the argument." << endl
+ << "//" << endl;
+ if (comb_values)
+ os << "if (line[p] == '=')" << endl // Keep whitespaces after '='.
+ << "++p;"
+ << "else"
+ << "{";
+ os << "n = line.size ();"
+ << "for (++p; p < n; ++p)"
+ << "{"
+ << "char c (line[p]);"
+ << "if (c != ' ' && c != '\\t' && c != '\\r')" << endl
+ << "break;"
+ << "}";
+ if (comb_values)
+ os << "}";
+ os << "}";
+ if (sep)
+ os << "else if (!skip_)" << endl
+ << "skip_ = (line == \"" << ctx.opt_sep << "\");"
+ << endl;
+
+ os << "string s2 (line, p != string::npos ? p : 0);"
+ << endl
+ << "// If the string (which is an option value or argument) is" << endl
+ << "// wrapped in quotes, remove them." << endl
+ << "//" << endl
+ << "n = s2.size ();"
+ << "char cf (s2[0]), cl (s2[n - 1]);"
+ << endl
+ << "if (cf == '\"' || cf == '\\'' || cl == '\"' || cl == '\\'')"
+ << "{"
+ << "if (n == 1 || cf != cl)" << endl
+ << "throw unmatched_quote (s2);"
+ << endl
+ << "s2 = string (s2, 1, n - 2);"
+ << "}";
+
+ os << "if (!s1.empty ())"
+ << "{"
+ << "// See if this is another file option." << endl
+ << "//" << endl
+ << "const option_info* oi;"
+ << "if (" << (sep ? "!skip_ && " : "") <<
+ "(oi = find (s1.c_str ())))" << endl
+ << "{"
+ << "if (s2.empty ())" << endl
+ << "throw missing_value (oi->option);"
+ << endl
+ << "if (oi->search_func != 0)"
+ << "{"
+ << "std::string f (oi->search_func (s2.c_str (), oi->arg));"
+ << "if (!f.empty ())" << endl
+ << "load (f);"
+ << "}"
+ << "else" << endl
+ << "load (s2);"
+ << endl
+ << "continue;"
+ << "}"
+ << "a.value = s1;"
+ << "args_.push_back (a);"
+ << "}"
+ << "a.value = s2;"
+ << "args_.push_back (a);"
+ << "}" // while
+ << "}";
+ }
+
+ // group_scanner
+ //
+ if (ctx.options.generate_group_scanner ())
+ {
+ os << "// group_scanner" << endl
+ << "//" << endl
+
+ << "bool group_scanner::" << endl
+ << "more ()"
+ << "{"
+ << "// We don't want to call scan_group() here since that" << endl
+ << "// would invalidate references to previous arguments." << endl
+ << "// But we do need to check that the previous group was" << endl
+ << "// handled." << endl
+ << "//" << endl
+ << "if (state_ == scanned)"
+ << "{"
+ << "if (group_scan_.end () != group_.size ())" << endl
+ << "throw unexpected_group (arg_[i_], group_scan_.next ());"
+ << "}"
+ << "return scan_.more ();"
+ << "}"
+
+ << "const char* group_scanner::" << endl
+ << "peek ()"
+ << "{"
+ << "if (state_ != peeked)" << endl
+ << "scan_group (peeked);"
+ << "scan_.peek ();"
+ << "// Return unescaped." << endl
+ << "return arg_[i_].c_str ();"
+ << "}"
+
+ << "const char* group_scanner::" << endl
+ << "next ()"
+ << "{"
+ << "if (state_ != peeked)" << endl
+ << "scan_group (peeked);"
+ << "scan_.next ();"
+ << "scan_group (scanned);"
+ << "// Return unescaped." << endl
+ << "return arg_[i_].c_str ();"
+ << "}"
+
+ << "void group_scanner::" << endl
+ << "skip ()"
+ << "{"
+ << "if (state_ != peeked)" << endl
+ << "scan_group (peeked);"
+ << "scan_.skip ();"
+ << "scan_group (skipped);"
+ << "}"
+
+ << "void group_scanner::" << endl
+ << "scan_group (state st)"
+ << "{"
+ << "// If the previous argument has been scanned, then make" << endl
+ << "// sure the group has been scanned (handled) as well." << endl
+ << "//" << endl
+ << "if (state_ == scanned)"
+ << "{"
+ << "if (group_scan_.end () != group_.size ())" << endl
+ << "throw unexpected_group (arg_[i_], group_scan_.next ());"
+ << "}"
+
+ << "if (state_ != peeked)"
+ << "{"
+ << "arg_[i_ == 0 ? ++i_ : --i_].clear ();"
+ << "group_.clear ();"
+ << "group_scan_.reset ();"
+ << "}"
+
+ << "// We recognize all group sequences both before and " << endl
+ << "// after the argument and diagnose any misuse. We may" << endl
+ << "// also have multiple groups:" << endl
+ << "//" << endl
+ << "// { -x }+ { -y }+ arg" << endl
+ << "//" << endl
+ << endl
+ << "// Using group_ won't cover empty groups." << endl
+ << "//" << endl
+ << "bool g (false);"
+ << endl
+ << "while (scan_.more ())"
+ << "{"
+ << "const char* a (scan_.peek ());"
+ << "size_t i (*a == '\\\\' ? 1 : 0);"
+ << "separator s (sense (a + i));"
+ << endl
+ << "if (s == none || i != 0)"
+ << "{"
+ << "if (state_ != peeked)" << endl
+ << "arg_[i_] = a + (s != none ? i : 0);"
+ << "break;"
+ << "}"
+
+ << "// Start of a leading group for the next argument." << endl
+ << "//" << endl
+ << "if (s == open && state_ == peeked)" << endl
+ << "break;"
+ << endl
+ << "if (s != (state_ == peeked ? open_plus : open))" << endl
+ << "throw group_separator (a, \"\");"
+ << endl
+ << "g = true;"
+ << endl
+ << "// Scan the group until the closing separator." << endl
+ << "//" << endl
+ << "scan_.next ();"
+ << "s = none;"
+ << "while (s == none && scan_.more ())"
+ << "{"
+ << "a = scan_.next ();"
+ << "i = (*a == '\\\\' ? 1 : 0);"
+ << "s = sense (a + i);"
+ << endl
+ << "if (s == none || i != 0)"
+ << "{"
+ << "group_.push_back (a + (s != none ? i : 0));"
+ << "s = none;"
+ << "}"
+ << "}"
+
+ << "if (s != (state_ == peeked ? close : close_plus))"
+ << "{"
+ << "throw group_separator ((s != none ? a : \"\")," << endl
+ << "(state_ == peeked ? \"}\" : \"}+\"));"
+ << "}"
+ << "}"
+
+ << "// Handle the case where we have seen the leading group" << endl
+ << "// but there are no more arguments." << endl
+ << "//" << endl
+ << "if (g && state_ != peeked && !scan_.more ())" << endl
+ << "throw group_separator (\"{\", \"\");"
+ << endl
+ << "state_ = st;"
+ << "}";
+ }
+
+ // Option description.
+ //
+ if (ctx.options.generate_description ())
+ {
+ // options
+ //
+ os << "void options::" << endl
+ << "push_back (const option& o)"
+ << "{"
+ << "container_type::size_type n (size ());"
+ << "container_type::push_back (o);"
+ << "map_[o.name ()] = n;"
+ << endl
+ << "for (option_names::const_iterator i (o.aliases ().begin ());" << endl
+ << "i != o.aliases ().end (); ++i)" << endl
+ << "map_[*i] = n;"
+ << "}";
+ }
+ }
+
+ // To reduce the number of standard headers we have to include in the
+ // generated header file, we always generate the following templates
+ // in the source file.
+ //
+ bool sp (ctx.gen_specifier);
+ bool gen_merge (ctx.gen_merge);
+
+ // parser class template & its specializations
+ //
+ os << "template <typename X>" << endl
+ << "struct parser"
+ << "{";
+
+ os << "static void" << endl
+ << "parse (X& x, " << (sp ? "bool& xs, " : "") << "scanner& s)"
+ << "{"
+ << "using namespace std;"
+ << endl
+ << "const char* o (s.next ());"
+ << "if (s.more ())"
+ << "{"
+ << "string v (s.next ());"
+ << "istringstream is (v);"
+ << "if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))" << endl
+ << "throw invalid_value (o, v);"
+ << "}"
+ << "else" << endl
+ << "throw missing_value (o);";
+ if (sp)
+ os << endl
+ << "xs = true;";
+ os << "}";
+
+ if (gen_merge)
+ os << "static void" << endl
+ << "merge (X& b, const X& a)"
+ << "{"
+ << "b = a;"
+ << "}";
+
+ os << "};";
+
+ // parser<bool>
+ //
+ os << "template <>" << endl
+ << "struct parser<bool>"
+ << "{";
+
+ os << "static void" << endl
+ << "parse (bool& x, scanner& s)"
+ << "{"
+ << "s.next ();"
+ << "x = true;"
+ << "}";
+
+ if (gen_merge)
+ os << "static void" << endl
+ << "merge (bool& b, const bool&)"
+ << "{"
+ << "b = true;" // We wouldn't be here if a is false.
+ << "}";
+
+ os << "};";
+
+ // parser<string>
+ //
+ os << "template <>" << endl
+ << "struct parser<std::string>"
+ << "{";
+
+ os << "static void" << endl
+ << "parse (std::string& x, " << (sp ? "bool& xs, " : "") << "scanner& s)"
+ << "{"
+ << "const char* o (s.next ());"
+ << endl
+ << "if (s.more ())" << endl
+ << "x = s.next ();"
+ << "else" << endl
+ << "throw missing_value (o);";
+ if (sp)
+ os << endl
+ << "xs = true;";
+ os << "}";
+
+ if (gen_merge)
+ os << "static void" << endl
+ << "merge (std::string& b, const std::string& a)"
+ << "{"
+ << "b = a;"
+ << "}";
+
+ os << "};";
+
+ // parser<std::vector<X>>
+ //
+ os << "template <typename X>" << endl
+ << "struct parser<std::vector<X> >"
+ << "{";
+
+ os << "static void" << endl
+ << "parse (std::vector<X>& c, " << (sp ? "bool& xs, " : "") << "scanner& s)"
+ << "{"
+ << "X x;";
+ if (sp)
+ os << "bool dummy;";
+ os << "parser<X>::parse (x, " << (sp ? "dummy, " : "") << "s);"
+ << "c.push_back (x);";
+ if (sp)
+ os << "xs = true;";
+ os << "}";
+
+ if (gen_merge)
+ os << "static void" << endl
+ << "merge (std::vector<X>& b, const std::vector<X>& a)"
+ << "{"
+ << "b.insert (b.end (), a.begin (), a.end ());"
+ << "}";
+
+ os << "};";
+
+ // parser<std::set<X>>
+ //
+ os << "template <typename X>" << endl
+ << "struct parser<std::set<X> >"
+ << "{";
+
+ os << "static void" << endl
+ << "parse (std::set<X>& c, " << (sp ? "bool& xs, " : "") << "scanner& s)"
+ << "{"
+ << "X x;";
+ if (sp)
+ os << "bool dummy;";
+ os << "parser<X>::parse (x, " << (sp ? "dummy, " : "") << "s);"
+ << "c.insert (x);";
+ if (sp)
+ os << "xs = true;";
+ os << "}";
+
+ if (gen_merge)
+ os << "static void" << endl
+ << "merge (std::set<X>& b, const std::set<X>& a)"
+ << "{"
+ << "b.insert (a.begin (), a.end ());"
+ << "}";
+
+ os << "};";
+
+ // parser<std::map<K,V>>
+ //
+ os << "template <typename K, typename V>" << endl
+ << "struct parser<std::map<K, V> >"
+ << "{";
+
+ os << "static void" << endl
+ << "parse (std::map<K, V>& m, " << (sp ? "bool& xs, " : "") << "scanner& s)"
+ << "{"
+ << "const char* o (s.next ());"
+ << endl
+ << "if (s.more ())"
+ << "{"
+ << "std::string ov (s.next ());"
+ << "std::string::size_type p = ov.find ('=');"
+ << endl
+ << "K k = K ();"
+ << "V v = V ();"
+ << "std::string kstr (ov, 0, p);"
+ << "std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));"
+ << endl
+ << "int ac (2);"
+ << "char* av[] = {const_cast<char*> (o), 0};";
+ if (sp)
+ os << "bool dummy;";
+ os << "if (!kstr.empty ())"
+ << "{"
+ << "av[1] = const_cast<char*> (kstr.c_str ());"
+ << "argv_scanner s (0, ac, av);"
+ << "parser<K>::parse (k, " << (sp ? "dummy, " : "") << "s);"
+ << "}"
+ << "if (!vstr.empty ())"
+ << "{"
+ << "av[1] = const_cast<char*> (vstr.c_str ());"
+ << "argv_scanner s (0, ac, av);"
+ << "parser<V>::parse (v, " << (sp ? "dummy, " : "") << "s);"
+ << "}"
+ << "m[k] = v;"
+ << "}"
+ << "else" << endl
+ << "throw missing_value (o);";
+ if (sp)
+ os << endl
+ << "xs = true;";
+ os << "}";
+
+ if (gen_merge)
+ os << "static void" << endl
+ << "merge (std::map<K, V>& b, const std::map<K, V>& a)"
+ << "{"
+ << "for (typename std::map<K, V>::const_iterator i (a.begin ()); " << endl
+ << "i != a.end (); " << endl
+ << "++i)" << endl
+ << "b[i->first] = i->second;"
+ << "}";
+
+ os << "};";
+
+ // Parser thunk.
+ //
+ os << "template <typename X, typename T, T X::*M>" << endl
+ << "void" << endl
+ << "thunk (X& x, scanner& s)"
+ << "{"
+ << "parser<T>::parse (x.*M, s);"
+ << "}";
+
+ if (ctx.gen_specifier)
+ os << "template <typename X, typename T, T X::*M, bool X::*S>" << endl
+ << "void" << endl
+ << "thunk (X& x, scanner& s)"
+ << "{"
+ << "parser<T>::parse (x.*M, x.*S, s);"
+ << "}";
+
+ ctx.ns_close (ctx.cli);
+}
diff --git a/cli/cli/runtime-source.hxx b/cli/cli/runtime-source.hxx
new file mode 100644
index 0000000..0e51c4b
--- /dev/null
+++ b/cli/cli/runtime-source.hxx
@@ -0,0 +1,13 @@
+// file : cli/runtime-source.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_RUNTIME_SOURCE_HXX
+#define CLI_RUNTIME_SOURCE_HXX
+
+#include <cli/context.hxx>
+
+void
+generate_runtime_source (context&, bool complete);
+
+#endif // CLI_RUNTIME_SOURCE_HXX
diff --git a/cli/cli/semantics.hxx b/cli/cli/semantics.hxx
new file mode 100644
index 0000000..4b18629
--- /dev/null
+++ b/cli/cli/semantics.hxx
@@ -0,0 +1,16 @@
+// file : cli/semantics.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_SEMANTICS_HXX
+#define CLI_SEMANTICS_HXX
+
+#include <cli/semantics/class.hxx>
+#include <cli/semantics/doc.hxx>
+#include <cli/semantics/elements.hxx>
+#include <cli/semantics/expression.hxx>
+#include <cli/semantics/namespace.hxx>
+#include <cli/semantics/option.hxx>
+#include <cli/semantics/unit.hxx>
+
+#endif // CLI_SEMANTICS_HXX
diff --git a/cli/cli/semantics/class.cxx b/cli/cli/semantics/class.cxx
new file mode 100644
index 0000000..494d5d0
--- /dev/null
+++ b/cli/cli/semantics/class.cxx
@@ -0,0 +1,39 @@
+// file : cli/semantics/class.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cutl/compiler/type-info.hxx>
+
+#include <cli/semantics/class.hxx>
+
+namespace semantics
+{
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // inherits
+ //
+ {
+ type_info ti (typeid (inherits));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // class_
+ //
+ {
+ type_info ti (typeid (class_));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/cli/cli/semantics/class.hxx b/cli/cli/semantics/class.hxx
new file mode 100644
index 0000000..ca7de8c
--- /dev/null
+++ b/cli/cli/semantics/class.hxx
@@ -0,0 +1,106 @@
+// file : cli/semantics/class.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_SEMANTICS_CLASS_HXX
+#define CLI_SEMANTICS_CLASS_HXX
+
+#include <vector>
+
+#include <cli/semantics/elements.hxx>
+
+namespace semantics
+{
+ class class_;
+
+ class inherits: public edge
+ {
+ public:
+ class_&
+ base () const
+ {
+ return *base_;
+ }
+
+ class_&
+ derived () const
+ {
+ return *derived_;
+ }
+
+ public:
+ void
+ set_left_node (class_& n)
+ {
+ derived_ = &n;
+ }
+
+ void
+ set_right_node (class_& n)
+ {
+ base_ = &n;
+ }
+
+ protected:
+ class_* base_;
+ class_* derived_;
+ };
+
+ class class_: public scope
+ {
+ private:
+ typedef std::vector<inherits*> inherits_list;
+
+ public:
+ bool
+ abstract () const
+ {
+ return abstract_;
+ }
+
+ void
+ abstract (bool a)
+ {
+ abstract_ = a;
+ }
+
+ public:
+ typedef pointer_iterator<inherits_list::const_iterator> inherits_iterator;
+
+ inherits_iterator
+ inherits_begin () const
+ {
+ return inherits_.begin ();
+ }
+
+ inherits_iterator
+ inherits_end () const
+ {
+ return inherits_.end ();
+ }
+
+ public:
+ class_ (path const& file, size_t line, size_t column)
+ : node (file, line, column), abstract_ (false)
+ {
+ }
+
+ void
+ add_edge_left (inherits& e)
+ {
+ inherits_.push_back (&e);
+ }
+
+ void
+ add_edge_right (inherits&) {}
+
+ using scope::add_edge_left;
+ using scope::add_edge_right;
+
+ private:
+ bool abstract_;
+ inherits_list inherits_;
+ };
+}
+
+#endif // CLI_SEMANTICS_CLASS_HXX
diff --git a/cli/cli/semantics/doc.cxx b/cli/cli/semantics/doc.cxx
new file mode 100644
index 0000000..c31260c
--- /dev/null
+++ b/cli/cli/semantics/doc.cxx
@@ -0,0 +1,27 @@
+// file : cli/semantics/doc.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cutl/compiler/type-info.hxx>
+
+#include <cli/semantics/doc.hxx>
+
+namespace semantics
+{
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ type_info ti (typeid (doc));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+ } init_;
+ }
+}
diff --git a/cli/cli/semantics/doc.hxx b/cli/cli/semantics/doc.hxx
new file mode 100644
index 0000000..8dacb7a
--- /dev/null
+++ b/cli/cli/semantics/doc.hxx
@@ -0,0 +1,22 @@
+// file : cli/semantics/doc.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_SEMANTICS_DOC_HXX
+#define CLI_SEMANTICS_DOC_HXX
+
+#include <cli/semantics/elements.hxx>
+
+namespace semantics
+{
+ // Scope-level documentation node.
+ //
+ class doc: public nameable, public doc_strings
+ {
+ public:
+ doc (path const& file, size_t line, size_t column)
+ : node (file, line, column) {}
+ };
+}
+
+#endif // CLI_SEMANTICS_DOC_HXX
diff --git a/cli/cli/semantics/elements.cxx b/cli/cli/semantics/elements.cxx
new file mode 100644
index 0000000..ed8eb7d
--- /dev/null
+++ b/cli/cli/semantics/elements.cxx
@@ -0,0 +1,129 @@
+// file : cli/semantics/elements.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cutl/compiler/type-info.hxx>
+
+#include <cli/semantics/elements.hxx>
+
+namespace semantics
+{
+ // nameable
+ //
+ string nameable::
+ fq_name () const
+ {
+ string const& n (name ());
+
+ if (n.empty ())
+ return n;
+ else
+ return scope ().fq_name () + "::" + n;
+ }
+
+ // scope
+ //
+
+ scope::names_iterator_pair scope::
+ find (string const& name) const
+ {
+ names_map::const_iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ return names_iterator_pair (names_.end (), names_.end ());
+ else
+ return names_iterator_pair (i->second.begin (), i->second.end ());
+ }
+
+ scope::names_iterator scope::
+ find (names& e)
+ {
+ list_iterator_map::iterator i (iterator_map_.find (&e));
+ return i != iterator_map_.end () ? i->second : names_.end ();
+ }
+
+ void scope::
+ add_edge_left (names& e)
+ {
+ names_list::iterator it (names_.insert (names_.end (), &e));
+ iterator_map_[&e] = it;
+
+ for (names::name_iterator i (e.name_begin ()); i != e.name_end (); ++i)
+ names_map_[*i].push_back (&e);
+ }
+
+ void scope::
+ remove_edge_left (names& e)
+ {
+ list_iterator_map::iterator i (iterator_map_.find (&e));
+ assert (i != iterator_map_.end ());
+
+ names_.erase (i->second);
+ iterator_map_.erase (i);
+
+ for (names::name_iterator ni (e.name_begin ()); ni != e.name_end (); ++ni)
+ {
+ names_map::iterator j (names_map_.find (*ni));
+
+ for (names_list::iterator i (j->second.begin ());
+ i != j->second.end (); ++i)
+ {
+ if (*i == &e)
+ i = j->second.erase (i);
+ }
+ }
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // node
+ //
+ insert (type_info (typeid (node)));
+
+ // edge
+ //
+ insert (type_info (typeid (edge)));
+
+ // names
+ //
+ {
+ type_info ti (typeid (names));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // nameable
+ //
+ {
+ type_info ti (typeid (nameable));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // scope
+ //
+ {
+ type_info ti (typeid (scope));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+
+ // type
+ //
+ {
+ type_info ti (typeid (type));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/cli/cli/semantics/elements.hxx b/cli/cli/semantics/elements.hxx
new file mode 100644
index 0000000..6235a06
--- /dev/null
+++ b/cli/cli/semantics/elements.hxx
@@ -0,0 +1,407 @@
+// file : cli/semantics/elements.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_SEMANTICS_ELEMENTS_HXX
+#define CLI_SEMANTICS_ELEMENTS_HXX
+
+#include <map>
+#include <list>
+#include <vector>
+#include <string>
+#include <cstddef> // std::size_t
+#include <cstdlib> // std::abort
+#include <utility> // std::pair
+#include <cassert>
+
+#include <cutl/fs/path.hxx>
+
+#include <cutl/container/graph.hxx>
+#include <cutl/container/pointer-iterator.hxx>
+
+#include <cutl/compiler/context.hxx>
+
+namespace semantics
+{
+ using namespace cutl;
+
+ using std::size_t;
+ using std::string;
+
+ using container::graph;
+ using container::pointer_iterator;
+
+ using compiler::context;
+
+
+ //
+ //
+ using fs::path;
+ using fs::invalid_path;
+
+ //
+ //
+ typedef std::vector<string> doc_strings;
+
+ //
+ //
+ class node;
+ class edge;
+
+
+ //
+ //
+ class edge
+ {
+ public:
+ virtual
+ ~edge () {}
+
+ public:
+ typedef semantics::context context_type;
+
+ context_type&
+ context ()
+ {
+ return context_;
+ }
+
+ public:
+ template <typename X>
+ bool
+ is_a () const
+ {
+ return dynamic_cast<X const*> (this) != 0;
+ }
+
+ private:
+ context_type context_;
+ };
+
+ //
+ //
+ class node
+ {
+ public:
+ virtual
+ ~node () {}
+
+ public:
+ typedef semantics::context context_type;
+
+ context_type&
+ context ()
+ {
+ return context_;
+ }
+
+ public:
+ path const&
+ file () const
+ {
+ return file_;
+ }
+
+ size_t
+ line () const
+ {
+ return line_;
+ }
+
+ size_t
+ column () const
+ {
+ return column_;
+ }
+
+ public:
+ template <typename X>
+ bool
+ is_a () const
+ {
+ return dynamic_cast<X const*> (this) != 0;
+ }
+
+ public:
+ node (path const& file, size_t line, size_t column)
+ : file_ (file), line_ (line), column_ (column)
+ {
+ }
+
+ // For virtual inheritance. Should never be actually called.
+ //
+ node ()
+ : file_ ("")
+ {
+ std::abort ();
+ }
+
+ // Sink functions that allow extensions in the form of one-way
+ // edges.
+ //
+ void
+ add_edge_right (edge&)
+ {
+ }
+
+ private:
+ context_type context_;
+ path file_;
+ size_t line_;
+ size_t column_;
+ };
+
+ //
+ //
+ class scope;
+ class nameable;
+
+
+ //
+ //
+ class names: public edge
+ {
+ public:
+ typedef semantics::scope scope_type;
+
+ typedef std::vector<string> name_list;
+ typedef name_list::const_iterator name_iterator;
+
+ // First name.
+ //
+ string const&
+ name () const
+ {
+ return names_[0];
+ }
+
+ name_iterator
+ name_begin () const
+ {
+ return names_.begin ();
+ }
+
+ name_iterator
+ name_end () const
+ {
+ return names_.end ();
+ }
+
+ scope_type&
+ scope () const
+ {
+ return *scope_;
+ }
+
+ nameable&
+ named () const
+ {
+ return *named_;
+ }
+
+ public:
+ names (string const& name)
+ {
+ names_.push_back (name);
+ }
+
+ names (name_list const& names)
+ : names_ (names)
+ {
+ }
+
+ void
+ set_left_node (scope_type& n)
+ {
+ scope_ = &n;
+ }
+
+ void
+ set_right_node (nameable& n)
+ {
+ named_ = &n;
+ }
+
+ void
+ clear_left_node (scope_type& n)
+ {
+ assert (scope_ == &n);
+ scope_ = 0;
+ }
+
+ void
+ clear_right_node (nameable& n)
+ {
+ assert (named_ == &n);
+ named_ = 0;
+ }
+
+ protected:
+ scope_type* scope_;
+ nameable* named_;
+ name_list names_;
+ };
+
+
+ //
+ //
+ class nameable: public virtual node
+ {
+ public:
+ typedef semantics::scope scope_type;
+
+ string
+ name () const
+ {
+ return named_->name ();
+ }
+
+ string
+ fq_name () const;
+
+ scope_type&
+ scope ()
+ {
+ return named_->scope ();
+ }
+
+ scope_type const&
+ scope () const
+ {
+ return named_->scope ();
+ }
+
+ names&
+ named ()
+ {
+ return *named_;
+ }
+
+ public:
+ nameable ()
+ : named_ (0)
+ {
+ }
+
+ void
+ add_edge_right (names& e)
+ {
+ assert (named_ == 0);
+ named_ = &e;
+ }
+
+ void
+ remove_edge_right (names& e)
+ {
+ assert (named_ == &e);
+ named_ = 0;
+ }
+
+ using node::add_edge_right;
+
+ private:
+ names* named_;
+ };
+
+
+ //
+ //
+ class scope: public virtual nameable
+ {
+ protected:
+ typedef std::list<names*> names_list;
+ typedef std::map<names*, names_list::iterator> list_iterator_map;
+ typedef std::map<string, names_list> names_map;
+
+ public:
+ typedef pointer_iterator<names_list::iterator> names_iterator;
+ typedef pointer_iterator<names_list::const_iterator> names_const_iterator;
+
+ typedef
+ std::pair<names_const_iterator, names_const_iterator>
+ names_iterator_pair;
+
+ public:
+ names_iterator
+ names_begin ()
+ {
+ return names_.begin ();
+ }
+
+ names_iterator
+ names_end ()
+ {
+ return names_.end ();
+ }
+
+ names_const_iterator
+ names_begin () const
+ {
+ return names_.begin ();
+ }
+
+ names_const_iterator
+ names_end () const
+ {
+ return names_.end ();
+ }
+
+ bool
+ names_empty () const
+ {
+ return names_.empty ();
+ }
+
+ virtual names_iterator_pair
+ find (string const& name) const;
+
+ names_iterator
+ find (names&);
+
+ public:
+ scope (path const& file, size_t line, size_t column)
+ : node (file, line, column)
+ {
+ }
+
+ scope ()
+ {
+ }
+
+ void
+ add_edge_left (names&);
+
+ void
+ remove_edge_left (names&);
+
+ using nameable::add_edge_right;
+
+ private:
+ names_list names_;
+ list_iterator_map iterator_map_;
+ names_map names_map_;
+ };
+
+ //
+ //
+ class type: public node
+ {
+ public:
+ string const&
+ name () const
+ {
+ return name_;
+ }
+
+ public:
+ type (path const& file, size_t line, size_t column, string const& name)
+ : node (file, line, column), name_ (name)
+ {
+ }
+
+ private:
+ string name_;
+ };
+}
+
+#endif // CLI_SEMANTICS_ELEMENTS_HXX
diff --git a/cli/cli/semantics/expression.cxx b/cli/cli/semantics/expression.cxx
new file mode 100644
index 0000000..18d3312
--- /dev/null
+++ b/cli/cli/semantics/expression.cxx
@@ -0,0 +1,27 @@
+// file : cli/semantics/expression.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cutl/compiler/type-info.hxx>
+
+#include <cli/semantics/expression.hxx>
+
+namespace semantics
+{
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ type_info ti (typeid (expression));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+ } init_;
+ }
+}
diff --git a/cli/cli/semantics/expression.hxx b/cli/cli/semantics/expression.hxx
new file mode 100644
index 0000000..e36a0cb
--- /dev/null
+++ b/cli/cli/semantics/expression.hxx
@@ -0,0 +1,76 @@
+// file : cli/semantics/expression.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_SEMANTICS_EXPRESSION_HXX
+#define CLI_SEMANTICS_EXPRESSION_HXX
+
+#include <string>
+#include <cli/semantics/elements.hxx>
+
+namespace semantics
+{
+ //
+ //
+ class initialized;
+
+ //
+ //
+ class expression: public node
+ {
+ public:
+ enum expression_type
+ {
+ string_lit,
+ char_lit,
+ bool_lit,
+ int_lit,
+ float_lit,
+ call_expr,
+ identifier
+ };
+
+ expression_type
+ type () const
+ {
+ return type_;
+ }
+
+ std::string const&
+ value () const
+ {
+ return value_;
+ }
+
+ public:
+ initialized&
+ initializes () const
+ {
+ return *initializes_;
+ }
+
+ public:
+ expression (path const& file,
+ size_t line,
+ size_t column,
+ expression_type type,
+ std::string const& value)
+ : node (file, line, column), type_ (type), value_ (value)
+ {
+ }
+
+ void
+ add_edge_right (initialized& e)
+ {
+ initializes_ = &e;
+ }
+
+ private:
+ initialized* initializes_;
+
+ expression_type type_;
+ std::string value_;
+ };
+}
+
+#endif // CLI_SEMANTICS_EXPRESSION_HXX
diff --git a/cli/cli/semantics/namespace.cxx b/cli/cli/semantics/namespace.cxx
new file mode 100644
index 0000000..3c2643c
--- /dev/null
+++ b/cli/cli/semantics/namespace.cxx
@@ -0,0 +1,27 @@
+// file : cli/semantics/namespace.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cutl/compiler/type-info.hxx>
+
+#include <cli/semantics/namespace.hxx>
+
+namespace semantics
+{
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ type_info ti (typeid (namespace_));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ } init_;
+ }
+}
diff --git a/cli/cli/semantics/namespace.hxx b/cli/cli/semantics/namespace.hxx
new file mode 100644
index 0000000..00c7bfc
--- /dev/null
+++ b/cli/cli/semantics/namespace.hxx
@@ -0,0 +1,26 @@
+// file : cli/semantics/namespace.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_SEMANTICS_NAMESPACE_HXX
+#define CLI_SEMANTICS_NAMESPACE_HXX
+
+#include <cli/semantics/elements.hxx>
+
+namespace semantics
+{
+ class namespace_: public scope
+ {
+ public:
+ namespace_ (path const& file, size_t line, size_t column)
+ : node (file, line, column)
+ {
+ }
+
+ namespace_ ()
+ {
+ }
+ };
+}
+
+#endif // CLI_SEMANTICS_NAMESPACE_HXX
diff --git a/cli/cli/semantics/option.cxx b/cli/cli/semantics/option.cxx
new file mode 100644
index 0000000..8746a5e
--- /dev/null
+++ b/cli/cli/semantics/option.cxx
@@ -0,0 +1,47 @@
+// file : cli/semantics/option.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cutl/compiler/type-info.hxx>
+
+#include <cli/semantics/option.hxx>
+
+namespace semantics
+{
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // belongs
+ //
+ {
+ type_info ti (typeid (belongs));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // initializes
+ //
+ {
+ type_info ti (typeid (initialized));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // option
+ //
+ {
+ type_info ti (typeid (option));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/cli/cli/semantics/option.hxx b/cli/cli/semantics/option.hxx
new file mode 100644
index 0000000..a9bb963
--- /dev/null
+++ b/cli/cli/semantics/option.hxx
@@ -0,0 +1,189 @@
+// file : cli/semantics/option.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_SEMANTICS_OPTION_HXX
+#define CLI_SEMANTICS_OPTION_HXX
+
+#include <cli/semantics/elements.hxx>
+
+namespace semantics
+{
+ //
+ //
+ class option;
+
+ //
+ //
+ class belongs: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::option option_type;
+
+ option_type&
+ option () const
+ {
+ return *option_;
+ }
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ public:
+ void
+ set_left_node (option_type& n)
+ {
+ option_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ private:
+ option_type* option_;
+ type_type* type_;
+ };
+
+ //
+ //
+ class expression;
+
+ //
+ //
+ class initialized: public edge
+ {
+ public:
+ typedef semantics::option option_type;
+ typedef semantics::expression expression_type;
+
+ option_type&
+ option () const
+ {
+ return *option_;
+ }
+
+ expression_type&
+ expression () const
+ {
+ return *expression_;
+ }
+
+ public:
+ void
+ set_left_node (option_type& n)
+ {
+ option_ = &n;
+ }
+
+ void
+ set_right_node (expression_type& n)
+ {
+ expression_ = &n;
+ }
+
+ private:
+ option_type* option_;
+ expression_type* expression_;
+ };
+
+ //
+ //
+ class option: public nameable
+ {
+ public:
+ typedef semantics::belongs belongs_type;
+ typedef semantics::type type_type;
+
+ belongs_type&
+ belongs () const
+ {
+ return *belongs_;
+ }
+
+ type_type&
+ type () const
+ {
+ return belongs_->type ();
+ }
+
+ public:
+ typedef semantics::initialized initialized_type;
+
+ bool
+ initialized_p () const
+ {
+ return initialized_ != 0;
+ }
+
+ initialized_type&
+ initialized () const
+ {
+ return *initialized_;
+ }
+
+ expression&
+ initializer () const
+ {
+ return initialized_->expression ();
+ }
+
+ public:
+ typedef doc_strings::const_iterator doc_iterator;
+
+ doc_iterator
+ doc_begin () const
+ {
+ return doc_.begin ();
+ }
+
+ doc_iterator
+ doc_end () const
+ {
+ return doc_.end ();
+ }
+
+ doc_strings const&
+ doc () const
+ {
+ return doc_;
+ }
+
+ doc_strings&
+ doc ()
+ {
+ return doc_;
+ }
+
+ public:
+ option (path const& file, size_t line, size_t column)
+ : node (file, line, column), initialized_ (0)
+ {
+ }
+
+ void
+ add_edge_left (belongs_type& e)
+ {
+ belongs_ = &e;
+ }
+
+ void
+ add_edge_left (initialized_type& e)
+ {
+ initialized_ = &e;
+ }
+
+ private:
+ belongs_type* belongs_;
+ initialized_type* initialized_;
+ doc_strings doc_;
+ };
+}
+
+#endif // CLI_SEMANTICS_OPTION_HXX
diff --git a/cli/cli/semantics/unit.cxx b/cli/cli/semantics/unit.cxx
new file mode 100644
index 0000000..9c532ea
--- /dev/null
+++ b/cli/cli/semantics/unit.cxx
@@ -0,0 +1,63 @@
+// file : cli/semantics/unit.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cutl/compiler/type-info.hxx>
+
+#include <cli/semantics/unit.hxx>
+
+namespace semantics
+{
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // includes
+ //
+ {
+ type_info ti (typeid (includes));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // cxx_includes
+ //
+ {
+ type_info ti (typeid (cxx_includes));
+ ti.add_base (typeid (includes));
+ insert (ti);
+ }
+
+ // cli_includes
+ //
+ {
+ type_info ti (typeid (cli_includes));
+ ti.add_base (typeid (includes));
+ insert (ti);
+ }
+
+ // cxx_unit
+ //
+ {
+ type_info ti (typeid (cxx_unit));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // cli_unit
+ //
+ {
+ type_info ti (typeid (cli_unit));
+ ti.add_base (typeid (namespace_));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/cli/cli/semantics/unit.hxx b/cli/cli/semantics/unit.hxx
new file mode 100644
index 0000000..e37648a
--- /dev/null
+++ b/cli/cli/semantics/unit.hxx
@@ -0,0 +1,310 @@
+// file : cli/semantics/unit.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_SEMANTICS_UNIT_HXX
+#define CLI_SEMANTICS_UNIT_HXX
+
+#include <map>
+#include <vector>
+#include <string>
+
+#include <cli/semantics/elements.hxx>
+#include <cli/semantics/namespace.hxx>
+
+namespace semantics
+{
+ //
+ //
+ class cli_unit;
+ class cxx_unit;
+
+ //
+ //
+ class includes: public edge
+ {
+ public:
+ enum kind_type {quote, bracket};
+
+ cli_unit&
+ includer () const
+ {
+ return *includer_;
+ }
+
+ kind_type
+ kind () const
+ {
+ return kind_;
+ }
+
+ path const&
+ file () const
+ {
+ return file_;
+ }
+
+ public:
+ includes (kind_type kind, path const& file)
+ : kind_ (kind), file_ (file)
+ {
+ }
+
+ void
+ set_left_node (cli_unit& n)
+ {
+ includer_ = &n;
+ }
+
+ protected:
+ kind_type kind_;
+ path file_;
+ cli_unit* includer_;
+ };
+
+ //
+ //
+ class cli_includes: public includes
+ {
+ public:
+ cli_unit&
+ includee () const
+ {
+ return *includee_;
+ }
+
+ public:
+ cli_includes (kind_type kind, path const& file)
+ : includes (kind, file)
+ {
+ }
+
+ void
+ set_right_node (cli_unit& n)
+ {
+ includee_ = &n;
+ }
+
+ private:
+ cli_unit* includee_;
+ };
+
+ //
+ //
+ class cxx_includes: public includes
+ {
+ public:
+ cxx_unit&
+ includee () const
+ {
+ return *includee_;
+ }
+
+ public:
+ cxx_includes (kind_type kind, path const& file)
+ : includes (kind, file)
+ {
+ }
+
+ void
+ set_right_node (cxx_unit& n)
+ {
+ includee_ = &n;
+ }
+
+ private:
+ cxx_unit* includee_;
+ };
+
+ //
+ //
+ class cxx_unit: public node
+ {
+ public:
+ cxx_unit (path const& file, size_t line, size_t column)
+ : node (file, line, column)
+ {
+ }
+
+ void
+ add_edge_right (cxx_includes&)
+ {
+ }
+ };
+
+ //
+ //
+ class cli_unit: public graph<node, edge>, public namespace_
+ {
+ typedef std::vector<includes*> includes_list;
+
+ public:
+ // Lookup a name in the specified starting scope. Empty scope denotes
+ // the global namespace. Starting scope should be a fully-qualified
+ // name while name can be qualified but should not be fully-qualified
+ // (to lookup a fully-qualified name use the global namespace as the
+ // starting scope).
+ //
+ // The lookup starts in this unit and continues in all the units that
+ // this unit includes, transitively.
+ //
+ // The outer flag specifies whether to search the outer scopes.
+ //
+ template <typename T>
+ T*
+ lookup (std::string const& scope,
+ std::string const& name,
+ bool outer = true);
+
+ public:
+ typedef
+ pointer_iterator<includes_list::const_iterator>
+ includes_iterator;
+
+ includes_iterator
+ includes_begin () const
+ {
+ return includes_.begin ();
+ }
+
+ includes_iterator
+ includes_end () const
+ {
+ return includes_.end ();
+ }
+
+ public:
+ cli_unit (path const& file, size_t line, size_t column)
+ : node (file, line, column), graph_ (*this)
+ {
+ // Use a special edge to get this->name() return the global
+ // namespace name ("").
+ //
+ new_edge<global_names> (*this, *this);
+ }
+
+ public:
+ template <typename T>
+ T&
+ new_node (path const& file, size_t line, size_t column)
+ {
+ return graph_.new_node<T> (file, line, column);
+ }
+
+ template <typename T, typename A0>
+ T&
+ new_node (path const& file, size_t line, size_t column, A0 const& a0)
+ {
+ return graph_.new_node<T> (file, line, column, a0);
+ }
+
+ template <typename T, typename A0, typename A1>
+ T&
+ new_node (path const& file, size_t line, size_t column,
+ A0 const& a0, A1 const& a1)
+ {
+ return graph_.new_node<T> (file, line, column, a0, a1);
+ }
+
+ template <typename T, typename A0, typename A1, typename A2>
+ T&
+ new_node (path const& file, size_t line, size_t column,
+ A0 const& a0, A1 const& a1, A2 const& a2)
+ {
+ return graph_.new_node<T> (file, line, column, a0, a1, a2);
+ }
+
+ template <typename T, typename A0, typename A1, typename A2, typename A3>
+ T&
+ new_node (path const& file, size_t line, size_t column,
+ A0 const& a0, A1 const& a1, A2 const& a2, A3 const& a3)
+ {
+ return graph_.new_node<T> (file, line, column, a0, a1, a2, a3);
+ }
+
+ public:
+ type&
+ new_type (path const& file, size_t l, size_t c, string const& name)
+ {
+ type_map::iterator i (types_.find (name));
+
+ return i != types_.end ()
+ ? *i->second
+ : *(types_[name] = &new_node<type> (file, l, c, name));
+ }
+
+ protected:
+ // Special names edge for the global namespace.
+ //
+ class global_names: public names
+ {
+ public:
+ global_names ()
+ : names ("")
+ {
+ scope_ = 0;
+ }
+
+ void
+ set_left_node (cli_unit&)
+ {
+ }
+
+ void
+ set_right_node (nameable& n)
+ {
+ named_ = &n;
+ }
+
+ void
+ clear_left_node (cli_unit&)
+ {
+ }
+
+ void
+ clear_right_node (nameable& n)
+ {
+ assert (named_ == &n);
+ named_ = 0;
+ }
+ };
+
+ public:
+ void
+ add_edge_left (cli_includes& e)
+ {
+ includes_.push_back (&e);
+ }
+
+ void
+ add_edge_left (cxx_includes& e)
+ {
+ includes_.push_back (&e);
+ }
+
+ void
+ add_edge_left (global_names&)
+ {
+ }
+
+ void
+ add_edge_right (cli_includes&)
+ {
+ }
+
+ using namespace_::add_edge_left;
+ using namespace_::add_edge_right;
+
+ private:
+ typedef std::map<string, type*> type_map;
+
+ private:
+ graph<node, edge>& graph_;
+ includes_list includes_;
+ type_map types_;
+ };
+}
+
+#include <cli/semantics/unit.txx>
+
+#endif // CLI_SEMANTICS_UNIT_HXX
diff --git a/cli/cli/semantics/unit.txx b/cli/cli/semantics/unit.txx
new file mode 100644
index 0000000..99d178f
--- /dev/null
+++ b/cli/cli/semantics/unit.txx
@@ -0,0 +1,108 @@
+// file : cli/semantics/unit.txx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+namespace semantics
+{
+ template <typename T>
+ T* cli_unit::
+ lookup (std::string const& ss, std::string const& name, bool outer)
+ {
+ using std::string;
+
+ // Resolve the starting scope in this unit, if any.
+ //
+ string::size_type b (0), e;
+ scope* s (0);
+
+ do
+ {
+ e = ss.find ("::", b);
+ string n (ss, b, e == string::npos ? e : e - b);
+
+ if (n.empty ())
+ s = this;
+ else
+ {
+ scope::names_iterator_pair ip (s->find (n));
+
+ for (s = 0; ip.first != ip.second; ++ip.first)
+ if ((s = dynamic_cast<scope*> (&ip.first->named ())))
+ break;
+
+ if (s == 0)
+ break; // No such scope in this unit.
+ }
+
+ b = e;
+
+ if (b == string::npos)
+ break;
+
+ b += 2;
+ } while (true);
+
+ // If we have the starting scope, then try to resolve the name in it.
+ //
+ if (s != 0)
+ {
+ b = 0;
+
+ do
+ {
+ e = name.find ("::", b);
+ string n (name, b, e == string::npos ? e : e - b);
+
+ scope::names_iterator_pair ip (s->find (n));
+
+ // If this is the last name, then see if we have the desired type.
+ //
+ if (e == string::npos)
+ {
+ for (; ip.first != ip.second; ++ip.first)
+ if (T* r = dynamic_cast<T*> (&ip.first->named ()))
+ return r;
+ }
+ // Otherwise, this should be a scope.
+ //
+ else
+ {
+ for (s = 0; ip.first != ip.second; ++ip.first)
+ if ((s = dynamic_cast<scope*> (&ip.first->named ())))
+ break;
+
+ if (s == 0)
+ break; // No such inner scope.
+ }
+
+ b = e;
+
+ if (b == string::npos)
+ break;
+
+ b += 2;
+ } while (true);
+ }
+
+ // If we are here, then that means the lookup didn't find anything in
+ // this unit. The next step is to examine all the included units.
+ //
+ for (includes_iterator i (includes_begin ()); i != includes_end (); ++i)
+ {
+ if (cli_includes* ci = dynamic_cast<cli_includes*> (&*i))
+ if (T* r = ci->includee ().lookup<T> (ss, name, false))
+ return r;
+ }
+
+ // If we still haven't found anything, then the next step is to search
+ // one-outer scope, unless it is the global namespace.
+ //
+ if (outer && !ss.empty ())
+ {
+ string n (ss, 0, ss.rfind ("::"));
+ return lookup<T> (n, name, true);
+ }
+
+ return 0;
+ }
+}
diff --git a/cli/cli/source.cxx b/cli/cli/source.cxx
new file mode 100644
index 0000000..9cd2382
--- /dev/null
+++ b/cli/cli/source.cxx
@@ -0,0 +1,1374 @@
+// file : cli/source.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <iostream>
+
+#include <cli/source.hxx>
+#include <cli/txt.hxx> // txt_size(), txt_wrap_lines()
+
+using namespace std;
+
+namespace
+{
+ struct option_init: traversal::option, context
+ {
+ option_init (context& c) : context (c), comma_ (false) {}
+
+ virtual void
+ traverse (type& o)
+ {
+ if (comma_)
+ os << "," << endl
+ << " ";
+ else
+ {
+ os << endl
+ << ": ";
+ comma_ = true;
+ }
+
+ os << emember (o);
+
+ if (o.initialized_p ())
+ {
+ using semantics::expression;
+ expression const& i (o.initializer ());
+
+ switch (i.type ())
+ {
+ case expression::string_lit:
+ case expression::char_lit:
+ case expression::bool_lit:
+ case expression::int_lit:
+ case expression::float_lit:
+ case expression::identifier:
+ {
+ os << " (" << i.value () << ")";
+ break;
+ }
+ case expression::call_expr:
+ {
+ os << " " << i.value ();
+ break;
+ }
+ }
+ }
+ else
+ os << " ()";
+
+ if (gen_specifier && o.type ().name () != "bool")
+ os << "," << endl
+ << " " << especifier_member (o) << " (false)";
+ }
+
+ private:
+ bool comma_;
+ };
+
+ struct option_merge: traversal::option, context
+ {
+ option_merge (context& c) : context (c) {}
+
+ virtual void
+ traverse (type& o)
+ {
+ string type (o.type ().name ());
+ bool b (type == "bool");
+
+ string member (emember (o));
+ string spec_member (b ? member : especifier_member (o));
+
+ os << "if (a." << spec_member << ")"
+ << "{"
+ << cli << "::parser< " << type << ">::merge (" << endl
+ << "this->" << member << ", a." << member << ");";
+ if (!b)
+ os << "this->" << spec_member << " = true;";
+ os << "}";
+ }
+ };
+
+ //
+ //
+ struct option_map: traversal::option, context
+ {
+ option_map (context& c) : context (c) {}
+
+ virtual void
+ traverse (type& o)
+ {
+ using semantics::names;
+
+ string member (emember (o));
+ string type (o.type ().name ());
+ string scope (escape (o.scope ().name ()));
+ string map ("_cli_" + scope + "_map_");
+
+ names& n (o.named ());
+
+ for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i)
+ {
+ os << "_cli_" << scope << "_map_[\"" << *i << "\"] = " << endl
+ << "&" << cli << "::thunk< " << scope << ", " << type << ", " <<
+ "&" << scope << "::" << member;
+
+ if (gen_specifier && type != "bool")
+ os << "," << endl
+ << " &" << scope << "::" << especifier_member (o);
+
+ os << " >;";
+ }
+ }
+ };
+
+ //
+ //
+ struct option_desc: traversal::option, context
+ {
+ option_desc (context& c) : context (c) {}
+
+ virtual void
+ traverse (type& o)
+ {
+ using semantics::names;
+
+ names& n (o.named ());
+
+ os << "// " << o.name () << endl
+ << "//" << endl
+ << "{"
+ << cli << "::option_names a;";
+
+ for (names::name_iterator b (n.name_begin ()), i (b);
+ i != n.name_end (); ++i)
+ {
+ if (i == b) // Skip the primary name.
+ continue;
+
+ os << "a.push_back (\"" << *i << "\");";
+ }
+
+ if (o.initialized_p ())
+ {
+ using semantics::expression;
+ expression const& i (o.initializer ());
+
+ switch (i.type ())
+ {
+ case expression::string_lit:
+ {
+ os << "std::string dv (" << i.value () << ");";
+ break;
+ }
+ case expression::char_lit:
+ {
+ os << "std::string dv (1, " << i.value () << ");";
+ break;
+ }
+ case expression::bool_lit:
+ case expression::int_lit:
+ case expression::float_lit:
+ {
+ os << "std::string dv (\"" << i.value () << "\");";
+ break;
+ }
+ case expression::identifier:
+ case expression::call_expr:
+ {
+ os << "std::string dv;";
+ break;
+ }
+ }
+ }
+ else
+ os << "std::string dv;";
+
+
+ os << cli << "::option o (\"" << o.name () << "\", a, " <<
+ (o.type ().name () == "bool" ? "true" : "false") << ", dv);"
+ << "os.push_back (o);"
+ << "}";
+ }
+ };
+
+ static string
+ escape_str (string const& s)
+ {
+ string r;
+
+ for (size_t i (0), n (s.size ()); i < n; ++i)
+ {
+ switch (s[i])
+ {
+ case '\\':
+ {
+ r += "\\\\";
+ break;
+ }
+ case '"':
+ {
+ r += "\\\"";
+ break;
+ }
+ case '\033':
+ {
+ r += "\\033";
+ break;
+ }
+ default:
+ {
+ r += s[i];
+ break;
+ }
+ }
+ }
+
+ return r;
+ }
+
+ inline void
+ wrap_lines (ostream& os,
+ string& d,
+ size_t indent = 0,
+ size_t first = 0)
+ {
+ txt_wrap_lines (os,
+ d,
+ indent,
+ first,
+ " << \"", // line_start
+ "\" << ::std::endl", // line_end
+ " << ::std::endl", // line_blank
+ &escape_str);
+ }
+
+ enum paragraph {para_unknown, para_text, para_option};
+
+ struct doc: traversal::doc, context
+ {
+ doc (context& c, usage_type u, paragraph& p)
+ : context (c), usage_ (u), para_ (p) {}
+
+ virtual void
+ traverse (type& ds)
+ {
+ if (ds.name ().compare (0, 3, "doc") != 0) // Ignore doc variables.
+ return;
+
+ // Figure out which documentation string we should use.
+ //
+ // 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 ());
+ string d;
+
+ if (gen_usage == ut_both && usage_ == ut_long)
+ {
+ d = n > 2 // Have both short and long?
+ ? ds[2] // Then use long.
+ : (n == 1 ? ds[0] : ds[1]); // Else, use common.
+ }
+ else // Short or long.
+ {
+ d = n > 2 // Have both short and long?
+ ? ds[1] // Then use short,
+ : (n == 1 ? ds[0] : ds[1]); // Else, use common (no first sentence).
+ }
+
+ std::set<string> arg_set;
+ if (n > 1 && options.ansi_color ())
+ translate_arg (ds[0], arg_set);
+
+ d = format (ds.scope (), translate (d, arg_set), true);
+
+ if (d.empty ())
+ return;
+
+ string up (cli + "::usage_para");
+
+ if (para_ == para_unknown)
+ os << "if (p != " << up << "::none)" << endl
+ << "os << ::std::endl;"
+ << endl
+ << "os << \"";
+ else
+ os << "os << std::endl" << endl
+ << " << \"";
+
+ wrap_lines (os, d);
+ os << ";"
+ << endl;
+
+ para_ = para_text;
+ }
+
+ private:
+ usage_type usage_;
+ paragraph& para_;
+ };
+
+ struct option_length: traversal::option, context
+ {
+ option_length (context& c, size_t& l)
+ : context (c), length_ (l), option_ (0) {}
+
+ option_length (context& c, size_t& l, type*& o)
+ : context (c), length_ (l), option_ (&o) {}
+
+ virtual void
+ traverse (type& o)
+ {
+ using semantics::names;
+
+ semantics::doc_strings const& doc (o.doc ());
+
+ if (options.suppress_undocumented () && doc.empty ())
+ return;
+
+ size_t l (0);
+ names& n (o.named ());
+
+ for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i)
+ {
+ if (l != 0)
+ l++; // '|' seperator.
+
+ l += i->size ();
+ }
+
+ string type (o.type ().name ());
+
+ if (type != "bool" || doc.size () >= 3)
+ {
+ l++; // ' ' seperator
+
+ string s (doc.size () > 0 ? doc[0] : string ("<arg>"));
+
+ if (options.ansi_color ())
+ {
+ std::set<string> arg_set;
+ s = translate_arg (s, arg_set);
+ }
+
+ l += txt_size (format (o.scope (), s, false));
+ }
+
+ if (l > length_)
+ {
+ length_ = l;
+ if (option_ != 0)
+ *option_ = &o;
+ }
+ }
+
+ private:
+ size_t& length_;
+ type** option_;
+ };
+
+ //
+ //
+ struct option_usage: traversal::option, context
+ {
+ option_usage (context& c, size_t l, usage_type u, paragraph& p)
+ : context (c), length_ (l), usage_ (u), para_ (p) {}
+
+ virtual void
+ traverse (type& o)
+ {
+ using semantics::names;
+
+ semantics::doc_strings const& doc (o.doc ());
+
+ if (options.suppress_undocumented () && doc.empty ())
+ return;
+
+ bool color (options.ansi_color ());
+
+ size_t l (0);
+ names& n (o.named ());
+
+ string up (cli + "::usage_para");
+
+ // For long usage we want options separated by blank lines.
+ //
+ if (para_ == para_unknown)
+ {
+ if (usage_ == ut_long)
+ os << "if (p != " << up << "::none)" << endl;
+ else
+ os << "if (p == " << up << "::text)" << endl;
+
+ os << "os << ::std::endl;"
+ << endl
+ << "os << \"";
+ }
+ else if (para_ == para_text || usage_ == ut_long)
+ os << "os << std::endl" << endl
+ << " << \"";
+ else
+ os << "os << \"";
+
+ for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i)
+ {
+ if (l != 0)
+ {
+ os << '|';
+ l++;
+ }
+
+ if (color)
+ os << "\\033[1m"; // Bold.
+
+ os << escape_str (*i);
+
+ if (color)
+ os << "\\033[0m";
+
+ l += i->size ();
+ }
+
+ string type (o.type ().name ());
+
+ std::set<string> arg_set;
+ if (type != "bool" || doc.size () >= 3)
+ {
+ os << ' ';
+ l++;
+
+ string s (doc.size () > 0 ? doc[0] : string ("<arg>"));
+
+ if (color)
+ s = translate_arg (s, arg_set);
+
+ s = format (o.scope (), s, false);
+
+ os << escape_str (s);
+ l += txt_size (s);
+ }
+
+ // Figure out which documentation string we should use.
+ //
+ string d;
+ {
+ size_t i (type == "bool" && doc.size () < 3 ? 0 : 1);
+
+ if (doc.size () > i) // Have at least one.
+ {
+ if (gen_usage == ut_both && usage_ == ut_long)
+ {
+ d = doc.size () > i + 1 // Have both short and long?
+ ? doc[i + 1] // Then use long.
+ : doc[i];
+ }
+ else // Short or long.
+ {
+ d = doc.size () > i + 1 // Have both short and long?
+ ? doc[i] // Then use short,
+ : (gen_usage == ut_long // Otherwise, if asked for long,
+ ? doc[i] // Then use long,
+ : first_sentence (doc[i])); // Else first sentence of long.
+ }
+ }
+ }
+
+ // Format the documentation string.
+ //
+ if (color)
+ d = translate (d, arg_set);
+
+ d = format (o.scope (), d, false);
+
+ if (!d.empty ())
+ wrap_lines (os, d, length_ + 1, l); // +1 for extra space after arg.
+ else
+ os << "\" << std::endl";
+
+ os << ";"
+ << endl;
+
+ para_ = para_option;
+ }
+
+ private:
+ size_t length_;
+ usage_type usage_;
+ paragraph& para_;
+ };
+
+ //
+ //
+ struct base_parse: traversal::class_, context
+ {
+ base_parse (context& c): context (c) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ os << "// " << escape (c.name ()) << " base" << endl
+ << "//" << endl
+ << "if (" << fq_name (c) << "::_parse (o, s))" << endl
+ << "return true;"
+ << endl;
+ }
+ };
+
+ //
+ //
+ struct base_merge: traversal::class_, context
+ {
+ base_merge (context& c): context (c) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ os << "// " << escape (c.name ()) << " base" << endl
+ << "//" << endl
+ << fq_name (c) << "::merge (a);"
+ << endl;
+ }
+ };
+
+ //
+ //
+ struct base_desc: traversal::class_, context
+ {
+ base_desc (context& c): context (c) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ os << "// " << escape (c.name ()) << " base" << endl
+ << "//" << endl
+ << fq_name (c) << "::fill (os);"
+ << endl;
+ }
+ };
+
+ struct base_usage: traversal::class_, context
+ {
+ base_usage (context& c, usage_type u): context (c), usage_ (u) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ class_doc_type cd (class_doc (c));
+
+ if (cd == cd_exclude || cd == cd_exclude_base)
+ return;
+
+ const char* t (
+ (cd == cd_default
+ ? gen_usage != ut_both || usage_ == ut_short
+ : cd == cd_short) ? "" : "long_");
+
+ os << "// " << escape (c.name ()) << " base" << endl
+ << "//" << endl
+ << "p = " << fq_name (c) << "::print_" << t << "usage (os, p);"
+ << endl;
+ }
+
+ private:
+ usage_type usage_;
+ };
+
+ //
+ //
+ struct class_: traversal::class_, context
+ {
+ class_ (context& c)
+ : context (c),
+ base_parse_ (c),
+ base_merge_ (c),
+ base_desc_ (c),
+ option_merge_ (c),
+ option_map_ (c),
+ option_desc_ (c)
+ {
+ inherits_base_parse_ >> base_parse_;
+ inherits_base_merge_ >> base_merge_;
+ inherits_base_desc_ >> base_desc_;
+ names_option_merge_ >> option_merge_;
+ names_option_map_ >> option_map_;
+ names_option_desc_ >> option_desc_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ string name (escape (c.name ()));
+
+ bool abst (c.abstract ());
+ bool ho (has<semantics::option> (c));
+ bool hb (c.inherits_begin () != c.inherits_end ());
+
+ os << "// " << name << endl
+ << "//" << endl
+ << endl;
+
+ // c-tors
+ //
+ string um (cli + "::unknown_mode");
+
+ os << name << "::" << endl
+ << name << " ()";
+ {
+ option_init init (*this);
+ traversal::names names_init (init);
+ names (c, names_init);
+ }
+ os << "{"
+ << "}";
+
+ if (!abst)
+ {
+ bool p (gen_parse);
+
+ string n, res, ret;
+ if (p)
+ {
+ n = "bool " + name + "::\n" + (name != "parse" ? "parse" : "parse_");
+ res = "bool r = ";
+ ret = "return r;";
+ }
+ else
+ n = name + "::\n" + name;
+
+ os << n << " (int& argc," << endl
+ << "char** argv," << endl
+ << "bool erase," << endl
+ << um << " opt," << endl
+ << um << " arg)";
+ if (!p)
+ {
+ option_init init (*this);
+ traversal::names names_init (init);
+ names (c, names_init);
+ }
+ os << "{"
+ << cli << "::argv_scanner s (argc, argv, erase);"
+ << res << "_parse (s, opt, arg);"
+ << ret
+ << "}";
+
+ os << n << " (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "bool erase," << endl
+ << um << " opt," << endl
+ << um << " arg)";
+ if (!p)
+ {
+ option_init init (*this);
+ traversal::names names_init (init);
+ names (c, names_init);
+ }
+ os << "{"
+ << cli << "::argv_scanner s (start, argc, argv, erase);"
+ << res << "_parse (s, opt, arg);"
+ << ret
+ << "}";
+
+ os << n << " (int& argc," << endl
+ << "char** argv," << endl
+ << "int& end," << endl
+ << "bool erase," << endl
+ << um << " opt," << endl
+ << um << " arg)";
+ if (!p)
+ {
+ option_init init (*this);
+ traversal::names names_init (init);
+ names (c, names_init);
+ }
+ os << "{"
+ << cli << "::argv_scanner s (argc, argv, erase);"
+ << res << "_parse (s, opt, arg);"
+ << "end = s.end ();"
+ << ret
+ << "}";
+
+ os << n << " (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "int& end," << endl
+ << "bool erase," << endl
+ << um << " opt," << endl
+ << um << " arg)";
+ if (!p)
+ {
+ option_init init (*this);
+ traversal::names names_init (init);
+ names (c, names_init);
+ }
+ os << "{"
+ << cli << "::argv_scanner s (start, argc, argv, erase);"
+ << res << "_parse (s, opt, arg);"
+ << "end = s.end ();"
+ << ret
+ << "}";
+
+ os << n << " (" << cli << "::scanner& s," << endl
+ << um << " opt," << endl
+ << um << " arg)";
+ if (!p)
+ {
+ option_init init (*this);
+ traversal::names names_init (init);
+ names (c, names_init);
+ }
+ os << "{"
+ << res << "_parse (s, opt, arg);"
+ << ret
+ << "}";
+ }
+
+ // merge()
+ //
+ if (gen_merge)
+ {
+ os << "void " << name << "::" << endl
+ << "merge (const " << name << "& a)"
+ << "{"
+ << "CLI_POTENTIALLY_UNUSED (a);"
+ << endl;
+
+ // First merge all our bases.
+ //
+ inherits (c, inherits_base_merge_);
+
+ // Then our options.
+ //
+ names (c, names_option_merge_);
+
+ os << "}";
+ }
+
+ // Usage.
+ //
+ if (gen_usage != ut_none)
+ {
+ bool b (hb && !options.exclude_base ());
+
+ // Calculate option length.
+ //
+ size_t len (0);
+ {
+ semantics::option* o (0);
+ size_t max (options.option_length ());
+
+ // We need to go into our bases unless --exclude-base was
+ // specified.
+ //
+ {
+ traversal::class_ ct;
+ option_length olt (*this, len, o);
+ traversal::inherits i;
+ traversal::names n;
+
+ if (b)
+ ct >> i >> ct;
+
+ ct >> n >> olt;
+ ct.traverse (c);
+
+ // Now do the same for each base and issue a warning if any
+ // base has shorter option length than derived.
+ //
+ if (b && max == 0)
+ {
+ size_t d_len (len);
+ semantics::option* d_o (o);
+
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end (); ++i)
+ {
+ type& b (i->base ());
+
+ len = 0;
+ ct.traverse (b);
+
+ if (len == d_len)
+ continue;
+
+ cerr << c.file () << ":" << c.line () << ":" << c.column ()
+ << " warning: derived class option length is greater "
+ << "than that of a base class '" << b.name () << "'"
+ << endl;
+
+ cerr << b.file () << ":" << b.line () << ":" << b.column ()
+ << " note: class '" << b.name () << "' is defined here"
+ << endl;
+
+ cerr << c.file () << ":" << c.line () << ":" << c.column ()
+ << " note: use --option-length to specify uniform length"
+ << endl;
+ }
+
+ len = d_len;
+ o = d_o;
+ }
+ }
+
+ if (len != 0 && max != 0)
+ {
+ if (len > max)
+ {
+ cerr << o->file () << ":" << o->line () << ":" << o->column ()
+ << " error: option length " << len << " is greater than "
+ << max << " specified with --option-length" << endl;
+ throw generation_failed ();
+ }
+
+ len = max;
+ }
+
+ verify_id_ref ();
+ }
+
+ string up (cli + "::usage_para");
+ string const& ost (options.ostream_type ());
+
+ os << up << " " << name << "::" << endl
+ << "print_usage (" << ost << "& os, " << up << " p)"
+ << "{"
+ << "CLI_POTENTIALLY_UNUSED (os);"
+ << endl;
+ {
+ usage_type u (gen_usage == ut_both ? ut_short : gen_usage);
+
+ base_usage bu (*this, u);
+ traversal::inherits i (bu);
+
+ if (b && !options.include_base_last ())
+ inherits (c, i);
+
+ paragraph p (para_unknown);
+
+ doc dc (*this, u, p);
+ option_usage ou (*this, len, u, p);
+ traversal::names n;
+ n >> dc;
+ n >> ou;
+
+ names (c, n);
+
+ if (p != para_unknown)
+ os << "p = " << up << (p == para_text ? "::text;" : "::option;")
+ << endl;
+
+ if (b && options.include_base_last ())
+ inherits (c, i);
+ }
+
+ os << "return p;"
+ << "}";
+
+ verify_id_ref ();
+
+ // Long version.
+ //
+ if (gen_usage == ut_both)
+ {
+ os << up << " " << name << "::" << endl
+ << "print_long_usage (" << ost << "& os, " << up << " p)"
+ << "{"
+ << "CLI_POTENTIALLY_UNUSED (os);"
+ << endl;
+
+ base_usage bu (*this, ut_long);
+ traversal::inherits i (bu);
+
+ if (b && !options.include_base_last ())
+ inherits (c, i);
+
+ paragraph p (para_unknown);
+
+ doc dc (*this, ut_long, p);
+ option_usage ou (*this, len, ut_long, p);
+ traversal::names n;
+ n >> dc;
+ n >> ou;
+
+ names (c, n);
+
+ if (p != para_unknown)
+ os << "p = " << up << (p == para_text ? "::text;" : "::option;")
+ << endl;
+
+ if (b && options.include_base_last ())
+ inherits (c, i);
+
+ os << "return p;"
+ << "}";
+
+ verify_id_ref ();
+ }
+ }
+
+ // Description.
+ //
+ if (options.generate_description ())
+ {
+ string desc ("_cli_" + name + "_desc");
+
+ os << "struct " << desc << "_type: " << cli << "::options"
+ << "{"
+ << desc << "_type ()"
+ << "{"
+ << fq_name (c) << "::fill (*this);"
+ << "}"
+ << "};";
+
+ if (options.std () < cxx_version::cxx11)
+ os << "static " << desc << "_type " << desc << "_;"
+ << endl;
+
+ os << "void " << name << "::" << endl
+ << "fill (" << cli << "::options&" << (ho || hb ? " os)" : ")")
+ << "{";
+
+ // Add the entries from our bases first so that our entires
+ // override any conflicts.
+ //
+ inherits (c, inherits_base_desc_);
+ names (c, names_option_desc_);
+
+ os << "}";
+
+ os << "const " << cli << "::options& " << name << "::" << endl
+ << "description ()"
+ << "{";
+
+ if (options.std () >= cxx_version::cxx11)
+ os << "static " << desc << "_type " << desc << "_;";
+
+ os << "return " << desc << "_;"
+ << "}";
+ }
+
+ // _parse ()
+ //
+ string map ("_cli_" + name + "_map");
+
+ os << "typedef" << endl
+ << "std::map<std::string, void (*) (" <<
+ name << "&, " << cli << "::scanner&)>" << endl
+ << map << ";"
+ << endl
+ << "static " << map << " " << map << "_;"
+ << endl;
+
+ os << "struct " << map << "_init"
+ << "{"
+ << map << "_init ()"
+ << "{";
+
+ names (c, names_option_map_);
+
+ os << "}"
+ << "};"
+ << "static " << map << "_init " << map << "_init_;"
+ << endl;
+
+ os << "bool " << name << "::" << endl
+ << "_parse (const char* o, " << cli << "::scanner& s)"
+ << "{"
+ << map << "::const_iterator i (" << map << "_.find (o));"
+ << endl
+ << "if (i != " << map << "_.end ())"
+ << "{"
+ << "(*(i->second)) (*this, s);"
+ << "return true;"
+ << "}";
+
+ // Try our bases, from left-to-right.
+ //
+ inherits (c, inherits_base_parse_);
+
+ os << "return false;"
+ << "}";
+
+ if (!abst)
+ {
+ bool pfx (!opt_prefix.empty ());
+ bool sep (!opt_sep.empty ());
+
+ bool comb_flags (pfx && !options.no_combined_flags ());
+ bool comb_values (pfx && !options.no_combined_values ());
+
+ os << "bool " << name << "::" << endl
+ << "_parse (" << cli << "::scanner& s," << endl
+ << um << (pfx ? " opt_mode" : "") << "," << endl
+ << um << " arg_mode)"
+ << "{";
+
+ if (comb_flags)
+ os << "// Can't skip combined flags (--no-combined-flags)." << endl
+ << "//" << endl
+ << "assert (opt_mode != " << cli << "::unknown_mode::skip);"
+ << endl;
+
+ os << "bool r = false;";
+
+ if (sep)
+ os << "bool opt = true;" // Still recognizing options.
+ << endl;
+
+ os << "while (s.more ())"
+ << "{"
+ << "const char* o = s.peek ();";
+
+ if (sep)
+ {
+ os << endl
+ << "if (std::strcmp (o, \"" << opt_sep << "\") == 0)"
+ << "{"
+ << "opt = false;";
+ if (!options.keep_separator ())
+ {
+ os << "s.skip ();" // We don't want to erase the separator.
+ << "r = true;"
+ << "continue;";
+ }
+ os << "}";
+ }
+
+ if (sep)
+ os << "if (opt)"
+ << "{";
+
+ // First try the argument as is.
+ //
+ os << "if (_parse (o, s))"
+ << "{"
+ << "r = true;"
+ << "continue;"
+ << "}";
+
+ if (pfx)
+ {
+ size_t n (opt_prefix.size ());
+
+ os << "if (std::strncmp (o, \"" << opt_prefix << "\", " <<
+ n << ") == 0 && o[" << n << "] != '\\0')"
+ << "{";
+
+ if (comb_values)
+ {
+ os << "// Handle combined option values." << endl
+ << "//" << endl
+ << "std::string co;" // Need to live until next block.
+ << "if (const char* v = std::strchr (o, '='))"
+ << "{"
+ << "co.assign (o, 0, v - o);"
+ << "++v;"
+ << endl
+ << "int ac (2);"
+ << "char* av[] ="
+ << "{"
+ << "const_cast<char*> (co.c_str ())," << endl
+ << "const_cast<char*> (v)"
+ << "};"
+ << cli << "::argv_scanner ns (0, ac, av);"
+ << endl
+ << "if (_parse (co.c_str (), ns))"
+ << "{"
+ << "// Parsed the option but not its value?" << endl
+ << "//" << endl
+ << "if (ns.end () != 2)" << endl
+ << "throw " << cli << "::invalid_value (co, v);"
+ << endl
+ << "s.next ();"
+ << "r = true;"
+ << "continue;"
+ << "}"
+ << "else"
+ << "{"
+ << "// Set the unknown option and fall through." << endl
+ << "//" << endl
+ << "o = co.c_str ();"
+ << "}"
+ << "}";
+ }
+
+ if (comb_flags)
+ {
+ os << "// Handle combined flags." << endl
+ << "//" << endl
+ << "char cf[" << n + 2 << "];" // Need to live until next block.
+ << "{"
+ << "const char* p = o + " << n << ";"
+ << "for (; *p != '\\0'; ++p)"
+ << "{"
+ << "if (!((*p >= 'a' && *p <= 'z') ||" << endl
+ << "(*p >= 'A' && *p <= 'Z') ||" << endl
+ << "(*p >= '0' && *p <= '9')))" << endl
+ << "break;"
+ << "}"
+ << "if (*p == '\\0')"
+ << "{"
+ << "for (p = o + " << n << "; *p != '\\0'; ++p)"
+ << "{"
+ << "std::strcpy (cf, \"" << opt_prefix << "\");"
+ << "cf[" << n << "] = *p;"
+ << "cf[" << n + 1 << "] = '\\0';"
+ << endl
+ << "int ac (1);"
+ << "char* av[] = {cf};"
+ << cli << "::argv_scanner ns (0, ac, av);"
+ << endl
+ << "if (!_parse (cf, ns))" << endl
+ << "break;"
+ << "}"
+ << "if (*p == '\\0')"
+ << "{"
+ << "// All handled." << endl
+ << "//" << endl
+ << "s.next ();"
+ << "r = true;"
+ << "continue;"
+ << "}"
+ << "else"
+ << "{"
+ << "// Set the unknown option and fall through." << endl
+ << "//" << endl
+ << "o = cf;"
+ << "}"
+ << "}"
+ << "}";
+ }
+
+ // Unknown option.
+ //
+ os << "switch (opt_mode)"
+ << "{"
+ << "case " << cli << "::unknown_mode::skip:" << endl
+ << "{"
+ << "s.skip ();"
+ << "r = true;"
+ << "continue;"
+ << "}"
+ << "case " << cli << "::unknown_mode::stop:" << endl
+ << "{"
+ << "break;"
+ << "}"
+ << "case " << cli << "::unknown_mode::fail:" << endl
+ << "{"
+ << "throw " << cli << "::unknown_option (o);"
+ << "}"
+ << "}" // switch
+ << "break;" // The stop case.
+ << "}";
+ }
+
+ if (sep)
+ os << "}";
+
+ // Unknown argument.
+ //
+ os << "switch (arg_mode)"
+ << "{"
+ << "case " << cli << "::unknown_mode::skip:" << endl
+ << "{"
+ << "s.skip ();"
+ << "r = true;"
+ << "continue;"
+ << "}"
+ << "case " << cli << "::unknown_mode::stop:" << endl
+ << "{"
+ << "break;"
+ << "}"
+ << "case " << cli << "::unknown_mode::fail:" << endl
+ << "{"
+ << "throw " << cli << "::unknown_argument (o);"
+ << "}"
+ << "}" // switch
+ << "break;" // The stop case.
+
+ << "}" // for
+ << "return r;"
+ << "}";
+ }
+ }
+
+ private:
+ base_parse base_parse_;
+ traversal::inherits inherits_base_parse_;
+
+ base_merge base_merge_;
+ traversal::inherits inherits_base_merge_;
+
+ base_desc base_desc_;
+ traversal::inherits inherits_base_desc_;
+
+ option_merge option_merge_;
+ traversal::names names_option_merge_;
+
+ option_map option_map_;
+ traversal::names names_option_map_;
+
+ option_desc option_desc_;
+ traversal::names names_option_desc_;
+ };
+
+ // Page usage.
+ //
+ struct class_usage: traversal::class_, context
+ {
+ class_usage (context& c, usage_type u, paragraph& p)
+ : context (c), usage_ (u), para_ (p) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ class_doc_type cd (class_doc (c));
+
+ if (cd == cd_exclude)
+ return;
+
+ const char* t (
+ (cd == cd_default || cd == cd_exclude_base
+ ? gen_usage != ut_both || usage_ == ut_short
+ : cd == cd_short) ? "" : "long_");
+
+ string p (
+ para_ == para_unknown
+ ? "p"
+ : cli + "::usage_para::" + (para_ == para_text ? "text" : "option"));
+
+ os << "p = " << fq_name (c) << "::print_" << t << "usage (os, " <<
+ p << ");"
+ << endl;
+
+ para_ = para_unknown;
+ }
+
+ private:
+ usage_type usage_;
+ paragraph& para_;
+ };
+}
+
+void
+generate_source (context& ctx)
+{
+ ostream& os (ctx.os);
+
+ os << "#include <map>" << endl
+ << "#include <cstring>" << endl
+ << endl;
+
+ 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);
+
+ // Entire page usage.
+ //
+ if (ctx.gen_usage != ut_none && ctx.options.page_usage_specified ())
+ {
+ const string& qn (ctx.options.page_usage ());
+ string n (ctx.escape (ctx.substitute (ctx.ns_open (qn, false))));
+
+ usage u (ctx.gen_usage);
+ string up (ctx.cli + "::usage_para");
+ string const& ost (ctx.options.ostream_type ());
+
+ {
+ os << up << endl
+ << n << "usage (" << ost << "& os, " << up << " p)"
+ << "{"
+ << "CLI_POTENTIALLY_UNUSED (os);"
+ << endl;
+
+ paragraph p (para_unknown);
+
+ traversal::cli_unit unit;
+ traversal::names unit_names;
+ traversal::namespace_ ns;
+ doc dc (ctx, u == ut_both ? ut_short : u, p);
+ class_usage cl (ctx, u == ut_both ? ut_short : u, p);
+
+ 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;
+
+ unit.dispatch (ctx.unit);
+
+ if (p != para_unknown)
+ os << "p = " << up << (p == para_text ? "::text;" : "::option;")
+ << endl;
+
+ os << "return p;"
+ << "}";
+
+ ctx.verify_id_ref ();
+ }
+
+ // Long version.
+ //
+ if (u == ut_both)
+ {
+ os << up << endl
+ << n << "long_usage (" << ost << "& os, " << up << " p)"
+ << "{"
+ << "CLI_POTENTIALLY_UNUSED (os);"
+ << endl;
+
+ paragraph p (para_unknown);
+
+ traversal::cli_unit unit;
+ traversal::names unit_names;
+ traversal::namespace_ ns;
+ doc dc (ctx, ut_long, p);
+ class_usage cl (ctx, ut_long, p);
+
+ 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;
+
+ unit.dispatch (ctx.unit);
+
+ if (p != para_unknown)
+ os << "p = " << up << (p == para_text ? "::text;" : "::option;")
+ << endl;
+
+ os << "return p;"
+ << "}";
+
+ ctx.verify_id_ref ();
+ }
+
+ ctx.ns_close (qn, false);
+ }
+}
diff --git a/cli/cli/source.hxx b/cli/cli/source.hxx
new file mode 100644
index 0000000..b27cf76
--- /dev/null
+++ b/cli/cli/source.hxx
@@ -0,0 +1,13 @@
+// file : cli/source.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_SOURCE_HXX
+#define CLI_SOURCE_HXX
+
+#include <cli/context.hxx>
+
+void
+generate_source (context&);
+
+#endif // CLI_SOURCE_HXX
diff --git a/cli/cli/token.hxx b/cli/cli/token.hxx
new file mode 100644
index 0000000..7045826
--- /dev/null
+++ b/cli/cli/token.hxx
@@ -0,0 +1,135 @@
+// file : cli/token.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_TOKEN_HXX
+#define CLI_TOKEN_HXX
+
+#include <string>
+#include <cstddef> // std::size_t
+
+class token
+{
+public:
+ enum token_type
+ {
+ t_eos,
+ t_keyword,
+ t_identifier,
+ t_punctuation,
+ t_cxx_path_lit,
+ t_cli_path_lit,
+ t_string_lit,
+ t_char_lit,
+ t_bool_lit,
+ t_int_lit,
+ t_float_lit,
+ t_call_expr, // The so called "call expression", e.g., (2, a).
+ t_template_expr // The so called "template expression", e.g., <foo, 3>.
+ };
+
+ token_type
+ type () const;
+
+ std::size_t
+ line () const;
+
+ std::size_t
+ column () const;
+
+ // Keyword
+ //
+public:
+ enum keyword_type
+ {
+ k_source,
+ k_include,
+ k_namespace,
+ k_class,
+ k_signed,
+ k_unsigned,
+ k_bool,
+ k_char,
+ k_wchar,
+ k_short,
+ k_int,
+ k_long,
+ k_float,
+ k_double,
+ k_invalid
+ };
+
+ // Return the keyword id if type is t_keyword and k_invalid otherwise.
+ //
+ keyword_type
+ keyword () const;
+
+ // Identifier
+ //
+public:
+ std::string const&
+ identifier () const;
+
+ // Punctuation
+ //
+public:
+ enum punctuation_type
+ {
+ p_semi,
+ p_comma,
+ p_colon,
+ p_dcolon,
+ p_lcbrace,
+ p_rcbrace,
+ // p_lparen,
+ // p_rparen,
+ p_eq,
+ p_or,
+ p_invalid
+ };
+
+ // Return the punctuation id if type is t_punctuation and p_invalid
+ // otherwise.
+ //
+ punctuation_type
+ punctuation () const;
+
+ // Literals.
+ //
+public:
+ std::string const&
+ literal () const;
+
+ // Expressions.
+ //
+public:
+ std::string const&
+ expression () const;
+
+ // C-tors.
+ //
+public:
+ // EOS.
+ //
+ token (std::size_t l, std::size_t c);
+
+ token (keyword_type k, std::size_t l, std::size_t c);
+ token (punctuation_type p, std::size_t l, std::size_t c);
+
+ // Identifier, literals, and expressions.
+ //
+ token (token_type t, std::string const& s, std::size_t l, std::size_t c);
+
+private:
+ std::size_t l_;
+ std::size_t c_;
+
+ token_type type_;
+ keyword_type keyword_;
+ punctuation_type punctuation_;
+ std::string str_;
+};
+
+#include <cli/token.ixx>
+
+#endif // CLI_TOKEN_HXX
diff --git a/cli/cli/token.ixx b/cli/cli/token.ixx
new file mode 100644
index 0000000..77ab225
--- /dev/null
+++ b/cli/cli/token.ixx
@@ -0,0 +1,88 @@
+// file : cli/token.ixx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+inline token::token_type token::
+type () const
+{
+ return type_;
+}
+
+inline std::size_t token::
+line () const
+{
+ return l_;
+}
+
+inline std::size_t token::
+column () const
+{
+ return c_;
+}
+
+inline token::keyword_type token::
+keyword () const
+{
+ return type_ == t_keyword ? keyword_ : k_invalid;
+}
+
+inline std::string const& token::
+identifier () const
+{
+ return str_;
+}
+
+inline token::punctuation_type token::
+punctuation () const
+{
+ return type_ == t_punctuation ? punctuation_ : p_invalid;
+}
+
+inline std::string const& token::
+literal () const
+{
+ return str_;
+}
+
+inline std::string const& token::
+expression () const
+{
+ return str_;
+}
+
+inline token::
+token (std::size_t l, std::size_t c)
+ : l_ (l), c_ (c),
+ type_ (t_eos),
+ keyword_ (k_invalid),
+ punctuation_ (p_invalid)
+{
+}
+
+inline token::
+token (keyword_type k, std::size_t l, std::size_t c)
+ : l_ (l), c_ (c),
+ type_ (t_keyword),
+ keyword_ (k),
+ punctuation_ (p_invalid)
+{
+}
+
+inline token::
+token (punctuation_type p, std::size_t l, std::size_t c)
+ : l_ (l), c_ (c),
+ type_ (t_punctuation),
+ keyword_ (k_invalid),
+ punctuation_ (p)
+{
+}
+
+inline token::
+token (token_type t, std::string const& s, std::size_t l, std::size_t c)
+ : l_ (l), c_ (c),
+ type_ (t),
+ keyword_ (k_invalid),
+ punctuation_ (p_invalid),
+ str_ (s)
+{
+}
diff --git a/cli/cli/traversal.hxx b/cli/cli/traversal.hxx
new file mode 100644
index 0000000..1961f7b
--- /dev/null
+++ b/cli/cli/traversal.hxx
@@ -0,0 +1,16 @@
+// file : cli/traversal.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_TRAVERSAL_HXX
+#define CLI_TRAVERSAL_HXX
+
+#include <cli/traversal/class.hxx>
+#include <cli/traversal/doc.hxx>
+#include <cli/traversal/elements.hxx>
+#include <cli/traversal/expression.hxx>
+#include <cli/traversal/namespace.hxx>
+#include <cli/traversal/option.hxx>
+#include <cli/traversal/unit.hxx>
+
+#endif // CLI_TRAVERSAL_HXX
diff --git a/cli/cli/traversal/class.cxx b/cli/cli/traversal/class.cxx
new file mode 100644
index 0000000..b920f1f
--- /dev/null
+++ b/cli/cli/traversal/class.cxx
@@ -0,0 +1,49 @@
+// file : cli/traversal/class.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cli/traversal/class.hxx>
+
+namespace traversal
+{
+ // inherits
+ //
+ void inherits::
+ traverse (type& i)
+ {
+ dispatch (i.base ());
+ }
+
+ // class_
+ //
+ void class_::
+ traverse (type& c)
+ {
+ pre (c);
+ inherits (c);
+ names (c);
+ post (c);
+ }
+
+ void class_::
+ inherits (type& c)
+ {
+ inherits (c, *this);
+ }
+
+ void class_::
+ inherits (type& c, edge_dispatcher& d)
+ {
+ iterate_and_dispatch (c.inherits_begin (), c.inherits_end (), d);
+ }
+
+ void class_::
+ pre (type&)
+ {
+ }
+
+ void class_::
+ post (type&)
+ {
+ }
+}
diff --git a/cli/cli/traversal/class.hxx b/cli/cli/traversal/class.hxx
new file mode 100644
index 0000000..38cbefc
--- /dev/null
+++ b/cli/cli/traversal/class.hxx
@@ -0,0 +1,41 @@
+// file : cli/traversal/class.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_TRAVERSAL_CLASS_HXX
+#define CLI_TRAVERSAL_CLASS_HXX
+
+#include <cli/traversal/elements.hxx>
+#include <cli/semantics/class.hxx>
+
+namespace traversal
+{
+ struct inherits: edge<semantics::inherits>
+ {
+ inherits () {}
+ inherits (node_dispatcher& n) {node_traverser (n);}
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct class_: scope_template<semantics::class_>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ pre (type&);
+
+ virtual void
+ inherits (type&);
+
+ virtual void
+ inherits (type&, edge_dispatcher&);
+
+ virtual void
+ post (type&);
+ };
+}
+
+#endif // CLI_TRAVERSAL_CLASS_HXX
diff --git a/cli/cli/traversal/doc.hxx b/cli/cli/traversal/doc.hxx
new file mode 100644
index 0000000..70a6dfd
--- /dev/null
+++ b/cli/cli/traversal/doc.hxx
@@ -0,0 +1,16 @@
+// file : cli/traversal/doc.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_TRAVERSAL_DOC_HXX
+#define CLI_TRAVERSAL_DOC_HXX
+
+#include <cli/traversal/elements.hxx>
+#include <cli/semantics/doc.hxx>
+
+namespace traversal
+{
+ struct doc: node<semantics::doc> {};
+}
+
+#endif // CLI_TRAVERSAL_DOC_HXX
diff --git a/cli/cli/traversal/elements.cxx b/cli/cli/traversal/elements.cxx
new file mode 100644
index 0000000..f3353f2
--- /dev/null
+++ b/cli/cli/traversal/elements.cxx
@@ -0,0 +1,14 @@
+// file : cli/traversal/elements.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cli/traversal/elements.hxx>
+
+namespace traversal
+{
+ void names::
+ traverse (type& e)
+ {
+ dispatch (e.named ());
+ }
+}
diff --git a/cli/cli/traversal/elements.hxx b/cli/cli/traversal/elements.hxx
new file mode 100644
index 0000000..a2ada23
--- /dev/null
+++ b/cli/cli/traversal/elements.hxx
@@ -0,0 +1,142 @@
+// file : cli/traversal/elements.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_TRAVERSAL_ELEMENTS_HXX
+#define CLI_TRAVERSAL_ELEMENTS_HXX
+
+#include <cutl/compiler/traversal.hxx>
+
+#include <cli/semantics/elements.hxx>
+
+namespace traversal
+{
+ using namespace cutl;
+
+ //
+ //
+ typedef compiler::dispatcher<semantics::node> node_dispatcher;
+ typedef compiler::dispatcher<semantics::edge> edge_dispatcher;
+
+ //
+ //
+ struct node_base: node_dispatcher, edge_dispatcher
+ {
+ void
+ edge_traverser (edge_dispatcher& d)
+ {
+ edge_dispatcher::traverser (d);
+ }
+
+ edge_dispatcher&
+ edge_traverser ()
+ {
+ return *this;
+ }
+
+ using node_dispatcher::dispatch;
+ using edge_dispatcher::dispatch;
+
+ using edge_dispatcher::iterate_and_dispatch;
+ };
+
+ struct edge_base: edge_dispatcher, node_dispatcher
+ {
+ void
+ node_traverser (node_dispatcher& d)
+ {
+ node_dispatcher::traverser (d);
+ }
+
+ node_dispatcher&
+ node_traverser ()
+ {
+ return *this;
+ }
+
+ using edge_dispatcher::dispatch;
+ using node_dispatcher::dispatch;
+
+ using node_dispatcher::iterate_and_dispatch;
+ };
+
+ inline edge_base&
+ operator>> (node_base& n, edge_base& e)
+ {
+ n.edge_traverser (e);
+ return e;
+ }
+
+ inline node_base&
+ operator>> (edge_base& e, node_base& n)
+ {
+ e.node_traverser (n);
+ return n;
+ }
+
+ //
+ //
+ template <typename X>
+ struct node: compiler::traverser_impl<X, semantics::node>,
+ virtual node_base
+ {
+ };
+
+ template <typename X>
+ struct edge: compiler::traverser_impl<X, semantics::edge>,
+ virtual edge_base
+ {
+ };
+
+ // Edges
+ //
+
+ struct names: edge<semantics::names>
+ {
+ names ()
+ {
+ }
+
+ names (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ // Nodes
+ //
+
+ struct nameable: node<semantics::nameable> {};
+
+ template <typename T>
+ struct scope_template: node<T>
+ {
+ public:
+ virtual void
+ traverse (T& s)
+ {
+ names (s);
+ }
+
+ virtual void
+ names (T& s)
+ {
+ names (s, *this);
+ }
+
+ virtual void
+ names (T& s, edge_dispatcher& d)
+ {
+ this->iterate_and_dispatch (s.names_begin (), s.names_end (), d);
+ }
+ };
+
+ typedef scope_template<semantics::scope> scope;
+
+ struct type: node<semantics::type> {};
+}
+
+#endif // CLI_TRAVERSAL_ELEMENTS_HXX
diff --git a/cli/cli/traversal/expression.hxx b/cli/cli/traversal/expression.hxx
new file mode 100644
index 0000000..0888455
--- /dev/null
+++ b/cli/cli/traversal/expression.hxx
@@ -0,0 +1,16 @@
+// file : cli/traversal/expression.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_TRAVERSAL_EXPRESSION_HXX
+#define CLI_TRAVERSAL_EXPRESSION_HXX
+
+#include <cli/traversal/elements.hxx>
+#include <cli/semantics/expression.hxx>
+
+namespace traversal
+{
+ struct expression: node<semantics::expression> {};
+}
+
+#endif // CLI_TRAVERSAL_EXPRESSION_HXX
diff --git a/cli/cli/traversal/namespace.cxx b/cli/cli/traversal/namespace.cxx
new file mode 100644
index 0000000..c938f77
--- /dev/null
+++ b/cli/cli/traversal/namespace.cxx
@@ -0,0 +1,26 @@
+// file : cli/traversal/namespace.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cli/traversal/namespace.hxx>
+
+namespace traversal
+{
+ void namespace_::
+ traverse (type& n)
+ {
+ pre (n);
+ names (n);
+ post (n);
+ }
+
+ void namespace_::
+ pre (type&)
+ {
+ }
+
+ void namespace_::
+ post (type&)
+ {
+ }
+}
diff --git a/cli/cli/traversal/namespace.hxx b/cli/cli/traversal/namespace.hxx
new file mode 100644
index 0000000..5709f2a
--- /dev/null
+++ b/cli/cli/traversal/namespace.hxx
@@ -0,0 +1,26 @@
+// file : cli/traversal/namespace.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_TRAVERSAL_NAMESPACE_HXX
+#define CLI_TRAVERSAL_NAMESPACE_HXX
+
+#include <cli/traversal/elements.hxx>
+#include <cli/semantics/namespace.hxx>
+
+namespace traversal
+{
+ struct namespace_: scope_template<semantics::namespace_>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ pre (type&);
+
+ virtual void
+ post (type&);
+ };
+}
+
+#endif // CLI_TRAVERSAL_NAMESPACE_HXX
diff --git a/cli/cli/traversal/option.cxx b/cli/cli/traversal/option.cxx
new file mode 100644
index 0000000..ff5dcce
--- /dev/null
+++ b/cli/cli/traversal/option.cxx
@@ -0,0 +1,59 @@
+// file : cli/traversal/option.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cli/traversal/option.hxx>
+#include <cli/traversal/expression.hxx>
+
+namespace traversal
+{
+ // belongs
+ //
+ void belongs::
+ traverse (type& e)
+ {
+ dispatch (e.type ());
+ }
+
+ // initialized
+ //
+ void initialized::
+ traverse (type& e)
+ {
+ dispatch (e.expression ());
+ }
+
+ // option
+ //
+ void option::
+ traverse (type& o)
+ {
+ pre (o);
+ belongs (o);
+ if (o.initialized_p ())
+ initialized (o);
+ post (o);
+ }
+
+ void option::
+ pre (type&)
+ {
+ }
+
+ void option::
+ belongs (type& o)
+ {
+ belongs (o, edge_traverser ());
+ }
+
+ void option::
+ initialized (type& o)
+ {
+ initialized (o, edge_traverser ());
+ }
+
+ void option::
+ post (type&)
+ {
+ }
+}
diff --git a/cli/cli/traversal/option.hxx b/cli/cli/traversal/option.hxx
new file mode 100644
index 0000000..e11fa21
--- /dev/null
+++ b/cli/cli/traversal/option.hxx
@@ -0,0 +1,74 @@
+// file : cli/traversal/option.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_TRAVERSAL_OPTION_HXX
+#define CLI_TRAVERSAL_OPTION_HXX
+
+#include <cli/traversal/elements.hxx>
+#include <cli/semantics/option.hxx>
+
+namespace traversal
+{
+ struct belongs: edge<semantics::belongs>
+ {
+ belongs ()
+ {
+ }
+
+ belongs (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct initialized: edge<semantics::initialized>
+ {
+ initialized ()
+ {
+ }
+
+ initialized (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct option: node<semantics::option>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ pre (type&);
+
+ virtual void
+ belongs (type&);
+
+ virtual void
+ initialized (type&);
+
+ virtual void
+ post (type&);
+
+ void
+ belongs (type& o, edge_dispatcher& d)
+ {
+ d.dispatch (o.belongs ());
+ }
+
+ void
+ initialized (type& o, edge_dispatcher& d)
+ {
+ d.dispatch (o.initialized ());
+ }
+ };
+}
+
+#endif // CLI_TRAVERSAL_OPTION_HXX
diff --git a/cli/cli/traversal/unit.cxx b/cli/cli/traversal/unit.cxx
new file mode 100644
index 0000000..cf10d1d
--- /dev/null
+++ b/cli/cli/traversal/unit.cxx
@@ -0,0 +1,46 @@
+// file : cli/traversal/unit.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <cli/traversal/unit.hxx>
+
+namespace traversal
+{
+ // cxx_includes
+ //
+ void cxx_includes::
+ traverse (type& e)
+ {
+ dispatch (e.includee ());
+ }
+
+ // cli_includes
+ //
+ void cli_includes::
+ traverse (type& e)
+ {
+ dispatch (e.includee ());
+ }
+
+ // cli_unit
+ //
+ void cli_unit::
+ traverse (type& u)
+ {
+ pre (u);
+ iterate_and_dispatch (
+ u.includes_begin (), u.includes_end (), edge_traverser ());
+ names (u);
+ post (u);
+ }
+
+ void cli_unit::
+ pre (type&)
+ {
+ }
+
+ void cli_unit::
+ post (type&)
+ {
+ }
+}
diff --git a/cli/cli/traversal/unit.hxx b/cli/cli/traversal/unit.hxx
new file mode 100644
index 0000000..eab42aa
--- /dev/null
+++ b/cli/cli/traversal/unit.hxx
@@ -0,0 +1,58 @@
+// file : cli/traversal/unit.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_TRAVERSAL_UNIT_HXX
+#define CLI_TRAVERSAL_UNIT_HXX
+
+#include <cli/traversal/elements.hxx>
+#include <cli/semantics/unit.hxx>
+
+namespace traversal
+{
+ struct cxx_includes: edge<semantics::cxx_includes>
+ {
+ cxx_includes ()
+ {
+ }
+
+ cxx_includes (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct cli_includes: edge<semantics::cli_includes>
+ {
+ cli_includes ()
+ {
+ }
+
+ cli_includes (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct cxx_unit: node<semantics::cxx_unit> {};
+
+ struct cli_unit: scope_template<semantics::cli_unit>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ pre (type&);
+
+ virtual void
+ post (type&);
+ };
+}
+
+#endif // CLI_TRAVERSAL_UNIT_HXX
diff --git a/cli/cli/txt.cxx b/cli/cli/txt.cxx
new file mode 100644
index 0000000..16de45a
--- /dev/null
+++ b/cli/cli/txt.cxx
@@ -0,0 +1,301 @@
+// file : cli/txt.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#include <vector>
+#include <cstring> // strlen()
+#include <iostream>
+
+#include <cli/txt.hxx>
+
+using namespace std;
+
+size_t
+txt_size (const string& s, size_t p, size_t n)
+{
+ size_t r (0);
+
+ n = n == string::npos ? s.size () : n + p;
+
+ // The start position (p) might be pointing half-way into the
+ // escape sequence. So we always have to scan from the start.
+ //
+ for (size_t i (0), m (s.size ()); i < n; ++i)
+ {
+ if (s[i] == '\033') // ANSI escape: "\033[Nm"
+ {
+ i += 3;
+ assert (i < m && s[i] == 'm');
+ continue;
+ }
+
+ if (i >= p)
+ ++r;
+ }
+
+ return r;
+}
+
+void
+txt_wrap_lines (ostream& os,
+ string& d,
+ size_t indent,
+ size_t first,
+ const char* line_start,
+ const char* line_end,
+ const char* line_blank,
+ string (*escape) (string const&))
+{
+ assert (!d.empty ());
+
+ os << string (indent - first, ' ');
+
+ // Count the number of leading spaces at the beginning of each line.
+ // Then use it as an extra indentation if we are breaking this line.
+ // This makes multi-line definition lists look decent. Note that this
+ // doesn't work for ordered/unordered lists because they start on the
+ // same line as number/bullet. However, there is hack to make it work:
+ // break the first line at the 80 characters boundary explicitly with
+ // \n.
+ //
+ size_t wc (0), wi (0); // Count and count-based indentation.
+ bool cws (true); // Count flag.
+
+ // @@ TODO: while this should support notes inside lists, lists inside
+ // notes will require splitting indentation into before/after '|'.
+ //
+ const char* ne (""); // Note.
+ const char* np (""); // Note prefix.
+
+ size_t b (0), e (0), i (0);
+ for (size_t n (d.size ()); i < n; ++i)
+ {
+ char& c (d[i]);
+
+ // Translate note character.
+ //
+ if (c == 0x07)
+ {
+ c = '|';
+ ne = "| ";
+ }
+
+ if (c == ' ' || c == '\n')
+ e = i;
+
+ if (c == '\n' || txt_size (d, b, i - b) == 79 - indent - wi - strlen (np))
+ {
+ if (b != 0) // Not a first line.
+ os << endl
+ << line_start << string (indent + wi, ' ') << np;
+
+ string s (d, b, (e != b ? e : i) - b);
+
+ if (escape != 0)
+ s = escape (s);
+
+ os << s << line_end;
+
+ // Handle consecutive newlines (e.g., pre, paragraph separator).
+ //
+ if (c == '\n')
+ {
+ for (; i + 1 < n && d[i + 1] == '\n'; e = ++i)
+ os << endl
+ << line_blank;
+ }
+
+ b = e = (e != b ? e : i) + 1;
+
+ // Start indenting/noting beginning with the next break.
+ //
+ wi = wc;
+ np = ne;
+ }
+
+ if (c == '\n')
+ {
+ // Reset counters, clear notes, and start counting.
+ //
+ wc = wi = 0;
+ ne = np = "";
+ cws = true;
+ }
+ else if (cws)
+ {
+ if (c == ' ')
+ ++wc;
+ else
+ cws = false;
+ }
+ }
+
+ // Flush the last line.
+ //
+ if (b != i)
+ {
+ if (b != 0)
+ os << endl
+ << line_start << string (indent + wi, ' ') << np;
+
+ string s (d, b, i - b);
+
+ if (escape != 0)
+ s = escape (s);
+
+ os << s << line_end;
+ }
+}
+
+namespace
+{
+ 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<string> arg_set;
+ if (n > 1 && options.ansi_color ())
+ translate_arg (ds[0], arg_set);
+
+ string s (format (ds.scope (), translate (d, arg_set), true));
+
+ if (s.empty ())
+ return;
+
+ txt_wrap_lines (os, s);
+ os << endl; //@@ ??
+ }
+
+ private:
+ class_doc_type cd_;
+ };
+
+ struct option: traversal::option, context
+ {
+ option (context& c, class_doc_type) : 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;
+
+ //@@ TODO
+ }
+
+ 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_txt (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<string>::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<semantics::class_> ("", n))
+ cl.traverse (*c);
+ else
+ {
+ cerr << "error: class '" << *i << "' not found" << endl;
+ throw generation_failed ();
+ }
+ }
+ }
+}
diff --git a/cli/cli/txt.hxx b/cli/cli/txt.hxx
new file mode 100644
index 0000000..cde31c9
--- /dev/null
+++ b/cli/cli/txt.hxx
@@ -0,0 +1,46 @@
+// file : cli/txt.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_TXT_HXX
+#define CLI_TXT_HXX
+
+#include <iosfwd> // ostream
+#include <string>
+#include <cstddef> // size_t
+
+#include <cli/context.hxx>
+
+void
+generate_txt (context&);
+
+// Return the number of "text characters", ignoring any escape sequences
+// (e.g., ANSI color).
+//
+std::size_t
+txt_size (const std::string&,
+ std::size_t p = 0,
+ std::size_t n = std::string::npos);
+
+// This function assumes that the initial opening part has already been
+// written with the 'first' argument being the number of characters already
+// written in the first line (e.g., an option name). The 'indent' argument
+// specified how many spaces to indent each line. The 'escape' argument is
+// the optional escape function (e.g., for the string literal output). The
+// line_{start, end, blank} arguments specify optional extra text for the
+// start/end of the non-empty line as well as for the blank (empty) line.
+//
+// Note that the funtion modifies the passed string in order to translate
+// notes.
+//
+void
+txt_wrap_lines (std::ostream& os,
+ std::string&,
+ std::size_t indent = 0,
+ std::size_t first = 0,
+ const char* line_start = "",
+ const char* line_end = "",
+ const char* line_blank = "",
+ std::string (*escape) (std::string const&) = 0);
+
+#endif // CLI_TXT_HXX
diff --git a/cli/cli/version.hxx.in b/cli/cli/version.hxx.in
new file mode 100644
index 0000000..90b649a
--- /dev/null
+++ b/cli/cli/version.hxx.in
@@ -0,0 +1,45 @@
+// file : cli/version.hxx.in
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// license : MIT; see accompanying LICENSE file
+
+#ifndef CLI_VERSION // Note: using the version macro itself.
+
+// The numeric version format is AAAAABBBBBCCCCCDDDE where:
+//
+// AAAAA - major version number
+// BBBBB - minor version number
+// CCCCC - bugfix version number
+// DDD - alpha / beta (DDD + 500) version number
+// E - final (0) / snapshot (1)
+//
+// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example:
+//
+// Version AAAAABBBBBCCCCCDDDE
+//
+// 0.1.0 0000000001000000000
+// 0.1.2 0000000001000020000
+// 1.2.3 0000100002000030000
+// 2.2.0-a.1 0000200001999990010
+// 3.0.0-b.2 0000299999999995020
+// 2.2.0-a.1.z 0000200001999990011
+//
+#define CLI_VERSION $cli.version.project_number$ULL
+#define CLI_VERSION_STR "$cli.version.project$"
+#define CLI_VERSION_ID "$cli.version.project_id$"
+
+#define CLI_VERSION_MAJOR $cli.version.major$
+#define CLI_VERSION_MINOR $cli.version.minor$
+#define CLI_VERSION_PATCH $cli.version.patch$
+
+#define CLI_PRE_RELEASE $cli.version.pre_release$
+
+#define CLI_SNAPSHOT $cli.version.snapshot_sn$ULL
+#define CLI_SNAPSHOT_ID "$cli.version.snapshot_id$"
+
+#define CLI_CHECKSUM "$cli.version$"
+
+#include <cutl/version.hxx>
+
+$libcutl.check(LIBCUTL_VERSION, LIBCUTL_SNAPSHOT)$
+
+#endif // CLI_VERSION