Merged the FsmCodeGen, TabCodeGen and JavaTabCodeGen classes. Updated the
[external/ragel.git] / rlgen-java / main.cpp
1 /*
2  *  Copyright 2001-2005 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 "rlgen-java.h"
30 #include "xmlparse.h"
31 #include "pcheck.h"
32 #include "vector.h"
33 #include "version.h"
34 #include "common.h"
35 #include "javacodegen.h"
36
37 using std::istream;
38 using std::ifstream;
39 using std::ostream;
40 using std::ios;
41 using std::cin;
42 using std::cout;
43 using std::cerr;
44 using std::endl;
45
46 /* Target language and output style. */
47 OutputFormat outputFormat = OutCode;
48 CodeStyleEnum codeStyle = GenTables;
49
50 /* Io globals. */
51 istream *inStream = 0;
52 ostream *outStream = 0;
53 output_filter *outFilter = 0;
54 char *outputFileName = 0;
55
56 /* Graphviz dot file generation. */
57 bool graphvizDone = false;
58
59 int numSplitPartitions = 0;
60 bool printPrintables = false;
61
62 /* Print a summary of the options. */
63 void usage()
64 {
65         cout <<
66 "usage: rlcodegen-java [options] file\n"
67 "general:\n"
68 "   -h, -H, -?, --help    Print this usage and exit\n"
69 "   -v, --version         Print version information and exit\n"
70 "   -o <file>             Write output to <file>\n"
71 "output:\n"
72 "   -V                    Generate a Graphviz dotfile instead of code\n"
73 "   -p                    Print printable characters in Graphviz output\n"
74 "generated code style:\n"
75 "   -T0                   Table driven FSM (default)\n"
76 "   -T1                   Faster table driven FSM\n"
77 "   -F0                   Flat table driven FSM\n"
78 "   -F1                   Faster flat table-driven FSM\n"
79 "   -G0                   Goto-driven FSM\n"
80 "   -G1                   Faster goto-driven FSM\n"
81 "   -G2                   Really fast goto-driven FSM\n"
82 "   -P<N>                 N-Way Split really fast goto-driven FSM\n"
83         ;       
84 }
85
86 /* Print version information. */
87 void version()
88 {
89         cout << "Ragel Code Generator version " VERSION << " " PUBDATE << endl <<
90                         "Copyright (c) 2001-2006 by Adrian Thurston" << endl;
91 }
92
93 /* Scans a string looking for the file extension. If there is a file
94  * extension then pointer returned points to inside the string
95  * passed in. Otherwise returns null. */
96 char *findFileExtension( char *stemFile )
97 {
98         char *ppos = stemFile + strlen(stemFile) - 1;
99
100         /* Scan backwards from the end looking for the first dot.
101          * If we encounter a '/' before the first dot, then stop the scan. */
102         while ( 1 ) {
103                 /* If we found a dot or got to the beginning of the string then
104                  * we are done. */
105                 if ( ppos == stemFile || *ppos == '.' )
106                         break;
107
108                 /* If we hit a / then there is no extension. Done. */
109                 if ( *ppos == '/' ) {
110                         ppos = stemFile;
111                         break;
112                 }
113                 ppos--;
114         } 
115
116         /* If we got to the front of the string then bail we 
117          * did not find an extension  */
118         if ( ppos == stemFile )
119                 ppos = 0;
120
121         return ppos;
122 }
123
124 /* Make a file name from a stem. Removes the old filename suffix and
125  * replaces it with a new one. Returns a newed up string. */
126 char *fileNameFromStem( char *stemFile, char *suffix )
127 {
128         int len = strlen( stemFile );
129         assert( len > 0 );
130
131         /* Get the extension. */
132         char *ppos = findFileExtension( stemFile );
133
134         /* If an extension was found, then shorten what we think the len is. */
135         if ( ppos != 0 )
136                 len = ppos - stemFile;
137
138         /* Make the return string from the stem and the suffix. */
139         char *retVal = new char[ len + strlen( suffix ) + 1 ];
140         strncpy( retVal, stemFile, len );
141         strcpy( retVal + len, suffix );
142
143         return retVal;
144 }
145
146 /* Total error count. */
147 int gblErrorCount = 0;
148
149 ostream &error()
150 {
151         gblErrorCount += 1;
152         cerr << PROGNAME ": ";
153         return cerr;
154 }
155
156 /* Counts newlines before sending sync. */
157 int output_filter::sync( )
158 {
159         line += 1;
160         return std::filebuf::sync();
161 }
162
163 /* Counts newlines before sending data out to file. */
164 std::streamsize output_filter::xsputn( const char *s, std::streamsize n )
165 {
166         for ( int i = 0; i < n; i++ ) {
167                 if ( s[i] == '\n' )
168                         line += 1;
169         }
170         return std::filebuf::xsputn( s, n );
171 }
172
173 void escapeLineDirectivePath( std::ostream &out, char *path )
174 {
175         for ( char *pc = path; *pc != 0; pc++ ) {
176                 if ( *pc == '\\' )
177                         out << "\\\\";
178                 else
179                         out << *pc;
180         }
181 }
182
183 /*
184  * Callbacks invoked by the XML data parser.
185  */
186
187 /* Invoked by the parser when the root element is opened. */
188 ostream *openOutput( char *inputFile, char *language )
189 {
190         if ( strcmp( language, "C" ) == 0 ) {
191                 hostLangType = CCode;
192                 hostLang = &hostLangC;
193         }
194         else if ( strcmp( language, "D" ) == 0 ) {
195                 hostLangType = DCode;
196                 hostLang = &hostLangD;
197         }
198         else if ( strcmp( language, "Java" ) == 0 ) {
199                 hostLangType = JavaCode;
200                 hostLang = &hostLangJava;
201         }
202
203         /* Eventually more types will be supported. */
204         if ( hostLangType == JavaCode && codeStyle != GenTables ) {
205                 error() << "java: only the table code style -T0 is "
206                                         "currently supported" << endl;
207         }
208
209         /* If the output format is code and no output file name is given, then
210          * make a default. */
211         if ( outputFormat == OutCode && outputFileName == 0 ) {
212                 char *ext = findFileExtension( inputFile );
213                 if ( ext != 0 && strcmp( ext, ".rh" ) == 0 )
214                         outputFileName = fileNameFromStem( inputFile, ".h" );
215                 else {
216                         char *defExtension = 0;
217                         switch ( hostLangType ) {
218                                 case CCode: defExtension = ".c"; break;
219                                 case DCode: defExtension = ".d"; break;
220                                 case JavaCode: defExtension = ".java"; break;
221                         }
222                         outputFileName = fileNameFromStem( inputFile, defExtension );
223                 }
224         }
225
226         /* Make sure we are not writing to the same file as the input file. */
227         if ( outputFileName != 0 && strcmp( inputFile, outputFileName  ) == 0 ) {
228                 error() << "output file \"" << outputFileName  << 
229                                 "\" is the same as the input file" << endl;
230         }
231
232         if ( outputFileName != 0 ) {
233                 /* Create the filter on the output and open it. */
234                 outFilter = new output_filter( outputFileName );
235                 outFilter->open( outputFileName, ios::out|ios::trunc );
236                 if ( !outFilter->is_open() ) {
237                         error() << "error opening " << outputFileName << " for writing" << endl;
238                         exit(1);
239                 }
240
241                 /* Open the output stream, attaching it to the filter. */
242                 outStream = new ostream( outFilter );
243         }
244         else {
245                 /* Writing out ot std out. */
246                 outStream = &cout;
247         }
248         return outStream;
249 }
250
251 /* Invoked by the parser when a ragel definition is opened. */
252 CodeGenData *makeCodeGen( char *sourceFileName, char *fsmName, 
253                 ostream &out, bool wantComplete )
254 {
255         CodeGenData *codeGen = new JavaTabCodeGen(out);
256
257         codeGen->sourceFileName = sourceFileName;
258         codeGen->fsmName = fsmName;
259         codeGen->wantComplete = wantComplete;
260
261         return codeGen;
262 }
263
264 /* Main, process args and call yyparse to start scanning input. */
265 int main(int argc, char **argv)
266 {
267         ParamCheck pc("o:VpT:F:G:vHh?-:P:", argc, argv);
268         char *xmlInputFileName = 0;
269
270         while ( pc.check() ) {
271                 switch ( pc.state ) {
272                 case ParamCheck::match:
273                         switch ( pc.parameter ) {
274                         /* Output. */
275                         case 'o':
276                                 if ( *pc.parameterArg == 0 )
277                                         error() << "a zero length output file name was given" << endl;
278                                 else if ( outputFileName != 0 )
279                                         error() << "more than one output file name was given" << endl;
280                                 else {
281                                         /* Ok, remember the output file name. */
282                                         outputFileName = pc.parameterArg;
283                                 }
284                                 break;
285
286                         /* Output formats. */
287                         case 'V':
288                                 outputFormat = OutGraphvizDot;
289                                 break;
290
291                         case 'p':
292                                 printPrintables = true;
293                                 break;
294
295                         /* Code style. */
296                         case 'T':
297                                 if ( pc.parameterArg[0] == '0' )
298                                         codeStyle = GenTables;
299                                 else if ( pc.parameterArg[0] == '1' )
300                                         codeStyle = GenFTables;
301                                 else {
302                                         error() << "-T" << pc.parameterArg[0] << 
303                                                         " is an invalid argument" << endl;
304                                         exit(1);
305                                 }
306                                 break;
307                         case 'F':
308                                 if ( pc.parameterArg[0] == '0' )
309                                         codeStyle = GenFlat;
310                                 else if ( pc.parameterArg[0] == '1' )
311                                         codeStyle = GenFFlat;
312                                 else {
313                                         error() << "-F" << pc.parameterArg[0] << 
314                                                         " is an invalid argument" << endl;
315                                         exit(1);
316                                 }
317                                 break;
318                         case 'G':
319                                 if ( pc.parameterArg[0] == '0' )
320                                         codeStyle = GenGoto;
321                                 else if ( pc.parameterArg[0] == '1' )
322                                         codeStyle = GenFGoto;
323                                 else if ( pc.parameterArg[0] == '2' )
324                                         codeStyle = GenIpGoto;
325                                 else {
326                                         error() << "-G" << pc.parameterArg[0] << 
327                                                         " is an invalid argument" << endl;
328                                         exit(1);
329                                 }
330                                 break;
331                         case 'P':
332                                 codeStyle = GenSplit;
333                                 numSplitPartitions = atoi( pc.parameterArg );
334                                 break;
335
336                         /* Version and help. */
337                         case 'v':
338                                 version();
339                                 exit(0);
340                         case 'H': case 'h': case '?':
341                                 usage();
342                                 exit(0);
343                         case '-':
344                                 if ( strcasecmp(pc.parameterArg, "help") == 0 ) {
345                                         usage();
346                                         exit(0);
347                                 }
348                                 else if ( strcasecmp(pc.parameterArg, "version") == 0 ) {
349                                         version();
350                                         exit(0);
351                                 }
352                                 else {
353                                         error() << "--" << pc.parameterArg << 
354                                                         " is an invalid argument" << endl;
355                                         break;
356                                 }
357                         }
358                         break;
359
360                 case ParamCheck::invalid:
361                         error() << "-" << pc.parameter << " is an invalid argument" << endl;
362                         break;
363
364                 case ParamCheck::noparam:
365                         if ( *pc.curArg == 0 )
366                                 error() << "a zero length input file name was given" << endl;
367                         else if ( xmlInputFileName != 0 )
368                                 error() << "more than one input file name was given" << endl;
369                         else {
370                                 /* OK, Remember the filename. */
371                                 xmlInputFileName = pc.curArg;
372                         }
373                         break;
374                 }
375         }
376
377         /* Bail on above errors. */
378         if ( gblErrorCount > 0 )
379                 exit(1);
380
381         /* Open the input file for reading. */
382         if ( xmlInputFileName != 0 ) {
383                 /* Open the input file for reading. */
384                 ifstream *inFile = new ifstream( xmlInputFileName );
385                 inStream = inFile;
386                 if ( ! inFile->is_open() )
387                         error() << "could not open " << xmlInputFileName << " for reading" << endl;
388         }
389         else {
390                 xmlInputFileName = "<stdin>";
391                 inStream = &cin;
392         }
393
394         /* Bail on above errors. */
395         if ( gblErrorCount > 0 )
396                 exit(1);
397
398         bool wantComplete = outputFormat != OutGraphvizDot;
399         bool outputActive = outputFormat == OutCode;
400
401         /* Parse the input! */
402         xml_parse( *inStream, xmlInputFileName, outputActive, wantComplete );
403
404         /* If writing to a file, delete the ostream, causing it to flush.
405          * Standard out is flushed automatically. */
406         if ( outputFileName != 0 ) {
407                 delete outStream;
408                 delete outFilter;
409         }
410
411         /* Finished, final check for errors.. */
412         if ( gblErrorCount > 0 ) {
413                 /* If we opened an output file, remove it. */
414                 if ( outputFileName != 0 )
415                         unlink( outputFileName );
416                 exit(1);
417         }
418         return 0;
419 }