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-ruby.h"
35 #include "rubycodegen.h"
47 istream *inStream = 0;
48 ostream *outStream = 0;
49 output_filter *outFilter = 0;
50 char *outputFileName = 0;
52 /* Graphviz dot file generation. */
53 bool graphvizDone = false;
55 int numSplitPartitions = 0;
56 bool printPrintables = false;
58 /* Print a summary of the options. */
62 "usage: " PROGNAME " [options] file\n"
64 " -h, -H, -?, --help Print this usage and exit\n"
65 " -v, --version Print version information and exit\n"
66 " -o <file> Write output to <file>\n"
70 /* Print version information. */
73 cout << "Ragel Code Generator for Ruby" << endl <<
74 "Version " VERSION << ", " PUBDATE << endl <<
75 "Copyright (c) 2001-2007 by Adrian Thurston" << endl;
78 /* Scans a string looking for the file extension. If there is a file
79 * extension then pointer returned points to inside the string
80 * passed in. Otherwise returns null. */
81 char *findFileExtension( char *stemFile )
83 char *ppos = stemFile + strlen(stemFile) - 1;
85 /* Scan backwards from the end looking for the first dot.
86 * If we encounter a '/' before the first dot, then stop the scan. */
88 /* If we found a dot or got to the beginning of the string then
90 if ( ppos == stemFile || *ppos == '.' )
93 /* If we hit a / then there is no extension. Done. */
101 /* If we got to the front of the string then bail we
102 * did not find an extension */
103 if ( ppos == stemFile )
109 /* Make a file name from a stem. Removes the old filename suffix and
110 * replaces it with a new one. Returns a newed up string. */
111 char *fileNameFromStem( char *stemFile, char *suffix )
113 int len = strlen( stemFile );
116 /* Get the extension. */
117 char *ppos = findFileExtension( stemFile );
119 /* If an extension was found, then shorten what we think the len is. */
121 len = ppos - stemFile;
123 /* Make the return string from the stem and the suffix. */
124 char *retVal = new char[ len + strlen( suffix ) + 1 ];
125 strncpy( retVal, stemFile, len );
126 strcpy( retVal + len, suffix );
131 /* Total error count. */
132 int gblErrorCount = 0;
137 cerr << PROGNAME ": ";
141 /* Counts newlines before sending sync. */
142 int output_filter::sync( )
145 return std::filebuf::sync();
148 /* Counts newlines before sending data out to file. */
149 std::streamsize output_filter::xsputn( const char *s, std::streamsize n )
151 for ( int i = 0; i < n; i++ ) {
155 return std::filebuf::xsputn( s, n );
158 void escapeLineDirectivePath( std::ostream &out, char *path )
160 for ( char *pc = path; *pc != 0; pc++ ) {
169 * Callbacks invoked by the XML data parser.
172 /* Invoked by the parser when the root element is opened. */
173 ostream *openOutput( char *inputFile, char *language )
175 if ( strcmp( language, "Ruby" ) == 0 ) {
176 // hostLangType = JavaCode;
177 // hostLang = &hostLangJava;
180 error() << "this code genreator is for Java only" << endl;
183 /* If the output format is code and no output file name is given, then
185 if ( outputFileName == 0 ) {
186 char *ext = findFileExtension( inputFile );
187 if ( ext != 0 && strcmp( ext, ".rh" ) == 0 )
188 outputFileName = fileNameFromStem( inputFile, ".h" );
190 char *defExtension = 0;
191 switch ( hostLangType ) {
192 case CCode: defExtension = ".c"; break;
193 case DCode: defExtension = ".d"; break;
194 case JavaCode: defExtension = ".java"; break;
196 outputFileName = fileNameFromStem( inputFile, defExtension );
200 /* Make sure we are not writing to the same file as the input file. */
201 if ( outputFileName != 0 && strcmp( inputFile, outputFileName ) == 0 ) {
202 error() << "output file \"" << outputFileName <<
203 "\" is the same as the input file" << endl;
206 if ( outputFileName != 0 ) {
207 /* Create the filter on the output and open it. */
208 outFilter = new output_filter( outputFileName );
209 outFilter->open( outputFileName, ios::out|ios::trunc );
210 if ( !outFilter->is_open() ) {
211 error() << "error opening " << outputFileName << " for writing" << endl;
215 /* Open the output stream, attaching it to the filter. */
216 outStream = new ostream( outFilter );
219 /* Writing out ot std out. */
225 /* Invoked by the parser when a ragel definition is opened. */
226 CodeGenData *makeCodeGen( char *sourceFileName, char *fsmName,
227 ostream &out, bool wantComplete )
229 CodeGenData *codeGen = new RubyCodeGen(out);
231 codeGen->sourceFileName = sourceFileName;
232 codeGen->fsmName = fsmName;
233 codeGen->wantComplete = wantComplete;
238 /* Main, process args and call yyparse to start scanning input. */
239 int main(int argc, char **argv)
241 ParamCheck pc("o:VpT:F:G:vHh?-:P:", argc, argv);
242 char *xmlInputFileName = 0;
244 while ( pc.check() ) {
245 switch ( pc.state ) {
246 case ParamCheck::match:
247 switch ( pc.parameter ) {
250 if ( *pc.parameterArg == 0 )
251 error() << "a zero length output file name was given" << endl;
252 else if ( outputFileName != 0 )
253 error() << "more than one output file name was given" << endl;
255 /* Ok, remember the output file name. */
256 outputFileName = pc.parameterArg;
260 /* Version and help. */
264 case 'H': case 'h': case '?':
268 if ( strcasecmp(pc.parameterArg, "help") == 0 ) {
272 else if ( strcasecmp(pc.parameterArg, "version") == 0 ) {
277 error() << "--" << pc.parameterArg <<
278 " is an invalid argument" << endl;
284 case ParamCheck::invalid:
285 error() << "-" << pc.parameter << " is an invalid argument" << endl;
288 case ParamCheck::noparam:
289 if ( *pc.curArg == 0 )
290 error() << "a zero length input file name was given" << endl;
291 else if ( xmlInputFileName != 0 )
292 error() << "more than one input file name was given" << endl;
294 /* OK, Remember the filename. */
295 xmlInputFileName = pc.curArg;
301 /* Bail on above errors. */
302 if ( gblErrorCount > 0 )
305 /* Open the input file for reading. */
306 if ( xmlInputFileName != 0 ) {
307 /* Open the input file for reading. */
308 ifstream *inFile = new ifstream( xmlInputFileName );
310 if ( ! inFile->is_open() )
311 error() << "could not open " << xmlInputFileName << " for reading" << endl;
314 xmlInputFileName = "<stdin>";
318 /* Bail on above errors. */
319 if ( gblErrorCount > 0 )
322 bool wantComplete = true;
323 bool outputActive = true;
325 /* Parse the input! */
326 xml_parse( *inStream, xmlInputFileName, outputActive, wantComplete );
328 /* If writing to a file, delete the ostream, causing it to flush.
329 * Standard out is flushed automatically. */
330 if ( outputFileName != 0 ) {
335 /* Finished, final check for errors.. */
336 if ( gblErrorCount > 0 ) {
337 /* If we opened an output file, remove it. */
338 if ( outputFileName != 0 )
339 unlink( outputFileName );