host/config/fromheader.cpp (538 lines of code) (raw):

// // fromheader.cpp: Create serialize*.{cpp,h} files from config header files // // Limitations: // - Does not use pre-processor to do conditional compilation // - Does not handle nested structure definitions. // - Does not handle forward template declarations // #include <map> #include <vector> #include <algorithm> #include <cctype> #include <fstream> #include <cstring> #include <boost/spirit/include/classic.hpp> #include <boost/spirit/include/classic_core.hpp> #include <boost/spirit/include/phoenix1_primitives.hpp> #include <boost/spirit/include/phoenix1_functions.hpp> #include "inmsafecapis.h" using namespace BOOST_SPIRIT_CLASSIC_NS; using namespace phoenix; using namespace std; struct HeaderContext { string filename; map< string, vector<string> > info; vector<string> includes; }; static bool enableDebug = false; struct SlurpException : std::exception { virtual const char* what() const throw() { return "couldn't slurp file contents"; } }; struct ParseException : std::exception { virtual const char* what() const throw() { return "couldn't parse header file"; } }; struct store_field_impl { template <typename Container, typename Key, typename Item> struct result { typedef void type; }; template <typename Container, typename Key, typename Item> void operator()(Container& c, Key const& key, Item item) const { c[ key ].push_back( item ); } }; phoenix::function<store_field_impl> const store_field_a = store_field_impl(); typedef void (*Printer_t)(HeaderContext const&); typedef std::map<std::string, Printer_t> OutputDispatcher_t; string slurpFile( const char* filename ) { FILE* ifp = fopen( filename, "r" ); if( NULL == ifp ) { throw SlurpException(); } size_t count; char buffer[ 8192 ]; string result; while( (count = fread( buffer, 1, sizeof( buffer ) - 1 , ifp )) > 0 ) { buffer[ count ] = 0; result += buffer; } fclose( ifp ); return result; } struct CppGrammar : grammar<CppGrammar> { template <typename ScannerT> struct definition { definition( CppGrammar const& self ) { typedefDefinition = str_p( "typedef" ) >> lexeme_d[ +(~ch_p(';')) ] >> ';'; operatorDefinition = str_p("operator") >> ((str_p("new")>>!str_p("[]")) |(str_p("delete")>>!str_p("[]")) |str_p("->*") |str_p("<<=") |str_p(">>=") |str_p("+=") |str_p("-=") |str_p("<<") |str_p(">>") |str_p("==") |str_p("!=") |str_p("<=") |str_p(">=") |str_p("++") |str_p("--") |str_p("&=") |str_p("^=") |str_p("|=") |str_p("&&") |str_p("||") |str_p("%=") |str_p("[]") |str_p("()") |str_p("->") |ch_p('+') |'-' |'*' |'/' |'=' |'<' |'>' |'%' |'&' |'^' |'!' |'|' |'~' |','); templateDefinition = str_p( "template" ) >> comment_nest_p("<",">") >> (((str_p("struct")|str_p("class")) >> lexeme_d[+(alnum_p| '_')]) |(lexeme_d[+(alnum_p| '_' | ':' )] >> !comment_nest_p("<",">") >> !*(str_p("const")|'*'|'&') >> operatorDefinition | lexeme_d[+(alnum_p|'_')] >> comment_nest_p('(',')') >> !str_p( "const" ))) >> comment_nest_p( "{", "}" ) >> !ch_p(';'); // won't handle forward decls staticDefinition = str_p( "static" ) >> lexeme_d[ +(~ch_p(';')) ] >> ';'; enumDefinition = str_p( "enum" ) >> !lexeme_d[+(alnum_p| '_')] >> comment_nest_p( "{","}" ) >> ';'; declaration = typedefDefinition | templateDefinition | staticDefinition | enumDefinition; function = lexeme_d[+(alnum_p| '_' | ':' )] >> !comment_nest_p('<','>') >> !*(str_p("const")|'*'|'&') >> ( operatorDefinition | lexeme_d[+(alnum_p|'_')] ) >> comment_nest_p('(',')') >> !str_p( "const" ) >> !(str_p("throw") >> comment_nest_p("(", ")")) >> (comment_nest_p( "{", "}" ) | ';'); constructor = lexeme_d[+(alnum_p| '_' | '~' )] >> comment_nest_p('(',')') >> !(':' >> lexeme_d[+(alnum_p| '_')] >> comment_nest_p('(',')') >> *(ch_p(',') >> lexeme_d[+(alnum_p| '_')] >> comment_nest_p('(',')'))) >> (comment_nest_p("{", "}")|';'); memberType = ( str_p( "enum" ) >> !lexeme_d[+(alnum_p| '_')] >> comment_nest_p( "{","}" ) | (((str_p("unsigned") >> !str_p("long"))|str_p("long")) >> (str_p("int")|str_p("char")|str_p("long")) ) | ( lexeme_d[+(alnum_p| '_' | ':' )] >> !comment_nest_p('<','>') ) ) >> !(str_p("const")|'*'|'&'); memberName = lexeme_d[+(alnum_p|'_')] [store_field_a( phoenix::var(self.hash), phoenix::var(self.structName), construct_<std::string>(arg1,arg2))]; member = memberType >> memberName >> !comment_nest_p('[',']') >> *(ch_p(',') >> !ch_p('*') >> memberName >> !comment_nest_p('[',']')) >> ';'; item = declaration | function | constructor | member; structDefinition = str_p( "struct" ) >> ( +( alnum_p | '_' ) )[assign_a(self.structName)] >> '{' >> *item >> '}' >> ';'; header = *( structDefinition | anychar_p ); } rule<ScannerT> header, structDefinition, item; rule<ScannerT> declaration, function, constructor, member; rule<ScannerT> typedefDefinition, templateDefinition; rule<ScannerT> staticDefinition, enumDefinition; rule<ScannerT> operatorDefinition; rule<ScannerT> memberType, memberName; rule<ScannerT> const& start() const { return header; } }; mutable map< string,vector<string> > hash; // hack: store state better mutable string structName; }; map< string,vector<string> > parseHeader( const char* filename ) { // // Do not use file_iterator<> as it crashes on HP-UX // string content = slurpFile( filename ); CppGrammar grammar; bool const headerParsed = parse( content.begin(), content.end(), grammar >> end_p, space_p | comment_p( "//" ) | comment_p( "/*", "*/" ) ).full; if( !headerParsed ) { throw ParseException(); } return grammar.hash; } void showUsage( const char* programName ) { cerr << "Usage: " << programName << " [--includes <include_list|@include.inc>] <src|header> <headerfile.h> <--testbed|--normal>" << endl; } char const* basename( const char* str, char delim ) { char const* result = strrchr( str, delim ); return result ? result + 1 : str; } void printSerializeSource( HeaderContext const& context ) { #ifdef SV_UNIX const char* filename = basename( context.filename.c_str(), '/' ); #else const char* filename = basename( context.filename.c_str(), '\\' ); #endif string cppName = filename; const char* filenameStr = cppName.c_str(); if( filenameStr == strstr( filenameStr, "intermediate_" ) ) { cppName = cppName.substr( sizeof( "intermediate_" ) - 1 ); } else { cppName[ cppName.length() -1 ] = 'c'; cppName += "pp"; } cout << "//" << endl << "// serialize" << cppName << ": (generated file) marshal/unmarshal code" << endl << "//" << endl << endl; for( vector<string>::const_iterator i( context.includes.begin() ), end( context.includes.end() ); i != end; ++i ) { // If using gcc 3.3.3 (SuSe's default), because compiler refuses to // compile header files (even when using -MM), we rename as // intermediate<filename>.cpp. So we undo that hack here. string finalName = *i; const char* includeFilename = finalName.c_str(); if( includeFilename == strstr( includeFilename, "intermediate_" ) ) { finalName = finalName.substr( sizeof( "intermediate_" ) - 1 ); finalName = finalName.substr( 0, finalName.size() - 3 ); finalName += "h"; } cout << "#include \"serialize" << finalName << "\"" << endl; } if (enableDebug) { cout << "using namespace std;" << endl; } cout << endl; for( map< string,vector<string> >::const_iterator i( context.info.begin() ), end( context.info.end() ); i != end; ++i ) { cout << "template<>" << endl << (*i).first << " unmarshal<" << (*i).first << ">( UnmarshalStream& in, int version ) {" << endl; cout << " " << (*i).first << " s;" << endl << endl; if (enableDebug) { cout << " cout << endl;" << endl; cout << " tabs++;" << endl; cout << " cout.width(tabs*TAB_SPACE+MAX_WORD_LEN);" << endl; cout << " cout << \"" << (*i).first << "\" << endl;" << endl; cout << " tabs++;" << endl; } for( vector<string>::const_iterator j( (*i).second.begin() ), endJ( (*i).second.end() ); j != endJ; ++j ) { if (enableDebug) { cout << " cout.width(tabs*TAB_SPACE+MAX_WORD_LEN);" << endl; cout << " cout << \"" << *j << "\";" << endl; } cout << " in >> s." << *j << ";" << endl; if (enableDebug) { cout << " cout << endl;" << endl; } } if (enableDebug) { cout << " tabs--;" << endl; cout << " tabs--;" << endl; cout << " cout.width(tabs*TAB_SPACE+MAX_WORD_LEN);" << endl; } cout << endl << " return s;" << endl << "}" << endl << endl; cout << "std::ostream& operator<<( std::ostream& o, CxArgObj<" << (*i).first << "> v ) {" << endl << " " << (*i).first << " const& x = v.value;" << endl << endl << " o << " << "\"a:" << static_cast<unsigned>( (*i).second.size()+1 ) << ":{i:0;i:0;\";" << endl; int index = 1; for( vector<string>::const_iterator j( (*i).second.begin() ), end( (*i).second.end() ); j != end; ++j, ++index ) { cout << " o << \"i:" << index << ";\" << cxArg( x." << *j << " );" << endl; } cout << " o << \"}\";" << endl << endl << " return o;" << endl << "}" << endl << endl; } } void printSerializeHeader( HeaderContext const& context ) { #ifdef SV_UNIX const char* filename = basename( context.filename.c_str(), '/' ); #else const char* filename = basename( context.filename.c_str(), '\\' ); #endif string tempFilename; if( filename == strstr( filename, "intermediate_" ) ) { filename = filename + sizeof( "intermediate_" ) - 1; tempFilename = filename; tempFilename = tempFilename.substr( 0, tempFilename.size() - 3 ); tempFilename += 'h'; filename = tempFilename.c_str(); } string headerGuard = filename; headerGuard[ headerGuard.find('.') ] = '_'; transform( headerGuard.begin(), headerGuard.end(), headerGuard.begin(), static_cast<int(*)(int)>( toupper ) ); cout << "//" << endl << "// serialize" << filename << ": (generated file) " << "marshaling/unmarshaling code" << endl << "//" << endl << "#ifndef SERIALIZE" << headerGuard << endl << "#define SERIALIZE" << headerGuard << endl << endl << "#include \"unmarshal.h\"" << endl << "#include \"marshal.h\"" << endl << "#include \"" << filename << "\"" << endl << endl; for( map< string,vector<string> >::const_iterator i( context.info.begin() ), end( context.info.end() ); i != end; ++i ) { cout << "template<>" << endl << "struct UnmarshalIsStructure<" << (*i).first << "> {" << endl << " static const bool Value = true;" << endl << "};" << endl << endl << "template<>" << endl << (*i).first << " unmarshal<" << (*i).first << ">( UnmarshalStream& in, int version );" << endl << endl << "std::ostream& operator<<( std::ostream& o, CxArgObj<" << (*i).first << "> v );" << endl << endl; } cout << "#endif // SERIALIZE" << headerGuard << endl; } void printXmlizeSource( HeaderContext const& context ) { #ifdef SV_UNIX const char* filename = basename( context.filename.c_str(), '/' ); #else const char* filename = basename( context.filename.c_str(), '\\' ); #endif string cppName = filename; const char* filenameStr = cppName.c_str(); if( filenameStr == strstr( filenameStr, "intermediate_" ) ) { cppName = cppName.substr( sizeof( "intermediate_" ) - 1 ); } else { cppName[ cppName.length() -1 ] = 'c'; cppName += "pp"; } cout << "//" << endl << "// xmlize" << cppName << ": (generated file) xmlmarshal/xmlunmarshal code" << endl << "//" << endl << endl; for( vector<string>::const_iterator i( context.includes.begin() ), end( context.includes.end() ); i != end; ++i ) { // If using gcc 3.3.3 (SuSe's default), because compiler refuses to // compile header files (even when using -MM), we rename as // intermediate<filename>.cpp. So we undo that hack here. string finalName = *i; const char* includeFilename = finalName.c_str(); if( includeFilename == strstr( includeFilename, "intermediate_" ) ) { finalName = finalName.substr( sizeof( "intermediate_" ) - 1 ); finalName = finalName.substr( 0, finalName.size() - 3 ); finalName += "h"; } cout << "#include \"xmlize" << finalName << "\"" << endl; } cout << endl; for( map< string,vector<string> >::const_iterator i( context.info.begin() ), end( context.info.end() ); i != end; ++i ) { cout << "void extractfromreqresp( const ParameterGroup& o, " << (*i).first << " &x, const std::string &name ) {" << endl << " " << "const ParameterGroup *ppg = &o;" << endl << " " << "if (!name.empty()) {" << endl << " " << " " << "ParameterGroups_t::const_iterator i = o.m_ParamGroups.find(name);" << endl << " " << " " << "if (i != o.m_ParamGroups.end()) {" << endl << " " << " " << " " << "const ParameterGroup &pg = i->second;" << endl << " " << " " << " " << "ppg = &pg;" << endl << " " << " " << "}" << endl << " " << " " << "else {" << endl << " " << " " << " " << "throw INMAGE_EX(\"could not find expected parameter name:\")(name);" << endl << " " << " " << "}" << endl << " " << "}" << endl << endl; for( vector<string>::const_iterator j( (*i).second.begin() ), end( (*i).second.end() ); j != end; ++j ) { cout << " extractfromreqresp(*ppg, x." << *j << ", " << "\"" << *j << "\"" << " );" << endl; } cout << endl << "}" << endl << endl; cout << "void insertintoreqresp( ParameterGroup& o, CxArgObj<" << (*i).first << "> v, const std::string &name ) {" << endl << " " << "ParameterGroup *ppg = &o;" << endl << " " << "if (!name.empty()) {" << endl << " " << " " <<"std::pair<ParameterGroupsIter_t, bool> pgpair = o.m_ParamGroups.insert(std::make_pair(name, ParameterGroup()));" << endl << " " << " " << "ParameterGroupsIter_t &pgpairiter = pgpair.first;" << endl << " " << " " << "ParameterGroup &pg = pgpairiter->second;" << endl << " " << " " << "ppg = &pg;" << endl << " " << "}" << endl << endl << " " << (*i).first << " const& x = v.value;" << endl; for( vector<string>::const_iterator j( (*i).second.begin() ), end( (*i).second.end() ); j != end; ++j ) { cout << " insertintoreqresp(*ppg, cxArg( x." << *j << " )" << ", " << "\"" << *j << "\"" << " );" << endl; } cout << endl << "}" << endl << endl; } if (enableDebug) { cout << "using namespace std;" << endl; } } void printXmlizeHeader( HeaderContext const& context ) { #ifdef SV_UNIX const char* filename = basename( context.filename.c_str(), '/' ); #else const char* filename = basename( context.filename.c_str(), '\\' ); #endif string tempFilename; if( filename == strstr( filename, "intermediate_" ) ) { filename = filename + sizeof( "intermediate_" ) - 1; tempFilename = filename; tempFilename = tempFilename.substr( 0, tempFilename.size() - 3 ); tempFilename += 'h'; filename = tempFilename.c_str(); } string headerGuard = filename; headerGuard[ headerGuard.find('.') ] = '_'; transform( headerGuard.begin(), headerGuard.end(), headerGuard.begin(), static_cast<int(*)(int)>( toupper ) ); cout << "//" << endl << "// xmlize" << filename << ": (generated file) " << "xmlmarshaling/xmlunmarshaling code" << endl << "//" << endl << "#ifndef XMLIZE" << headerGuard << endl << "#define XMLIZE" << headerGuard << endl << endl << "#include \"xmlunmarshal.h\"" << endl << "#include \"xmlmarshal.h\"" << endl << "#include \"" << filename << "\"" << endl << endl; cout << endl; for( map< string,vector<string> >::const_iterator i( context.info.begin() ), end( context.info.end() ); i != end; ++i ) { cout << "void insertintoreqresp( ParameterGroup& o, CxArgObj<" << (*i).first << "> v, const std::string &name = std::string() );" << endl << endl; cout << "void extractfromreqresp( const ParameterGroup& o, " << (*i).first << " &x, const std::string &name = std::string() );" << endl << endl; cout << "namespace NS_" << (*i).first << endl << '{' << endl; for( vector<string>::const_iterator j( (*i).second.begin() ), end( (*i).second.end() ); j != end; ++j ) { cout << " const char " << *j << "[] = " << "\"" << *j << "\"" << ";" << endl; } cout << '}' << endl << endl; } cout << "#endif // XMLIZE" << headerGuard << endl; } void trimEndOfLine( std::string& s ) { size_t pos = s.size(); for( ; pos && ( '\r' == s[pos-1] || '\n' == s[pos-1] ); --pos ) ; s.erase( pos, s.size() - pos ); } vector<string> parseIncludes( const char* str ) { vector<string> result; // Windows style includes specified by --includes @filename.inc if( '@' == str[0] ) { ifstream in( &str[1] ); if( !in ) { cerr << "couldn't read from " << &str[1] << endl; return result; } // Process line by line rather than parsing string line; getline( in, line ); trimEndOfLine( line ); result.push_back( line ); for( getline( in, line ); !in.eof(); getline( in, line ) ) { trimEndOfLine( line ); const char prefix[] = "Note: including file: "; if( 0 == line.rfind( prefix ) && ' ' != line[sizeof(prefix)-1] ) { const char* includeFile = strrchr( line.c_str(), '\\' ); if( NULL != includeFile && strstr( includeFile-7, "\\config" ) == includeFile-7 ) { result.push_back( &includeFile[1] ); } } } return result; } // Unix style includes if( !parse( str, +(alnum_p|'_'|'-') >> ".o:" >> !ch_p( '\\' ) >> *(((str_p("config/") >> lexeme_d[+(alnum_p|'_'|'/'|'.'|'-'|'+')][push_back_a(result)]) | ( lexeme_d[+(alnum_p|'_'|'/'|'.'|'-'|'+')] ) ) >> !ch_p('\\') ), space_p | comment_p( "#" ) ).full ) { cerr << "Couldn't parse argument to `includes': " << str << endl; } return result; } int main( int argc, const char* argv[] ) { init_inm_safe_c_apis(); const char* program = argv[0]; const std::string XMLIZE = "xmlize"; const std::string SERIALIZE = "serialize"; if( argc < 3 ) { showUsage( program ); return -1; } HeaderContext context; const char* action = argv[1]; context.filename = argv[2]; vector<string> includes; if( string(argv[1]) == "--includes" ) { if( 7 != argc ) { showUsage( program ); return -1; } action = argv[3]; context.filename = argv[4]; context.includes = parseIncludes( argv[2] ); if( string(argv[5]) == "--normal" ) { enableDebug = false; } else if ( string(argv[5]) == "--testbed" ) { enableDebug = true; } } OutputDispatcher_t serialdispatch; serialdispatch["src"] = printSerializeSource; serialdispatch["header"] = printSerializeHeader; OutputDispatcher_t xmldispatch; xmldispatch["src"] = printXmlizeSource; xmldispatch["header"] = printXmlizeHeader; Printer_t func = serialdispatch[ action ]; if (XMLIZE == argv[6]) { func = xmldispatch[ action ]; } if( 0 == func ) { showUsage( program ); return -1; } try { context.info = parseHeader( context.filename.c_str() ); } catch( std::exception const& e ) { cerr << program << ": exception " << e.what() << endl; return -1; } (*func)( context ); return 0; }