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