From 369470005607b9501a769be0ae2a4c79c90bad84 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sun, 8 Nov 2009 15:35:19 +0200 Subject: Implement usage generation Also migrate the CLI compiler usage handling to the auto-generated version. --- cli/source.cxx | 293 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) (limited to 'cli/source.cxx') diff --git a/cli/source.cxx b/cli/source.cxx index 7a23c30..2f835fa 100644 --- a/cli/source.cxx +++ b/cli/source.cxx @@ -3,8 +3,12 @@ // copyright : Copyright (c) 2009 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file +#include + #include "source.hxx" +using std::cerr; + namespace { // @@ -85,6 +89,251 @@ namespace // // + struct option_length: traversal::option, context + { + option_length (context& c, size_t& l, type*& o) + : context (c), length_ (l), option_ (o) + { + } + + virtual void + traverse (type& o) + { + using semantics::names; + + size_t l (0); + names& n (o.named ()); + + for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i) + { + if (l != 0) + l++; // '|' seperator. + + l += i->size (); + } + + string type (o.type ().name ()); + + if (type != "bool") + { + l++; // ' ' seperator + + type::doc_list const& d (o.doc ()); + + if (d.size () > 0) + l += d[0].size (); + else + l += 5; // + } + + if (l > length_) + { + length_ = l; + option_ = &o; + } + } + + private: + size_t& length_; + type*& option_; + }; + + // + // + struct option_usage: traversal::option, context + { + option_usage (context& c, size_t l) : context (c), length_ (l) {} + + virtual void + traverse (type& o) + { + using semantics::names; + + size_t l (0); + names& n (o.named ()); + + os << "os << \""; + + for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i) + { + if (l != 0) + { + os << '|'; + l++; + } + + os << *i; + l += i->size (); + } + + type::doc_list const& doc (o.doc ()); + string type (o.type ().name ()); + + if (type != "bool") + { + os << ' '; + l++; + + if (doc.size () > 0) + { + os << escape_str (doc[0]); + l += doc[0].size (); + } + else + { + os << ""; + l += 5; + } + } + + // If we have both the long and the short descriptions, use + // the short one. Otherwise, use the first sentence from the + // long one. + // + string d; + + if (type == "bool") + { + if (doc.size () > 1) + d = doc[0]; + else if (doc.size () > 0) + d = frist_sentence (doc[0]); + } + else + { + if (doc.size () > 2) + d = doc[1]; + else if (doc.size () > 1) + d = frist_sentence (doc[1]); + } + + // Format the documentation string. + // + d = format (d, ot_plain); + + if (!d.empty ()) + { + pad (length_ - l); + + size_t b (0), e (0), i (0); + + for (size_t n (d.size ()); i < n; ++i) + { + if (d[i] == ' ' || d[i] == '\n') + e = i; + + if (d[i] == '\n' || i - b == 79 - length_) + { + if (b != 0) + { + os << endl + << " << \""; + pad (); + } + + string s (d, b, (e != b ? e : i) - b); + os << escape_str (s) << "\" << ::std::endl"; + + if (d[i] == '\n') + os << endl + << " << ::std::endl"; + + b = e = (e != b ? e : i) + 1; + } + } + + // Flush the last line. + // + if (b != i) + { + if (b != 0) + { + os << endl + << " << \""; + pad (); + } + + string s (d, b, i - b); + os << escape_str (s) << "\" << ::std::endl"; + } + } + else + os << "\" << std::endl"; + + os << ";" + << endl; + } + + private: + void + pad (size_t n) + { + for (; n > 0; --n) + os << ' '; + + os << ' '; // Space between arg and description. + } + + void + pad () + { + pad (length_); + } + + string + frist_sentence (string const& s) + { + size_t p (s.find ('.')); + + // Add some heuristics here: check that there is a space + // (or end of text) after the period. + // + while (p != string::npos && + p + 1 <= s.size () && + s[p + 1] != ' ' && + s[p + 1] != '\n') + p = s.find ('.', p + 1); + + return p == string::npos ? s : string (s, 0, p + 1); + } + + string + escape_str (string const& s) + { + string r; + r.reserve (s.size ()); + + for (size_t i (0), n (s.size ()); i < n; ++i) + { + switch (s[i]) + { + case '\\': + { + r += "\\\\"; + break; + } + case '"': + { + r += "\\\""; + break; + } + default: + { + r += s[i]; + break; + } + } + } + + return r; + } + + private: + size_t length_; + }; + + // + // struct class_: traversal::class_, context { class_ (context& c) @@ -170,6 +419,50 @@ namespace << "end = _parse (start, argc, argv, opt, arg);" << "}"; + // usage + // + if (usage) + { + os << "void " << name << "::" << endl + << "print_usage (::std::ostream& os)" + << "{"; + + // Calculate option length. + // + size_t len (0); + { + semantics::option* o (0); + option_length t (*this, len, o); + traversal::names n (t); + names (c, n); + + size_t max (options.option_length ()); + + if (max != 0) + { + if (len > max) + { + cerr << o->file () << ":" << o->line () << ":" << o->column () + << " error: option length " << len << " is greater than " + << max << " specified with --option-length" << endl; + throw generation_failed (); + } + + len = max; + } + } + + // Print option usage. + // + { + option_usage t (*this, len); + traversal::names n (t); + names (c, n); + } + + os << "}"; + } + // _parse() // string map ("_cli_" + name + "_map"); -- cgit v1.1