1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 package org.codehaus.groovy.control;
48
49 import groovy.lang.GroovyClassLoader;
50
51 import java.io.File;
52 import java.io.FileWriter;
53 import java.io.IOException;
54 import java.io.Reader;
55 import java.net.URL;
56
57 import org.codehaus.groovy.GroovyBugError;
58 import org.codehaus.groovy.ast.ModuleNode;
59 import org.codehaus.groovy.control.io.FileReaderSource;
60 import org.codehaus.groovy.control.io.ReaderSource;
61 import org.codehaus.groovy.control.io.StringReaderSource;
62 import org.codehaus.groovy.control.io.URLReaderSource;
63 import org.codehaus.groovy.control.messages.ExceptionMessage;
64 import org.codehaus.groovy.control.messages.Message;
65 import org.codehaus.groovy.control.messages.SimpleMessage;
66 import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
67 import org.codehaus.groovy.syntax.*;
68 import org.codehaus.groovy.tools.Utilities;
69
70 import antlr.MismatchedTokenException;
71 import antlr.NoViableAltException;
72
73 import com.thoughtworks.xstream.XStream;
74
75
76 /***
77 * Provides an anchor for a single source unit (usually a script file)
78 * as it passes through the compiler system.
79 *
80 * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
81 * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
82 * @version $Id: SourceUnit.java,v 1.14 2005/11/13 16:42:11 blackdrag Exp $
83 */
84
85 public class SourceUnit extends ProcessingUnit {
86
87 /***
88 * The pluggable parser used to generate the AST - we allow pluggability currently as we need to have Classic and JSR support
89 */
90 private ParserPlugin parserPlugin;
91
92 /***
93 * Where we can get Readers for our source unit
94 */
95 protected ReaderSource source;
96 /***
97 * A descriptive name of the source unit
98 */
99 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
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
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
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
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
271
272 Reader reader = null;
273 try {
274 reader = source.getReader();
275
276
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
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
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