From 28b0b90f965f08c6363555b6e672da807f9faf7a Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 19 Sep 2009 10:47:42 +0200 Subject: Open output files and generate boilerplate code --- cli/cli.cxx | 6 ++ cli/context.cxx | 164 ++++++++++++++++++++++++++++++++++++++++++++- cli/context.hxx | 23 ++++++- cli/generator.cxx | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++++- cli/generator.hxx | 2 + 5 files changed, 383 insertions(+), 6 deletions(-) (limited to 'cli') diff --git a/cli/cli.cxx b/cli/cli.cxx index 3d54452..3a38782 100644 --- a/cli/cli.cxx +++ b/cli/cli.cxx @@ -56,4 +56,10 @@ int main (int argc, char* argv[]) // return 1; } + catch (generator::failed const&) + { + // Diagnostics has already been issued by the generator. + // + return 1; + } } diff --git a/cli/context.cxx b/cli/context.cxx index 3c62e38..d90df99 100644 --- a/cli/context.cxx +++ b/cli/context.cxx @@ -3,18 +3,178 @@ // copyright : Copyright (c) 2009 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file +#include // std::binary_search + #include "context.hxx" +using namespace std; + context:: context (std::ostream& os_) : data_ (new (shared) data), - os (os_) + os (os_), + reserved_name_map (data_->reserved_name_map_) { } context:: context (context& c) : data_ (c.data_), - os (c.os) + os (c.os), + reserved_name_map (c.reserved_name_map) +{ +} + +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" + }; +} + +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 + // + size const ks (sizeof (keywords) / sizeof (char*)); + + if (std::binary_search (keywords, keywords + ks, r)) + { + 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; } diff --git a/cli/context.hxx b/cli/context.hxx index 28b74eb..8dc9fff 100644 --- a/cli/context.hxx +++ b/cli/context.hxx @@ -6,6 +6,8 @@ #ifndef CLI_CONTEXT_HXX #define CLI_CONTEXT_HXX +#include +#include #include #include @@ -17,15 +19,30 @@ using std::endl; class context { +public: + typedef std::string string; + +private: + struct data; + cutl::shared_ptr data_; + +public: + std::ostream& os; + + typedef std::map reserved_name_map_type; + reserved_name_map_type& reserved_name_map; + private: struct data { + reserved_name_map_type reserved_name_map_; }; - cutl::shared_ptr data_; - public: - std::ostream& os; + // Escape C++ keywords, reserved names, and illegal characters. + // + string + escape (string const&) const; public: context (std::ostream& os_); diff --git a/cli/generator.cxx b/cli/generator.cxx index a5c588c..cf9de69 100644 --- a/cli/generator.cxx +++ b/cli/generator.cxx @@ -3,13 +3,205 @@ // copyright : Copyright (c) 2009 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file +#include // std::toupper, std::is{alpha,upper,lower} +#include +#include +#include + +#include + +#include +#include + +#include "context.hxx" #include "generator.hxx" +using namespace std; +using namespace cutl; +using namespace cutl; + +using semantics::path; + +namespace +{ + static char const header[] = + "// This code was generated by CLI, a command line interface\n" + "// compiler for C++.\n" + "//\n\n"; + + string + make_guard (string const& file, string const& prefix, context& ctx) + { + string g (file); + + if (!prefix.empty ()) + g = prefix + '_' + g; + + // 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); + } +} + generator:: generator () { } + void generator:: -generate (semantics::cli_unit& unit, semantics::path const& path) +generate (semantics::cli_unit& unit, path const& p) { + try + { + bool inl (false); + + string hxx_suffix (".hxx"); + string ixx_suffix (".ixx"); + string cxx_suffix (".cxx"); + + string out_dir; + + path file (p.leaf ()); + string base (file.base ().string ()); + + string hxx_name (base + hxx_suffix); + string ixx_name (base + ixx_suffix); + string cxx_name (base + cxx_suffix); + + path hxx_path (hxx_name); + path ixx_path (ixx_name); + path cxx_path (cxx_name); + + if (!out_dir.empty ()) + { + path dir (out_dir); + + hxx_path = dir / hxx_path; + ixx_path = dir / ixx_path; + cxx_path = dir / cxx_path; + } + + fs::auto_removes auto_rm; + + // + // + 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 << header; + if (inl) + ixx << header; + cxx << header; + + typedef compiler::ostream_filter cxx_filter; + + // HXX + // + { + cxx_filter filt (hxx); + context ctx (hxx); + + string guard (make_guard (hxx_name, "", ctx)); + + hxx << "#ifndef " << guard << endl + << "#define " << guard << endl + << endl; + + + if (inl) + { + hxx << "#include \"" << ixx_name << "\"" << endl + << endl; + } + + hxx << "#endif // " << guard << endl; + } + + // IXX + // + if (inl) + { + cxx_filter filt (ixx); + context ctx (ixx); + } + + // CXX + // + { + cxx_filter filt (cxx); + context ctx (cxx); + + cxx << "#include \"" << hxx_name << "\"" << endl + << endl; + } + + auto_rm.cancel (); + } + 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/generator.hxx b/cli/generator.hxx index 5db2aa5..40081af 100644 --- a/cli/generator.hxx +++ b/cli/generator.hxx @@ -13,6 +13,8 @@ class generator public: generator (); + class failed {}; + void generate (semantics::cli_unit&, semantics::path const&); -- cgit v1.1