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 "rlcodegen.h"
30 #include "rlcodegen.h"
47 /* Target language and output style. */
48 OutputFormat outputFormat = OutCode;
49 CodeStyleEnum codeStyle = GenTables;
52 istream *inStream = 0;
53 ostream *outStream = 0;
54 output_filter *outFilter = 0;
55 char *outputFileName = 0;
57 /* Graphviz dot file generation. */
58 bool graphvizDone = false;
60 char *gblFileName = "<unknown>";
62 int numSplitPartitions = 0;
64 bool printPrintables = false;
66 /* Print a summary of the options. */
70 "usage: rlcodegen [options] file\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"
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"
90 /* Print version information. */
93 cout << "Ragel Code Generator version " VERSION << " " PUBDATE << endl <<
94 "Copyright (c) 2001-2006 by Adrian Thurston" << endl;
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 )
102 char *ppos = stemFile + strlen(stemFile) - 1;
104 /* Scan backwards from the end looking for the first dot.
105 * If we encounter a '/' before the first dot, then stop the scan. */
107 /* If we found a dot or got to the beginning of the string then
109 if ( ppos == stemFile || *ppos == '.' )
112 /* If we hit a / then there is no extension. Done. */
113 if ( *ppos == '/' ) {
120 /* If we got to the front of the string then bail we
121 * did not find an extension */
122 if ( ppos == stemFile )
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 )
132 int len = strlen( stemFile );
135 /* Get the extension. */
136 char *ppos = findFileExtension( stemFile );
138 /* If an extension was found, then shorten what we think the len is. */
140 len = ppos - stemFile;
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 );
150 /* Total error count. */
151 int gblErrorCount = 0;
153 /* Print the opening to a program error, then return the error stream. */
157 cerr << PROGNAME ": ";
161 /* Print the opening to an error in the input, then return the error ostream. */
162 //ostream &error( const YYLTYPE &loc )
164 // gblErrorCount += 1;
165 // cerr << gblFileName << ":" << loc.first_line << ":" << loc.first_column << ": ";
169 /* Print the opening to an error in the input, then return the error ostream. */
170 //ostream &error( const InputLoc &loc )
172 // gblErrorCount += 1;
173 // cerr << gblFileName << ":" << loc.line << ":" << loc.col << ": ";
177 ostream &error( int first_line, int first_column )
180 cerr << gblFileName << ":" << ":" << first_line << ":" << first_column << ": ";
186 cerr << gblFileName << ":" << ": warning: ";
190 ostream &warning( const InputLoc &loc )
192 cerr << gblFileName << loc.line << ":" << loc.col << ": warning: ";
196 std::ostream &warning( int first_line, int first_column )
198 cerr << gblFileName << ":" << first_line << ":" <<
199 first_column << ": warning: ";
203 //ostream &xml_error( const YYLTYPE &loc )
205 // gblErrorCount += 1;
206 // cerr << "<xml-input>:" << loc.first_line << ":" << loc.first_column << ": ";
210 ostream &xml_error( const InputLoc &loc )
213 cerr << "<xml-input>:" << loc.line << ":" << loc.col << ": ";
217 /* Counts newlines before sending sync. */
218 int output_filter::sync( )
221 return std::filebuf::sync();
224 /* Counts newlines before sending data out to file. */
225 std::streamsize output_filter::xsputn( const char *s, std::streamsize n )
227 for ( int i = 0; i < n; i++ ) {
231 return std::filebuf::xsputn( s, n );
234 void escapeLineDirectivePath( std::ostream &out, char *path )
236 for ( char *pc = path; *pc != 0; pc++ ) {
244 /* Invoked by the parser, after the source file
245 * name is taken from XML file. */
246 void openOutput( char *inputFile )
248 /* If the output format is code and no output file name is given, then
250 if ( outputFormat == OutCode && outputFileName == 0 ) {
251 char *ext = findFileExtension( inputFile );
252 if ( ext != 0 && strcmp( ext, ".rh" ) == 0 )
253 outputFileName = fileNameFromStem( inputFile, ".h" );
255 char *defExtension = 0;
256 switch ( hostLangType ) {
257 case CCode: defExtension = ".c"; break;
258 case DCode: defExtension = ".d"; break;
259 case JavaCode: defExtension = ".java"; break;
261 outputFileName = fileNameFromStem( inputFile, defExtension );
265 /* Make sure we are not writing to the same file as the input file. */
266 if ( outputFileName != 0 && strcmp( inputFile, outputFileName ) == 0 ) {
267 error() << "output file \"" << outputFileName <<
268 "\" is the same as the input file" << endl;
271 if ( outputFileName != 0 ) {
272 /* Create the filter on the output and open it. */
273 outFilter = new output_filter;
274 outFilter->open( outputFileName, ios::out|ios::trunc );
275 if ( !outFilter->is_open() ) {
276 error() << "error opening " << outputFileName << " for writing" << endl;
280 /* Open the output stream, attaching it to the filter. */
281 outStream = new ostream( outFilter );
284 /* Writing out ot std out. */
289 /* Main, process args and call yyparse to start scanning input. */
290 int main(int argc, char **argv)
292 ParamCheck pc("o:VpT:F:G:vHh?-:P:", argc, argv);
293 char *xmlInputFileName = 0;
295 while ( pc.check() ) {
296 switch ( pc.state ) {
297 case ParamCheck::match:
298 switch ( pc.parameter ) {
301 if ( *pc.parameterArg == 0 )
302 error() << "a zero length output file name was given" << endl;
303 else if ( outputFileName != 0 )
304 error() << "more than one output file name was given" << endl;
306 /* Ok, remember the output file name. */
307 outputFileName = pc.parameterArg;
311 /* Output formats. */
313 outputFormat = OutGraphvizDot;
317 printPrintables = true;
322 if ( pc.parameterArg[0] == '0' )
323 codeStyle = GenTables;
324 else if ( pc.parameterArg[0] == '1' )
325 codeStyle = GenFTables;
327 error() << "-T" << pc.parameterArg[0] <<
328 " is an invalid argument" << endl;
333 if ( pc.parameterArg[0] == '0' )
335 else if ( pc.parameterArg[0] == '1' )
336 codeStyle = GenFFlat;
338 error() << "-F" << pc.parameterArg[0] <<
339 " is an invalid argument" << endl;
344 if ( pc.parameterArg[0] == '0' )
346 else if ( pc.parameterArg[0] == '1' )
347 codeStyle = GenFGoto;
348 else if ( pc.parameterArg[0] == '2' )
349 codeStyle = GenIpGoto;
351 error() << "-G" << pc.parameterArg[0] <<
352 " is an invalid argument" << endl;
357 codeStyle = GenSplit;
358 numSplitPartitions = atoi( pc.parameterArg );
361 /* Version and help. */
365 case 'H': case 'h': case '?':
369 if ( strcasecmp(pc.parameterArg, "help") == 0 ) {
373 else if ( strcasecmp(pc.parameterArg, "version") == 0 ) {
378 error() << "--" << pc.parameterArg <<
379 " is an invalid argument" << endl;
385 case ParamCheck::invalid:
386 error() << "-" << pc.parameter << " is an invalid argument" << endl;
389 case ParamCheck::noparam:
390 if ( *pc.curArg == 0 )
391 error() << "a zero length input file name was given" << endl;
392 else if ( xmlInputFileName != 0 )
393 error() << "more than one input file name was given" << endl;
395 /* OK, Remember the filename. */
396 xmlInputFileName = pc.curArg;
402 /* Bail on above errors. */
403 if ( gblErrorCount > 0 )
406 /* Open the input file for reading. */
407 if ( xmlInputFileName != 0 ) {
408 /* Open the input file for reading. */
409 ifstream *inFile = new ifstream( xmlInputFileName );
411 if ( ! inFile->is_open() )
412 error() << "could not open " << xmlInputFileName << " for reading" << endl;
415 xmlInputFileName = "<stdin>";
419 /* Bail on above errors. */
420 if ( gblErrorCount > 0 )
423 /* Parse the input! */
424 xml_parse( *inStream, xmlInputFileName );
426 /* If writing to a file, delete the ostream, causing it to flush.
427 * Standard out is flushed automatically. */
428 if ( outputFileName != 0 ) {
433 /* Finished, final check for errors.. */
434 if ( gblErrorCount > 0 ) {
435 /* If we opened an output file, remove it. */
436 if ( outputFileName != 0 )
437 unlink( outputFileName );