1 // =========================================================================
2 // @author Leonardo Florez-Valencia (florez-l@javeriana.edu.co)
3 // =========================================================================
15 #include <boost/algorithm/string/replace.hpp>
16 #include <boost/filesystem/operations.hpp>
17 #include <boost/program_options.hpp>
18 #include <boost/tokenizer.hpp>
19 #include <mstch/mstch.hpp>
21 // -------------------------------------------------------------------------
22 typedef boost::filesystem::path TPath;
23 typedef std::vector< std::string > TStrings;
24 typedef std::map< std::string, TStrings > TDefinitions;
29 std::set< TPath > IncludePaths;
31 TStrings CompleteLines;
32 TDefinitions Definitions;
36 // -------------------------------------------------------------------------
37 bool Arguments( int argc, char* argv[] );
38 bool Read( const std::string& input, std::string& buffer );
39 bool Lines( const std::string& input, TStrings& lines );
40 void Includes( TStrings& new_lines, const TStrings& lines );
41 void Definitions( TDefinitions& defs, const TStrings& lines );
43 void Render( std::ostream& out, const std::string& in, mstch::map& context );
45 // -------------------------------------------------------------------------
46 int main( int argc, char* argv[] )
48 if( Arguments( argc, argv ) )
50 if( Lines( Data.Input.string( ), Data.InputLines ) )
52 Includes( Data.CompleteLines, Data.InputLines );
53 Definitions( Data.Definitions, Data.CompleteLines );
63 // -------------------------------------------------------------------------
64 bool Arguments( int argc, char* argv[] )
66 // Declare the supported options.
67 boost::program_options::options_description desc( "Allowed options" );
69 ( "help,h", "produce help message" )
72 boost::program_options::value< std::string >( ),
77 boost::program_options::value< std::string >( ),
82 boost::program_options::value< TStrings >( ),
83 "Include directory(ies)"
89 // Parse input arguments
90 boost::program_options::variables_map vm;
91 boost::program_options::store(
92 boost::program_options::parse_command_line( argc, argv, desc ), vm
94 boost::program_options::notify( vm );
95 if( vm.count( "help" ) )
97 std::cerr << desc << std::endl;
101 if( vm.count( "input" ) == 0 || vm.count( "output" ) == 0 )
104 << "Invalid usage: --input and --output are required."
105 << std::endl << desc << std::endl;
112 boost::filesystem::canonical(
113 TPath( vm[ "input" ].as< std::string >( ) )
115 Data.Output = TPath( vm[ "output" ].as< std::string >( ) );
116 Data.IncludePaths.clear( );
117 Data.IncludePaths.insert( Data.Input.parent_path( ) );
118 if( vm.count( "include" ) > 0 )
119 for( const std::string& dir: vm[ "include" ].as< TStrings >( ) )
120 Data.IncludePaths.insert( boost::filesystem::canonical( TPath( dir ) ) );
123 catch( std::exception& err )
125 std::cerr << "Error caught: " << err.what( ) << std::endl;
126 std::cerr << desc << std::endl;
132 // -------------------------------------------------------------------------
133 bool Read( const std::string& input, std::string& buffer )
135 std::ifstream in( input.c_str( ) );
139 << "===============================" << std::endl
140 << "Error caught: " << std::endl
141 << "could not load input file \"" << input << "\"" << std::endl
142 << "===============================" << std::endl
147 typedef std::istreambuf_iterator< char > _TDIt;
148 std::istringstream str( std::string( ( _TDIt( in ) ), _TDIt( ) ) );
154 // -------------------------------------------------------------------------
155 bool Lines( const std::string& input, TStrings& lines )
158 if( Read( input, buffer ) )
161 std::istringstream str( buffer );
163 while( std::getline( str, line ) )
164 lines.push_back( line );
171 // -------------------------------------------------------------------------
172 void Includes( TStrings& new_lines, const TStrings& lines )
176 for( const std::string& line: lines )
178 std::size_t a = line.find_first_of( "$" );
179 std::size_t b = line.find( "$include", a );
180 if( a == b && a != std::string::npos )
182 std::size_t s = line.find_first_of( "\"" );
183 std::size_t l = line.find_last_of( "\"" );
184 std::string fname = line.substr( s + 1, l - s - 1 );
185 std::set< TPath >::const_iterator pIt = Data.IncludePaths.begin( );
187 while( pIt != Data.IncludePaths.end( ) && !found )
191 if( boost::filesystem::exists( incl ) )
194 if( Lines( incl.string( ), incl_lines ) )
196 for( const std::string& l: incl_lines )
198 if( l.find( "$include" ) != std::string::npos )
200 new_lines.push_back( l );
213 new_lines.push_back( line );
219 TStrings new_new_lines;
220 Includes( new_new_lines, new_lines );
221 new_lines = new_new_lines;
226 // -------------------------------------------------------------------------
227 void Definitions( TDefinitions& defs, const TStrings& lines )
230 typedef boost::char_separator< char > _TSep;
231 typedef boost::tokenizer< _TSep > _TTok;
233 // Identify definitions
234 for( const std::string& line: lines )
236 std::size_t a = line.find_first_not_of( " \t" );
237 if( a != std::string::npos )
239 if( line[ a ] == '$' )
241 _TTok tokens( line, _TSep( "=;" ) );
242 _TTok::const_iterator t = tokens.begin( );
243 std::string d = *t + "$";
245 std::pair< TDefinitions::iterator, bool > i =
246 defs.insert( TDefinitions::value_type( d, TStrings( ) ) );
249 for( ++t; t != tokens.end( ); ++t )
250 i.first->second.push_back( *t );
251 if( i.first->second.size( ) == 0 )
252 defs.erase( i.first );
255 throw std::runtime_error( "Duplicated defition: " + d );
263 // Expand definitions
267 // Change all possible values
268 for( TDefinitions::value_type& d: defs )
271 for( const std::string& v: d.second )
273 std::size_t a = v.find_first_of( "$" );
274 if( a != std::string::npos )
276 std::size_t b = v.find_first_of( "$", a + 1 );
277 std::string c = v.substr( a, b - a + 1 );
278 TDefinitions::const_iterator cIt = defs.find( c );
279 if( cIt != defs.end( ) )
281 for( std::string cV: cIt->second )
283 std::string vnew = v;
284 boost::algorithm::replace_all( vnew, c, cV );
285 nValues.push_back( vnew );
292 nValues.push_back( v );
301 for( TDefinitions::value_type& d: defs )
303 for( const std::string& v: d.second )
304 stop &= ( v.find_first_of( "$" ) == std::string::npos );
311 TDefinitions::iterator dIt = defs.begin( );
312 while( dIt != defs.end( ) )
314 if( dIt->second.size( ) == 0 )
316 TDefinitions::iterator eIt = dIt;
326 // -------------------------------------------------------------------------
330 std::stringstream lines;
331 for( const std::string& line: Data.InputLines )
333 // Check if a this is a definition line
334 std::size_t a = line.find_first_of( "$" );
335 std::size_t b = line.find_first_of( "=" );
337 if( a != std::string::npos && b != std::string::npos )
339 std::string c = line.substr( a, b - a ) + "$";
340 if( Data.Definitions.find( c ) != Data.Definitions.end( ) )
345 if( a != std::string::npos )
347 if( a == line.find( "$include", a ) )
354 std::string t = line;
356 std::set< std::string > tags;
357 for( const TDefinitions::value_type& d: Data.Definitions )
359 if( line.find( d.first ) != std::string::npos )
360 tags.insert( d.first );
364 if( tags.size( ) > 0 )
366 std::stringstream pre, pos;
367 std::set< std::string >::const_iterator tIt = tags.begin( );
368 std::set< std::string >::const_reverse_iterator rtIt = tags.rbegin( );
369 for( ; tIt != tags.end( ); ++tIt, ++rtIt )
371 pre << "{{#" << tIt->substr( 1, tIt->size( ) - 2 ) << "}}";
372 pos << "{{/" << rtIt->substr( 1, rtIt->size( ) - 2 ) << "}}";
373 boost::algorithm::replace_all(
374 t, *tIt, "{{" + tIt->substr( 1, tIt->size( ) - 2 ) + "}}"
378 pre << std::endl << t << std::endl << pos.str( );
382 lines << t << std::endl;
389 // Create moustache context
390 typedef std::pair< std::string, std::string > TStringPair;
391 typedef std::pair< std::string, mstch::array > TStringArrayPair;
393 for( const TDefinitions::value_type& d: Data.Definitions )
396 std::string c = d.first.substr( 1, d.first.size( ) - 2 );
397 for( const std::string& v: d.second )
400 def.insert( TStringPair( c, v ) );
401 values.push_back( def );
404 context.insert( TStringArrayPair( c, values ) );
408 // Parse moustache syntax and save output file
409 std::ofstream out( Data.Output.string( ).c_str( ) );
413 << "===============================" << std::endl
414 << "Error caught: " << std::endl
415 << "could not save to output file \"" << Data.Output.string( )
417 << "===============================" << std::endl
423 Render( out, lines.str( ), context );
429 // -------------------------------------------------------------------------
430 void Render( std::ostream& out, const std::string& in, mstch::map& context )
432 std::string s = mstch::render( in, context );
433 boost::algorithm::replace_all( s, "<", "<" );
434 boost::algorithm::replace_all( s, ">", ">" );