summaryrefslogtreecommitdiff
path: root/cli/source.cxx
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 /cli/source.cxx
parent907b5fed58d53bbb5e25c590df97f01a0ac93733 (diff)
Implement usage generation
Also migrate the CLI compiler usage handling to the auto-generated version.
Diffstat (limited to 'cli/source.cxx')
-rw-r--r--cli/source.cxx293
1 files changed, 293 insertions, 0 deletions
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");