Implemented an export feature, for exporting single-character machine
[external/ragel.git] / rlgen-cd / main.cpp
1 /*
2  *  Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
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
29 #include "common.h"
30 #include "rlgen-cd.h"
31 #include "xmlparse.h"
32 #include "pcheck.h"
33 #include "vector.h"
34 #include "version.h"
35
36 /* Code generators. */
37 #include "tabcodegen.h"
38 #include "ftabcodegen.h"
39 #include "flatcodegen.h"
40 #include "fflatcodegen.h"
41 #include "gotocodegen.h"
42 #include "fgotocodegen.h"
43 #include "ipgotocodegen.h"
44 #include "splitcodegen.h"
45
46 using std::istream;
47 using std::ifstream;
48 using std::ostream;
49 using std::ios;
50 using std::cin;
51 using std::cout;
52 using std::cerr;
53 using std::endl;
54
55 /* Target language and output style. */
56 CodeStyleEnum codeStyle = GenTables;
57
58 /* Io globals. */
59 istream *inStream = 0;
60 ostream *outStream = 0;
61 output_filter *outFilter = 0;
62 char *outputFileName = 0;
63
64 /* Graphviz dot file generation. */
65 bool graphvizDone = false;
66
67 int numSplitPartitions = 0;
68 bool noLineDirectives = false;
69 bool printPrintables = false;
70
71 /* Print a summary of the options. */
72 void usage()
73 {
74         cout <<
75 "usage: " PROGNAME " [options] file\n"
76 "general:\n"
77 "   -h, -H, -?, --help    Print this usage and exit\n"
78 "   -v, --version         Print version information and exit\n"
79 "   -o <file>             Write output to <file>\n"
80 "code generation options:\n"
81 "   -l                    Inhibit writing of #line directives\n"
82 "generated code style:\n"
83 "   -T0                   Table driven FSM (default)\n"
84 "   -T1                   Faster table driven FSM\n"
85 "   -F0                   Flat table driven FSM\n"
86 "   -F1                   Faster flat table-driven FSM\n"
87 "   -G0                   Goto-driven FSM\n"
88 "   -G1                   Faster goto-driven FSM\n"
89 "   -G2                   Really fast goto-driven FSM\n"
90 "   -P<N>                 N-Way Split really fast goto-driven FSM\n"
91         ;       
92 }
93
94 /* Print version information. */
95 void version()
96 {
97         cout << "Ragel Code Generator for C, C++, Objective-C and D" << endl <<
98                         "Version " VERSION << ", " PUBDATE << endl <<
99                         "Copyright (c) 2001-2007 by Adrian Thurston" << endl;
100 }
101
102 /* Total error count. */
103 int gblErrorCount = 0;
104
105 ostream &error()
106 {
107         gblErrorCount += 1;
108         cerr << PROGNAME ": ";
109         return cerr;
110 }
111
112 /*
113  * Callbacks invoked by the XML data parser.
114  */
115
116 /* Invoked by the parser when the root element is opened. */
117 ostream *openOutput( char *inputFile )
118 {
119         if ( hostLangType != CCode && hostLangType != DCode ) {
120                 error() << "this code generator is for C and D only" << endl;
121                 exit(1);
122         }
123
124         /* If the output format is code and no output file name is given, then
125          * make a default. */
126         if ( outputFileName == 0 ) {
127                 char *ext = findFileExtension( inputFile );
128                 if ( ext != 0 && strcmp( ext, ".rh" ) == 0 )
129                         outputFileName = fileNameFromStem( inputFile, ".h" );
130                 else {
131                         char *defExtension = 0;
132                         switch ( hostLangType ) {
133                                 case CCode: defExtension = ".c"; break;
134                                 case DCode: defExtension = ".d"; break;
135                                 default: break;
136                         }
137                         outputFileName = fileNameFromStem( inputFile, defExtension );
138                 }
139         }
140
141         /* Make sure we are not writing to the same file as the input file. */
142         if ( outputFileName != 0 && strcmp( inputFile, outputFileName  ) == 0 ) {
143                 error() << "output file \"" << outputFileName  << 
144                                 "\" is the same as the input file" << endl;
145         }
146
147         if ( outputFileName != 0 ) {
148                 /* Create the filter on the output and open it. */
149                 outFilter = new output_filter( outputFileName );
150                 outFilter->open( outputFileName, ios::out|ios::trunc );
151                 if ( !outFilter->is_open() ) {
152                         error() << "error opening " << outputFileName << " for writing" << endl;
153                         exit(1);
154                 }
155
156                 /* Open the output stream, attaching it to the filter. */
157                 outStream = new ostream( outFilter );
158         }
159         else {
160                 /* Writing out ot std out. */
161                 outStream = &cout;
162         }
163         return outStream;
164 }
165
166 /* Invoked by the parser when a ragel definition is opened. */
167 CodeGenData *makeCodeGen( char *sourceFileName, char *fsmName, 
168                 ostream &out, bool wantComplete )
169 {
170         CodeGenData *codeGen = 0;
171         switch ( hostLangType ) {
172         case CCode:
173                 switch ( codeStyle ) {
174                 case GenTables:
175                         codeGen = new CTabCodeGen(out);
176                         break;
177                 case GenFTables:
178                         codeGen = new CFTabCodeGen(out);
179                         break;
180                 case GenFlat:
181                         codeGen = new CFlatCodeGen(out);
182                         break;
183                 case GenFFlat:
184                         codeGen = new CFFlatCodeGen(out);
185                         break;
186                 case GenGoto:
187                         codeGen = new CGotoCodeGen(out);
188                         break;
189                 case GenFGoto:
190                         codeGen = new CFGotoCodeGen(out);
191                         break;
192                 case GenIpGoto:
193                         codeGen = new CIpGotoCodeGen(out);
194                         break;
195                 case GenSplit:
196                         codeGen = new CSplitCodeGen(out);
197                         break;
198                 }
199                 break;
200
201         case DCode:
202                 switch ( codeStyle ) {
203                 case GenTables:
204                         codeGen = new DTabCodeGen(out);
205                         break;
206                 case GenFTables:
207                         codeGen = new DFTabCodeGen(out);
208                         break;
209                 case GenFlat:
210                         codeGen = new DFlatCodeGen(out);
211                         break;
212                 case GenFFlat:
213                         codeGen = new DFFlatCodeGen(out);
214                         break;
215                 case GenGoto:
216                         codeGen = new DGotoCodeGen(out);
217                         break;
218                 case GenFGoto:
219                         codeGen = new DFGotoCodeGen(out);
220                         break;
221                 case GenIpGoto:
222                         codeGen = new DIpGotoCodeGen(out);
223                         break;
224                 case GenSplit:
225                         codeGen = new DSplitCodeGen(out);
226                         break;
227                 }
228                 break;
229
230         default: break;
231         }
232
233         codeGen->sourceFileName = sourceFileName;
234         codeGen->fsmName = fsmName;
235         codeGen->wantComplete = wantComplete;
236
237         return codeGen;
238 }
239
240
241
242 /* Main, process args and call yyparse to start scanning input. */
243 int main(int argc, char **argv)
244 {
245         ParamCheck pc("-:Hh?vlo:T:F:G:P:", argc, argv);
246         char *xmlInputFileName = 0;
247
248         while ( pc.check() ) {
249                 switch ( pc.state ) {
250                 case ParamCheck::match:
251                         switch ( pc.parameter ) {
252                         /* Output. */
253                         case 'o':
254                                 if ( *pc.parameterArg == 0 )
255                                         error() << "a zero length output file name was given" << endl;
256                                 else if ( outputFileName != 0 )
257                                         error() << "more than one output file name was given" << endl;
258                                 else {
259                                         /* Ok, remember the output file name. */
260                                         outputFileName = pc.parameterArg;
261                                 }
262                                 break;
263
264                         case 'l':
265                                 noLineDirectives = true;
266                                 break;
267
268                         /* Code style. */
269                         case 'T':
270                                 if ( pc.parameterArg[0] == '0' )
271                                         codeStyle = GenTables;
272                                 else if ( pc.parameterArg[0] == '1' )
273                                         codeStyle = GenFTables;
274                                 else {
275                                         error() << "-T" << pc.parameterArg[0] << 
276                                                         " is an invalid argument" << endl;
277                                         exit(1);
278                                 }
279                                 break;
280                         case 'F':
281                                 if ( pc.parameterArg[0] == '0' )
282                                         codeStyle = GenFlat;
283                                 else if ( pc.parameterArg[0] == '1' )
284                                         codeStyle = GenFFlat;
285                                 else {
286                                         error() << "-F" << pc.parameterArg[0] << 
287                                                         " is an invalid argument" << endl;
288                                         exit(1);
289                                 }
290                                 break;
291                         case 'G':
292                                 if ( pc.parameterArg[0] == '0' )
293                                         codeStyle = GenGoto;
294                                 else if ( pc.parameterArg[0] == '1' )
295                                         codeStyle = GenFGoto;
296                                 else if ( pc.parameterArg[0] == '2' )
297                                         codeStyle = GenIpGoto;
298                                 else {
299                                         error() << "-G" << pc.parameterArg[0] << 
300                                                         " is an invalid argument" << endl;
301                                         exit(1);
302                                 }
303                                 break;
304                         case 'P':
305                                 codeStyle = GenSplit;
306                                 numSplitPartitions = atoi( pc.parameterArg );
307                                 break;
308
309                         /* Version and help. */
310                         case 'v':
311                                 version();
312                                 exit(0);
313                         case 'H': case 'h': case '?':
314                                 usage();
315                                 exit(0);
316                         case '-':
317                                 if ( strcasecmp(pc.parameterArg, "help") == 0 ) {
318                                         usage();
319                                         exit(0);
320                                 }
321                                 else if ( strcasecmp(pc.parameterArg, "version") == 0 ) {
322                                         version();
323                                         exit(0);
324                                 }
325                                 else {
326                                         error() << "--" << pc.parameterArg << 
327                                                         " is an invalid argument" << endl;
328                                         break;
329                                 }
330                         }
331                         break;
332
333                 case ParamCheck::invalid:
334                         error() << "-" << pc.parameter << " is an invalid argument" << endl;
335                         break;
336
337                 case ParamCheck::noparam:
338                         if ( *pc.curArg == 0 )
339                                 error() << "a zero length input file name was given" << endl;
340                         else if ( xmlInputFileName != 0 )
341                                 error() << "more than one input file name was given" << endl;
342                         else {
343                                 /* OK, Remember the filename. */
344                                 xmlInputFileName = pc.curArg;
345                         }
346                         break;
347                 }
348         }
349
350         /* Bail on above errors. */
351         if ( gblErrorCount > 0 )
352                 exit(1);
353
354         /* Open the input file for reading. */
355         if ( xmlInputFileName != 0 ) {
356                 /* Open the input file for reading. */
357                 ifstream *inFile = new ifstream( xmlInputFileName );
358                 inStream = inFile;
359                 if ( ! inFile->is_open() )
360                         error() << "could not open " << xmlInputFileName << " for reading" << endl;
361         }
362         else {
363                 xmlInputFileName = "<stdin>";
364                 inStream = &cin;
365         }
366
367         /* Bail on above errors. */
368         if ( gblErrorCount > 0 )
369                 exit(1);
370
371         bool wantComplete = true;
372         bool outputActive = true;
373
374         /* Parse the input! */
375         xml_parse( *inStream, xmlInputFileName, outputActive, wantComplete );
376
377         /* If writing to a file, delete the ostream, causing it to flush.
378          * Standard out is flushed automatically. */
379         if ( outputFileName != 0 ) {
380                 delete outStream;
381                 delete outFilter;
382         }
383
384         /* Finished, final check for errors.. */
385         if ( gblErrorCount > 0 ) {
386                 /* If we opened an output file, remove it. */
387                 if ( outputFileName != 0 )
388                         unlink( outputFileName );
389                 exit(1);
390         }
391         return 0;
392 }