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