// file : xsd/cxx/parser/generator.cxx // author : Boris Kolpackov // 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 "../../../libxsd/xsd/cxx/version.hxx" using std::endl; using std::wcerr; using std::wcout; 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, an XML Schema to\n" "// C++ data binding compiler.\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" "// In addition, as a special exception, Code Synthesis Tools CC gives\n" "// permission to link this program with the Xerces-C++ library (or with\n" "// modified versions of Xerces-C++ that use the same license as Xerces-C++),\n" "// and distribute linked combinations including the two. You must obey\n" "// the GNU General Public License version 2 in all respects for all of\n" "// the code used other than Xerces-C++. If you modify this copy of the\n" "// program, you may extend this exception to your version of the program,\n" "// but you are not obligated to do so. If you do not wish to do so, delete\n" "// this exception statement from your version.\n" "//\n" "// Furthermore, Code Synthesis Tools CC makes a special exception for\n" "// the Free/Libre and Open Source Software (FLOSS) which is described\n" "// in the accompanying FLOSSE file.\n" "//\n\n"; char const copyright_proprietary[] = "// Copyright (c) 2005-2011 Code Synthesis Tools CC\n" "//\n" "// This program was generated by CodeSynthesis XSD, an XML Schema\n" "// to C++ data binding compiler, in the Proprietary License mode.\n" "// You should have received a proprietary license from Code Synthesis\n" "// Tools CC prior to generating this code. See the license text for\n" "// conditions.\n" "//\n\n"; char const copyright_impl[] = "// Not copyrighted - public domain.\n" "//\n" "// This sample parser implementation was generated by CodeSynthesis XSD,\n" "// an XML Schema to C++ data binding compiler. You may use it in your\n" "// programs without any restrictions.\n" "//\n\n"; } void Parser::Generator:: usage () { CXX::Parser::options::print_usage (wcout); CXX::options::print_usage (wcout); } 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 Parser::Generator::Failed (); } } catch (InvalidPath const&) { wcerr << "error: '" << path.c_str () << "' is not a valid " << "filesystem path" << endl; throw Parser::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) { NarrowStrings const& v (primary.empty () ? def : primary); for (NarrowStrings::const_iterator i (v.begin ()), e (v.end ()); i != e; ++i) { os << i->c_str () << endl; } } } size_t Parser::Generator:: generate (Parser::options const& ops, Schema& schema, Path const& file_path, bool fpt, StringLiteralMap const& string_literal_map, bool gen_driver, const WarningSet& disabled_warnings, FileList& file_list, AutoUnlinks& unlinks) { using std::ios_base; typedef cutl::re::regexsub 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; } } bool impl (!generate_xml_schema && (ops.generate_noop_impl () || ops.generate_print_impl ())); bool driver (gen_driver && !generate_xml_schema && ops.generate_test_driver ()); // Evaluate the graph for possibility of generating something useful. // { Validator validator; if (!validator.validate ( ops, schema, file_path, driver, disabled_warnings)) throw Failed (); } // Process names. // { NameProcessor proc; proc.process (ops, schema, file_path, string_literal_map); } bool validation ((ops.xml_parser () == "expat" || ops.generate_validation ()) && !ops.suppress_validation ()); // Compute state machine info. // if (validation) { StateProcessor proc; proc.process (schema, file_path); } // Read-in type maps. // TypeMap::Namespaces type_map; { using namespace TypeMap; NarrowStrings const& files (ops.type_map ()); for (NarrowStrings::const_iterator f (files.begin ()); f != files.end (); ++f ) { NarrowInputFileStream ifs; open (ifs, *f); Lexer l (ifs, *f); TypeMap::Parser p (l, *f); if (!p.parse (type_map)) throw Failed (); } // Add the built-in mappings at the end. // // String-based types. // String char_type (ops.char_type ()); String string_type; if (char_type == L"char") string_type = L"::std::string"; else if (char_type == L"wchar_t") string_type = L"::std::wstring"; else string_type = L"::std::basic_string< " + char_type + L" >"; String xns; { Context ctx (std::wcerr, schema, file_path, ops, 0, 0, 0, 0); xns = ctx.xs_ns_name (); } String buffer (L"::std::auto_ptr< " + xns + L"::buffer >"); TypeMap::Namespace xsd ("http://www\\.w3\\.org/2001/XMLSchema"); xsd.types_push_back ("string", string_type); xsd.types_push_back ("normalizedString", string_type); xsd.types_push_back ("token", string_type); xsd.types_push_back ("Name", string_type); xsd.types_push_back ("NMTOKEN", string_type); xsd.types_push_back ("NMTOKENS", xns + L"::string_sequence"); xsd.types_push_back ("NCName", string_type); xsd.types_push_back ("ID", string_type); xsd.types_push_back ("IDREF", string_type); xsd.types_push_back ("IDREFS", xns + L"::string_sequence"); xsd.types_push_back ("language", string_type); xsd.types_push_back ("anyURI", string_type); xsd.types_push_back ("QName", xns + L"::qname"); xsd.types_push_back ("base64Binary", buffer, buffer); xsd.types_push_back ("hexBinary", buffer, buffer); xsd.types_push_back ("gDay", xns + L"::gday"); xsd.types_push_back ("gMonth", xns + L"::gmonth"); xsd.types_push_back ("gYear", xns + L"::gyear"); xsd.types_push_back ("gMonthDay", xns + L"::gmonth_day"); xsd.types_push_back ("gYearMonth", xns + L"::gyear_month"); xsd.types_push_back ("date", xns + L"::date"); xsd.types_push_back ("time", xns + L"::time"); xsd.types_push_back ("dateTime", xns + L"::date_time"); xsd.types_push_back ("duration", xns + L"::duration"); // Fundamental C++ types. // xsd.types_push_back ("boolean", "bool", "bool"); xsd.types_push_back ("byte", "signed char", "signed char"); xsd.types_push_back ("unsignedByte", "unsigned char", "unsigned char"); xsd.types_push_back ("short", "short", "short"); xsd.types_push_back ("unsignedShort", "unsigned short", "unsigned short"); xsd.types_push_back ("int", "int", "int"); xsd.types_push_back ("unsignedInt", "unsigned int", "unsigned int"); xsd.types_push_back ("long", "long long", "long long"); xsd.types_push_back ("unsignedLong", "unsigned long long", "unsigned long long"); xsd.types_push_back ("integer", "long long", "long long"); xsd.types_push_back ("negativeInteger", "long long", "long long"); xsd.types_push_back ("nonPositiveInteger", "long long", "long long"); xsd.types_push_back ("positiveInteger", "unsigned long long", "unsigned long long"); xsd.types_push_back ("nonNegativeInteger", "unsigned long long", "unsigned long long"); xsd.types_push_back ("float", "float", "float"); xsd.types_push_back ("double", "double", "double"); xsd.types_push_back ("decimal", "double", "double"); type_map.push_back (xsd); // Everything else maps to void. // TypeMap::Namespace rest (".*"); rest.types_push_back (".*", "void", "void"); type_map.push_back (rest); } // Process types. // { TypeProcessor proc; proc.process (ops, schema, gen_driver, type_map); } // // bool inline_ (ops.generate_inline () && !generate_xml_schema); bool source (!generate_xml_schema); // Generate code. // NarrowString name (file_path.leaf ().string ()); NarrowString skel_suffix (ops.skel_file_suffix ()); NarrowString impl_suffix (ops.impl_file_suffix ()); NarrowString hxx_suffix (ops.hxx_suffix ()); NarrowString ixx_suffix (ops.ixx_suffix ()); NarrowString cxx_suffix (ops.cxx_suffix ()); Regex hxx_expr ( ops.hxx_regex ().empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + hxx_suffix + "#" : ops.hxx_regex ()); Regex ixx_expr ( ops.ixx_regex ().empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + ixx_suffix + "#" : ops.ixx_regex ()); Regex cxx_expr ( ops.cxx_regex ().empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + cxx_suffix + "#" : ops.cxx_regex ()); Regex hxx_impl_expr; Regex cxx_impl_expr; Regex cxx_driver_expr; if (impl || driver) { hxx_impl_expr = "#^(.+?)(\\.[^./\\\\]+)?$#$1" + impl_suffix + hxx_suffix + "#"; cxx_impl_expr = "#^(.+?)(\\.[^./\\\\]+)?$#$1" + impl_suffix + cxx_suffix + "#"; cxx_driver_expr = "#^(.+?)(\\.[^./\\\\]+)?$#$1-driver" + cxx_suffix + "#"; } 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 (impl || driver) { if (!hxx_impl_expr.match (name)) { wcerr << "error: implementation header expression '" << hxx_impl_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } if (!cxx_impl_expr.match (name)) { wcerr << "error: implementation source expression '" << cxx_impl_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } if (!cxx_driver_expr.match (name)) { wcerr << "error: driver source expression '" << cxx_driver_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 hxx_impl_name; NarrowString cxx_impl_name; NarrowString cxx_driver_name; if (impl || driver) { hxx_impl_name = hxx_impl_expr.replace (name); cxx_impl_name = cxx_impl_expr.replace (name); cxx_driver_name = cxx_driver_expr.replace (name); } Path hxx_path (hxx_name); Path ixx_path (ixx_name); Path cxx_path (cxx_name); Path hxx_impl_path; Path cxx_impl_path; Path cxx_driver_path; if (impl || driver) { hxx_impl_path = Path (hxx_impl_name); cxx_impl_path = Path (cxx_impl_name); cxx_driver_path = Path (cxx_driver_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; if (impl || driver) { hxx_impl_path = out_dir / hxx_impl_path; cxx_impl_path = out_dir / cxx_impl_path; cxx_driver_path = out_dir /cxx_driver_path; } } // Open the impl files first so that if open fails, the skel files // are not deleted. // WideOutputFileStream hxx_impl; WideOutputFileStream cxx_impl; WideOutputFileStream cxx_driver; if (impl) { if (!ops.force_overwrite ()) { WideInputFileStream tmp ( hxx_impl_path.string ().c_str (), ios_base::in); if (tmp.is_open ()) { wcerr << hxx_impl_path << ": error: cowardly refusing to " << "overwrite an existing file" << endl; throw Failed (); } tmp.close (); } hxx_impl.open (hxx_impl_path.string ().c_str (), ios_base::out); if (!hxx_impl.is_open ()) { wcerr << hxx_impl_path << ": error: unable to open in write mode" << endl; throw Failed (); } unlinks.add (hxx_impl_path); file_list.push_back (hxx_impl_path.string ()); if (!ops.force_overwrite ()) { WideInputFileStream tmp ( cxx_impl_path.string ().c_str (), ios_base::in); if (tmp.is_open ()) { wcerr << cxx_impl_path << ": error: cowardly refusing to " << "overwrite an existing file" << endl; throw Failed (); } tmp.close (); } cxx_impl.open (cxx_impl_path.string ().c_str (), ios_base::out); if (!cxx_impl.is_open ()) { wcerr << cxx_impl_path << ": error: unable to open in write mode" << endl; throw Failed (); } unlinks.add (cxx_impl_path); file_list.push_back (cxx_impl_path.string ()); } if (driver) { if (!ops.force_overwrite ()) { WideInputFileStream tmp ( cxx_driver_path.string ().c_str (), ios_base::in); if (tmp.is_open ()) { wcerr << cxx_driver_path << ": error: cowardly refusing to " << "overwrite an existing file" << endl; throw Failed (); } tmp.close (); } cxx_driver.open (cxx_driver_path.string ().c_str (), ios_base::out); if (!cxx_driver.is_open ()) { wcerr << cxx_driver_path << ": error: unable to open in write " << "mode" << endl; throw Failed (); } unlinks.add (cxx_driver_path); file_list.push_back (cxx_driver_path.string ()); } // Open the skel files. // WideOutputFileStream hxx (hxx_path.string ().c_str (), ios_base::out); WideOutputFileStream ixx; WideOutputFileStream cxx; 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); hxx << copyright; if (inline_) ixx << copyright; if (source) cxx << copyright; if (impl) { hxx_impl << copyright_impl; cxx_impl << copyright_impl; } if (driver) cxx_driver << copyright_impl; // Prologue. // WideInputFileStream prologue; { NarrowString name (ops.prologue_file ()); if (name) open (prologue, name); } // Epilogue. // WideInputFileStream epilogue; { NarrowString name (ops.epilogue_file ()); if (name) open (epilogue, name); } // SLOC counter. // size_t sloc_total (0); bool show_sloc (ops.show_sloc ()); typedef compiler::ostream_filter ind_filter; typedef compiler::ostream_filter sloc_filter; // // 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 += '_'; // HXX // { Context ctx (hxx, schema, file_path, ops, &string_literal_map, &hxx_expr, &ixx_expr, &hxx_impl_expr); 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; // Version check. // hxx << "#include " << endl << endl << "#if (XSD_INT_VERSION != " << XSD_INT_VERSION << "L)" << endl << "#error XSD runtime version mismatch" << endl << "#endif" << endl << endl; hxx << "#include " << endl << endl; // Copy prologue. // hxx << "// Begin prologue." << endl << "//" << endl; append (hxx, ops.hxx_prologue (), ops.prologue ()); append (hxx, ops.hxx_prologue_file (), prologue); hxx << "//" << endl << "// End prologue." << endl << endl; // Generate. // { ind_filter ind (hxx); // We don't want to indent prologues/epilogues. if (!generate_xml_schema) generate_parser_forward (ctx); generate_parser_header (ctx, generate_xml_schema); } if (inline_) hxx << "#include " << ctx.process_include_path (ixx_name) << endl; // Copy epilogue. // hxx << "// Begin epilogue." << endl << "//" << endl; append (hxx, ops.hxx_epilogue_file (), epilogue); append (hxx, ops.hxx_epilogue (), ops.epilogue ()); hxx << "//" << endl << "// End epilogue." << endl << endl; hxx << "#include " << 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, &string_literal_map, &hxx_expr, &ixx_expr, &hxx_impl_expr); sloc_filter sloc (ixx); // Copy prologue. // ixx << "// Begin prologue." << endl << "//" << endl; append (ixx, ops.ixx_prologue (), ops.prologue ()); append (ixx, 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_parser_inline (ctx); } // Copy epilogue. // ixx << "// Begin epilogue." << endl << "//" << endl; append (ixx, ops.ixx_epilogue_file (), epilogue); append (ixx, ops.ixx_epilogue (), ops.epilogue ()); ixx << "//" << endl << "// End epilogue." << endl << 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, &string_literal_map, &hxx_expr, &ixx_expr, &hxx_impl_expr); sloc_filter sloc (cxx); // Copy prologue. // cxx << "// Begin prologue." << endl << "//" << endl; append (cxx, ops.cxx_prologue (), ops.prologue ()); append (cxx, ops.cxx_prologue_file (), prologue); cxx << "//" << endl << "// End prologue." << 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_parser_inline (ctx); generate_parser_source (ctx); if (validation) { generate_element_validation_source (ctx); generate_attribute_validation_source (ctx); generate_characters_validation_source (ctx); } } // Copy epilogue. // cxx << "// Begin epilogue." << endl << "//" << endl; append (cxx, 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 (); } // HXX impl // if (impl) { Context ctx (hxx_impl, schema, file_path, ops, &string_literal_map, &hxx_expr, &ixx_expr, &hxx_impl_expr); String guard (guard_expr.replace (guard_prefix + hxx_impl_name)); guard = ctx.escape (guard); // Make it a C++ id. std::transform (guard.begin (), guard.end(), guard.begin (), upcase); hxx_impl << "#ifndef " << guard << endl << "#define " << guard << endl << endl; hxx_impl << "#include " << ctx.process_include_path (hxx_name) << endl << endl; { ind_filter ind (hxx_impl); generate_impl_header (ctx); } hxx_impl << "#endif // " << guard << endl; } // CXX impl // if (impl) { Context ctx (cxx_impl, schema, file_path, ops, &string_literal_map, &hxx_expr, &ixx_expr, &hxx_impl_expr); cxx_impl << "#include " << ctx.process_include_path (hxx_impl_name) << endl << endl; { ind_filter ind (cxx_impl); generate_impl_source (ctx); } } // CXX driver // if (driver) { Context ctx (cxx_driver, schema, file_path, ops, &string_literal_map, &hxx_expr, &ixx_expr, &hxx_impl_expr); cxx_driver << "#include " << ctx.process_include_path (hxx_impl_name) << endl << endl; { ind_filter ind (cxx_driver); generate_driver_source (ctx); } } 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; wcerr << "info: use the --custom-literals option to provide custom " << "string literals mapping" << 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 (); } } }