001 /*
002 $Id: SourceUnit.java,v 1.14 2005/11/13 16:42:11 blackdrag Exp $
003
004 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005
006 Redistribution and use of this software and associated documentation
007 ("Software"), with or without modification, are permitted provided
008 that the following conditions are met:
009
010 1. Redistributions of source code must retain copyright
011 statements and notices. Redistributions must also contain a
012 copy of this document.
013
014 2. Redistributions in binary form must reproduce the
015 above copyright notice, this list of conditions and the
016 following disclaimer in the documentation and/or other
017 materials provided with the distribution.
018
019 3. The name "groovy" must not be used to endorse or promote
020 products derived from this Software without prior written
021 permission of The Codehaus. For written permission,
022 please contact info@codehaus.org.
023
024 4. Products derived from this Software may not be called "groovy"
025 nor may "groovy" appear in their names without prior written
026 permission of The Codehaus. "groovy" is a registered
027 trademark of The Codehaus.
028
029 5. Due credit should be given to The Codehaus -
030 http://groovy.codehaus.org/
031
032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043 OF THE POSSIBILITY OF SUCH DAMAGE.
044
045 */
046
047 package org.codehaus.groovy.control;
048
049 import groovy.lang.GroovyClassLoader;
050
051 import java.io.File;
052 import java.io.FileWriter;
053 import java.io.IOException;
054 import java.io.Reader;
055 import java.net.URL;
056
057 import org.codehaus.groovy.GroovyBugError;
058 import org.codehaus.groovy.ast.ModuleNode;
059 import org.codehaus.groovy.control.io.FileReaderSource;
060 import org.codehaus.groovy.control.io.ReaderSource;
061 import org.codehaus.groovy.control.io.StringReaderSource;
062 import org.codehaus.groovy.control.io.URLReaderSource;
063 import org.codehaus.groovy.control.messages.ExceptionMessage;
064 import org.codehaus.groovy.control.messages.Message;
065 import org.codehaus.groovy.control.messages.SimpleMessage;
066 import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
067 import org.codehaus.groovy.syntax.*;
068 import org.codehaus.groovy.tools.Utilities;
069
070 import antlr.MismatchedTokenException;
071 import antlr.NoViableAltException;
072
073 import com.thoughtworks.xstream.XStream;
074
075
076 /**
077 * Provides an anchor for a single source unit (usually a script file)
078 * as it passes through the compiler system.
079 *
080 * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
081 * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
082 * @version $Id: SourceUnit.java,v 1.14 2005/11/13 16:42:11 blackdrag Exp $
083 */
084
085 public class SourceUnit extends ProcessingUnit {
086
087 /**
088 * The pluggable parser used to generate the AST - we allow pluggability currently as we need to have Classic and JSR support
089 */
090 private ParserPlugin parserPlugin;
091
092 /**
093 * Where we can get Readers for our source unit
094 */
095 protected ReaderSource source;
096 /**
097 * A descriptive name of the source unit
098 */
099 protected String name;
100 /**
101 * A Concrete Syntax Tree of the source
102 */
103 protected Reduction cst;
104
105 /**
106 * A facade over the CST
107 */
108 protected SourceSummary sourceSummary;
109 /**
110 * The root of the Abstract Syntax Tree for the source
111 */
112 protected ModuleNode ast;
113
114
115 /**
116 * Initializes the SourceUnit from existing machinery.
117 */
118 public SourceUnit(String name, ReaderSource source, CompilerConfiguration flags, GroovyClassLoader loader, ErrorCollector er) {
119 super(flags, loader, er);
120
121 this.name = name;
122 this.source = source;
123 }
124
125
126 /**
127 * Initializes the SourceUnit from the specified file.
128 */
129 public SourceUnit(File source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
130 this(source.getPath(), new FileReaderSource(source, configuration), configuration, loader, er);
131 }
132
133
134 /**
135 * Initializes the SourceUnit from the specified URL.
136 */
137 public SourceUnit(URL source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
138 this(source.getPath(), new URLReaderSource(source, configuration), configuration, loader, er);
139 }
140
141
142 /**
143 * Initializes the SourceUnit for a string of source.
144 */
145 public SourceUnit(String name, String source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
146 this(name, new StringReaderSource(source, configuration), configuration, loader, er);
147 }
148
149
150 /**
151 * Returns the name for the SourceUnit.
152 */
153 public String getName() {
154 return name;
155 }
156
157
158 /**
159 * Returns the Concrete Syntax Tree produced during parse()ing.
160 */
161 public Reduction getCST() {
162 return this.cst;
163 }
164
165 /**
166 * Returns the Source Summary
167 */
168 public SourceSummary getSourceSummary() {
169 return this.sourceSummary;
170 }
171 /**
172 * Returns the Abstract Syntax Tree produced during parse()ing
173 * and expanded during later phases.
174 */
175 public ModuleNode getAST() {
176 return this.ast;
177 }
178
179
180 /**
181 * Convenience routine, primarily for use by the InteractiveShell,
182 * that returns true if parse() failed with an unexpected EOF.
183 */
184 public boolean failedWithUnexpectedEOF() {
185 boolean result = false;
186 if (getErrorCollector().hasErrors()) {
187 // Classic support
188 Message last = (Message) getErrorCollector().getLastError();
189 if (last instanceof SyntaxErrorMessage) {
190 SyntaxException cause = ((SyntaxErrorMessage) last).getCause();
191 if (cause instanceof UnexpectedTokenException) {
192 Token unexpected = ((UnexpectedTokenException) cause).getUnexpectedToken();
193 if (unexpected.isA(Types.EOF)) {
194 result = true;
195 }
196 }
197 }
198 // JSR support
199 if (last instanceof ExceptionMessage) {
200 ExceptionMessage exceptionMessage = (ExceptionMessage) last;
201 Exception cause = exceptionMessage.getCause();
202 if (cause instanceof NoViableAltException) {
203 NoViableAltException antlrException = (NoViableAltException) cause;
204 result = isEofToken(antlrException.token);
205 }
206 if (cause instanceof MismatchedTokenException) {
207 MismatchedTokenException antlrException = (MismatchedTokenException) cause;
208 result = isEofToken(antlrException.token);
209 }
210 }
211 }
212 return result;
213 }
214
215 protected boolean isEofToken(antlr.Token token) {
216 return token.getType() == antlr.Token.EOF_TYPE;
217 }
218
219
220
221 //---------------------------------------------------------------------------
222 // FACTORIES
223
224
225 /**
226 * A convenience routine to create a standalone SourceUnit on a String
227 * with defaults for almost everything that is configurable.
228 */
229 public static SourceUnit create(String name, String source) {
230 CompilerConfiguration configuration = new CompilerConfiguration();
231 configuration.setTolerance(1);
232
233 return new SourceUnit(name, source, configuration, null, new ErrorCollector(configuration));
234 }
235
236
237 /**
238 * A convenience routine to create a standalone SourceUnit on a String
239 * with defaults for almost everything that is configurable.
240 */
241 public static SourceUnit create(String name, String source, int tolerance) {
242 CompilerConfiguration configuration = new CompilerConfiguration();
243 configuration.setTolerance(tolerance);
244
245 return new SourceUnit(name, source, configuration, null, new ErrorCollector(configuration));
246 }
247
248
249
250
251
252 //---------------------------------------------------------------------------
253 // PROCESSING
254
255
256 /**
257 * Parses the source to a CST. You can retrieve it with getCST().
258 */
259 public void parse() throws CompilationFailedException {
260 if (this.phase > Phases.PARSING) {
261 throw new GroovyBugError("parsing is already complete");
262 }
263
264 if (this.phase == Phases.INITIALIZATION) {
265 nextPhase();
266 }
267
268
269 //
270 // Create a reader on the source and run the parser.
271
272 Reader reader = null;
273 try {
274 reader = source.getReader();
275
276 // lets recreate the parser each time as it tends to keep around state
277 parserPlugin = getConfiguration().getPluginFactory().createParserPlugin();
278
279 cst = parserPlugin.parseCST(this, reader);
280 sourceSummary = parserPlugin.getSummary();
281
282 reader.close();
283
284 }
285 catch (IOException e) {
286 getErrorCollector().addFatalError(new SimpleMessage(e.getMessage(),this));
287 }
288 finally {
289 if (reader != null) {
290 try {
291 reader.close();
292 }
293 catch (IOException e) {
294 }
295 }
296 }
297 }
298
299
300 /**
301 * Generates an AST from the CST. You can retrieve it with getAST().
302 */
303 public void convert() throws CompilationFailedException {
304 if (this.phase == Phases.PARSING && this.phaseComplete) {
305 gotoPhase(Phases.CONVERSION);
306 }
307
308 if (this.phase != Phases.CONVERSION) {
309 throw new GroovyBugError("SourceUnit not ready for convert()");
310 }
311
312
313 //
314 // Build the AST
315
316 try {
317 this.ast = parserPlugin.buildAST(this, this.classLoader, this.cst);
318
319 this.ast.setDescription(this.name);
320 }
321 catch (SyntaxException e) {
322 getErrorCollector().addError(new SyntaxErrorMessage(e,this));
323 }
324
325 if ("xml".equals(System.getProperty("groovy.ast"))) {
326 saveAsXML(name,ast);
327 }
328 }
329
330 private void saveAsXML(String name, ModuleNode ast) {
331 XStream xstream = new XStream();
332 try {
333 xstream.toXML(ast,new FileWriter(name + ".xml"));
334 System.out.println("Written AST to " + name + ".xml");
335 } catch (Exception e) {
336 System.out.println("Couldn't write to " + name + ".xml");
337 e.printStackTrace();
338 }
339 }
340 //--------------------------------------------------------------------------- // SOURCE SAMPLING
341
342 /**
343 * Returns a sampling of the source at the specified line and column,
344 * of null if it is unavailable.
345 */
346 public String getSample(int line, int column, Janitor janitor) {
347 String sample = null;
348 String text = source.getLine(line, janitor);
349
350 if (text != null) {
351 if (column > 0) {
352 String marker = Utilities.repeatString(" ", column - 1) + "^";
353
354 if (column > 40) {
355 int start = column - 30 - 1;
356 int end = (column + 10 > text.length() ? text.length() : column + 10 - 1);
357 sample = " " + text.substring(start, end) + Utilities.eol() + " " + marker.substring(start, marker.length());
358 }
359 else {
360 sample = " " + text + Utilities.eol() + " " + marker;
361 }
362 }
363 else {
364 sample = text;
365 }
366 }
367
368 return sample;
369 }
370
371 public void addException(Exception e) throws CompilationFailedException {
372 getErrorCollector().addException(e,this);
373 }
374
375 public void addError(SyntaxException se) throws CompilationFailedException {
376 getErrorCollector().addError(se,this);
377 }
378 }
379
380
381
382