// file : xsde/cxx/hybrid/generator.cxx // copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC // license : GNU GPL v2 + exceptions; see accompanying LICENSE file #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../../libxsde/xsde/cxx/version.hxx" using namespace std; using namespace XSDFrontend::SemanticGraph; // // typedef std::wifstream WideInputFileStream; typedef std::wofstream WideOutputFileStream; typedef std::ifstream NarrowInputFileStream; namespace CXX { namespace { char const copyright_gpl[] = "// Copyright (c) 2005-2011 Code Synthesis Tools CC\n" "//\n" "// This program was generated by CodeSynthesis XSD/e, an XML Schema\n" "// to C++ data binding compiler for embedded systems.\n" "//\n" "// This program is free software; you can redistribute it and/or modify\n" "// it under the terms of the GNU General Public License version 2 as\n" "// published by the Free Software Foundation.\n" "//\n" "// This program is distributed in the hope that it will be useful,\n" "// but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "// GNU General Public License for more details.\n" "//\n" "// You should have received a copy of the GNU General Public License\n" "// along with this program; if not, write to the Free Software\n" "// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" "//\n" "//\n\n"; char const copyright_proprietary[] = "// Copyright (c) 2005-2011 Code Synthesis Tools CC\n" "//\n" "// This program was generated by CodeSynthesis XSD/e, an XML Schema to\n" "// C++ data binding compiler for embedded systems, in the Proprietary\n" "// License mode. You should have received a proprietary license from\n" "// Code Synthesis Tools CC prior to generating this code. See the\n" "// license text for conditions.\n" "//\n\n"; } void Hybrid::Generator:: usage () { CXX::Hybrid::options::print_usage (wcout); CXX::options::print_usage (wcout); } namespace { typedef compiler::ostream_filter ind_filter; typedef compiler::ostream_filter sloc_filter; NarrowString find_value (NarrowStrings const& v, char const* key) { for (NarrowStrings::const_iterator i (v.begin ()), e (v.end ()); i != e; ++i) { size_t p (i->find ('=')); if (p == NarrowString::npos) { if (key[0] != '\0') continue; } else { NarrowString k (*i, 0, p); // Unless it is one of the valid keys, assume there is no key. // if (!(k.empty () || k == "*" || k == "pskel" || k == "pimpl" || k == "sskel" || k == "simpl")) { k.clear (); p = NarrowString::npos; } if (k != key && k != "*") continue; } return NarrowString ( *i, p == NarrowString::npos ? 0 : p + 1, NarrowString::npos); } return NarrowString (); } void copy_values (NarrowStrings& dst, NarrowStrings const& src, char const* key) { dst.clear (); for (NarrowStrings::const_iterator i (src.begin ()), e (src.end ()); i != e; ++i) { size_t p (i->find ('=')); if (p == NarrowString::npos) { if (key[0] != '\0') continue; } else { NarrowString k (*i, 0, p); // Unless it is one of the valid keys, assume there is no key. // if (!(k.empty () || k == "*" || k == "pskel" || k == "pimpl" || k == "sskel" || k == "simpl")) { k.clear (); p = NarrowString::npos; } if (k != key && k != "*") continue; } dst.push_back ( NarrowString ( *i, p == NarrowString::npos ? 0 : p + 1, NarrowString::npos)); } } struct FundNamespace: Namespace, Hybrid::Context { FundNamespace (Hybrid::Context& c, char type) : Namespace (c), Hybrid::Context (c), type_ (type) { } void traverse (Type& ns) { pre (ns); os << "using ::xsde::cxx::hybrid::any_type;" << endl; bool us, ui; String skel, impl; if (type_ == 'p') { skel = options.pskel_type_suffix (); impl = options.pimpl_type_suffix (); us = skel == L"_pskel"; ui = impl == L"_pimpl"; } else { skel = options.sskel_type_suffix (); impl = options.simpl_type_suffix (); us = skel == L"_sskel"; ui = impl == L"_simpl"; } if (us) os << "using ::xsde::cxx::hybrid::any_type_" << type_ << "skel;"; else os << "using ::xsde::cxx::hybrid::any_type_" << type_ << "skel " << "any_type" << skel << ";"; if (ui) os << "using ::xsde::cxx::hybrid::any_type_" << type_ << "impl;"; else os << "using ::xsde::cxx::hybrid::any_type_" << type_ << "impl " << "any_type" << impl << ";"; post (ns); } private: char type_; }; } auto_ptr Hybrid::Generator:: parser_options (options const& h, Schema& schema, Path const& path) { auto_ptr r (new Parser::options); static_cast (*r) = h; // Assign common options. r->reuse_style_mixin (h.reuse_style_mixin ()); r->suppress_validation (h.suppress_validation () || h.suppress_parser_val ()); r->generate_polymorphic (h.generate_polymorphic ()); r->runtime_polymorphic (h.runtime_polymorphic ()); r->skel_file_suffix (h.pskel_file_suffix ()); r->skel_type_suffix (h.pskel_type_suffix ()); r->impl_type_suffix (h.pimpl_type_suffix ()); char const* k = "pskel"; r->hxx_regex (find_value (h.hxx_regex (), k)); r->ixx_regex (find_value (h.ixx_regex (), k)); r->cxx_regex (find_value (h.cxx_regex (), k)); r->prologue_file (find_value (h.prologue_file (), k)); r->epilogue_file (find_value (h.epilogue_file (), k)); r->hxx_prologue_file (find_value (h.hxx_prologue_file (), k)); r->ixx_prologue_file (find_value (h.ixx_prologue_file (), k)); r->cxx_prologue_file (find_value (h.cxx_prologue_file (), k)); r->hxx_epilogue_file (find_value (h.hxx_epilogue_file (), k)); r->ixx_epilogue_file (find_value (h.ixx_epilogue_file (), k)); r->cxx_epilogue_file (find_value (h.cxx_epilogue_file (), k)); copy_values (r->prologue (), h.prologue (), k); copy_values (r->epilogue (), h.epilogue (), k); copy_values (r->hxx_prologue (), h.hxx_prologue (), k); copy_values (r->ixx_prologue (), h.ixx_prologue (), k); copy_values (r->cxx_prologue (), h.cxx_prologue (), k); copy_values (r->hxx_epilogue (), h.hxx_epilogue (), k); copy_values (r->ixx_epilogue (), h.ixx_epilogue (), k); copy_values (r->cxx_epilogue (), h.cxx_epilogue (), k); // Add the anyType parser. // { std::wostringstream os; Context ctx (os, schema, path, h, 0, 0, 0); os << endl << "#include " << endl << "#include " << endl << "#include " << endl << endl; { ind_filter ind (os); FundNamespace ns (ctx, 'p'); ns.dispatch (ctx.xs_ns ()); } r->hxx_prologue ().push_back (String (os.str ()).to_narrow ()); } return r; } auto_ptr Hybrid::Generator:: serializer_options (options const& h, Schema& schema, Path const& path) { auto_ptr r (new Serializer::options); static_cast (*r) = h; // Assign common options. r->reuse_style_mixin (h.reuse_style_mixin ()); r->suppress_validation (h.suppress_validation () || h.suppress_serializer_val ()); r->generate_polymorphic (h.generate_polymorphic ()); r->runtime_polymorphic (h.runtime_polymorphic ()); r->skel_file_suffix (h.sskel_file_suffix ()); r->skel_type_suffix (h.sskel_type_suffix ()); r->impl_type_suffix (h.simpl_type_suffix ()); char const* k = "pskel"; r->hxx_regex (find_value (h.hxx_regex (), k)); r->ixx_regex (find_value (h.ixx_regex (), k)); r->cxx_regex (find_value (h.cxx_regex (), k)); r->prologue_file (find_value (h.prologue_file (), k)); r->epilogue_file (find_value (h.epilogue_file (), k)); r->hxx_prologue_file (find_value (h.hxx_prologue_file (), k)); r->ixx_prologue_file (find_value (h.ixx_prologue_file (), k)); r->cxx_prologue_file (find_value (h.cxx_prologue_file (), k)); r->hxx_epilogue_file (find_value (h.hxx_epilogue_file (), k)); r->ixx_epilogue_file (find_value (h.ixx_epilogue_file (), k)); r->cxx_epilogue_file (find_value (h.cxx_epilogue_file (), k)); copy_values (r->prologue (), h.prologue (), k); copy_values (r->epilogue (), h.epilogue (), k); copy_values (r->hxx_prologue (), h.hxx_prologue (), k); copy_values (r->ixx_prologue (), h.ixx_prologue (), k); copy_values (r->cxx_prologue (), h.cxx_prologue (), k); copy_values (r->hxx_epilogue (), h.hxx_epilogue (), k); copy_values (r->ixx_epilogue (), h.ixx_epilogue (), k); copy_values (r->cxx_epilogue (), h.cxx_epilogue (), k); // Add the anyType parser. // { std::wostringstream os; Context ctx (os, schema, path, h, 0, 0, 0); os << endl << "#include " << endl << "#include " << endl << "#include " << endl << endl; { ind_filter ind (os); FundNamespace ns (ctx, 's'); ns.dispatch (ctx.xs_ns ()); } r->hxx_prologue ().push_back (String (os.str ()).to_narrow ()); } return r; } void Hybrid::Generator:: calculate_size (options const& ops, XSDFrontend::SemanticGraph::Schema& schema, XSDFrontend::SemanticGraph::Path const& file, const WarningSet& disabled_warnings) { // Determine which types are fixed/variable-sized. // TreeSizeProcessor proc; if (!proc.process (ops, schema, file, disabled_warnings)) throw Failed (); } void Hybrid::Generator:: process_tree_names (options const& ops, XSDFrontend::SemanticGraph::Schema& schema, XSDFrontend::SemanticGraph::Path const& file) { TreeNameProcessor proc; proc.process (ops, schema, file, false); } void Hybrid::Generator:: process_parser_names (options const& ops, XSDFrontend::SemanticGraph::Schema& schema, XSDFrontend::SemanticGraph::Path const& file) { ParserNameProcessor proc; if (!proc.process (ops, schema, file, false)) throw Failed (); } void Hybrid::Generator:: process_serializer_names (options const& ops, XSDFrontend::SemanticGraph::Schema& schema, XSDFrontend::SemanticGraph::Path const& file) { SerializerNameProcessor proc; if (!proc.process (ops, schema, file, false)) throw Failed (); } namespace { template void open (S& ifs, NarrowString const& path) { try { Path fs_path (path); ifs.open (fs_path.string ().c_str (), std::ios_base::in | std::ios_base::binary); if (!ifs.is_open ()) { wcerr << path.c_str () << ": error: unable to open in read mode" << endl; throw Hybrid::Generator::Failed (); } } catch (InvalidPath const&) { wcerr << "error: '" << path.c_str () << "' is not a valid " << "filesystem path" << endl; throw Hybrid::Generator::Failed (); } } void append (WideOutputFileStream& os, NarrowString const& path, WideInputFileStream& default_is) { using std::ios_base; if (path) { WideInputFileStream is; open (is, path); os << is.rdbuf (); } else if (default_is.is_open ()) { os << default_is.rdbuf (); default_is.seekg (0, ios_base::beg); } } void append (WideOutputFileStream& os, NarrowStrings const& primary, NarrowStrings const& def, char const* primary_key, char const* def_key) { for (NarrowStrings const* v = &primary; v != 0; v = (v == &def ? 0 : &def)) { bool found (false); char const* key (v == &primary ? primary_key : def_key); for (NarrowStrings::const_iterator i (v->begin ()), e (v->end ()); i != e; ++i) { if (key == 0) os << i->c_str () << endl; else { size_t p (i->find ('=')); if (p == NarrowString::npos) { if (key[0] != '\0') continue; } else { NarrowString k (*i, 0, p); // Unless it is one of the valid keys, assume there is no key. // if (!(k.empty () || k == "*" || k == "pskel" || k == "pimpl" || k == "sskel" || k == "simpl")) { k.clear (); p = NarrowString::npos; } if (k != key && k != "*") continue; } NarrowString s ( *i, p == NarrowString::npos ? 0 : p + 1, NarrowString::npos); os << s.c_str () << endl; } found = true; } if (found) break; } } void append (WideOutputFileStream& os, NarrowStrings const& primary, NarrowStrings const& def, char const* key) { append (os, primary, def, key, key); } } size_t Hybrid::Generator:: generate_tree (Hybrid::options const& ops, Schema& schema, Path const& file_path, bool fpt, const WarningSet& disabled_warnings, TypeMap::Namespaces& parser_type_map, TypeMap::Namespaces& serializer_type_map, FileList& file_list, AutoUnlinks& unlinks) { using std::ios_base; typedef Context::Regex Regex; try { bool generate_xml_schema (ops.generate_xml_schema ()); // We could be compiling several schemas at once in which case // handling of the --generate-xml-schema option gets tricky: we // will need to rely on the presence of the --extern-xml-schema // to tell us which (fake) schema file corresponds to XML Schema. // if (generate_xml_schema) { if (NarrowString name = ops.extern_xml_schema ()) { if (file_path.string () != name) generate_xml_schema = false; } } // Evaluate the graph for possibility of generating something useful. // { Validator validator; if (!validator.validate (ops, schema, file_path, disabled_warnings)) throw Failed (); } // Process names. // { TreeNameProcessor proc; proc.process (ops, schema, file_path, true); } // Generate code. // bool inline_ (ops.generate_inline () && !generate_xml_schema); bool forward (ops.generate_forward () && !generate_xml_schema); bool source (!generate_xml_schema); NarrowString name (file_path.leaf ().string ()); NarrowString hxx_suffix (ops.hxx_suffix ()); NarrowString ixx_suffix (ops.ixx_suffix ()); NarrowString cxx_suffix (ops.cxx_suffix ()); NarrowString fwd_suffix (ops.fwd_suffix ()); NarrowString hxx_regex (find_value (ops.hxx_regex (), "")); NarrowString ixx_regex (find_value (ops.ixx_regex (), "")); NarrowString cxx_regex (find_value (ops.cxx_regex (), "")); Regex hxx_expr ( hxx_regex.empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + hxx_suffix + "#" : hxx_regex); Regex ixx_expr ( ixx_regex.empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + ixx_suffix + "#" : ixx_regex); Regex cxx_expr ( cxx_regex.empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + cxx_suffix + "#" : cxx_regex); Regex fwd_expr ( ops.fwd_regex ().empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + fwd_suffix + "#" : ops.fwd_regex ()); if (!hxx_expr.match (name)) { wcerr << "error: header expression '" << hxx_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } if (inline_ && !ixx_expr.match (name)) { wcerr << "error: inline expression '" << ixx_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } if (source && !cxx_expr.match (name)) { wcerr << "error: source expression '" << cxx_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } if (forward && !fwd_expr.match (name)) { wcerr << "error: forward expression '" << fwd_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } NarrowString hxx_name (hxx_expr.replace (name)); NarrowString ixx_name (inline_ ? ixx_expr.replace (name) : NarrowString ()); NarrowString cxx_name (source ? cxx_expr.replace (name) : NarrowString ()); NarrowString fwd_name (forward ? fwd_expr.replace (name) : NarrowString ()); Path hxx_path (hxx_name); Path ixx_path (ixx_name); Path cxx_path (cxx_name); Path fwd_path (fwd_name); Path out_dir; if (NarrowString dir = ops.output_dir ()) { try { out_dir = Path (dir); } catch (InvalidPath const&) { wcerr << dir.c_str () << ": error: invalid path" << endl; throw Failed (); } } if (fpt && !generate_xml_schema) { // In the file-per-type mode the schema files are always local // unless the user added the directory so that we propagate this // to the output files. // Path fpt_dir (file_path.directory ()); if (!fpt_dir.empty ()) out_dir /= fpt_dir; } if (!out_dir.empty ()) { hxx_path = out_dir / hxx_path; ixx_path = out_dir / ixx_path; cxx_path = out_dir / cxx_path; fwd_path = out_dir / fwd_path; } // Open the tree files. // WideOutputFileStream hxx (hxx_path.string ().c_str (), ios_base::out); WideOutputFileStream ixx; WideOutputFileStream cxx; WideOutputFileStream fwd; // FWD // if (forward) { fwd.open (fwd_path.string ().c_str (), ios_base::out); if (!fwd.is_open ()) { wcerr << fwd_path << ": error: unable to open in write mode" << endl; throw Failed (); } unlinks.add (fwd_path); file_list.push_back (fwd_path.string ()); } if (!hxx.is_open ()) { wcerr << hxx_path << ": error: unable to open in write mode" << endl; throw Failed (); } unlinks.add (hxx_path); file_list.push_back (hxx_path.string ()); if (inline_) { ixx.open (ixx_path.string ().c_str (), ios_base::out); if (!ixx.is_open ()) { wcerr << ixx_path << ": error: unable to open in write mode" << endl; throw Failed (); } unlinks.add (ixx_path); file_list.push_back (ixx_path.string ()); } if (source) { cxx.open (cxx_path.string ().c_str (), ios_base::out); if (!cxx.is_open ()) { wcerr << cxx_path << ": error: unable to open in write mode" << endl; throw Failed (); } unlinks.add (cxx_path); file_list.push_back (cxx_path.string ()); } // Print copyright and license. // char const* copyright ( ops.proprietary_license () ? copyright_proprietary : copyright_gpl); if (forward) fwd << copyright; hxx << copyright; if (inline_) ixx << copyright; if (source) cxx << copyright; // Prologue. // WideInputFileStream prologue; { NarrowString name (find_value (ops.prologue_file (), "")); if (name) open (prologue, name); } // Epilogue. // WideInputFileStream epilogue; { NarrowString name (find_value (ops.epilogue_file (), "")); if (name) open (epilogue, name); } // SLOC counter. // size_t sloc_total (0); bool show_sloc (ops.show_sloc ()); // // Regex guard_expr ("/([a-z])([A-Z])/$1_$2/"); // Split words. NarrowString guard_prefix (ops.guard_prefix ()); if (!guard_prefix) guard_prefix = file_path.directory ().string (); if (guard_prefix) guard_prefix += '_'; // FWD // if (forward) { Context ctx ( fwd, schema, file_path, ops, &fwd_expr, &hxx_expr, &ixx_expr); sloc_filter sloc (fwd); String guard (guard_expr.replace (guard_prefix + fwd_name)); guard = ctx.escape (guard); // Make it a C++ id. std::transform (guard.begin (), guard.end(), guard.begin (), upcase); fwd << "#ifndef " << guard << endl << "#define " << guard << endl << endl; // Copy prologue. // fwd << "// Begin prologue." << endl << "//" << endl; append (fwd, ops.fwd_prologue (), ops.prologue (), 0, ""); append (fwd, ops.fwd_prologue_file (), prologue); fwd << "//" << endl << "// End prologue." << endl << endl; // Version check. // fwd << "#include " << endl << endl << "#if (XSDE_INT_VERSION != " << XSDE_INT_VERSION << "L)" << endl << "#error XSD/e runtime version mismatch" << endl << "#endif" << endl << endl; fwd << "#include " << endl << endl; // Generate. // { ind_filter ind (fwd); // We don't want to indent prologues/epilogues. generate_tree_forward (ctx, false); } fwd << "#include " << endl << endl; // Copy epilogue. // fwd << "// Begin epilogue." << endl << "//" << endl; append (fwd, ops.fwd_epilogue_file (), epilogue); append (fwd, ops.fwd_epilogue (), ops.epilogue (), 0, ""); fwd << "//" << endl << "// End epilogue." << endl << endl; fwd << "#endif // " << guard << endl; if (show_sloc) wcerr << fwd_path << ": " << sloc.stream ().count () << endl; sloc_total += sloc.stream ().count (); } // C++ namespace mapping for the XML Schema namespace. // String xs_ns; // HXX // { Context ctx ( hxx, schema, file_path, ops, &fwd_expr, &hxx_expr, &ixx_expr); xs_ns = ctx.xs_ns_name (); sloc_filter sloc (hxx); String guard (guard_expr.replace (guard_prefix + hxx_name)); guard = ctx.escape (guard); // Make it a C++ id. std::transform (guard.begin (), guard.end(), guard.begin (), upcase); hxx << "#ifndef " << guard << endl << "#define " << guard << endl << endl; // Copy prologue. // hxx << "// Begin prologue." << endl << "//" << endl; append (hxx, ops.hxx_prologue (), ops.prologue (), ""); append (hxx, find_value (ops.hxx_prologue_file (), ""), prologue); hxx << "//" << endl << "// End prologue." << endl << endl; // Version check. // hxx << "#include " << endl << endl << "#if (XSDE_INT_VERSION != " << XSDE_INT_VERSION << "L)" << endl << "#error XSD/e runtime version mismatch" << endl << "#endif" << endl << endl; // Runtime/generated code compatibility checks. // hxx << "#include " << endl << endl; if (ops.char_encoding () == "iso8859-1") { hxx << "#ifndef XSDE_ENCODING_ISO8859_1" << endl << "#error the generated code uses the ISO-8859-1 encoding" << "while the XSD/e runtime does not (reconfigure the runtime " << "or change the --char-encoding value)" << endl << "#endif" << endl << endl; } else { hxx << "#ifndef XSDE_ENCODING_UTF8" << endl << "#error the generated code uses the UTF-8 encoding" << "while the XSD/e runtime does not (reconfigure the runtime " << "or change the --char-encoding value)" << endl << "#endif" << endl << endl; } if (ops.no_stl ()) { hxx << "#ifdef XSDE_STL" << endl << "#error the XSD/e runtime uses STL while the " << "generated code does not (reconfigure the runtime or " << "remove --no-stl)" << endl << "#endif" << endl << endl; } else { hxx << "#ifndef XSDE_STL" << endl << "#error the generated code uses STL while the " << "XSD/e runtime does not (reconfigure the runtime or " << "add --no-stl)" << endl << "#endif" << endl << endl; } if (ops.no_exceptions ()) { hxx << "#ifdef XSDE_EXCEPTIONS" << endl << "#error the XSD/e runtime uses exceptions while the " << "generated code does not (reconfigure the runtime or " << "remove --no-exceptions)" << endl << "#endif" << endl << endl; } else { hxx << "#ifndef XSDE_EXCEPTIONS" << endl << "#error the generated code uses exceptions while the " << "XSD/e runtime does not (reconfigure the runtime or " << "add --no-exceptions)" << endl << "#endif" << endl << endl; } if (ops.no_long_long ()) { hxx << "#ifdef XSDE_LONGLONG" << endl << "#error the XSD/e runtime uses long long while the " << "generated code does not (reconfigure the runtime or " << "remove --no-long-long)" << endl << "#endif" << endl << endl; } else { hxx << "#ifndef XSDE_LONGLONG" << endl << "#error the generated code uses long long while the " << "XSD/e runtime does not (reconfigure the runtime or " << "add --no-long-long)" << endl << "#endif" << endl << endl; } if (ops.custom_allocator ()) { hxx << "#ifndef XSDE_CUSTOM_ALLOCATOR" << endl << "#error the generated code uses custom allocator while " << "the XSD/e runtime does not (reconfigure the runtime or " << "remove --custom-allocator)" << endl << "#endif" << endl << endl; } else { hxx << "#ifdef XSDE_CUSTOM_ALLOCATOR" << endl << "#error the XSD/e runtime uses custom allocator while " << "the generated code does not (reconfigure the runtime or " << "add --custom-allocator)" << endl << "#endif" << endl << endl; } hxx << "#include " << endl << endl; // Generate. // { ind_filter ind (hxx); // We don't want to indent prologues/epilogues. if (!generate_xml_schema) { if (forward) hxx << "#include " << ctx.process_include_path (fwd_name) << endl << endl; else generate_tree_forward (ctx, false); generate_tree_header (ctx); if (!ops.generate_insertion ().empty ()) generate_insertion_header (ctx); if (!ops.generate_extraction ().empty ()) generate_extraction_header (ctx); } else generate_tree_forward (ctx, true); } if (inline_) { hxx << "#ifndef XSDE_DONT_INCLUDE_INLINE" << endl << "#include " << ctx.process_include_path (ixx_name) << endl << "#endif // XSDE_DONT_INCLUDE_INLINE" << endl << endl; } hxx << "#include " << endl << endl; // Copy epilogue. // hxx << "// Begin epilogue." << endl << "//" << endl; append (hxx, find_value (ops.hxx_epilogue_file (), ""), epilogue); append (hxx, ops.hxx_epilogue (), ops.epilogue (), ""); hxx << "//" << endl << "// End epilogue." << endl << endl; hxx << "#endif // " << guard << endl; if (show_sloc) wcerr << hxx_path << ": " << sloc.stream ().count () << endl; sloc_total += sloc.stream ().count (); } // IXX // if (inline_) { Context ctx ( ixx, schema, file_path, ops, &fwd_expr, &hxx_expr, &ixx_expr); sloc_filter sloc (ixx); // Guard // String guard (guard_expr.replace (guard_prefix + ixx_name)); guard = ctx.escape (guard); // make a c++ id std::transform (guard.begin (), guard.end(), guard.begin (), upcase); ixx << "#ifndef " << guard.c_str () << endl << "#define " << guard.c_str () << endl << endl; // Copy prologue. // ixx << "// Begin prologue." << endl << "//" << endl; append (ixx, ops.ixx_prologue (), ops.prologue (), ""); append (ixx, find_value (ops.ixx_prologue_file (), ""), prologue); ixx << "//" << endl << "// End prologue." << endl << endl; // Generate. // { ind_filter ind (ixx); // We don't want to indent prologues/epilogues. generate_tree_inline (ctx); } // Copy epilogue. // ixx << "// Begin epilogue." << endl << "//" << endl; append (ixx, find_value (ops.ixx_epilogue_file (), ""), epilogue); append (ixx, ops.ixx_epilogue (), ops.epilogue (), ""); ixx << "//" << endl << "// End epilogue." << endl << endl; ixx << "#endif // " << guard.c_str () << endl; if (show_sloc) wcerr << ixx_path << ": " << sloc.stream ().count () << endl; sloc_total += sloc.stream ().count (); } // CXX // if (source) { Context ctx ( cxx, schema, file_path, ops, &fwd_expr, &hxx_expr, &ixx_expr); sloc_filter sloc (cxx); // Copy prologue. // cxx << "// Begin prologue." << endl << "//" << endl; append (cxx, ops.cxx_prologue (), ops.prologue (), ""); append (cxx, find_value (ops.cxx_prologue_file (), ""), prologue); cxx << "//" << endl << "// End prologue." << endl << endl; cxx << "#include " << endl << endl; cxx << "#include " << ctx.process_include_path (hxx_name) << endl << endl; // Generate. // { ind_filter ind (cxx); // We don't want to indent prologues/epilogues. if (!inline_) generate_tree_inline (ctx); generate_tree_source (ctx); if (!ops.generate_insertion ().empty ()) generate_insertion_source (ctx); if (!ops.generate_extraction ().empty ()) generate_extraction_source (ctx); } cxx << "#include " << endl << endl; // Copy epilogue. // cxx << "// Begin epilogue." << endl << "//" << endl; append (cxx, find_value (ops.cxx_epilogue_file (), ""), epilogue); append (cxx, ops.cxx_epilogue (), ops.epilogue (), ""); cxx << "//" << endl << "// End epilogue." << endl << endl; if (show_sloc) wcerr << cxx_path << ": " << sloc.stream ().count () << endl; sloc_total += sloc.stream ().count (); } // Populate the type maps if we are generating parsing or // serialization code. // if (ops.generate_parser () || ops.generate_serializer ()) { generate_tree_type_map (ops, schema, file_path, hxx_name, parser_type_map, serializer_type_map); // Re-map anyType. // if (ops.generate_parser ()) { parser_type_map.push_back ( TypeMap::Namespace ("http://www.w3.org/2001/XMLSchema")); TypeMap::Namespace& xs (parser_type_map.back ()); xs.types_push_back ("anyType", xs_ns + L"::any_type*"); } if (ops.generate_serializer ()) { serializer_type_map.push_back ( TypeMap::Namespace ("http://www.w3.org/2001/XMLSchema")); TypeMap::Namespace& xs (serializer_type_map.back ()); xs.types_push_back ("anyType", L"const " + xs_ns + L"::any_type&"); } } return sloc_total; } catch (NoNamespaceMapping const& e) { wcerr << e.file () << ":" << e.line () << ":" << e.column () << ": error: unable to map XML Schema namespace '" << e.ns () << "' to C++ namespace" << endl; wcerr << e.file () << ":" << e.line () << ":" << e.column () << ": info: use the --namespace-map or --namespace-regex option " << "to provide custom mapping" << endl; throw Failed (); } catch (InvalidNamespaceMapping const& e) { wcerr << "error: invalid XML to C++ namespace mapping specified: " << "'" << e.mapping () << "': " << e.reason () << endl; throw Failed (); } catch (cutl::re::format const& e) { wcerr << "error: invalid regex: '" << e.regex ().c_str () << "': " << e.description ().c_str () << endl; throw Failed (); } catch (cutl::re::wformat const& e) { wcerr << "error: invalid regex: '" << e.regex () << "': " << e.description ().c_str () << endl; throw Failed (); } } size_t Hybrid::Generator:: generate_parser (Hybrid::options const& ops, Schema& schema, Path const& file_path, bool fpt, const WarningSet&, FileList& file_list, AutoUnlinks& unlinks) { using std::ios_base; typedef cutl::re::regexsub Regex; try { { bool gen_xml_schema (ops.generate_xml_schema ()); // We could be compiling several schemas at once in which case // handling of the --generate-xml-schema option gets tricky: we // will need to rely on the presence of the --extern-xml-schema // to tell us which (fake) schema file corresponds to XML Schema. // if (gen_xml_schema) { if (NarrowString name = ops.extern_xml_schema ()) { if (file_path.string () != name) gen_xml_schema = false; } } if (gen_xml_schema) return 0; } // Process names. // { ParserNameProcessor proc; if (!proc.process (ops, schema, file_path, true)) throw Failed (); } NarrowString name (file_path.leaf ().string ()); NarrowString skel_suffix (ops.pskel_file_suffix ()); NarrowString impl_suffix (ops.pimpl_file_suffix ()); NarrowString hxx_suffix (ops.hxx_suffix ()); NarrowString cxx_suffix (ops.cxx_suffix ()); NarrowString hxx_obj_regex (find_value (ops.hxx_regex (), "")); NarrowString hxx_skel_regex (find_value (ops.hxx_regex (), "pskel")); NarrowString hxx_regex (find_value (ops.hxx_regex (), "pimpl")); NarrowString cxx_regex (find_value (ops.cxx_regex (), "pimpl")); // Here we need to make sure that hxx_obj_expr is the same // as in generate(). // Regex hxx_obj_expr ( hxx_obj_regex.empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + hxx_suffix + "#" : hxx_obj_regex); // Here we need to make sure that hxx_skel_expr is the same // as in the C++/Parser generator. // Regex hxx_skel_expr ( hxx_skel_regex.empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + hxx_suffix + "#" : hxx_skel_regex); Regex hxx_expr ( hxx_regex.empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + impl_suffix + hxx_suffix + "#" : hxx_regex); Regex cxx_expr ( cxx_regex.empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + impl_suffix + cxx_suffix + "#" : cxx_regex); if (!hxx_expr.match (name)) { wcerr << "error: parser implementation header expression '" << hxx_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } if (!cxx_expr.match (name)) { wcerr << "error: parser implementation source expression '" << cxx_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } NarrowString hxx_skel_name (hxx_skel_expr.replace (name)); NarrowString hxx_name (hxx_expr.replace (name)); NarrowString cxx_name (cxx_expr.replace (name)); Path hxx_path (hxx_name); Path cxx_path (cxx_name); Path out_dir; if (NarrowString dir = ops.output_dir ()) { try { out_dir = Path (dir); } catch (InvalidPath const&) { wcerr << dir.c_str () << ": error: invalid path" << endl; throw Failed (); } } if (fpt) { // In the file-per-type mode the schema files are always local // unless the user added the directory so that we propagate this // to the output files. // Path fpt_dir (file_path.directory ()); if (!fpt_dir.empty ()) out_dir /= fpt_dir; } if (!out_dir.empty ()) { hxx_path = out_dir / hxx_path; cxx_path = out_dir / cxx_path; } WideOutputFileStream hxx (hxx_path.string ().c_str (), ios_base::out); WideOutputFileStream cxx (cxx_path.string ().c_str (), ios_base::out); if (!hxx.is_open ()) { wcerr << hxx_path << ": error: unable to open in write mode" << endl; throw Failed (); } unlinks.add (hxx_path); file_list.push_back (hxx_path.string ()); if (!cxx.is_open ()) { wcerr << cxx_path << ": error: unable to open in write mode" << endl; throw Failed (); } unlinks.add (cxx_path); file_list.push_back (cxx_path.string ()); // Print copyright and license. // char const* copyright ( ops.proprietary_license () ? copyright_proprietary : copyright_gpl); hxx << copyright; cxx << copyright; // Prologue. // WideInputFileStream prologue; { NarrowString name (find_value (ops.prologue_file (), "pimpl")); if (name) open (prologue, name); } // Epilogue. // WideInputFileStream epilogue; { NarrowString name (find_value (ops.epilogue_file (), "pimpl")); if (name) open (epilogue, name); } // SLOC counter. // size_t sloc_total (0); bool show_sloc (ops.show_sloc ()); // // Regex guard_expr ("/([a-z])([A-Z])/$1_$2/"); // Split words. NarrowString guard_prefix (ops.guard_prefix ()); if (!guard_prefix) guard_prefix = file_path.directory ().string (); if (guard_prefix) guard_prefix += '_'; bool aggr (ops.generate_aggregate ()); // HXX // { Context ctx (hxx, schema, file_path, ops, 0, &hxx_expr, 0); sloc_filter sloc (hxx); String guard (guard_expr.replace (guard_prefix + hxx_name)); guard = ctx.escape (guard); // Make it a C++ id. std::transform (guard.begin (), guard.end(), guard.begin (), upcase); hxx << "#ifndef " << guard << endl << "#define " << guard << endl << endl; // Copy prologue. // hxx << "// Begin prologue." << endl << "//" << endl; append (hxx, ops.hxx_prologue (), ops.prologue (), "pimpl"); append (hxx, find_value (ops.hxx_prologue_file (), "pimpl"), prologue); hxx << "//" << endl << "// End prologue." << endl << endl; hxx << "#include " << endl << endl; { ind_filter ind (hxx); // We don't want to indent prologues/epilogues. // Define omit aggregate macro. // hxx << "#ifndef XSDE_OMIT_PAGGR" << endl << "# define XSDE_OMIT_PAGGR" << endl << "# define " << guard << "_CLEAR_OMIT_PAGGR" << endl << "#endif" << endl << endl; // Generate. // hxx << "#include " << ctx.process_include_path (hxx_skel_name) << endl << endl; generate_parser_header (ctx); // Clear omit aggregate macro. // hxx << "#ifdef " << guard << "_CLEAR_OMIT_PAGGR" << endl << "# undef XSDE_OMIT_PAGGR" << endl << "#endif" << endl << endl; if (aggr) { hxx << "#ifndef XSDE_OMIT_PAGGR" << endl << endl; generate_parser_aggregate_header (ctx); hxx << "#endif // XSDE_OMIT_PAGGR" << endl << endl; } } hxx << "#include " << endl << endl; // Copy epilogue. // hxx << "// Begin epilogue." << endl << "//" << endl; append (hxx, find_value (ops.hxx_epilogue_file (), "pimpl"), epilogue); append (hxx, ops.hxx_epilogue (), ops.epilogue (), "pimpl"); hxx << "//" << endl << "// End epilogue." << endl << endl; hxx << "#endif // " << guard << endl; if (show_sloc) wcerr << hxx_path << ": " << sloc.stream ().count () << endl; sloc_total += sloc.stream ().count (); } // CXX // { Context ctx (cxx, schema, file_path, ops, 0, &hxx_expr, 0); sloc_filter sloc (cxx); // Copy prologue. // cxx << "// Begin prologue." << endl << "//" << endl; append (cxx, ops.cxx_prologue (), ops.prologue (), "pimpl"); append (cxx, find_value (ops.cxx_prologue_file (), "pimpl"), prologue); cxx << "//" << endl << "// End prologue." << endl << endl; cxx << "#include " << endl << endl; cxx << "#include " << ctx.process_include_path (hxx_name) << endl << endl; // Generate. // { ind_filter ind (cxx); // We don't want to indent prologues/epilogues. generate_parser_source (ctx, hxx_obj_expr); if (aggr) generate_parser_aggregate_source (ctx); } cxx << "#include " << endl << endl; // Copy epilogue. // cxx << "// Begin epilogue." << endl << "//" << endl; append (cxx, find_value (ops.cxx_epilogue_file (), "pimpl"), epilogue); append (cxx, ops.cxx_epilogue (), ops.epilogue (), "pimpl"); cxx << "//" << endl << "// End epilogue." << endl << endl; if (show_sloc) wcerr << cxx_path << ": " << sloc.stream ().count () << endl; sloc_total += sloc.stream ().count (); } return sloc_total; } catch (NoNamespaceMapping const& e) { wcerr << e.file () << ":" << e.line () << ":" << e.column () << ": error: unable to map XML Schema namespace '" << e.ns () << "' to C++ namespace" << endl; wcerr << e.file () << ":" << e.line () << ":" << e.column () << ": info: use the --namespace-map or --namespace-regex option " << "to provide custom mapping" << endl; throw Failed (); } catch (InvalidNamespaceMapping const& e) { wcerr << "error: invalid XML to C++ namespace mapping specified: " << "'" << e.mapping () << "': " << e.reason () << endl; throw Failed (); } catch (cutl::re::format const& e) { wcerr << "error: invalid regex: '" << e.regex ().c_str () << "': " << e.description ().c_str () << endl; throw Failed (); } catch (cutl::re::wformat const& e) { wcerr << "error: invalid regex: '" << e.regex () << "': " << e.description ().c_str () << endl; throw Failed (); } } size_t Hybrid::Generator:: generate_serializer (Hybrid::options const& ops, Schema& schema, Path const& file_path, bool fpt, const WarningSet&, FileList& file_list, AutoUnlinks& unlinks) { using std::ios_base; typedef cutl::re::regexsub Regex; try { { bool gen_xml_schema (ops.generate_xml_schema ()); // We could be compiling several schemas at once in which case // handling of the --generate-xml-schema option gets tricky: we // will need to rely on the presence of the --extern-xml-schema // to tell us which (fake) schema file corresponds to XML Schema. // if (gen_xml_schema) { if (NarrowString name = ops.extern_xml_schema ()) { if (file_path.string () != name) gen_xml_schema = false; } } if (gen_xml_schema) return 0; } // Process names. // { SerializerNameProcessor proc; if (!proc.process (ops, schema, file_path, true)) throw Failed (); } NarrowString name (file_path.leaf ().string ()); NarrowString skel_suffix (ops.sskel_file_suffix ()); NarrowString impl_suffix (ops.simpl_file_suffix ()); NarrowString hxx_suffix (ops.hxx_suffix ()); NarrowString cxx_suffix (ops.cxx_suffix ()); NarrowString hxx_obj_regex (find_value (ops.hxx_regex (), "")); NarrowString hxx_skel_regex (find_value (ops.hxx_regex (), "sskel")); NarrowString hxx_regex (find_value (ops.hxx_regex (), "simpl")); NarrowString cxx_regex (find_value (ops.cxx_regex (), "simpl")); // Here we need to make sure that hxx_obj_expr is the same // as in generate(). // Regex hxx_obj_expr ( hxx_obj_regex.empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + hxx_suffix + "#" : hxx_obj_regex); // Here we need to make sure that hxx_skel_expr is the same // as in the C++/Serializer generator. // Regex hxx_skel_expr ( hxx_skel_regex.empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + hxx_suffix + "#" : hxx_skel_regex); Regex hxx_expr ( hxx_regex.empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + impl_suffix + hxx_suffix + "#" : hxx_regex); Regex cxx_expr ( cxx_regex.empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + impl_suffix + cxx_suffix + "#" : cxx_regex); if (!hxx_expr.match (name)) { wcerr << "error: serializer implementation header expression '" << hxx_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } if (!cxx_expr.match (name)) { wcerr << "error: serializer implementation source expression '" << cxx_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } NarrowString hxx_skel_name (hxx_skel_expr.replace (name)); NarrowString hxx_name (hxx_expr.replace (name)); NarrowString cxx_name (cxx_expr.replace (name)); Path hxx_path (hxx_name); Path cxx_path (cxx_name); Path out_dir; if (NarrowString dir = ops.output_dir ()) { try { out_dir = Path (dir); } catch (InvalidPath const&) { wcerr << dir.c_str () << ": error: invalid path" << endl; throw Failed (); } } if (fpt) { // In the file-per-type mode the schema files are always local // unless the user added the directory so that we propagate this // to the output files. // Path fpt_dir (file_path.directory ()); if (!fpt_dir.empty ()) out_dir /= fpt_dir; } if (!out_dir.empty ()) { hxx_path = out_dir / hxx_path; cxx_path = out_dir / cxx_path; } WideOutputFileStream hxx (hxx_path.string ().c_str (), ios_base::out); WideOutputFileStream cxx (cxx_path.string ().c_str (), ios_base::out); if (!hxx.is_open ()) { wcerr << hxx_path << ": error: unable to open in write mode" << endl; throw Failed (); } unlinks.add (hxx_path); file_list.push_back (hxx_path.string ()); if (!cxx.is_open ()) { wcerr << cxx_path << ": error: unable to open in write mode" << endl; throw Failed (); } unlinks.add (cxx_path); file_list.push_back (cxx_path.string ()); // Print copyright and license. // char const* copyright ( ops.proprietary_license () ? copyright_proprietary : copyright_gpl); hxx << copyright; cxx << copyright; // Prologue. // WideInputFileStream prologue; { NarrowString name (find_value (ops.prologue_file (), "simpl")); if (name) open (prologue, name); } // Epilogue. // WideInputFileStream epilogue; { NarrowString name (find_value (ops.epilogue_file (), "simpl")); if (name) open (epilogue, name); } // SLOC counter. // size_t sloc_total (0); bool show_sloc (ops.show_sloc ()); // // Regex guard_expr ("/([a-z])([A-Z])/$1_$2/"); // Split words. NarrowString guard_prefix (ops.guard_prefix ()); if (!guard_prefix) guard_prefix = file_path.directory ().string (); if (guard_prefix) guard_prefix += '_'; bool aggr (ops.generate_aggregate ()); // HXX // { Context ctx (hxx, schema, file_path, ops, 0, &hxx_expr, 0); sloc_filter sloc (hxx); String guard (guard_expr.replace (guard_prefix + hxx_name)); guard = ctx.escape (guard); // Make it a C++ id. std::transform (guard.begin (), guard.end(), guard.begin (), upcase); hxx << "#ifndef " << guard << endl << "#define " << guard << endl << endl; // Copy prologue. // hxx << "// Begin prologue." << endl << "//" << endl; append (hxx, ops.hxx_prologue (), ops.prologue (), "simpl"); append (hxx, find_value (ops.hxx_prologue_file (), "simpl"), prologue); hxx << "//" << endl << "// End prologue." << endl << endl; hxx << "#include " << endl << endl; { ind_filter ind (hxx); // We don't want to indent prologues/epilogues. // Define omit aggregate macro. // hxx << "#ifndef XSDE_OMIT_SAGGR" << endl << "# define XSDE_OMIT_SAGGR" << endl << "# define " << guard << "_CLEAR_OMIT_SAGGR" << endl << "#endif" << endl << endl; // Generate. // hxx << "#include " << ctx.process_include_path (hxx_skel_name) << endl << endl; generate_serializer_header (ctx); // Clear omit aggregate macro. // hxx << "#ifdef " << guard << "_CLEAR_OMIT_SAGGR" << endl << "# undef XSDE_OMIT_SAGGR" << endl << "#endif" << endl << endl; if (aggr) { hxx << "#ifndef XSDE_OMIT_SAGGR" << endl << endl; generate_serializer_aggregate_header (ctx); hxx << "#endif // XSDE_OMIT_SAGGR" << endl << endl; } } hxx << "#include " << endl << endl; // Copy epilogue. // hxx << "// Begin epilogue." << endl << "//" << endl; append (hxx, find_value (ops.hxx_epilogue_file (), "simpl"), epilogue); append (hxx, ops.hxx_epilogue (), ops.epilogue (), "simpl"); hxx << "//" << endl << "// End epilogue." << endl << endl; hxx << "#endif // " << guard << endl; if (show_sloc) wcerr << hxx_path << ": " << sloc.stream ().count () << endl; sloc_total += sloc.stream ().count (); } // CXX // { Context ctx (cxx, schema, file_path, ops, 0, &hxx_expr, 0); sloc_filter sloc (cxx); // Copy prologue. // cxx << "// Begin prologue." << endl << "//" << endl; append (cxx, ops.cxx_prologue (), ops.prologue (), "simpl"); append (cxx, find_value (ops.cxx_prologue_file (), "simpl"), prologue); cxx << "//" << endl << "// End prologue." << endl << endl; cxx << "#include " << endl << endl; cxx << "#include " << ctx.process_include_path (hxx_name) << endl << endl; // Generate. // { ind_filter ind (cxx); // We don't want to indent prologues/epilogues. generate_serializer_source (ctx, hxx_obj_expr); if (aggr) generate_serializer_aggregate_source (ctx); } cxx << "#include " << endl << endl; // Copy epilogue. // cxx << "// Begin epilogue." << endl << "//" << endl; append (cxx, find_value (ops.cxx_epilogue_file (), "simpl"), epilogue); append (cxx, ops.cxx_epilogue (), ops.epilogue (), "simpl"); cxx << "//" << endl << "// End epilogue." << endl << endl; if (show_sloc) wcerr << cxx_path << ": " << sloc.stream ().count () << endl; sloc_total += sloc.stream ().count (); } return sloc_total; } catch (UnrepresentableCharacter const& e) { wcerr << "error: character at position " << e.position () << " " << "in string '" << e.string () << "' is unrepresentable in " << "the target encoding" << endl; throw Failed (); } catch (NoNamespaceMapping const& e) { wcerr << e.file () << ":" << e.line () << ":" << e.column () << ": error: unable to map XML Schema namespace '" << e.ns () << "' to C++ namespace" << endl; wcerr << e.file () << ":" << e.line () << ":" << e.column () << ": info: use the --namespace-map or --namespace-regex option " << "to provide custom mapping" << endl; throw Failed (); } catch (InvalidNamespaceMapping const& e) { wcerr << "error: invalid XML to C++ namespace mapping specified: " << "'" << e.mapping () << "': " << e.reason () << endl; throw Failed (); } catch (cutl::re::format const& e) { wcerr << "error: invalid regex: '" << e.regex ().c_str () << "': " << e.description ().c_str () << endl; throw Failed (); } catch (cutl::re::wformat const& e) { wcerr << "error: invalid regex: '" << e.regex () << "': " << e.description ().c_str () << endl; throw Failed (); } } }