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