// ========================================================================= // @author Leonardo Florez-Valencia (florez-l@javeriana.edu.co) // ========================================================================= #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ------------------------------------------------------------------------- typedef boost::filesystem::path TPath; typedef std::vector< std::string > TStrings; typedef std::map< std::string, TStrings > TDefinitions; struct TData { TPath Input; TPath Output; std::set< TPath > IncludePaths; TStrings InputLines; TStrings CompleteLines; TDefinitions Definitions; }; TData Data; // ------------------------------------------------------------------------- bool Arguments( int argc, char* argv[] ); bool Read( const std::string& input, std::string& buffer ); bool Lines( const std::string& input, TStrings& lines ); void Includes( TStrings& new_lines, const TStrings& lines ); void Definitions( TDefinitions& defs, const TStrings& lines ); int Parse( ); void Render( std::ostream& out, const std::string& in, mstch::map& context ); // ------------------------------------------------------------------------- int main( int argc, char* argv[] ) { if( Arguments( argc, argv ) ) { if( Lines( Data.Input.string( ), Data.InputLines ) ) { Includes( Data.CompleteLines, Data.InputLines ); Definitions( Data.Definitions, Data.CompleteLines ); return( Parse( ) ); } else return( 1 ); } else return( 1 ); } // ------------------------------------------------------------------------- bool Arguments( int argc, char* argv[] ) { // Declare the supported options. boost::program_options::options_description desc( "Allowed options" ); desc.add_options( ) ( "help,h", "produce help message" ) ( "input,i", boost::program_options::value< std::string >( ), "Input" ) ( "output,o", boost::program_options::value< std::string >( ), "Output" ) ( "include,I", boost::program_options::value< TStrings >( ), "Include directory(ies)" ) ; try { // Parse input arguments boost::program_options::variables_map vm; boost::program_options::store( boost::program_options::parse_command_line( argc, argv, desc ), vm ); boost::program_options::notify( vm ); if( vm.count( "help" ) ) { std::cerr << desc << std::endl; return( false ); } // fi if( vm.count( "input" ) == 0 || vm.count( "output" ) == 0 ) { std::cerr << "Invalid usage: --input and --output are required." << std::endl << desc << std::endl; return( false ); } // fi // Get values Data.Input = boost::filesystem::canonical( TPath( vm[ "input" ].as< std::string >( ) ) ); Data.Output = TPath( vm[ "output" ].as< std::string >( ) ); Data.IncludePaths.clear( ); Data.IncludePaths.insert( Data.Input.parent_path( ) ); if( vm.count( "include" ) > 0 ) for( const std::string& dir: vm[ "include" ].as< TStrings >( ) ) Data.IncludePaths.insert( boost::filesystem::canonical( TPath( dir ) ) ); return( true ); } catch( std::exception& err ) { std::cerr << "Error caught: " << err.what( ) << std::endl; std::cerr << desc << std::endl; return( false ); } // yrt } // ------------------------------------------------------------------------- bool Read( const std::string& input, std::string& buffer ) { std::ifstream in( input.c_str( ) ); if( !in ) { std::cerr << "===============================" << std::endl << "Error caught: " << std::endl << "could not load input file \"" << input << "\"" << std::endl << "===============================" << std::endl << std::endl; return( false ); } // fi typedef std::istreambuf_iterator< char > _TDIt; std::istringstream str( std::string( ( _TDIt( in ) ), _TDIt( ) ) ); buffer = str.str( ); in.close( ); return( true ); } // ------------------------------------------------------------------------- bool Lines( const std::string& input, TStrings& lines ) { std::string buffer; if( Read( input, buffer ) ) { lines.clear( ); std::istringstream str( buffer ); std::string line; while( std::getline( str, line ) ) lines.push_back( line ); return( true ); } else return( false ); } // ------------------------------------------------------------------------- void Includes( TStrings& new_lines, const TStrings& lines ) { bool ok = true; new_lines.clear( ); for( const std::string& line: lines ) { std::size_t a = line.find_first_of( "$" ); std::size_t b = line.find( "$include", a ); if( a == b && a != std::string::npos ) { std::size_t s = line.find_first_of( "\"" ); std::size_t l = line.find_last_of( "\"" ); std::string fname = line.substr( s + 1, l - s - 1 ); std::set< TPath >::const_iterator pIt = Data.IncludePaths.begin( ); bool found = false; while( pIt != Data.IncludePaths.end( ) && !found ) { TPath incl = *pIt; incl /= fname; if( boost::filesystem::exists( incl ) ) { TStrings incl_lines; if( Lines( incl.string( ), incl_lines ) ) { for( const std::string& l: incl_lines ) { if( l.find( "$include" ) != std::string::npos ) ok = false; new_lines.push_back( l ); } // rof } // fi found = true; } // fi pIt++; } // elihw } else new_lines.push_back( line ); } // rof if( !ok ) { TStrings new_new_lines; Includes( new_new_lines, new_lines ); new_lines = new_new_lines; } // fi } // ------------------------------------------------------------------------- void Definitions( TDefinitions& defs, const TStrings& lines ) { // Tokenizer typedef boost::char_separator< char > _TSep; typedef boost::tokenizer< _TSep > _TTok; // Identify definitions for( const std::string& line: lines ) { std::size_t a = line.find_first_not_of( " \t" ); if( a != std::string::npos ) { if( line[ a ] == '$' ) { _TTok tokens( line, _TSep( "=;" ) ); _TTok::const_iterator t = tokens.begin( ); std::string d = *t + "$"; std::pair< TDefinitions::iterator, bool > i = defs.insert( TDefinitions::value_type( d, TStrings( ) ) ); if( i.second ) { for( ++t; t != tokens.end( ); ++t ) i.first->second.push_back( *t ); if( i.first->second.size( ) == 0 ) defs.erase( i.first ); } else throw std::runtime_error( "Duplicated defition: " + d ); } // fi } // fi } // rof // Expand definitions bool stop = false; while( !stop ) { // Change all possible values for( TDefinitions::value_type& d: defs ) { TStrings nValues; for( const std::string& v: d.second ) { std::size_t a = v.find_first_of( "$" ); if( a != std::string::npos ) { std::size_t b = v.find_first_of( "$", a + 1 ); std::string c = v.substr( a, b - a + 1 ); TDefinitions::const_iterator cIt = defs.find( c ); if( cIt != defs.end( ) ) { for( std::string cV: cIt->second ) { std::string vnew = v; boost::algorithm::replace_all( vnew, c, cV ); nValues.push_back( vnew ); } // rof } // fi } else nValues.push_back( v ); } // rof d.second = nValues; } // rof // Check stop stop = true; for( TDefinitions::value_type& d: defs ) { for( const std::string& v: d.second ) stop &= ( v.find_first_of( "$" ) == std::string::npos ); } // rof } // elihw // Clear definitions TDefinitions::iterator dIt = defs.begin( ); while( dIt != defs.end( ) ) { if( dIt->second.size( ) == 0 ) { TDefinitions::iterator eIt = dIt; dIt++; defs.erase( eIt ); } else dIt++; } // elihw } // ------------------------------------------------------------------------- int Parse( ) { // Parse lines std::stringstream lines; for( const std::string& line: Data.InputLines ) { // Check if a this is a definition line std::size_t a = line.find_first_of( "$" ); std::size_t b = line.find_first_of( "=" ); bool ok = true; if( a != std::string::npos && b != std::string::npos ) { std::string c = line.substr( a, b - a ) + "$"; if( Data.Definitions.find( c ) != Data.Definitions.end( ) ) ok = false; } // fi if( a != std::string::npos ) { if( a == line.find( "$include", a ) ) ok = false; } // fi if( ok ) { std::string t = line; std::set< std::string > tags; for( const TDefinitions::value_type& d: Data.Definitions ) { if( line.find( d.first ) != std::string::npos ) tags.insert( d.first ); } // rof if( tags.size( ) > 0 ) { std::stringstream pre, pos; std::set< std::string >::const_iterator tIt = tags.begin( ); std::set< std::string >::const_reverse_iterator rtIt = tags.rbegin( ); for( ; tIt != tags.end( ); ++tIt, ++rtIt ) { pre << "{{#" << tIt->substr( 1, tIt->size( ) - 2 ) << "}}"; pos << "{{/" << rtIt->substr( 1, rtIt->size( ) - 2 ) << "}}"; boost::algorithm::replace_all( t, *tIt, "{{" + tIt->substr( 1, tIt->size( ) - 2 ) + "}}" ); } // rof pre << std::endl << t << std::endl << pos.str( ); t = pre.str( ); } // fi lines << t << std::endl; } // fi } // rof // Create moustache context typedef std::pair< std::string, std::string > TStringPair; typedef std::pair< std::string, mstch::array > TStringArrayPair; mstch::map context; for( const TDefinitions::value_type& d: Data.Definitions ) { mstch::array values; std::string c = d.first.substr( 1, d.first.size( ) - 2 ); for( const std::string& v: d.second ) { mstch::map def; def.insert( TStringPair( c, v ) ); values.push_back( def ); } // rof context.insert( TStringArrayPair( c, values ) ); } // rof // Parse moustache syntax and save output file std::ofstream out( Data.Output.string( ).c_str( ) ); if( !out ) { std::cerr << "===============================" << std::endl << "Error caught: " << std::endl << "could not save to output file \"" << Data.Output.string( ) << "\"" << std::endl << "===============================" << std::endl << std::endl; return( 1 ); } // fi Render( out, lines.str( ), context ); out << std::endl; out.close( ); return( 0 ); } // ------------------------------------------------------------------------- void Render( std::ostream& out, const std::string& in, mstch::map& context ) { std::string s = mstch::render( in, context ); boost::algorithm::replace_all( s, "<", "<" ); boost::algorithm::replace_all( s, ">", ">" ); out << s; } // eof - $RCSfile$