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