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