]> Creatis software - cpPlugins.git/blob - appli/bash/MOC.cxx
Moved to version 1.0
[cpPlugins.git] / appli / bash / MOC.cxx
1 // =========================================================================
2 // @author Leonardo Florez-Valencia (florez-l@javeriana.edu.co)
3 // =========================================================================
4
5 #include <fstream>
6 #include <iostream>
7 #include <map>
8 #include <queue>
9 #include <set>
10 #include <sstream>
11 #include <streambuf>
12 #include <string>
13 #include <vector>
14
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>
20
21 // -------------------------------------------------------------------------
22 typedef boost::filesystem::path TPath;
23 typedef std::vector< std::string > TStrings;
24 typedef std::map< std::string, TStrings > TDefinitions;
25 struct TData
26 {
27   TPath             Input;
28   TPath             Output;
29   std::set< TPath > IncludePaths;
30   TStrings          InputLines;
31   TStrings          CompleteLines;
32   TDefinitions      Definitions;
33 };
34 TData Data;
35
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 );
42 int Parse( );
43 void Render( std::ostream& out, const std::string& in, mstch::map& context );
44
45 // -------------------------------------------------------------------------
46 int main( int argc, char* argv[] )
47 {
48   if( Arguments( argc, argv ) )
49   {
50     if( Lines( Data.Input.string( ), Data.InputLines ) )
51     {
52       Includes( Data.CompleteLines, Data.InputLines );
53       Definitions( Data.Definitions, Data.CompleteLines );
54       return( Parse( ) );
55     }
56     else
57       return( 1 );
58   }
59   else
60     return( 1 );
61 }
62
63 // -------------------------------------------------------------------------
64 bool Arguments( int argc, char* argv[] )
65 {
66   // Declare the supported options.
67   boost::program_options::options_description desc( "Allowed options" );
68   desc.add_options( )
69     ( "help,h", "produce help message" )
70     (
71       "input,i",
72       boost::program_options::value< std::string >( ),
73       "Input"
74       )
75     (
76       "output,o",
77       boost::program_options::value< std::string >( ),
78       "Output"
79       )
80     (
81       "include,I",
82       boost::program_options::value< TStrings >( ),
83       "Include directory(ies)"
84       )
85     ;
86
87   try
88   {
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
93       );
94     boost::program_options::notify( vm );
95     if( vm.count( "help" ) )
96     {
97       std::cerr << desc << std::endl;
98       return( false );
99
100     } // fi
101     if( vm.count( "input" ) == 0 || vm.count( "output" ) == 0 )
102     {
103       std::cerr
104         << "Invalid usage: --input and --output are required."
105         << std::endl << desc << std::endl;
106       return( false );
107
108     } // fi
109
110     // Get values
111     Data.Input =
112       boost::filesystem::canonical(
113         TPath( vm[ "input" ].as< std::string >( ) )
114         );
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 ) ) );
121     return( true );
122   }
123   catch( std::exception& err )
124   {
125     std::cerr << "Error caught: " << err.what( ) << std::endl;
126     std::cerr << desc << std::endl;
127     return( false );
128
129   } // yrt
130 }
131
132 // -------------------------------------------------------------------------
133 bool Read( const std::string& input, std::string& buffer )
134 {
135   std::ifstream in( input.c_str( ) );
136   if( !in )
137   {
138     std::cerr
139       << "===============================" << std::endl
140       << "Error caught: " << std::endl
141       << "could not load input file \"" << input << "\"" << std::endl
142       << "===============================" << std::endl
143       << std::endl;
144     return( false );
145
146   } // fi
147   typedef std::istreambuf_iterator< char > _TDIt;
148   std::istringstream str( std::string( ( _TDIt( in ) ), _TDIt( ) ) );
149   buffer = str.str( );
150   in.close( );
151   return( true );
152 }
153
154 // -------------------------------------------------------------------------
155 bool Lines( const std::string& input, TStrings& lines )
156 {
157   std::string buffer;
158   if( Read( input, buffer ) )
159   {
160     lines.clear( );
161     std::istringstream str( buffer );
162     std::string line;
163     while( std::getline( str, line ) )
164       lines.push_back( line );
165     return( true );
166   }
167   else
168     return( false );
169 }
170
171 // -------------------------------------------------------------------------
172 void Includes( TStrings& new_lines, const TStrings& lines )
173 {
174   bool ok = true;
175   new_lines.clear( );
176   for( const std::string& line: lines )
177   {
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 )
181     {
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( );
186       bool found = false;
187       while( pIt != Data.IncludePaths.end( ) && !found )
188       {
189         TPath incl = *pIt;
190         incl /= fname;
191         if( boost::filesystem::exists( incl ) )
192         {
193           TStrings incl_lines;
194           if( Lines( incl.string( ), incl_lines ) )
195           {
196             for( const std::string& l: incl_lines )
197             {
198               if( l.find( "$include" ) != std::string::npos )
199                 ok = false;
200               new_lines.push_back( l );
201
202             } // rof
203
204           } // fi
205           found = true;
206
207         } // fi
208         pIt++;
209
210       } // elihw
211     }
212     else
213       new_lines.push_back( line );
214
215   } // rof
216
217   if( !ok )
218   {
219     TStrings new_new_lines;
220     Includes( new_new_lines, new_lines );
221     new_lines = new_new_lines;
222
223   } // fi
224 }
225
226 // -------------------------------------------------------------------------
227 void Definitions( TDefinitions& defs, const TStrings& lines )
228 {
229   // Tokenizer
230   typedef boost::char_separator< char > _TSep;
231   typedef boost::tokenizer< _TSep > _TTok;
232
233   // Identify definitions
234   for( const std::string& line: lines )
235   {
236     std::size_t a = line.find_first_not_of( " \t" );
237     if( a != std::string::npos )
238     {
239       if( line[ a ] == '$' )
240       {
241         _TTok tokens( line, _TSep( "=;" ) );
242         _TTok::const_iterator t = tokens.begin( );
243         std::string d = *t + "$";
244
245         std::pair< TDefinitions::iterator, bool > i =
246           defs.insert( TDefinitions::value_type( d, TStrings( ) ) );
247         if( i.second )
248         {
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 );
253         }
254         else
255           throw std::runtime_error( "Duplicated defition: " + d );
256
257       } // fi
258
259     } // fi
260
261   } // rof
262
263   // Expand definitions
264   bool stop = false;
265   while( !stop )
266   {
267     // Change all possible values
268     for( TDefinitions::value_type& d: defs )
269     {
270       TStrings nValues;
271       for( const std::string& v: d.second )
272       {
273         std::size_t a = v.find_first_of( "$" );
274         if( a != std::string::npos )
275         {
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( ) )
280           {
281             for( std::string cV: cIt->second )
282             {
283               std::string vnew = v;
284               boost::algorithm::replace_all( vnew, c, cV );
285               nValues.push_back( vnew );
286
287             } // rof
288
289           } // fi
290         }
291         else
292           nValues.push_back( v );
293
294       } // rof
295       d.second = nValues;
296
297     } // rof
298
299     // Check stop
300     stop = true;
301     for( TDefinitions::value_type& d: defs )
302     {
303       for( const std::string& v: d.second )
304         stop &= ( v.find_first_of( "$" ) == std::string::npos );
305
306     } // rof
307
308   } // elihw
309
310   // Clear definitions
311   TDefinitions::iterator dIt = defs.begin( );
312   while( dIt != defs.end( ) )
313   {
314     if( dIt->second.size( ) == 0 )
315     {
316       TDefinitions::iterator eIt = dIt;
317       dIt++;
318       defs.erase( eIt );
319     }
320     else
321       dIt++;
322
323   } // elihw
324 }
325
326 // -------------------------------------------------------------------------
327 int Parse( )
328 {
329   // Parse lines
330   std::stringstream lines;
331   for( const std::string& line: Data.InputLines )
332   {
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( "=" );
336     bool ok = true;
337     if( a != std::string::npos && b != std::string::npos )
338     {
339       std::string c = line.substr( a, b - a ) + "$";
340       if( Data.Definitions.find( c ) != Data.Definitions.end( ) )
341         ok = false;
342
343     } // fi
344
345     if( a != std::string::npos )
346     {
347       if( a == line.find( "$include", a ) )
348         ok = false;
349
350     } // fi
351
352     if( ok )
353     {
354       std::string t = line;
355
356       std::set< std::string > tags;
357       for( const TDefinitions::value_type& d: Data.Definitions )
358       {
359         if( line.find( d.first ) != std::string::npos )
360           tags.insert( d.first );
361
362       } // rof
363
364       if( tags.size( ) > 0 )
365       {
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 )
370         {
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 ) + "}}"
375             );
376
377         } // rof
378         pre << std::endl << t << std::endl << pos.str( );
379         t = pre.str( );
380
381       } // fi
382       lines << t << std::endl;
383
384     } // fi
385
386   } // rof
387
388
389   // Create moustache context
390   typedef std::pair< std::string, std::string >  TStringPair;
391   typedef std::pair< std::string, mstch::array > TStringArrayPair;
392   mstch::map context;
393   for( const TDefinitions::value_type& d: Data.Definitions )
394   {
395     mstch::array values;
396     std::string c = d.first.substr( 1, d.first.size( ) - 2 );
397     for( const std::string& v: d.second )
398     {
399       mstch::map def;
400       def.insert( TStringPair( c, v ) );
401       values.push_back( def );
402
403     } // rof
404     context.insert( TStringArrayPair( c, values ) );
405
406   } // rof
407
408   // Parse  moustache syntax and save output file
409   std::ofstream out( Data.Output.string( ).c_str( ) );
410   if( !out )
411   {
412     std::cerr
413       << "===============================" << std::endl
414       << "Error caught: " << std::endl
415       << "could not save to output file \"" << Data.Output.string( )
416       << "\"" << std::endl
417       << "===============================" << std::endl
418       << std::endl;
419     return( 1 );
420
421   } // fi
422
423   Render( out, lines.str( ), context );
424   out << std::endl;
425   out.close( );
426   return( 0 );
427 }
428
429 // -------------------------------------------------------------------------
430 void Render( std::ostream& out, const std::string& in, mstch::map& context )
431 {
432   std::string s = mstch::render( in, context );
433   boost::algorithm::replace_all( s, "&lt;", "<" );
434   boost::algorithm::replace_all( s, "&gt;", ">" );
435   out << s;
436 }
437
438 // eof - $RCSfile$