Initialize Tizen 2.3
[external/ragel.git] / ragel / main.cpp
1 /*
2  *  Copyright 2001-2007 Adrian Thurston <thurston@complang.org>
3  */
4
5 /*  This file is part of Ragel.
6  *
7  *  Ragel is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  * 
12  *  Ragel is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  * 
17  *  You should have received a copy of the GNU General Public License
18  *  along with Ragel; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
20  */
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <iostream>
26 #include <fstream>
27 #include <unistd.h>
28 #include <sstream>
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <errno.h>
34
35 #ifdef _WIN32
36 #include <windows.h>
37 #include <psapi.h>
38 #include <time.h>
39 #include <io.h>
40 #include <process.h>
41
42 #if _MSC_VER
43 #define S_IRUSR _S_IREAD
44 #define S_IWUSR _S_IWRITE
45 #endif
46 #endif
47
48 /* Parsing. */
49 #include "ragel.h"
50 #include "rlscan.h"
51
52 /* Parameters and output. */
53 #include "pcheck.h"
54 #include "vector.h"
55 #include "version.h"
56 #include "common.h"
57 #include "inputdata.h"
58
59 using std::istream;
60 using std::ostream;
61 using std::ifstream;
62 using std::ofstream;
63 using std::cin;
64 using std::cout;
65 using std::cerr;
66 using std::endl;
67 using std::ios;
68 using std::streamsize;
69
70 /* Controls minimization. */
71 MinimizeLevel minimizeLevel = MinimizePartition2;
72 MinimizeOpt minimizeOpt = MinimizeMostOps;
73
74 /* Graphviz dot file generation. */
75 const char *machineSpec = 0, *machineName = 0;
76 bool machineSpecFound = false;
77 bool wantDupsRemoved = true;
78
79 bool printStatistics = false;
80 bool generateXML = false;
81 bool generateDot = false;
82
83 /* Target language and output style. */
84 CodeStyle codeStyle = GenTables;
85
86 int numSplitPartitions = 0;
87 bool noLineDirectives = false;
88
89 bool displayPrintables = false;
90
91 /* Target ruby impl */
92 RubyImplEnum rubyImpl = MRI;
93
94 /* Print a summary of the options. */
95 void usage()
96 {
97         cout <<
98 "usage: ragel [options] file\n"
99 "general:\n"
100 "   -h, -H, -?, --help   Print this usage and exit\n"
101 "   -v, --version        Print version information and exit\n"
102 "   -o <file>            Write output to <file>\n"
103 "   -s                   Print some statistics on stderr\n"
104 "   -d                   Do not remove duplicates from action lists\n"
105 "   -I <dir>             Add <dir> to the list of directories to search\n"
106 "                        for included an imported files\n"
107 "error reporting format:\n"
108 "   --error-format=gnu   file:line:column: message (default)\n"
109 "   --error-format=msvc  file(line,column): message\n"
110 "fsm minimization:\n"
111 "   -n                   Do not perform minimization\n"
112 "   -m                   Minimize at the end of the compilation\n"
113 "   -l                   Minimize after most operations (default)\n"
114 "   -e                   Minimize after every operation\n"
115 "visualization:\n"
116 "   -x                   Run the frontend only: emit XML intermediate format\n"
117 "   -V                   Generate a dot file for Graphviz\n"
118 "   -p                   Display printable characters on labels\n"
119 "   -S <spec>            FSM specification to output (for graphviz output)\n"
120 "   -M <machine>         Machine definition/instantiation to output (for graphviz output)\n"
121 "host language:\n"
122 "   -C                   The host language is C, C++, Obj-C or Obj-C++ (default)\n"
123 "   -D                   The host language is D\n"
124 "   -J                   The host language is Java\n"
125 "   -R                   The host language is Ruby\n"
126 "   -A                   The host language is C#\n"
127 "line direcives: (C/D/C#)\n"
128 "   -L                   Inhibit writing of #line directives\n"
129 "code style: (C/D/Java/Ruby/C#)\n"
130 "   -T0                  Table driven FSM (default)\n"
131 "code style: (C/D/Ruby/C#)\n"
132 "   -T1                  Faster table driven FSM\n"
133 "   -F0                  Flat table driven FSM\n"
134 "   -F1                  Faster flat table-driven FSM\n"
135 "code style: (C/D/C#)\n"
136 "   -G0                  Goto-driven FSM\n"
137 "   -G1                  Faster goto-driven FSM\n"
138 "code style: (C/D)\n"
139 "   -G2                  Really fast goto-driven FSM\n"
140 "   -P<N>                N-Way Split really fast goto-driven FSM\n"
141         ;       
142
143         exit(0);
144 }
145
146 /* Print version information and exit. */
147 void version()
148 {
149         cout << "Ragel State Machine Compiler version " VERSION << " " PUBDATE << endl <<
150                         "Copyright (c) 2001-2009 by Adrian Thurston" << endl;
151         exit(0);
152 }
153
154 /* Error reporting format. */
155 ErrorFormat errorFormat = ErrorFormatGNU;
156
157 InputLoc makeInputLoc( const char *fileName, int line, int col)
158 {
159         InputLoc loc = { fileName, line, col };
160         return loc;
161 }
162
163 ostream &operator<<( ostream &out, const InputLoc &loc )
164 {
165         assert( loc.fileName != 0 );
166         switch ( errorFormat ) {
167         case ErrorFormatMSVC:
168                 out << loc.fileName << "(" << loc.line;
169                 if ( loc.col )
170                         out << "," << loc.col;
171                 out << ")";
172                 break;
173
174         default:
175                 out << loc.fileName << ":" << loc.line;
176                 if ( loc.col )
177                         out << ":" << loc.col;
178                 break;
179         }
180         return out;
181 }
182
183 /* Total error count. */
184 int gblErrorCount = 0;
185
186 /* Print the opening to a warning in the input, then return the error ostream. */
187 ostream &warning( const InputLoc &loc )
188 {
189         cerr << loc << ": warning: ";
190         return cerr;
191 }
192
193 /* Print the opening to a program error, then return the error stream. */
194 ostream &error()
195 {
196         gblErrorCount += 1;
197         cerr << PROGNAME ": ";
198         return cerr;
199 }
200
201 ostream &error( const InputLoc &loc )
202 {
203         gblErrorCount += 1;
204         cerr << loc << ": ";
205         return cerr;
206 }
207
208 void escapeLineDirectivePath( std::ostream &out, char *path )
209 {
210         for ( char *pc = path; *pc != 0; pc++ ) {
211                 if ( *pc == '\\' )
212                         out << "\\\\";
213                 else
214                         out << *pc;
215         }
216 }
217
218 void processArgs( int argc, const char **argv, InputData &id )
219 {
220         ParamCheck pc("xo:dnmleabjkS:M:I:CDJRAvHh?-:sT:F:G:P:LpV", argc, argv);
221
222         /* FIXME: Need to check code styles VS langauge. */
223
224         while ( pc.check() ) {
225                 switch ( pc.state ) {
226                 case ParamCheck::match:
227                         switch ( pc.parameter ) {
228                         case 'V':
229                                 generateDot = true;
230                                 break;
231
232                         case 'x':
233                                 generateXML = true;
234                                 break;
235
236                         /* Output. */
237                         case 'o':
238                                 if ( *pc.paramArg == 0 )
239                                         error() << "a zero length output file name was given" << endl;
240                                 else if ( id.outputFileName != 0 )
241                                         error() << "more than one output file name was given" << endl;
242                                 else {
243                                         /* Ok, remember the output file name. */
244                                         id.outputFileName = pc.paramArg;
245                                 }
246                                 break;
247
248                         /* Flag for turning off duplicate action removal. */
249                         case 'd':
250                                 wantDupsRemoved = false;
251                                 break;
252
253                         /* Minimization, mostly hidden options. */
254                         case 'n':
255                                 minimizeOpt = MinimizeNone;
256                                 break;
257                         case 'm':
258                                 minimizeOpt = MinimizeEnd;
259                                 break;
260                         case 'l':
261                                 minimizeOpt = MinimizeMostOps;
262                                 break;
263                         case 'e':
264                                 minimizeOpt = MinimizeEveryOp;
265                                 break;
266                         case 'a':
267                                 minimizeLevel = MinimizeApprox;
268                                 break;
269                         case 'b':
270                                 minimizeLevel = MinimizeStable;
271                                 break;
272                         case 'j':
273                                 minimizeLevel = MinimizePartition1;
274                                 break;
275                         case 'k':
276                                 minimizeLevel = MinimizePartition2;
277                                 break;
278
279                         /* Machine spec. */
280                         case 'S':
281                                 if ( *pc.paramArg == 0 )
282                                         error() << "please specify an argument to -S" << endl;
283                                 else if ( machineSpec != 0 )
284                                         error() << "more than one -S argument was given" << endl;
285                                 else {
286                                         /* Ok, remember the path to the machine to generate. */
287                                         machineSpec = pc.paramArg;
288                                 }
289                                 break;
290
291                         /* Machine path. */
292                         case 'M':
293                                 if ( *pc.paramArg == 0 )
294                                         error() << "please specify an argument to -M" << endl;
295                                 else if ( machineName != 0 )
296                                         error() << "more than one -M argument was given" << endl;
297                                 else {
298                                         /* Ok, remember the machine name to generate. */
299                                         machineName = pc.paramArg;
300                                 }
301                                 break;
302
303                         case 'I':
304                                 if ( *pc.paramArg == 0 )
305                                         error() << "please specify an argument to -I" << endl;
306                                 else {
307                                         id.includePaths.append( pc.paramArg );
308                                 }
309                                 break;
310
311                         /* Host language types. */
312                         case 'C':
313                                 hostLang = &hostLangC;
314                                 break;
315                         case 'D':
316                                 hostLang = &hostLangD;
317                                 break;
318                         case 'J':
319                                 hostLang = &hostLangJava;
320                                 break;
321                         case 'R':
322                                 hostLang = &hostLangRuby;
323                                 break;
324                         case 'A':
325                                 hostLang = &hostLangCSharp;
326                                 break;
327
328                         /* Version and help. */
329                         case 'v':
330                                 version();
331                                 break;
332                         case 'H': case 'h': case '?':
333                                 usage();
334                                 break;
335                         case 's':
336                                 printStatistics = true;
337                                 break;
338                         case '-': {
339                                 char *arg = strdup( pc.paramArg );
340                                 char *eq = strchr( arg, '=' );
341
342                                 if ( eq != 0 )
343                                         *eq++ = 0;
344
345                                 if ( strcmp( arg, "help" ) == 0 )
346                                         usage();
347                                 else if ( strcmp( arg, "version" ) == 0 )
348                                         version();
349                                 else if ( strcmp( arg, "error-format" ) == 0 ) {
350                                         if ( eq == 0 )
351                                                 error() << "expecting '=value' for error-format" << endl;
352                                         else if ( strcmp( eq, "gnu" ) == 0 )
353                                                 errorFormat = ErrorFormatGNU;
354                                         else if ( strcmp( eq, "msvc" ) == 0 )
355                                                 errorFormat = ErrorFormatMSVC;
356                                         else
357                                                 error() << "invalid value for error-format" << endl;
358                                 }
359                                 else if ( strcmp( arg, "rbx" ) == 0 )
360                                         rubyImpl = Rubinius;
361                                 else {
362                                         error() << "--" << pc.paramArg << 
363                                                         " is an invalid argument" << endl;
364                                 }
365                                 free( arg );
366                                 break;
367                         }
368
369                         /* Passthrough args. */
370                         case 'T': 
371                                 if ( pc.paramArg[0] == '0' )
372                                         codeStyle = GenTables;
373                                 else if ( pc.paramArg[0] == '1' )
374                                         codeStyle = GenFTables;
375                                 else {
376                                         error() << "-T" << pc.paramArg[0] << 
377                                                         " is an invalid argument" << endl;
378                                         exit(1);
379                                 }
380                                 break;
381                         case 'F': 
382                                 if ( pc.paramArg[0] == '0' )
383                                         codeStyle = GenFlat;
384                                 else if ( pc.paramArg[0] == '1' )
385                                         codeStyle = GenFFlat;
386                                 else {
387                                         error() << "-F" << pc.paramArg[0] << 
388                                                         " is an invalid argument" << endl;
389                                         exit(1);
390                                 }
391                                 break;
392                         case 'G': 
393                                 if ( pc.paramArg[0] == '0' )
394                                         codeStyle = GenGoto;
395                                 else if ( pc.paramArg[0] == '1' )
396                                         codeStyle = GenFGoto;
397                                 else if ( pc.paramArg[0] == '2' )
398                                         codeStyle = GenIpGoto;
399                                 else {
400                                         error() << "-G" << pc.paramArg[0] << 
401                                                         " is an invalid argument" << endl;
402                                         exit(1);
403                                 }
404                                 break;
405                         case 'P':
406                                 codeStyle = GenSplit;
407                                 numSplitPartitions = atoi( pc.paramArg );
408                                 break;
409
410                         case 'p':
411                                 displayPrintables = true;
412                                 break;
413
414                         case 'L':
415                                 noLineDirectives = true;
416                                 break;
417                         }
418                         break;
419
420                 case ParamCheck::invalid:
421                         error() << "-" << pc.parameter << " is an invalid argument" << endl;
422                         break;
423
424                 case ParamCheck::noparam:
425                         /* It is interpreted as an input file. */
426                         if ( *pc.curArg == 0 )
427                                 error() << "a zero length input file name was given" << endl;
428                         else if ( id.inputFileName != 0 )
429                                 error() << "more than one input file name was given" << endl;
430                         else {
431                                 /* OK, Remember the filename. */
432                                 id.inputFileName = pc.curArg;
433                         }
434                         break;
435                 }
436         }
437 }
438
439 void process( InputData &id )
440 {
441         /* Open the input file for reading. */
442         assert( id.inputFileName != 0 );
443         ifstream *inFile = new ifstream( id.inputFileName );
444         if ( ! inFile->is_open() )
445                 error() << "could not open " << id.inputFileName << " for reading" << endp;
446
447         /* Used for just a few things. */
448         std::ostringstream hostData;
449
450         /* Make the first input item. */
451         InputItem *firstInputItem = new InputItem;
452         firstInputItem->type = InputItem::HostData;
453         firstInputItem->loc.fileName = id.inputFileName;
454         firstInputItem->loc.line = 1;
455         firstInputItem->loc.col = 1;
456         id.inputItems.append( firstInputItem );
457
458         Scanner scanner( id, id.inputFileName, *inFile, 0, 0, 0, false );
459         scanner.do_scan();
460
461         /* Finished, final check for errors.. */
462         if ( gblErrorCount > 0 )
463                 exit(1);
464
465         /* Now send EOF to all parsers. */
466         id.terminateAllParsers();
467
468         /* Bail on above error. */
469         if ( gblErrorCount > 0 )
470                 exit(1);
471
472         /* Locate the backend program */
473         /* Compiles machines. */
474         id.prepareMachineGen();
475
476         if ( gblErrorCount > 0 )
477                 exit(1);
478
479         id.makeOutputStream();
480
481         /* Generates the reduced machine, which we use to write output. */
482         if ( !generateXML ) {
483                 id.generateReduced();
484
485                 if ( gblErrorCount > 0 )
486                         exit(1);
487         }
488
489         id.verifyWritesHaveData();
490         if ( gblErrorCount > 0 )
491                 exit(1);
492
493         /*
494          * From this point on we should not be reporting any errors.
495          */
496
497         id.openOutput();
498         id.writeOutput();
499
500         /* Close the input and the intermediate file. */
501         delete inFile;
502
503         /* If writing to a file, delete the ostream, causing it to flush.
504          * Standard out is flushed automatically. */
505         if ( id.outputFileName != 0 ) {
506                 delete id.outStream;
507                 delete id.outFilter;
508         }
509
510         assert( gblErrorCount == 0 );
511 }
512
513 char *makeIntermedTemplate( const char *baseFileName )
514 {
515         char *result = 0;
516         const char *templ = "ragel-XXXXXX.xml";
517         const char *lastSlash = strrchr( baseFileName, '/' );
518         if ( lastSlash == 0 ) {
519                 result = new char[strlen(templ)+1];
520                 strcpy( result, templ );
521         }
522         else {
523                 int baseLen = lastSlash - baseFileName + 1;
524                 result = new char[baseLen + strlen(templ) + 1];
525                 memcpy( result, baseFileName, baseLen );
526                 strcpy( result+baseLen, templ );
527         }
528         return result;
529 };
530
531 /* Main, process args and call yyparse to start scanning input. */
532 int main( int argc, const char **argv )
533 {
534         InputData id;
535
536         processArgs( argc, argv, id );
537
538         /* Require an input file. If we use standard in then we won't have a file
539          * name on which to base the output. */
540         if ( id.inputFileName == 0 )
541                 error() << "no input file given" << endl;
542
543         /* Bail on argument processing errors. */
544         if ( gblErrorCount > 0 )
545                 exit(1);
546
547         /* Make sure we are not writing to the same file as the input file. */
548         if ( id.inputFileName != 0 && id.outputFileName != 0 && 
549                         strcmp( id.inputFileName, id.outputFileName  ) == 0 )
550         {
551                 error() << "output file \"" << id.outputFileName  << 
552                                 "\" is the same as the input file" << endp;
553         }
554
555         process( id );
556
557         return 0;
558 }