From 577a38358b295379511ea8bb130ef1dcb7157c0f Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sun, 8 Nov 2009 21:28:46 +0200 Subject: Implement HTML pages generation --- cli/context.cxx | 155 ++++++++++++++++++++++++++++++++ cli/context.hxx | 13 ++- cli/generator.cxx | 259 +++++++++++++++++++++++++++++++++--------------------- cli/html.cxx | 195 ++++++++++++++++++++++++++++++++++++++++ cli/html.hxx | 14 +++ cli/makefile | 1 + cli/options.cli | 27 ++++++ cli/options.cxx | 41 +++++++++ cli/options.hxx | 20 +++++ cli/options.ixx | 30 +++++++ 10 files changed, 652 insertions(+), 103 deletions(-) create mode 100644 cli/html.cxx create mode 100644 cli/html.hxx (limited to 'cli') diff --git a/cli/context.cxx b/cli/context.cxx index 849b7cb..ccd3c8b 100644 --- a/cli/context.cxx +++ b/cli/context.cxx @@ -199,11 +199,103 @@ escape (string const& name) const } string context:: +translate_arg (string const& s, std::set& set) +{ + string r; + r.reserve (s.size ()); + set.clear (); + + size_t p (string::npos); + + for (size_t i (0), n (s.size ()); i < n; ++i) + { + if (p == string::npos && s[i] == '<') + { + p = i; + r += "\\i{"; + continue; + } + + if (p != string::npos && s[i] == '>') + { + set.insert (string (s, p + 1, i - p - 1)); + r += '}'; + p = string::npos; + continue; + } + + if (p != string::npos && s[i] == '}' && s[i - 1] != '\\') + { + r += "\\}"; + continue; + } + + r += s[i]; + } + + return r; +} + +string context:: +translate (string const& s, std::set const& set) +{ + string r; + r.reserve (s.size ()); + + size_t p (string::npos); + + for (size_t i (0), n (s.size ()); i < n; ++i) + { + if (p == string::npos && s[i] == '<') + { + p = i; + continue; + } + + if (p != string::npos) + { + if (s[i] == '>') + { + string a (s, p + 1, i - p - 1); + + if (set.find (a) != set.end ()) + { + r += "\\i{"; + + 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 += s[i]; + } + + return r; +} + +string context:: format (string const& s, output_type ot) { string r; r.reserve (s.size ()); + bool para (false); bool escape (false); std::stack blocks; // Bit 0: code; 1: italic; 2: bold. @@ -211,6 +303,8 @@ format (string const& s, output_type ot) { if (escape) { + bool block (false); + switch (s[i]) { case '\\': @@ -263,6 +357,7 @@ format (string const& s, output_type ot) { i = j; blocks.push (b); + block = true; break; } @@ -304,6 +399,7 @@ format (string const& s, output_type ot) { i = j; blocks.push (b); + block = true; break; } @@ -345,6 +441,7 @@ format (string const& s, output_type ot) { i = j; blocks.push (b); + block = true; break; } @@ -358,6 +455,30 @@ format (string const& s, output_type ot) } } + if (block) + { + unsigned char b (blocks.top ()); + + switch (ot) + { + case ot_html: + { + if (b & 1) + r += ""; + + if (b & 2) + r += ""; + + if (b & 4) + r += ""; + + break; + } + default: + break; + } + } + escape = false; } else if (s[i] == '\\') @@ -373,16 +494,50 @@ format (string const& s, output_type ot) r += '\n'; break; } + case ot_html: + { + if (para) + r += "

"; + else + para = true; + + r += "\n

"; + break; + } } } else if (!blocks.empty () && s[i] == '}') { + unsigned char b (blocks.top ()); + + switch (ot) + { + case ot_html: + { + if (b & 1) + r += ""; + + if (b & 2) + r += ""; + + if (b & 4) + r += ""; + + break; + } + default: + break; + } + blocks.pop (); } else r += s[i]; } + if (para) + r += "

"; + return r; } diff --git a/cli/context.hxx b/cli/context.hxx index 194c008..f6d53c3 100644 --- a/cli/context.hxx +++ b/cli/context.hxx @@ -63,14 +63,23 @@ public: string escape (string const&) const; - // Format the documentation string. + // Translate and format the documentation string. Translate converts + // the -style constructs to \i{arg}. Format converts the string + // to the output format. // enum output_type { - ot_plain + ot_plain, + ot_html }; static string + translate_arg (string const&, std::set&); + + static string + translate (string const&, std::set const&); + + static string format (string const&, output_type); public: diff --git a/cli/generator.cxx b/cli/generator.cxx index e0e5d4a..6043825 100644 --- a/cli/generator.cxx +++ b/cli/generator.cxx @@ -21,6 +21,8 @@ #include "runtime-inline.hxx" #include "runtime-source.hxx" +#include "html.hxx" + #include "context.hxx" #include "generator.hxx" #include "name-processor.hxx" @@ -73,156 +75,211 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p) { try { - bool inl (!ops.suppress_inline ()); - path file (p.leaf ()); string base (file.base ().string ()); - string hxx_name (base + ops.hxx_suffix ()); - string ixx_name (base + ops.ixx_suffix ()); - string cxx_name (base + ops.cxx_suffix ()); + bool gen_cxx (ops.generate_cxx ()); + bool gen_man (ops.generate_man ()); + bool gen_html (ops.generate_html ()); - path hxx_path (hxx_name); - path ixx_path (ixx_name); - path cxx_path (cxx_name); + if (!gen_cxx && !gen_man && !gen_html) + gen_cxx = true; - if (!ops.output_dir ().empty ()) + if (ops.stdout ()) { - path dir (ops.output_dir ()); + if (gen_cxx) + { + cerr << "error: --stdout cannot be used with C++ output" << endl; + throw failed (); + } - hxx_path = dir / hxx_path; - ixx_path = dir / ixx_path; - cxx_path = dir / cxx_path; + if (gen_man && gen_html) + { + cerr << "error: --stdout cannot be used with man and html output" + << endl; + throw failed (); + } } - // Process names. - // + fs::auto_removes auto_rm; + + if (gen_cxx) { - context ctx (cerr, unit, ops); - process_names (ctx); - } + bool inl (!ops.suppress_inline ()); - fs::auto_removes auto_rm; + string hxx_name (base + ops.hxx_suffix ()); + string ixx_name (base + ops.ixx_suffix ()); + string cxx_name (base + ops.cxx_suffix ()); - // - // - ofstream hxx (hxx_path.string ().c_str ()); + path hxx_path (hxx_name); + path ixx_path (ixx_name); + path cxx_path (cxx_name); - if (!hxx.is_open ()) - { - cerr << "error: unable to open '" << hxx_path << "' in write mode" - << endl; - throw failed (); - } + if (!ops.output_dir ().empty ()) + { + path dir (ops.output_dir ()); - auto_rm.add (hxx_path); + hxx_path = dir / hxx_path; + ixx_path = dir / ixx_path; + cxx_path = dir / cxx_path; + } - // - // - ofstream ixx; + // Process names. + // + { + context ctx (cerr, unit, ops); + process_names (ctx); + } - if (inl) - { - ixx.open (ixx_path.string ().c_str (), ios_base::out); + // + // + ofstream hxx (hxx_path.string ().c_str ()); - if (!ixx.is_open ()) + if (!hxx.is_open ()) { - cerr << "error: unable to open '" << ixx_path << "' in write mode" + cerr << "error: unable to open '" << hxx_path << "' in write mode" << endl; throw failed (); } - auto_rm.add (ixx_path); - } + auto_rm.add (hxx_path); - // - // - ofstream cxx (cxx_path.string ().c_str ()); + // + // + ofstream ixx; - if (!cxx.is_open ()) - { - cerr << "error: unable to open '" << cxx_path << "' in write mode" - << endl; - throw failed (); - } + if (inl) + { + ixx.open (ixx_path.string ().c_str (), ios_base::out); - auto_rm.add (cxx_path); + if (!ixx.is_open ()) + { + cerr << "error: unable to open '" << ixx_path << "' in write mode" + << endl; + throw failed (); + } - // Print headers. - // - hxx << header; - if (inl) - ixx << header; - cxx << header; + auto_rm.add (ixx_path); + } - typedef compiler::ostream_filter cxx_filter; + // + // + ofstream cxx (cxx_path.string ().c_str ()); - // Include settings. - // - bool br (ops.include_with_brackets ()); - string ip (ops.include_prefix ()); - string gp (ops.guard_prefix ()); + if (!cxx.is_open ()) + { + cerr << "error: unable to open '" << cxx_path << "' in write mode" + << endl; + throw failed (); + } - if (!ip.empty () && ip[ip.size () - 1] != '/') - ip.append ("/"); + auto_rm.add (cxx_path); - if (!gp.empty () && gp[gp.size () - 1] != '_') - gp.append ("_"); + // Print headers. + // + hxx << header; + if (inl) + ixx << header; + cxx << header; - // HXX - // - { - cxx_filter filt (hxx); - context ctx (hxx, unit, ops); + typedef + compiler::ostream_filter + cxx_filter; + + // Include settings. + // + bool br (ops.include_with_brackets ()); + string ip (ops.include_prefix ()); + string gp (ops.guard_prefix ()); + + if (!ip.empty () && ip[ip.size () - 1] != '/') + ip.append ("/"); - string guard (make_guard (gp + hxx_name, ctx)); + if (!gp.empty () && gp[gp.size () - 1] != '_') + gp.append ("_"); - hxx << "#ifndef " << guard << endl - << "#define " << guard << endl - << endl; + // HXX + // + { + cxx_filter filt (hxx); + context ctx (hxx, unit, ops); + + string guard (make_guard (gp + hxx_name, ctx)); + + hxx << "#ifndef " << guard << endl + << "#define " << guard << endl + << endl; + + generate_runtime_header (ctx); + generate_header (ctx); - generate_runtime_header (ctx); - generate_header (ctx); + if (inl) + { + hxx << "#include " << (br ? '<' : '"') << ip << ixx_name << + (br ? '>' : '"') << endl + << endl; + } + hxx << "#endif // " << guard << endl; + } + + // IXX + // if (inl) { - hxx << "#include " << (br ? '<' : '"') << ip << ixx_name << + cxx_filter filt (ixx); + context ctx (ixx, unit, ops); + generate_runtime_inline (ctx); + generate_inline (ctx); + } + + // CXX + // + { + cxx_filter filt (cxx); + context ctx (cxx, unit, ops); + + cxx << "#include " << (br ? '<' : '"') << ip << hxx_name << (br ? '>' : '"') << endl << endl; - } - hxx << "#endif // " << guard << endl; - } + if (!inl) + generate_runtime_inline (ctx); - // IXX - // - if (inl) - { - cxx_filter filt (ixx); - context ctx (ixx, unit, ops); - generate_runtime_inline (ctx); - generate_inline (ctx); + generate_runtime_source (ctx); + + if (!inl) + generate_inline (ctx); + + generate_source (ctx); + } } - // CXX - // + if (gen_html) { - cxx_filter filt (cxx); - context ctx (cxx, unit, ops); + ofstream html; + + if (!ops.stdout ()) + { + path html_path (base + ops.html_suffix ()); - cxx << "#include " << (br ? '<' : '"') << ip << hxx_name << - (br ? '>' : '"') << endl - << endl; + if (!ops.output_dir ().empty ()) + html_path = path (ops.output_dir ()) / html_path; - if (!inl) - generate_runtime_inline (ctx); + html.open (html_path.string ().c_str ()); - generate_runtime_source (ctx); + if (!html.is_open ()) + { + cerr << "error: unable to open '" << html_path << "' in write mode" + << endl; + throw failed (); + } - if (!inl) - generate_inline (ctx); + auto_rm.add (html_path); + } - generate_source (ctx); + context ctx (ops.stdout () ? cout : html, unit, ops); + generate_html (ctx); } auto_rm.cancel (); diff --git a/cli/html.cxx b/cli/html.cxx new file mode 100644 index 0000000..b147703 --- /dev/null +++ b/cli/html.cxx @@ -0,0 +1,195 @@ +// file : cli/html.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include "html.hxx" + +namespace +{ + struct option: traversal::option, context + { + option (context& c) : context (c) {} + + virtual void + traverse (type& o) + { + using semantics::names; + + names& n (o.named ()); + + os << "
"; + + for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i) + { + if (i != n.name_begin ()) + os << "|"; + + os << escape_html (*i); + } + + os << ""; + + type::doc_list const& doc (o.doc ()); + string type (o.type ().name ()); + + std::set arg_set; + + if (type != "bool") + { + string s ( + translate_arg ( + doc.size () > 0 ? doc[0] : string (""), arg_set)); + + os << ' ' << format (escape_html (s), ot_html); + } + + os << "
" << endl; + + string d; + + // If we have both the long and the short descriptions, use + // the long one. + // + if (type == "bool") + { + if (doc.size () > 1) + d = doc[1]; + else if (doc.size () > 0) + d = doc[0]; + } + else + { + if (doc.size () > 2) + d = doc[2]; + else if (doc.size () > 1) + d = doc[1]; + } + + // Format the documentation string. + // + d = format (escape_html (translate (d, arg_set)), ot_html); + + os << "
"; + + if (!d.empty ()) + { + 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 >= 76 && e != b)) + { + if (b != 0) + { + os << endl + << " "; + } + + os << string (d, b, e - b); + + if (d[i] == '\n') + os << endl; + + b = e = e + 1; + } + } + + // Flush the last line. + // + if (b != i) + { + if (b != 0) + { + os << endl + << " "; + } + + os << string (d, b, i - b); + } + } + + os << "
" << endl + << endl; + } + + private: + string + escape_html (string const& s) + { + string r; + r.reserve (s.size ()); + + for (size_t i (0), n (s.size ()); i < n; ++i) + { + switch (s[i]) + { + case '<': + { + r += "<"; + break; + } + case '&': + { + r += "&"; + break; + } + default: + { + r += s[i]; + break; + } + } + } + + return r; + } + }; + + // + // + struct class_: traversal::class_, context + { + class_ (context& c) + : context (c), option_ (c) + { + names_option_ >> option_; + } + + virtual void + traverse (type& c) + { + os << "
" << endl; + + names (c, names_option_); + + os << "
" << endl; + } + + private: + option option_; + traversal::names names_option_; + }; +} + +void +generate_html (context& ctx) +{ + 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); +} diff --git a/cli/html.hxx b/cli/html.hxx new file mode 100644 index 0000000..5794dc7 --- /dev/null +++ b/cli/html.hxx @@ -0,0 +1,14 @@ +// file : cli/html.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifndef CLI_HTML_HXX +#define CLI_HTML_HXX + +#include "context.hxx" + +void +generate_html (context&); + +#endif // CLI_HTML_HXX diff --git a/cli/makefile b/cli/makefile index 1bea738..7edd27e 100644 --- a/cli/makefile +++ b/cli/makefile @@ -16,6 +16,7 @@ source.cxx \ runtime-header.cxx \ runtime-inline.cxx \ runtime-source.cxx \ +html.cxx \ generator.cxx \ name-processor.cxx diff --git a/cli/options.cli b/cli/options.cli index 3216e08..1127553 100644 --- a/cli/options.cli +++ b/cli/options.cli @@ -33,6 +33,26 @@ class options "Suppress generation of usage printing code." }; + bool --generate-cxx + { + "" + }; + + bool --generate-man + { + "" + }; + + bool --generate-html + { + "" + }; + + bool --stdout + { + "" + }; + std::size_t --option-length = 0 { "", @@ -60,6 +80,13 @@ class options the generated source file." }; + std::string --html-suffix = ".html" + { + "", + "Use instead of the default '.html' to construct the name of + the generated HTML file." + }; + std::string --option-prefix = "-" { "", diff --git a/cli/options.cxx b/cli/options.cxx index 3835155..1f495b9 100644 --- a/cli/options.cxx +++ b/cli/options.cxx @@ -243,10 +243,15 @@ options (int argc, output_dir_ (), suppress_inline_ (), suppress_usage_ (), + generate_cxx_ (), + generate_man_ (), + generate_html_ (), + stdout_ (), option_length_ (0), hxx_suffix_ (".hxx"), ixx_suffix_ (".ixx"), cxx_suffix_ (".cxx"), + html_suffix_ (".html"), option_prefix_ ("-"), option_separator_ ("--"), include_with_brackets_ (), @@ -268,10 +273,15 @@ options (int start, output_dir_ (), suppress_inline_ (), suppress_usage_ (), + generate_cxx_ (), + generate_man_ (), + generate_html_ (), + stdout_ (), option_length_ (0), hxx_suffix_ (".hxx"), ixx_suffix_ (".ixx"), cxx_suffix_ (".cxx"), + html_suffix_ (".html"), option_prefix_ ("-"), option_separator_ ("--"), include_with_brackets_ (), @@ -293,10 +303,15 @@ options (int argc, output_dir_ (), suppress_inline_ (), suppress_usage_ (), + generate_cxx_ (), + generate_man_ (), + generate_html_ (), + stdout_ (), option_length_ (0), hxx_suffix_ (".hxx"), ixx_suffix_ (".ixx"), cxx_suffix_ (".cxx"), + html_suffix_ (".html"), option_prefix_ ("-"), option_separator_ ("--"), include_with_brackets_ (), @@ -319,10 +334,15 @@ options (int start, output_dir_ (), suppress_inline_ (), suppress_usage_ (), + generate_cxx_ (), + generate_man_ (), + generate_html_ (), + stdout_ (), option_length_ (0), hxx_suffix_ (".hxx"), ixx_suffix_ (".ixx"), cxx_suffix_ (".cxx"), + html_suffix_ (".html"), option_prefix_ ("-"), option_separator_ ("--"), include_with_brackets_ (), @@ -346,6 +366,14 @@ print_usage (::std::ostream& os) os << "--suppress-usage Suppress generation of usage printing code." << ::std::endl; + os << "--generate-cxx" << std::endl; + + os << "--generate-man" << std::endl; + + os << "--generate-html" << std::endl; + + os << "--stdout" << std::endl; + os << "--option-length Indent option description characters when" << ::std::endl << " printing usage." << ::std::endl; @@ -358,6 +386,9 @@ print_usage (::std::ostream& os) os << "--cxx-suffix Use instead of the default '.cxx' to" << ::std::endl << " construct the name of the generated source file." << ::std::endl; + os << "--html-suffix Use instead of the default '.html' to" << ::std::endl + << " construct the name of the generated HTML file." << ::std::endl; + os << "--option-prefix Use instead of the default '-' as an" << ::std::endl << " option prefix." << ::std::endl; @@ -399,6 +430,14 @@ struct _cli_options_map_init &::cli::thunk< options, bool, &options::suppress_inline_ >; _cli_options_map_["--suppress-usage"] = &::cli::thunk< options, bool, &options::suppress_usage_ >; + _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_["--stdout"] = + &::cli::thunk< options, bool, &options::stdout_ >; _cli_options_map_["--option-length"] = &::cli::thunk< options, std::size_t, &options::option_length_ >; _cli_options_map_["--hxx-suffix"] = @@ -407,6 +446,8 @@ struct _cli_options_map_init &::cli::thunk< options, std::string, &options::ixx_suffix_ >; _cli_options_map_["--cxx-suffix"] = &::cli::thunk< options, std::string, &options::cxx_suffix_ >; + _cli_options_map_["--html-suffix"] = + &::cli::thunk< options, std::string, &options::html_suffix_ >; _cli_options_map_["--option-prefix"] = &::cli::thunk< options, std::string, &options::option_prefix_ >; _cli_options_map_["--option-separator"] = diff --git a/cli/options.hxx b/cli/options.hxx index 6f1210f..96f50e6 100644 --- a/cli/options.hxx +++ b/cli/options.hxx @@ -189,6 +189,18 @@ class options const bool& suppress_usage () const; + const bool& + generate_cxx () const; + + const bool& + generate_man () const; + + const bool& + generate_html () const; + + const bool& + stdout () const; + const std::size_t& option_length () const; @@ -202,6 +214,9 @@ class options cxx_suffix () const; const std::string& + html_suffix () const; + + const std::string& option_prefix () const; const std::string& @@ -238,10 +253,15 @@ class options std::string output_dir_; bool suppress_inline_; bool suppress_usage_; + bool generate_cxx_; + bool generate_man_; + bool generate_html_; + bool stdout_; std::size_t option_length_; std::string hxx_suffix_; std::string ixx_suffix_; std::string cxx_suffix_; + std::string html_suffix_; std::string option_prefix_; std::string option_separator_; bool include_with_brackets_; diff --git a/cli/options.ixx b/cli/options.ixx index 77ec790..6cf5899 100644 --- a/cli/options.ixx +++ b/cli/options.ixx @@ -118,6 +118,30 @@ suppress_usage () const return suppress_usage_; } +inline const bool& options:: +generate_cxx () const +{ + return generate_cxx_; +} + +inline const bool& options:: +generate_man () const +{ + return generate_man_; +} + +inline const bool& options:: +generate_html () const +{ + return generate_html_; +} + +inline const bool& options:: +stdout () const +{ + return stdout_; +} + inline const std::size_t& options:: option_length () const { @@ -143,6 +167,12 @@ cxx_suffix () const } inline const std::string& options:: +html_suffix () const +{ + return html_suffix_; +} + +inline const std::string& options:: option_prefix () const { return option_prefix_; -- cgit v1.1