a389afda534d47f0a07fce44e022508e1767526b
[external/ragel.git] / rlgen-cd / main.cpp
1 /*
2  *  Copyright 2001-2007 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 "common.h"
30 #include "rlgen-cd.h"
31 #include "xmlparse.h"
32 #include "pcheck.h"
33 #include "vector.h"
34 #include "version.h"
35
36 /* Code generators. */
37 #include "tabcodegen.h"
38 #include "ftabcodegen.h"
39 #include "flatcodegen.h"
40 #include "fflatcodegen.h"
41 #include "gotocodegen.h"
42 #include "fgotocodegen.h"
43 #include "ipgotocodegen.h"
44 #include "splitcodegen.h"
45
46 using std::istream;
47 using std::ifstream;
48 using std::ostream;
49 using std::ios;
50 using std::cin;
51 using std::cout;
52 using std::cerr;
53 using std::endl;
54
55 /* Target language and output style. */
56 extern CodeStyleEnum codeStyle;
57
58 /* Io globals. */
59 istream *inStream = 0;
60 ostream *outStream = 0;
61 output_filter *outFilter = 0;
62 extern const char *outputFileName;
63
64 /* Graphviz dot file generation. */
65 bool graphvizDone = false;
66
67 extern int numSplitPartitions;
68 extern bool noLineDirectives;
69
70 /* Print a summary of the options. */
71 void cd_usage()
72 {
73         cout <<
74 "usage: " PROGNAME " [options] file\n"
75 "general:\n"
76 "   -h, -H, -?, --help    Print this usage and exit\n"
77 "   -v, --version         Print version information and exit\n"
78 "   -o <file>             Write output to <file>\n"
79 "code generation options:\n"
80 "   -L                    Inhibit writing of #line directives\n"
81 "generated code style:\n"
82 "   -T0                   Table driven FSM (default)\n"
83 "   -T1                   Faster table driven FSM\n"
84 "   -F0                   Flat table driven FSM\n"
85 "   -F1                   Faster flat table-driven FSM\n"
86 "   -G0                   Goto-driven FSM\n"
87 "   -G1                   Faster goto-driven FSM\n"
88 "   -G2                   Really fast goto-driven FSM\n"
89 "   -P<N>                 N-Way Split really fast goto-driven FSM\n"
90         ;       
91 }
92
93 /* Print version information. */
94 void cd_version()
95 {
96         cout << "Ragel Code Generator for C, C++, Objective-C and D" << endl <<
97                         "Version " VERSION << ", " PUBDATE << endl <<
98                         "Copyright (c) 2001-2007 by Adrian Thurston" << endl;
99 }
100
101 ostream &cd_error()
102 {
103         gblErrorCount += 1;
104         cerr << PROGNAME ": ";
105         return cerr;
106 }
107
108 /*
109  * Callbacks invoked by the XML data parser.
110  */
111
112 /* Invoked by the parser when the root element is opened. */
113 ostream *cdOpenOutput( char *inputFile )
114 {
115         if ( hostLang->lang != HostLang::C && hostLang->lang != HostLang::D ) {
116                 cd_error() << "this code generator is for C and D only" << endl;
117                 exit(1);
118         }
119
120         /* If the output format is code and no output file name is given, then
121          * make a default. */
122         if ( outputFileName == 0 ) {
123                 char *ext = findFileExtension( inputFile );
124                 if ( ext != 0 && strcmp( ext, ".rh" ) == 0 )
125                         outputFileName = fileNameFromStem( inputFile, ".h" );
126                 else {
127                         const char *defExtension = 0;
128                         switch ( hostLang->lang ) {
129                                 case HostLang::C: defExtension = ".c"; break;
130                                 case HostLang::D: defExtension = ".d"; break;
131                                 default: break;
132                         }
133                         outputFileName = fileNameFromStem( inputFile, defExtension );
134                 }
135         }
136
137         /* Make sure we are not writing to the same file as the input file. */
138         if ( outputFileName != 0 && strcmp( inputFile, outputFileName  ) == 0 ) {
139                 cd_error() << "output file \"" << outputFileName  << 
140                                 "\" is the same as the input file" << endl;
141         }
142
143         if ( outputFileName != 0 ) {
144                 /* Create the filter on the output and open it. */
145                 outFilter = new output_filter( outputFileName );
146                 outFilter->open( outputFileName, ios::out|ios::trunc );
147                 if ( !outFilter->is_open() ) {
148                         cd_error() << "error opening " << outputFileName << " for writing" << endl;
149                         exit(1);
150                 }
151
152                 /* Open the output stream, attaching it to the filter. */
153                 outStream = new ostream( outFilter );
154         }
155         else {
156                 /* Writing out ot std out. */
157                 outStream = &cout;
158         }
159         return outStream;
160 }
161
162 /* Invoked by the parser when a ragel definition is opened. */
163 CodeGenData *cdMakeCodeGen( char *sourceFileName, char *fsmName, 
164                 ostream &out, bool wantComplete )
165 {
166         CodeGenData *codeGen = 0;
167         switch ( hostLang->lang ) {
168         case HostLang::C:
169                 switch ( codeStyle ) {
170                 case GenTables:
171                         codeGen = new CTabCodeGen(out);
172                         break;
173                 case GenFTables:
174                         codeGen = new CFTabCodeGen(out);
175                         break;
176                 case GenFlat:
177                         codeGen = new CFlatCodeGen(out);
178                         break;
179                 case GenFFlat:
180                         codeGen = new CFFlatCodeGen(out);
181                         break;
182                 case GenGoto:
183                         codeGen = new CGotoCodeGen(out);
184                         break;
185                 case GenFGoto:
186                         codeGen = new CFGotoCodeGen(out);
187                         break;
188                 case GenIpGoto:
189                         codeGen = new CIpGotoCodeGen(out);
190                         break;
191                 case GenSplit:
192                         codeGen = new CSplitCodeGen(out);
193                         break;
194                 }
195                 break;
196
197         case HostLang::D:
198                 switch ( codeStyle ) {
199                 case GenTables:
200                         codeGen = new DTabCodeGen(out);
201                         break;
202                 case GenFTables:
203                         codeGen = new DFTabCodeGen(out);
204                         break;
205                 case GenFlat:
206                         codeGen = new DFlatCodeGen(out);
207                         break;
208                 case GenFFlat:
209                         codeGen = new DFFlatCodeGen(out);
210                         break;
211                 case GenGoto:
212                         codeGen = new DGotoCodeGen(out);
213                         break;
214                 case GenFGoto:
215                         codeGen = new DFGotoCodeGen(out);
216                         break;
217                 case GenIpGoto:
218                         codeGen = new DIpGotoCodeGen(out);
219                         break;
220                 case GenSplit:
221                         codeGen = new DSplitCodeGen(out);
222                         break;
223                 }
224                 break;
225
226         default: break;
227         }
228
229         codeGen->sourceFileName = sourceFileName;
230         codeGen->fsmName = fsmName;
231         codeGen->wantComplete = wantComplete;
232
233         return codeGen;
234 }
235
236 /* Main, process args and call yyparse to start scanning input. */
237 int cd_main( const char *xmlInputFileName )
238 {
239         /* Open the input file for reading. */
240         ifstream *inFile = new ifstream( xmlInputFileName );
241         inStream = inFile;
242         if ( ! inFile->is_open() )
243                 cd_error() << "could not open " << xmlInputFileName << " for reading" << endl;
244
245         /* Bail on above errors. */
246         if ( gblErrorCount > 0 )
247                 exit(1);
248
249         bool wantComplete = true;
250         bool outputActive = true;
251
252         /* Parse the input! */
253         xml_parse( *inStream, xmlInputFileName, outputActive, wantComplete );
254
255         /* If writing to a file, delete the ostream, causing it to flush.
256          * Standard out is flushed automatically. */
257         if ( outputFileName != 0 ) {
258                 delete outStream;
259                 delete outFilter;
260         }
261
262         /* Finished, final check for errors.. */
263         if ( gblErrorCount > 0 ) {
264                 /* If we opened an output file, remove it. */
265                 if ( outputFileName != 0 )
266                         unlink( outputFileName );
267                 exit(1);
268         }
269         return 0;
270 }