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 import java.security.AccessController;
57 import java.security.PrivilegedAction;
58
59 import org.codehaus.groovy.GroovyBugError;
60 import org.codehaus.groovy.ast.ModuleNode;
61 import org.codehaus.groovy.control.io.FileReaderSource;
62 import org.codehaus.groovy.control.io.ReaderSource;
63 import org.codehaus.groovy.control.io.StringReaderSource;
64 import org.codehaus.groovy.control.io.URLReaderSource;
65 import org.codehaus.groovy.control.messages.Message;
66 import org.codehaus.groovy.control.messages.SimpleMessage;
67 import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
68 import org.codehaus.groovy.syntax.*;
69 import org.codehaus.groovy.tools.Utilities;
70
71 import antlr.CharScanner;
72 import antlr.MismatchedTokenException;
73 import antlr.NoViableAltException;
74 import antlr.NoViableAltForCharException;
75
76 import com.thoughtworks.xstream.XStream;
77
78
79 /***
80 * Provides an anchor for a single source unit (usually a script file)
81 * as it passes through the compiler system.
82 *
83 * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
84 * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
85 * @version $Id: SourceUnit.java 4098 2006-10-10 16:09:48Z blackdrag $
86 */
87
88 public class SourceUnit extends ProcessingUnit {
89
90 /***
91 * The pluggable parser used to generate the AST - we allow pluggability currently as we need to have Classic and JSR support
92 */
93 private ParserPlugin parserPlugin;
94
95 /***
96 * Where we can get Readers for our source unit
97 */
98 protected ReaderSource source;
99 /***
100 * A descriptive name of the source unit. This name shouldn't
101 * be used for controling the SourceUnit, it is only for error
102 * messages
103 */
104 protected String name;
105 /***
106 * A Concrete Syntax Tree of the source
107 */
108 protected Reduction cst;
109
110 /***
111 * A facade over the CST
112 */
113 protected SourceSummary sourceSummary;
114 /***
115 * The root of the Abstract Syntax Tree for the source
116 */
117 protected ModuleNode ast;
118
119
120 /***
121 * Initializes the SourceUnit from existing machinery.
122 */
123 public SourceUnit(String name, ReaderSource source, CompilerConfiguration flags, GroovyClassLoader loader, ErrorCollector er) {
124 super(flags, loader, er);
125
126 this.name = name;
127 this.source = source;
128 }
129
130
131 /***
132 * Initializes the SourceUnit from the specified file.
133 */
134 public SourceUnit(File source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
135 this(source.getPath(), new FileReaderSource(source, configuration), configuration, loader, er);
136 }
137
138
139 /***
140 * Initializes the SourceUnit from the specified URL.
141 */
142 public SourceUnit(URL source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
143 this(source.getPath(), new URLReaderSource(source, configuration), configuration, loader, er);
144 }
145
146
147 /***
148 * Initializes the SourceUnit for a string of source.
149 */
150 public SourceUnit(String name, String source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
151 this(name, new StringReaderSource(source, configuration), configuration, loader, er);
152 }
153
154
155 /***
156 * Returns the name for the SourceUnit. This name shouldn't
157 * be used for controling the SourceUnit, it is only for error
158 * messages
159 */
160 public String getName() {
161 return name;
162 }
163
164
165 /***
166 * Returns the Concrete Syntax Tree produced during parse()ing.
167 */
168 public Reduction getCST() {
169 return this.cst;
170 }
171
172 /***
173 * Returns the Source Summary
174 */
175 public SourceSummary getSourceSummary() {
176 return this.sourceSummary;
177 }
178 /***
179 * Returns the Abstract Syntax Tree produced during parse()ing
180 * and expanded during later phases.
181 */
182 public ModuleNode getAST() {
183 return this.ast;
184 }
185
186
187 /***
188 * Convenience routine, primarily for use by the InteractiveShell,
189 * that returns true if parse() failed with an unexpected EOF.
190 */
191 public boolean failedWithUnexpectedEOF() {
192
193
194
195 if (getErrorCollector().hasErrors()) {
196 Message last = (Message) getErrorCollector().getLastError();
197 Throwable cause = null;
198 if (last instanceof SyntaxErrorMessage) {
199 cause = ((SyntaxErrorMessage) last).getCause().getCause();
200 }
201 if (cause != null) {
202 if (cause instanceof NoViableAltException) {
203 return isEofToken(((NoViableAltException) cause).token);
204 } else if (cause instanceof NoViableAltForCharException) {
205 char badChar = ((NoViableAltForCharException) cause).foundChar;
206 return badChar == CharScanner.EOF_CHAR;
207 } else if (cause instanceof MismatchedTokenException) {
208 return isEofToken(((MismatchedTokenException) cause).token);
209 }
210 }
211 }
212 return false;
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 String property = (String) AccessController.doPrivileged(new PrivilegedAction() {
326 public Object run() {
327 return System.getProperty("groovy.ast");
328 }
329 });
330
331 if ("xml".equals(property)) {
332 saveAsXML(name,ast);
333 }
334 }
335
336 private void saveAsXML(String name, ModuleNode ast) {
337 XStream xstream = new XStream();
338 try {
339 xstream.toXML(ast,new FileWriter(name + ".xml"));
340 System.out.println("Written AST to " + name + ".xml");
341 } catch (Exception e) {
342 System.out.println("Couldn't write to " + name + ".xml");
343 e.printStackTrace();
344 }
345 }
346
347
348 /***
349 * Returns a sampling of the source at the specified line and column,
350 * of null if it is unavailable.
351 */
352 public String getSample(int line, int column, Janitor janitor) {
353 String sample = null;
354 String text = source.getLine(line, janitor);
355
356 if (text != null) {
357 if (column > 0) {
358 String marker = Utilities.repeatString(" ", column - 1) + "^";
359
360 if (column > 40) {
361 int start = column - 30 - 1;
362 int end = (column + 10 > text.length() ? text.length() : column + 10 - 1);
363 sample = " " + text.substring(start, end) + Utilities.eol() + " " + marker.substring(start, marker.length());
364 }
365 else {
366 sample = " " + text + Utilities.eol() + " " + marker;
367 }
368 }
369 else {
370 sample = text;
371 }
372 }
373
374 return sample;
375 }
376
377 public void addException(Exception e) throws CompilationFailedException {
378 getErrorCollector().addException(e,this);
379 }
380
381 public void addError(SyntaxException se) throws CompilationFailedException {
382 getErrorCollector().addError(se,this);
383 }
384 }
385
386
387
388