2 * Copyright 2001-2005 Adrian Thurston <thurston@cs.queensu.ca>
5 /* This file is part of Ragel.
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.
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.
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
29 #include "rlgen-java.h"
35 #include "javacodegen.h"
46 /* Target language and output style. */
47 OutputFormat outputFormat = OutCode;
48 CodeStyleEnum codeStyle = GenTables;
51 istream *inStream = 0;
52 ostream *outStream = 0;
53 output_filter *outFilter = 0;
54 char *outputFileName = 0;
56 /* Graphviz dot file generation. */
57 bool graphvizDone = false;
59 int numSplitPartitions = 0;
60 bool printPrintables = false;
62 /* Print a summary of the options. */
66 "usage: rlcodegen-java [options] file\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"
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"
86 /* Print version information. */
89 cout << "Ragel Code Generator version " VERSION << " " PUBDATE << endl <<
90 "Copyright (c) 2001-2006 by Adrian Thurston" << endl;
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 )
98 char *ppos = stemFile + strlen(stemFile) - 1;
100 /* Scan backwards from the end looking for the first dot.
101 * If we encounter a '/' before the first dot, then stop the scan. */
103 /* If we found a dot or got to the beginning of the string then
105 if ( ppos == stemFile || *ppos == '.' )
108 /* If we hit a / then there is no extension. Done. */
109 if ( *ppos == '/' ) {
116 /* If we got to the front of the string then bail we
117 * did not find an extension */
118 if ( ppos == stemFile )
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 )
128 int len = strlen( stemFile );
131 /* Get the extension. */
132 char *ppos = findFileExtension( stemFile );
134 /* If an extension was found, then shorten what we think the len is. */
136 len = ppos - stemFile;
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 );
146 /* Total error count. */
147 int gblErrorCount = 0;
152 cerr << PROGNAME ": ";
156 /* Counts newlines before sending sync. */
157 int output_filter::sync( )
160 return std::filebuf::sync();
163 /* Counts newlines before sending data out to file. */
164 std::streamsize output_filter::xsputn( const char *s, std::streamsize n )
166 for ( int i = 0; i < n; i++ ) {
170 return std::filebuf::xsputn( s, n );
173 void escapeLineDirectivePath( std::ostream &out, char *path )
175 for ( char *pc = path; *pc != 0; pc++ ) {
184 * Callbacks invoked by the XML data parser.
187 /* Invoked by the parser when the root element is opened. */
188 ostream *openOutput( char *inputFile, char *language )
190 if ( strcmp( language, "C" ) == 0 ) {
191 hostLangType = CCode;
192 hostLang = &hostLangC;
194 else if ( strcmp( language, "D" ) == 0 ) {
195 hostLangType = DCode;
196 hostLang = &hostLangD;
198 else if ( strcmp( language, "Java" ) == 0 ) {
199 hostLangType = JavaCode;
200 hostLang = &hostLangJava;
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;
209 /* If the output format is code and no output file name is given, then
211 if ( outputFormat == OutCode && outputFileName == 0 ) {
212 char *ext = findFileExtension( inputFile );
213 if ( ext != 0 && strcmp( ext, ".rh" ) == 0 )
214 outputFileName = fileNameFromStem( inputFile, ".h" );
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;
222 outputFileName = fileNameFromStem( inputFile, defExtension );
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;
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;
241 /* Open the output stream, attaching it to the filter. */
242 outStream = new ostream( outFilter );
245 /* Writing out ot std out. */
251 /* Invoked by the parser when a ragel definition is opened. */
252 CodeGenData *makeCodeGen( char *sourceFileName, char *fsmName,
253 ostream &out, bool wantComplete )
255 CodeGenData *codeGen = new JavaTabCodeGen(out);
257 codeGen->sourceFileName = sourceFileName;
258 codeGen->fsmName = fsmName;
259 codeGen->wantComplete = wantComplete;
264 /* Main, process args and call yyparse to start scanning input. */
265 int main(int argc, char **argv)
267 ParamCheck pc("o:VpT:F:G:vHh?-:P:", argc, argv);
268 char *xmlInputFileName = 0;
270 while ( pc.check() ) {
271 switch ( pc.state ) {
272 case ParamCheck::match:
273 switch ( pc.parameter ) {
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;
281 /* Ok, remember the output file name. */
282 outputFileName = pc.parameterArg;
286 /* Output formats. */
288 outputFormat = OutGraphvizDot;
292 printPrintables = true;
297 if ( pc.parameterArg[0] == '0' )
298 codeStyle = GenTables;
299 else if ( pc.parameterArg[0] == '1' )
300 codeStyle = GenFTables;
302 error() << "-T" << pc.parameterArg[0] <<
303 " is an invalid argument" << endl;
308 if ( pc.parameterArg[0] == '0' )
310 else if ( pc.parameterArg[0] == '1' )
311 codeStyle = GenFFlat;
313 error() << "-F" << pc.parameterArg[0] <<
314 " is an invalid argument" << endl;
319 if ( pc.parameterArg[0] == '0' )
321 else if ( pc.parameterArg[0] == '1' )
322 codeStyle = GenFGoto;
323 else if ( pc.parameterArg[0] == '2' )
324 codeStyle = GenIpGoto;
326 error() << "-G" << pc.parameterArg[0] <<
327 " is an invalid argument" << endl;
332 codeStyle = GenSplit;
333 numSplitPartitions = atoi( pc.parameterArg );
336 /* Version and help. */
340 case 'H': case 'h': case '?':
344 if ( strcasecmp(pc.parameterArg, "help") == 0 ) {
348 else if ( strcasecmp(pc.parameterArg, "version") == 0 ) {
353 error() << "--" << pc.parameterArg <<
354 " is an invalid argument" << endl;
360 case ParamCheck::invalid:
361 error() << "-" << pc.parameter << " is an invalid argument" << endl;
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;
370 /* OK, Remember the filename. */
371 xmlInputFileName = pc.curArg;
377 /* Bail on above errors. */
378 if ( gblErrorCount > 0 )
381 /* Open the input file for reading. */
382 if ( xmlInputFileName != 0 ) {
383 /* Open the input file for reading. */
384 ifstream *inFile = new ifstream( xmlInputFileName );
386 if ( ! inFile->is_open() )
387 error() << "could not open " << xmlInputFileName << " for reading" << endl;
390 xmlInputFileName = "<stdin>";
394 /* Bail on above errors. */
395 if ( gblErrorCount > 0 )
398 bool wantComplete = outputFormat != OutGraphvizDot;
399 bool outputActive = outputFormat == OutCode;
401 /* Parse the input! */
402 xml_parse( *inStream, xmlInputFileName, outputActive, wantComplete );
404 /* If writing to a file, delete the ostream, causing it to flush.
405 * Standard out is flushed automatically. */
406 if ( outputFileName != 0 ) {
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 );