137773c8dff43e72c5addb59fa139e9d18c9fe33
[external/ragel.git] / rlgen-ruby / main.cpp
1 /*
2  *  Copyright 2007 Victor Hugo Borja <vic@rubyforge.org>
3  *  Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
4  */
5
6 /*  This file is part of Ragel.
7  *
8  *  Ragel is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  * 
13  *  Ragel is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  * 
18  *  You should have received a copy of the GNU General Public License
19  *  along with Ragel; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
21  */
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <iostream>
27 #include <fstream>
28 #include <unistd.h>
29
30 #include "xmlparse.h"
31 #include "pcheck.h"
32 #include "vector.h"
33 #include "version.h"
34 #include "common.h"
35 #include "rlgen-ruby.h"
36 #include "ruby-tabcodegen.h"
37 #include "ruby-ftabcodegen.h"
38 #include "ruby-flatcodegen.h"
39 #include "ruby-fflatcodegen.h"
40 #include "rbx-gotocodegen.h"
41
42 using std::istream;
43 using std::ifstream;
44 using std::ostream;
45 using std::ios;
46 using std::cin;
47 using std::cout;
48 using std::cerr;
49 using std::endl;
50
51 /* Target ruby impl */
52 RubyImplEnum rubyImpl = MRI;
53
54 /* Target language and output style. */
55 CodeStyleEnum codeStyle = GenTables;
56
57 /* Io globals. */
58 istream *inStream = 0;
59 ostream *outStream = 0;
60 output_filter *outFilter = 0;
61 char *outputFileName = 0;
62
63 /* Graphviz dot file generation. */
64 bool graphvizDone = false;
65
66 int numSplitPartitions = 0;
67 bool printPrintables = false;
68
69 /* Print a summary of the options. */
70 void usage()
71 {
72         cout <<
73 "usage: " PROGNAME " [options] file\n"
74 "general:\n"
75 "   -h, -H, -?, --help    Print this usage and exit\n"
76 "   -v, --version         Print version information and exit\n"
77 "   -o <file>             Write output to <file>\n"
78 "   -x, --rbx             Allow to use Rubinius asm features\n"
79 "generated code style:\n"
80 "   -T0                   Table driven FSM (default)\n"
81 "   -T1                   Faster table driven FSM\n"
82 "   -F0                   Flat table driven FSM\n"
83 "   -F1                   Faster flat table-driven FSM\n"
84 "   -G0                   Goto-driven FSM\n"
85         ;       
86 }
87
88 /* Print version information. */
89 void version()
90 {
91         cout << "Ragel Code Generator for Ruby" << endl <<
92                         "Version " VERSION << ", " PUBDATE << endl <<
93                         "Copyright (c) 2001-2007 by Adrian Thurston" << endl <<
94                         "Copyright (c) 2007 by Victor Hugo Borja" << endl;
95 }
96
97 ostream &error()
98 {
99         gblErrorCount += 1;
100         cerr << PROGNAME ": ";
101         return cerr;
102 }
103
104 /*
105  * Callbacks invoked by the XML data parser.
106  */
107
108 /* Invoked by the parser when the root element is opened. */
109 ostream *openOutput( char *inputFile )
110 {
111         if ( hostLang->lang != HostLang::Ruby ) {
112                 error() << "this code generator is for Ruby only" << endl;
113                 exit(1);
114         }
115
116         /* If the output format is code and no output file name is given, then
117          * make a default. */
118         if ( outputFileName == 0 ) {
119                 char *ext = findFileExtension( inputFile );
120                 if ( ext != 0 && strcmp( ext, ".rh" ) == 0 )
121                         outputFileName = fileNameFromStem( inputFile, ".h" );
122                 else
123                         outputFileName = fileNameFromStem( inputFile, ".rb" );
124         }
125
126         /* Make sure we are not writing to the same file as the input file. */
127         if ( outputFileName != 0 && strcmp( inputFile, outputFileName  ) == 0 ) {
128                 error() << "output file \"" << outputFileName  << 
129                                 "\" is the same as the input file" << endl;
130         }
131
132         if ( outputFileName != 0 ) {
133                 /* Create the filter on the output and open it. */
134                 outFilter = new output_filter( outputFileName );
135                 outFilter->open( outputFileName, ios::out|ios::trunc );
136                 if ( !outFilter->is_open() ) {
137                         error() << "error opening " << outputFileName << " for writing" << endl;
138                         exit(1);
139                 }
140
141                 /* Open the output stream, attaching it to the filter. */
142                 outStream = new ostream( outFilter );
143         }
144         else {
145                 /* Writing out ot std out. */
146                 outStream = &cout;
147         }
148         return outStream;
149 }
150
151 /* Invoked by the parser when a ragel definition is opened. */
152 CodeGenData *makeCodeGen( char *sourceFileName, char *fsmName, 
153                 ostream &out, bool wantComplete )
154 {
155         CodeGenData *codeGen = 0;
156         switch ( codeStyle ) {
157                 case GenTables: 
158                         codeGen = new RubyTabCodeGen(out);
159                         break;
160                 case GenFTables:
161                         codeGen = new RubyFTabCodeGen(out);
162                         break;
163                 case GenFlat:
164                         codeGen = new RubyFlatCodeGen(out);
165                         break;
166                 case GenFFlat:
167                         codeGen = new RubyFFlatCodeGen(out);
168                         break;
169                 case GenGoto:
170                         if ( rubyImpl == Rubinius ) {
171                                 codeGen = new RbxGotoCodeGen(out);
172                         } else {
173                                 cout << "Goto style is still _very_ experimental " 
174                                         "and only supported using Rubinius.\n"
175                                         "You may want to enable the --rbx flag "
176                                         " to give it a try.\n";
177                                 exit(1);
178                         }
179                         break;
180                 default:
181                         cout << "Invalid code style\n";
182                         exit(1);
183                         break;
184         }
185         codeGen->sourceFileName = sourceFileName;
186         codeGen->fsmName = fsmName;
187         codeGen->wantComplete = wantComplete;
188
189         return codeGen;
190 }
191
192 /* Main, process args and call yyparse to start scanning input. */
193 int main(int argc, char **argv)
194 {
195         ParamCheck pc("-:xHlh?vo:T:F:G:P:", argc, argv);
196         char *xmlInputFileName = 0;
197
198         while ( pc.check() ) {
199                 switch ( pc.state ) {
200                 case ParamCheck::match:
201                         switch ( pc.parameter ) {
202                         /* Output. */
203                         case 'o':
204                                 if ( *pc.parameterArg == 0 )
205                                         error() << "a zero length output file name was given" << endl;
206                                 else if ( outputFileName != 0 )
207                                         error() << "more than one output file name was given" << endl;
208                                 else {
209                                         /* Ok, remember the output file name. */
210                                         outputFileName = pc.parameterArg;
211                                 }
212                                 break;
213
214                         /* Code style. */
215                         case 'T':
216                                 if ( pc.parameterArg[0] == '0' )
217                                         codeStyle = GenTables;
218                                 else if ( pc.parameterArg[0] == '1' )
219                                         codeStyle = GenFTables;
220                                 else {
221                                         error() << "-T" << pc.parameterArg[0] << 
222                                                         " is an invalid argument" << endl;
223                                         exit(1);
224                                 }
225                                 break;
226                         case 'F':
227                                 if ( pc.parameterArg[0] == '0' )
228                                         codeStyle = GenFlat;
229                                 else if ( pc.parameterArg[0] == '1' )
230                                         codeStyle = GenFFlat;
231                                 else {
232                                         error() << "-F" << pc.parameterArg[0] << 
233                                                         " is an invalid argument" << endl;
234                                         exit(1);
235                                 }
236                                 break;
237                         case 'G':
238                                 if ( pc.parameterArg[0] == '0' )
239                                         codeStyle = GenGoto;
240                                 else if ( pc.parameterArg[0] == '1' )
241                                         codeStyle = GenFGoto;
242                                 else if ( pc.parameterArg[0] == '2' )
243                                         codeStyle = GenIpGoto;
244                                 else {
245                                         error() << "-G" << pc.parameterArg[0] << 
246                                                         " is an invalid argument" << endl;
247                                         exit(1);
248                                 }
249                                 break;
250                         case 'P':
251                                 codeStyle = GenSplit;
252                                 numSplitPartitions = atoi( pc.parameterArg );
253                                 break;
254
255                         case 'x':
256                                 rubyImpl = Rubinius;
257                                 break;
258
259                         /* Version and help. */
260                         case 'v':
261                                 version();
262                                 exit(0);
263                         case 'H': case 'h': case '?':
264                                 usage();
265                                 exit(0);
266
267                         case '-':
268                                 if ( strcasecmp(pc.parameterArg, "help") == 0 ) {
269                                         usage();
270                                         exit(0);
271                                 }
272                                 else if ( strcasecmp(pc.parameterArg, "version") == 0 ) {
273                                         version();
274                                         exit(0);
275                                 }
276                                 else if ( strcasecmp(pc.parameterArg, "rbx") == 0 ) {
277                                         rubyImpl = Rubinius;
278                                 }
279                                 else {
280                                         error() << "--" << pc.parameterArg << 
281                                                         " is an invalid argument" << endl;
282                                         break;
283                                 }
284                         }
285                         break;
286
287                 case ParamCheck::invalid:
288                         error() << "-" << pc.parameter << " is an invalid argument" << endl;
289                         break;
290
291                 case ParamCheck::noparam:
292                         if ( *pc.curArg == 0 )
293                                 error() << "a zero length input file name was given" << endl;
294                         else if ( xmlInputFileName != 0 )
295                                 error() << "more than one input file name was given" << endl;
296                         else {
297                                 /* OK, Remember the filename. */
298                                 xmlInputFileName = pc.curArg;
299                         }
300                         break;
301                 }
302         }
303
304         /* Bail on above errors. */
305         if ( gblErrorCount > 0 )
306                 exit(1);
307
308         /* Open the input file for reading. */
309         if ( xmlInputFileName != 0 ) {
310                 /* Open the input file for reading. */
311                 ifstream *inFile = new ifstream( xmlInputFileName );
312                 inStream = inFile;
313                 if ( ! inFile->is_open() )
314                         error() << "could not open " << xmlInputFileName << " for reading" << endl;
315         }
316         else {
317                 xmlInputFileName = strdup("<stdin>");
318                 inStream = &cin;
319         }
320
321         /* Bail on above errors. */
322         if ( gblErrorCount > 0 )
323                 exit(1);
324
325         bool wantComplete = true;
326         bool outputActive = true;
327
328         /* Parse the input! */
329         xml_parse( *inStream, xmlInputFileName, outputActive, wantComplete );
330
331         /* If writing to a file, delete the ostream, causing it to flush.
332          * Standard out is flushed automatically. */
333         if ( outputFileName != 0 ) {
334                 delete outStream;
335                 delete outFilter;
336         }
337
338         /* Finished, final check for errors.. */
339         if ( gblErrorCount > 0 ) {
340                 /* If we opened an output file, remove it. */
341                 if ( outputFileName != 0 )
342                         unlink( outputFileName );
343                 exit(1);
344         }
345         return 0;
346 }
347
348 /*
349  * Local Variables:
350  * mode: c++
351  * indent-tabs-mode: 1
352  * c-file-style: "bsd"
353  * End:
354  */