1 /*
2 * Copyright 2008 Adrian Thurston <thurston@complang.org>
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 "ragel.h"
23 #include "common.h"
24 #include "inputdata.h"
25 #include "parsedata.h"
26 #include "rlparse.h"
27 #include <iostream>
28 #include "dotcodegen.h"
29
30 using std::cout;
31 using std::cerr;
32 using std::endl;
33 using std::ios;
34
35 /* Invoked by the parser when the root element is opened. */
cdDefaultFileName(const char * inputFile)36 void InputData::cdDefaultFileName( const char *inputFile )
37 {
38 /* If the output format is code and no output file name is given, then
39 * make a default. */
40 if ( outputFileName == 0 ) {
41 const char *ext = findFileExtension( inputFile );
42 if ( ext != 0 && strcmp( ext, ".rh" ) == 0 )
43 outputFileName = fileNameFromStem( inputFile, ".h" );
44 else {
45 const char *defExtension = 0;
46 switch ( hostLang->lang ) {
47 case HostLang::C: defExtension = ".c"; break;
48 case HostLang::D: defExtension = ".d"; break;
49 case HostLang::D2: defExtension = ".d"; break;
50 default: break;
51 }
52 outputFileName = fileNameFromStem( inputFile, defExtension );
53 }
54 }
55 }
56
57 /* Invoked by the parser when the root element is opened. */
goDefaultFileName(const char * inputFile)58 void InputData::goDefaultFileName( const char *inputFile )
59 {
60 /* If the output format is code and no output file name is given, then
61 * make a default. */
62 if ( outputFileName == 0 )
63 outputFileName = fileNameFromStem( inputFile, ".go" );
64 }
65
66 /* Invoked by the parser when the root element is opened. */
javaDefaultFileName(const char * inputFile)67 void InputData::javaDefaultFileName( const char *inputFile )
68 {
69 /* If the output format is code and no output file name is given, then
70 * make a default. */
71 if ( outputFileName == 0 )
72 outputFileName = fileNameFromStem( inputFile, ".java" );
73 }
74
75 /* Invoked by the parser when the root element is opened. */
rubyDefaultFileName(const char * inputFile)76 void InputData::rubyDefaultFileName( const char *inputFile )
77 {
78 /* If the output format is code and no output file name is given, then
79 * make a default. */
80 if ( outputFileName == 0 )
81 outputFileName = fileNameFromStem( inputFile, ".rb" );
82 }
83
84 /* Invoked by the parser when the root element is opened. */
csharpDefaultFileName(const char * inputFile)85 void InputData::csharpDefaultFileName( const char *inputFile )
86 {
87 /* If the output format is code and no output file name is given, then
88 * make a default. */
89 if ( outputFileName == 0 ) {
90 const char *ext = findFileExtension( inputFile );
91 if ( ext != 0 && strcmp( ext, ".rh" ) == 0 )
92 outputFileName = fileNameFromStem( inputFile, ".h" );
93 else
94 outputFileName = fileNameFromStem( inputFile, ".cs" );
95 }
96 }
97
98 /* Invoked by the parser when the root element is opened. */
ocamlDefaultFileName(const char * inputFile)99 void InputData::ocamlDefaultFileName( const char *inputFile )
100 {
101 /* If the output format is code and no output file name is given, then
102 * make a default. */
103 if ( outputFileName == 0 )
104 outputFileName = fileNameFromStem( inputFile, ".ml" );
105 }
106
makeOutputStream()107 void InputData::makeOutputStream()
108 {
109 if ( ! generateDot && ! generateXML ) {
110 switch ( hostLang->lang ) {
111 case HostLang::C:
112 case HostLang::D:
113 case HostLang::D2:
114 cdDefaultFileName( inputFileName );
115 break;
116 case HostLang::Java:
117 javaDefaultFileName( inputFileName );
118 break;
119 case HostLang::Go:
120 goDefaultFileName( inputFileName );
121 break;
122 case HostLang::Ruby:
123 rubyDefaultFileName( inputFileName );
124 break;
125 case HostLang::CSharp:
126 csharpDefaultFileName( inputFileName );
127 break;
128 case HostLang::OCaml:
129 ocamlDefaultFileName( inputFileName );
130 break;
131 }
132 }
133
134 /* Make sure we are not writing to the same file as the input file. */
135 if ( outputFileName != 0 ) {
136 if ( strcmp( inputFileName, outputFileName ) == 0 ) {
137 error() << "output file \"" << outputFileName <<
138 "\" is the same as the input file" << endl;
139 }
140
141 /* Create the filter on the output and open it. */
142 outFilter = new output_filter( outputFileName );
143
144 /* Open the output stream, attaching it to the filter. */
145 outStream = new ostream( outFilter );
146 }
147 else {
148 /* Writing out ot std out. */
149 outStream = &cout;
150 }
151 }
152
openOutput()153 void InputData::openOutput()
154 {
155 if ( outFilter != 0 ) {
156 outFilter->open( outputFileName, ios::out|ios::trunc );
157 if ( !outFilter->is_open() ) {
158 error() << "error opening " << outputFileName << " for writing" << endl;
159 exit(1);
160 }
161 }
162 }
163
prepareMachineGen()164 void InputData::prepareMachineGen()
165 {
166 if ( generateDot ) {
167 /* Locate a machine spec to generate dot output for. We can only emit.
168 * Dot takes one graph at a time. */
169 if ( machineSpec != 0 ) {
170 /* Machine specified. */
171 ParserDictEl *pdEl = parserDict.find( machineSpec );
172 if ( pdEl == 0 )
173 error() << "could not locate machine specified with -S and/or -M" << endp;
174 dotGenParser = pdEl->value;
175 }
176 else {
177 /* No machine spec given, just use the first one. */
178 if ( parserList.length() == 0 )
179 error() << "no machine specification to generate graphviz output" << endp;
180
181 dotGenParser = parserList.head;
182 }
183
184 GraphDictEl *gdEl = 0;
185
186 if ( machineName != 0 ) {
187 gdEl = dotGenParser->pd->graphDict.find( machineName );
188 if ( gdEl == 0 )
189 error() << "machine definition/instantiation not found" << endp;
190 }
191 else {
192 /* We are using the whole machine spec. Need to make sure there
193 * are instances in the spec. */
194 if ( dotGenParser->pd->instanceList.length() == 0 )
195 error() << "no machine instantiations to generate graphviz output" << endp;
196 }
197
198 dotGenParser->pd->prepareMachineGen( gdEl );
199 }
200 else {
201 /* No machine spec or machine name given. Generate everything. */
202 for ( ParserDict::Iter parser = parserDict; parser.lte(); parser++ ) {
203 ParseData *pd = parser->value->pd;
204 if ( pd->instanceList.length() > 0 )
205 pd->prepareMachineGen( 0 );
206 }
207 }
208 }
209
generateReduced()210 void InputData::generateReduced()
211 {
212 if ( generateDot )
213 dotGenParser->pd->generateReduced( *this );
214 else {
215 for ( ParserDict::Iter parser = parserDict; parser.lte(); parser++ ) {
216 ParseData *pd = parser->value->pd;
217 if ( pd->instanceList.length() > 0 )
218 pd->generateReduced( *this );
219 }
220 }
221 }
222
223 /* Send eof to all parsers. */
terminateAllParsers()224 void InputData::terminateAllParsers( )
225 {
226 /* FIXME: a proper token is needed here. Suppose we should use the
227 * location of EOF in the last file that the parser was referenced in. */
228 InputLoc loc;
229 loc.fileName = "<EOF>";
230 loc.line = 0;
231 loc.col = 0;
232 for ( ParserDict::Iter pdel = parserDict; pdel.lte(); pdel++ )
233 pdel->value->token( loc, Parser_tk_eof, 0, 0 );
234 }
235
verifyWritesHaveData()236 void InputData::verifyWritesHaveData()
237 {
238 if ( !generateXML && !generateDot ) {
239 for ( InputItemList::Iter ii = inputItems; ii.lte(); ii++ ) {
240 if ( ii->type == InputItem::Write ) {
241 if ( ii->pd->cgd == 0 )
242 error( ii->loc ) << "no machine instantiations to write" << endl;
243 }
244 }
245 }
246 }
247
writeOutput()248 void InputData::writeOutput()
249 {
250 if ( generateXML )
251 writeXML( *outStream );
252 else if ( generateDot )
253 static_cast<GraphvizDotGen*>(dotGenParser->pd->cgd)->writeDotFile();
254 else {
255 bool hostLineDirective = true;
256 for ( InputItemList::Iter ii = inputItems; ii.lte(); ii++ ) {
257 if ( ii->type == InputItem::Write ) {
258 CodeGenData *cgd = ii->pd->cgd;
259 ::keyOps = &cgd->thisKeyOps;
260
261 hostLineDirective = cgd->writeStatement( ii->loc,
262 ii->writeArgs.length()-1, ii->writeArgs.data );
263 }
264 else {
265 if ( hostLineDirective ) {
266 /* Write statements can turn off host line directives for
267 * host sections that follow them. */
268 *outStream << '\n';
269 lineDirective( *outStream, inputFileName, ii->loc.line );
270 }
271 *outStream << ii->data.str();
272 hostLineDirective = true;
273 }
274 }
275 }
276 }
277
278