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,v 1.17 2006/05/30 18:24:03 blackdrag Exp $
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
101 */
102 protected String name;
103 /***
104 * A Concrete Syntax Tree of the source
105 */
106 protected Reduction cst;
107
108 /***
109 * A facade over the CST
110 */
111 protected SourceSummary sourceSummary;
112 /***
113 * The root of the Abstract Syntax Tree for the source
114 */
115 protected ModuleNode ast;
116
117
118 /***
119 * Initializes the SourceUnit from existing machinery.
120 */
121 public SourceUnit(String name, ReaderSource source, CompilerConfiguration flags, GroovyClassLoader loader, ErrorCollector er) {
122 super(flags, loader, er);
123
124 this.name = name;
125 this.source = source;
126 }
127
128
129 /***
130 * Initializes the SourceUnit from the specified file.
131 */
132 public SourceUnit(File source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
133 this(source.getPath(), new FileReaderSource(source, configuration), configuration, loader, er);
134 }
135
136
137 /***
138 * Initializes the SourceUnit from the specified URL.
139 */
140 public SourceUnit(URL source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
141 this(source.getPath(), new URLReaderSource(source, configuration), configuration, loader, er);
142 }
143
144
145 /***
146 * Initializes the SourceUnit for a string of source.
147 */
148 public SourceUnit(String name, String source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
149 this(name, new StringReaderSource(source, configuration), configuration, loader, er);
150 }
151
152
153 /***
154 * Returns the name for the SourceUnit.
155 */
156 public String getName() {
157 return name;
158 }
159
160
161 /***
162 * Returns the Concrete Syntax Tree produced during parse()ing.
163 */
164 public Reduction getCST() {
165 return this.cst;
166 }
167
168 /***
169 * Returns the Source Summary
170 */
171 public SourceSummary getSourceSummary() {
172 return this.sourceSummary;
173 }
174 /***
175 * Returns the Abstract Syntax Tree produced during parse()ing
176 * and expanded during later phases.
177 */
178 public ModuleNode getAST() {
179 return this.ast;
180 }
181
182
183 /***
184 * Convenience routine, primarily for use by the InteractiveShell,
185 * that returns true if parse() failed with an unexpected EOF.
186 */
187 public boolean failedWithUnexpectedEOF() {
188
189
190
191 if (getErrorCollector().hasErrors()) {
192 Message last = (Message) getErrorCollector().getLastError();
193 Throwable cause = null;
194 if (last instanceof SyntaxErrorMessage) {
195 cause = ((SyntaxErrorMessage) last).getCause().getCause();
196 }
197 if (cause != null) {
198 if (cause instanceof NoViableAltException) {
199 return isEofToken(((NoViableAltException) cause).token);
200 } else if (cause instanceof NoViableAltForCharException) {
201 char badChar = ((NoViableAltForCharException) cause).foundChar;
202 return badChar == CharScanner.EOF_CHAR;
203 } else if (cause instanceof MismatchedTokenException) {
204 return isEofToken(((MismatchedTokenException) cause).token);
205 }
206 }
207 }
208 return false;
209 }
210
211 protected boolean isEofToken(antlr.Token token) {
212 return token.getType() == antlr.Token.EOF_TYPE;
213 }
214
215
216
217
218
219
220
221 /***
222 * A convenience routine to create a standalone SourceUnit on a String
223 * with defaults for almost everything that is configurable.
224 */
225 public static SourceUnit create(String name, String source) {
226 CompilerConfiguration configuration = new CompilerConfiguration();
227 configuration.setTolerance(1);
228
229 return new SourceUnit(name, source, configuration, null, new ErrorCollector(configuration));
230 }
231
232
233 /***
234 * A convenience routine to create a standalone SourceUnit on a String
235 * with defaults for almost everything that is configurable.
236 */
237 public static SourceUnit create(String name, String source, int tolerance) {
238 CompilerConfiguration configuration = new CompilerConfiguration();
239 configuration.setTolerance(tolerance);
240
241 return new SourceUnit(name, source, configuration, null, new ErrorCollector(configuration));
242 }
243
244
245
246
247
248
249
250
251
252 /***
253 * Parses the source to a CST. You can retrieve it with getCST().
254 */
255 public void parse() throws CompilationFailedException {
256 if (this.phase > Phases.PARSING) {
257 throw new GroovyBugError("parsing is already complete");
258 }
259
260 if (this.phase == Phases.INITIALIZATION) {
261 nextPhase();
262 }
263
264
265
266
267
268 Reader reader = null;
269 try {
270 reader = source.getReader();
271
272
273 parserPlugin = getConfiguration().getPluginFactory().createParserPlugin();
274
275 cst = parserPlugin.parseCST(this, reader);
276 sourceSummary = parserPlugin.getSummary();
277
278 reader.close();
279
280 }
281 catch (IOException e) {
282 getErrorCollector().addFatalError(new SimpleMessage(e.getMessage(),this));
283 }
284 finally {
285 if (reader != null) {
286 try {
287 reader.close();
288 }
289 catch (IOException e) {
290 }
291 }
292 }
293 }
294
295
296 /***
297 * Generates an AST from the CST. You can retrieve it with getAST().
298 */
299 public void convert() throws CompilationFailedException {
300 if (this.phase == Phases.PARSING && this.phaseComplete) {
301 gotoPhase(Phases.CONVERSION);
302 }
303
304 if (this.phase != Phases.CONVERSION) {
305 throw new GroovyBugError("SourceUnit not ready for convert()");
306 }
307
308
309
310
311
312 try {
313 this.ast = parserPlugin.buildAST(this, this.classLoader, this.cst);
314
315 this.ast.setDescription(this.name);
316 }
317 catch (SyntaxException e) {
318 getErrorCollector().addError(new SyntaxErrorMessage(e,this));
319 }
320
321 String property = (String) AccessController.doPrivileged(new PrivilegedAction() {
322 public Object run() {
323 return System.getProperty("groovy.ast");
324 }
325 });
326
327 if ("xml".equals(property)) {
328 saveAsXML(name,ast);
329 }
330 }
331
332 private void saveAsXML(String name, ModuleNode ast) {
333 XStream xstream = new XStream();
334 try {
335 xstream.toXML(ast,new FileWriter(name + ".xml"));
336 System.out.println("Written AST to " + name + ".xml");
337 } catch (Exception e) {
338 System.out.println("Couldn't write to " + name + ".xml");
339 e.printStackTrace();
340 }
341 }
342
343
344 /***
345 * Returns a sampling of the source at the specified line and column,
346 * of null if it is unavailable.
347 */
348 public String getSample(int line, int column, Janitor janitor) {
349 String sample = null;
350 String text = source.getLine(line, janitor);
351
352 if (text != null) {
353 if (column > 0) {
354 String marker = Utilities.repeatString(" ", column - 1) + "^";
355
356 if (column > 40) {
357 int start = column - 30 - 1;
358 int end = (column + 10 > text.length() ? text.length() : column + 10 - 1);
359 sample = " " + text.substring(start, end) + Utilities.eol() + " " + marker.substring(start, marker.length());
360 }
361 else {
362 sample = " " + text + Utilities.eol() + " " + marker;
363 }
364 }
365 else {
366 sample = text;
367 }
368 }
369
370 return sample;
371 }
372
373 public void addException(Exception e) throws CompilationFailedException {
374 getErrorCollector().addException(e,this);
375 }
376
377 public void addError(SyntaxException se) throws CompilationFailedException {
378 getErrorCollector().addError(se,this);
379 }
380 }
381
382
383
384