|
|||||||||||||||||||
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover | |||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
ASTBuilder.java | 0% | 0% | 0% | 0% |
|
1 |
/*
|
|
2 |
$Id: ASTBuilder.java,v 1.105 2004/07/10 03:31:43 bran Exp $
|
|
3 |
|
|
4 |
Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
|
|
5 |
|
|
6 |
Redistribution and use of this software and associated documentation
|
|
7 |
("Software"), with or without modification, are permitted provided
|
|
8 |
that the following conditions are met:
|
|
9 |
|
|
10 |
1. Redistributions of source code must retain copyright
|
|
11 |
statements and notices. Redistributions must also contain a
|
|
12 |
copy of this document.
|
|
13 |
|
|
14 |
2. Redistributions in binary form must reproduce the
|
|
15 |
above copyright notice, this list of conditions and the
|
|
16 |
following disclaimer in the documentation and/or other
|
|
17 |
materials provided with the distribution.
|
|
18 |
|
|
19 |
3. The name "groovy" must not be used to endorse or promote
|
|
20 |
products derived from this Software without prior written
|
|
21 |
permission of The Codehaus. For written permission,
|
|
22 |
please contact info@codehaus.org.
|
|
23 |
|
|
24 |
4. Products derived from this Software may not be called "groovy"
|
|
25 |
nor may "groovy" appear in their names without prior written
|
|
26 |
permission of The Codehaus. "groovy" is a registered
|
|
27 |
trademark of The Codehaus.
|
|
28 |
|
|
29 |
5. Due credit should be given to The Codehaus -
|
|
30 |
http://groovy.codehaus.org/
|
|
31 |
|
|
32 |
THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
|
|
33 |
``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
|
|
34 |
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
35 |
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
36 |
THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
37 |
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
38 |
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
39 |
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
40 |
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
41 |
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
42 |
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
43 |
OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
44 |
|
|
45 |
*/
|
|
46 |
package org.codehaus.groovy.syntax.parser;
|
|
47 |
|
|
48 |
import java.util.HashMap;
|
|
49 |
import java.util.Map;
|
|
50 |
import java.util.List;
|
|
51 |
import java.util.ArrayList;
|
|
52 |
|
|
53 |
import org.codehaus.groovy.ast.ClassNode;
|
|
54 |
import org.codehaus.groovy.ast.InnerClassNode;
|
|
55 |
import org.codehaus.groovy.ast.ConstructorNode;
|
|
56 |
import org.codehaus.groovy.ast.MethodNode;
|
|
57 |
import org.codehaus.groovy.ast.MixinNode;
|
|
58 |
import org.codehaus.groovy.ast.ModuleNode;
|
|
59 |
import org.codehaus.groovy.ast.Parameter;
|
|
60 |
import org.codehaus.groovy.ast.PropertyNode;
|
|
61 |
import org.codehaus.groovy.ast.Type;
|
|
62 |
import org.codehaus.groovy.ast.expr.ArrayExpression;
|
|
63 |
import org.codehaus.groovy.ast.expr.BinaryExpression;
|
|
64 |
import org.codehaus.groovy.ast.expr.BooleanExpression;
|
|
65 |
import org.codehaus.groovy.ast.expr.CastExpression;
|
|
66 |
import org.codehaus.groovy.ast.expr.ClassExpression;
|
|
67 |
import org.codehaus.groovy.ast.expr.ClosureExpression;
|
|
68 |
import org.codehaus.groovy.ast.expr.ConstantExpression;
|
|
69 |
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
|
|
70 |
import org.codehaus.groovy.ast.expr.Expression;
|
|
71 |
import org.codehaus.groovy.ast.expr.GStringExpression;
|
|
72 |
import org.codehaus.groovy.ast.expr.ListExpression;
|
|
73 |
import org.codehaus.groovy.ast.expr.MapExpression;
|
|
74 |
import org.codehaus.groovy.ast.expr.MethodCallExpression;
|
|
75 |
import org.codehaus.groovy.ast.expr.NegationExpression;
|
|
76 |
import org.codehaus.groovy.ast.expr.NotExpression;
|
|
77 |
import org.codehaus.groovy.ast.expr.PostfixExpression;
|
|
78 |
import org.codehaus.groovy.ast.expr.PrefixExpression;
|
|
79 |
import org.codehaus.groovy.ast.expr.PropertyExpression;
|
|
80 |
import org.codehaus.groovy.ast.expr.RangeExpression;
|
|
81 |
import org.codehaus.groovy.ast.expr.RegexExpression;
|
|
82 |
import org.codehaus.groovy.ast.expr.TernaryExpression;
|
|
83 |
import org.codehaus.groovy.ast.expr.TupleExpression;
|
|
84 |
import org.codehaus.groovy.ast.expr.VariableExpression;
|
|
85 |
import org.codehaus.groovy.ast.stmt.AssertStatement;
|
|
86 |
import org.codehaus.groovy.ast.stmt.BlockStatement;
|
|
87 |
import org.codehaus.groovy.ast.stmt.BreakStatement;
|
|
88 |
import org.codehaus.groovy.ast.stmt.CaseStatement;
|
|
89 |
import org.codehaus.groovy.ast.stmt.CatchStatement;
|
|
90 |
import org.codehaus.groovy.ast.stmt.ContinueStatement;
|
|
91 |
import org.codehaus.groovy.ast.stmt.DoWhileStatement;
|
|
92 |
import org.codehaus.groovy.ast.stmt.EmptyStatement;
|
|
93 |
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
|
94 |
import org.codehaus.groovy.ast.stmt.ForStatement;
|
|
95 |
import org.codehaus.groovy.ast.stmt.IfStatement;
|
|
96 |
import org.codehaus.groovy.ast.stmt.ReturnStatement;
|
|
97 |
import org.codehaus.groovy.ast.stmt.Statement;
|
|
98 |
import org.codehaus.groovy.ast.stmt.SwitchStatement;
|
|
99 |
import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
|
|
100 |
import org.codehaus.groovy.ast.stmt.ThrowStatement;
|
|
101 |
import org.codehaus.groovy.ast.stmt.TryCatchStatement;
|
|
102 |
import org.codehaus.groovy.ast.stmt.WhileStatement;
|
|
103 |
import org.codehaus.groovy.control.SourceUnit;
|
|
104 |
import org.codehaus.groovy.syntax.CSTNode;
|
|
105 |
import org.codehaus.groovy.syntax.Token;
|
|
106 |
import org.codehaus.groovy.syntax.Types;
|
|
107 |
import org.codehaus.groovy.syntax.Numbers;
|
|
108 |
import org.codehaus.groovy.GroovyBugError;
|
|
109 |
import org.objectweb.asm.Constants;
|
|
110 |
|
|
111 |
|
|
112 |
|
|
113 |
/**
|
|
114 |
* Builds an Abstract Syntax Tree from the Concrete Syntax Tree produced
|
|
115 |
* by the Parser. The resulting AST is very preliminary, and must still
|
|
116 |
* be validated and massaged before it is ready to be used.
|
|
117 |
* <code>build()</code> is the primary entry point.
|
|
118 |
*
|
|
119 |
* @author James Strachan
|
|
120 |
* @author Bob McWhirter
|
|
121 |
* @author Sam Pullara
|
|
122 |
* @author Chris Poirier
|
|
123 |
*/
|
|
124 |
|
|
125 |
public class ASTBuilder |
|
126 |
{ |
|
127 |
|
|
128 |
private static final String[] EMPTY_STRING_ARRAY = new String[0]; |
|
129 |
private static final String[] DEFAULT_IMPORTS = { "java.lang.", "groovy.lang.", "groovy.util." }; |
|
130 |
|
|
131 |
|
|
132 |
|
|
133 |
//---------------------------------------------------------------------------
|
|
134 |
// INITIALIZATION AND MEMBER ACCESS
|
|
135 |
|
|
136 |
|
|
137 |
private SourceUnit controller; // The SourceUnit controlling us |
|
138 |
private ClassLoader classLoader; // Our ClassLoader, which provides information on external types |
|
139 |
private Map imports; // Our imports, simple name => fully qualified name |
|
140 |
private String packageName; // The package name in which the module sits |
|
141 |
private List newClasses = new ArrayList(); // temporarily store the class names that the current modulenode contains |
|
142 |
private ModuleNode output;
|
|
143 |
|
|
144 |
|
|
145 |
/**
|
|
146 |
* Initializes the <code>ASTBuilder</code>.
|
|
147 |
*/
|
|
148 |
|
|
149 | 0 |
public ASTBuilder( SourceUnit sourceUnit, ClassLoader classLoader )
|
150 |
{ |
|
151 | 0 |
this.controller = sourceUnit;
|
152 | 0 |
this.classLoader = classLoader;
|
153 | 0 |
this.imports = new HashMap(); |
154 | 0 |
this.packageName = null; |
155 |
} |
|
156 |
|
|
157 |
|
|
158 |
|
|
159 |
/**
|
|
160 |
* Returns our class loader (as supplied on construction).
|
|
161 |
*/
|
|
162 |
|
|
163 | 0 |
public ClassLoader getClassLoader()
|
164 |
{ |
|
165 | 0 |
return this.classLoader; |
166 |
} |
|
167 |
|
|
168 |
|
|
169 |
|
|
170 |
|
|
171 |
//---------------------------------------------------------------------------
|
|
172 |
// ENTRY POINT
|
|
173 |
|
|
174 |
|
|
175 |
/**
|
|
176 |
* Builds an AST ModuleNode from a Parser.module() Reduction.
|
|
177 |
*/
|
|
178 |
|
|
179 | 0 |
public ModuleNode build( CSTNode input ) throws ParserException |
180 |
{ |
|
181 | 0 |
this.newClasses.clear();
|
182 | 0 |
this.output = new ModuleNode( controller ); |
183 | 0 |
resolutions.clear(); |
184 |
|
|
185 |
//
|
|
186 |
// input structure:
|
|
187 |
// 1: package
|
|
188 |
// 2: imports
|
|
189 |
// 3+: statements
|
|
190 |
|
|
191 | 0 |
packageName = packageDeclaration( input.get(1) ); |
192 | 0 |
output.setPackageName( packageName ); |
193 |
|
|
194 | 0 |
importStatements( output, input.get(2) ); |
195 |
|
|
196 | 0 |
for( int i = 3; i < input.size(); ++i ) |
197 |
{ |
|
198 | 0 |
topLevelStatement( output, input.get(i) ); |
199 |
} |
|
200 |
|
|
201 | 0 |
if( output.isEmpty() )
|
202 |
{ |
|
203 | 0 |
output.addStatement( new BlockStatement() );
|
204 |
} |
|
205 |
|
|
206 | 0 |
return output;
|
207 |
} |
|
208 |
|
|
209 |
|
|
210 |
|
|
211 |
|
|
212 |
//---------------------------------------------------------------------------
|
|
213 |
// DECLARATIONS
|
|
214 |
|
|
215 |
|
|
216 |
/**
|
|
217 |
* Processes the Reduction produced by Parser.packageDeclaration().
|
|
218 |
*/
|
|
219 |
|
|
220 | 0 |
protected String packageDeclaration( CSTNode reduction )
|
221 |
{ |
|
222 | 0 |
if( reduction.hasChildren() )
|
223 |
{ |
|
224 | 0 |
return makeName( reduction.get(1) );
|
225 |
} |
|
226 |
|
|
227 | 0 |
return null; |
228 |
|
|
229 |
} |
|
230 |
|
|
231 |
|
|
232 |
|
|
233 |
/**
|
|
234 |
* Processes the imports Reduction produced by Parser.module().
|
|
235 |
*/
|
|
236 |
|
|
237 | 0 |
protected void importStatements( ModuleNode module, CSTNode container ) |
238 |
{ |
|
239 | 0 |
for( int i = 1; i < container.size(); ++i) |
240 |
{ |
|
241 | 0 |
importStatement( module, container.get(i) ); |
242 |
} |
|
243 |
} |
|
244 |
|
|
245 |
|
|
246 |
|
|
247 |
/**
|
|
248 |
* Processes the Reduction produced by Parser.importStatement().
|
|
249 |
*/
|
|
250 |
|
|
251 | 0 |
protected void importStatement( ModuleNode module, CSTNode reduction ) |
252 |
{ |
|
253 |
//
|
|
254 |
// First, get the package name (if supplied).
|
|
255 |
|
|
256 | 0 |
String importPackage = makeName( reduction.get(1), null );
|
257 |
|
|
258 |
|
|
259 |
|
|
260 |
//
|
|
261 |
// If the first clause is Types.STAR, it's a package import.
|
|
262 |
|
|
263 | 0 |
if( reduction.get(2).isA(Types.STAR) )
|
264 |
{ |
|
265 | 0 |
String[] classes = module.addImportPackage( dot(importPackage) ); |
266 | 0 |
for( int i = 0; i < classes.length; i++ ) |
267 |
{ |
|
268 | 0 |
imports.put( classes[i], dot(importPackage, classes[i]) ); |
269 |
} |
|
270 |
} |
|
271 |
|
|
272 |
|
|
273 |
//
|
|
274 |
// Otherwise, it's a series of specific imports.
|
|
275 |
|
|
276 |
else
|
|
277 |
{ |
|
278 | 0 |
for( int i = 2; i < reduction.size(); i++ ) |
279 |
{ |
|
280 | 0 |
CSTNode clause = reduction.get(i); |
281 | 0 |
String name = identifier( clause ); |
282 | 0 |
String as = (clause.hasChildren() ? identifier(clause.get(1)) : name); |
283 |
|
|
284 |
//
|
|
285 |
// There appears to be a bug in the previous code for
|
|
286 |
// single imports, in that the old code passed unqualified
|
|
287 |
// class names to module.addImport(). This hasn't been a
|
|
288 |
// problem apparently because those names are resolved here.
|
|
289 |
// Passing module.addImport() a fully qualified name does
|
|
290 |
// currently causes problems with classgen, possibly because
|
|
291 |
// of name collisions. So, for now, we use the old method...
|
|
292 |
|
|
293 | 0 |
module.addImport( as, name ); // unqualified
|
294 |
|
|
295 | 0 |
name = dot( importPackage, name ); |
296 |
|
|
297 |
// module.addImport( as, name ); // qualified
|
|
298 | 0 |
imports.put( as, name ); |
299 |
} |
|
300 |
} |
|
301 |
} |
|
302 |
|
|
303 |
|
|
304 |
|
|
305 |
/**
|
|
306 |
* Processes the Reduction produced by Parser.topLevelStatement().
|
|
307 |
*/
|
|
308 |
|
|
309 | 0 |
protected void topLevelStatement( ModuleNode module, CSTNode reduction ) throws ParserException |
310 |
{ |
|
311 | 0 |
int type = reduction.getMeaning();
|
312 | 0 |
switch( type )
|
313 |
{ |
|
314 |
case Types.SYNTH_CLASS:
|
|
315 | 0 |
module.addClass( classDeclaration(null, reduction) );
|
316 | 0 |
break;
|
317 |
|
|
318 |
case Types.SYNTH_INTERFACE:
|
|
319 | 0 |
module.addClass( interfaceDeclaration(null, reduction) );
|
320 | 0 |
break;
|
321 |
|
|
322 |
case Types.SYNTH_METHOD:
|
|
323 | 0 |
module.addMethod( methodDeclaration(null, reduction) );
|
324 | 0 |
break;
|
325 |
|
|
326 |
default:
|
|
327 | 0 |
module.addStatement( statement(reduction) ); |
328 | 0 |
break;
|
329 |
} |
|
330 |
|
|
331 |
} |
|
332 |
|
|
333 |
|
|
334 |
|
|
335 |
/**
|
|
336 |
* Processes the Reduction produced by Parser.classDeclaration().
|
|
337 |
*/
|
|
338 |
|
|
339 | 0 |
protected ClassNode classDeclaration( ClassNode context, CSTNode reduction ) throws ParserException |
340 |
{ |
|
341 |
//
|
|
342 |
// Calculate the easy stuff
|
|
343 |
|
|
344 | 0 |
String name = identifier( reduction ); |
345 | 0 |
this.newClasses.add(name);
|
346 | 0 |
int modifiers = modifiers( reduction.get(1) );
|
347 | 0 |
String parent = resolveName( reduction.get(2).get(1) ); |
348 |
|
|
349 |
|
|
350 |
//
|
|
351 |
// Then process the interface list.
|
|
352 |
|
|
353 | 0 |
CSTNode interfaceReduction = reduction.get(3); |
354 | 0 |
String[] interfaces = new String[interfaceReduction.children()];
|
355 | 0 |
for( int i = 1; i < interfaceReduction.size(); i++ ) |
356 |
{ |
|
357 | 0 |
interfaces[i-1] = resolveName( interfaceReduction.get(i) ); |
358 |
} |
|
359 |
|
|
360 |
|
|
361 |
//
|
|
362 |
// Create the class.
|
|
363 |
|
|
364 | 0 |
ClassNode classNode = ( |
365 | 0 |
context == null
|
366 |
? new ClassNode( dot(packageName, name), modifiers, parent, interfaces, MixinNode.EMPTY_ARRAY )
|
|
367 |
: new InnerClassNode( context, dot(packageName, name), modifiers, parent, interfaces, MixinNode.EMPTY_ARRAY )
|
|
368 |
); |
|
369 |
|
|
370 | 0 |
classNode.setCSTNode( reduction.get(0) ); |
371 | 0 |
typeBody( classNode, reduction.get(4), 0, 0 ); |
372 | 0 |
return classNode;
|
373 |
} |
|
374 |
|
|
375 |
|
|
376 |
|
|
377 |
/**
|
|
378 |
* Processes a type body for classDeclaration() and others.
|
|
379 |
*/
|
|
380 |
|
|
381 | 0 |
protected void typeBody( ClassNode classNode, CSTNode body, int propertyModifiers, int methodModifiers ) throws ParserException |
382 |
{ |
|
383 | 0 |
for( int i = 1; i < body.size(); i++ ) |
384 |
{ |
|
385 | 0 |
CSTNode statement = body.get(i); |
386 | 0 |
switch( statement.getMeaning() )
|
387 |
{ |
|
388 |
case Types.SYNTH_PROPERTY:
|
|
389 | 0 |
addPropertyDeclaration( classNode, statement, propertyModifiers ); |
390 | 0 |
break;
|
391 |
|
|
392 |
case Types.SYNTH_METHOD:
|
|
393 | 0 |
methodDeclaration( classNode, statement, methodModifiers ); |
394 | 0 |
break;
|
395 |
|
|
396 |
case Types.SYNTH_CLASS:
|
|
397 | 0 |
classDeclaration( classNode, statement ); |
398 | 0 |
break;
|
399 |
|
|
400 |
case Types.SYNTH_INTERFACE:
|
|
401 | 0 |
interfaceDeclaration( classNode, statement ); |
402 | 0 |
break;
|
403 |
|
|
404 |
default:
|
|
405 | 0 |
throw new GroovyBugError( "unrecognized type body statement [" + statement.toString() + "]" ); |
406 |
} |
|
407 |
} |
|
408 |
} |
|
409 |
|
|
410 |
|
|
411 |
|
|
412 |
/**
|
|
413 |
* Processes the Reduction produced by Parser.propertyDeclaration().
|
|
414 |
* Adds the property to the supplied class.
|
|
415 |
*/
|
|
416 |
|
|
417 | 0 |
protected void addPropertyDeclaration( ClassNode classNode, CSTNode reduction, int extraModifiers ) throws ParserException |
418 |
{ |
|
419 | 0 |
String name = identifier( reduction ); |
420 | 0 |
int modifiers = modifiers( reduction.get(1) ) | extraModifiers;
|
421 | 0 |
String type = resolveName( reduction.get(2) ); |
422 |
|
|
423 | 0 |
Expression value = reduction.size() > 3 ? expression(reduction.get(3)) : null;
|
424 |
|
|
425 | 0 |
PropertyNode propertyNode = classNode.addProperty( name, modifiers, type, value, null, null ); |
426 | 0 |
propertyNode.setCSTNode( reduction.get(0) ); |
427 |
|
|
428 |
} |
|
429 |
|
|
430 |
|
|
431 |
|
|
432 |
/**
|
|
433 |
* A synonym for <code>addPropertyDeclaration( classNode, reduction, 0 )</code>.
|
|
434 |
*/
|
|
435 |
|
|
436 | 0 |
protected void addPropertyDeclaration( ClassNode classNode, CSTNode reduction ) throws ParserException |
437 |
{ |
|
438 | 0 |
addPropertyDeclaration( classNode, reduction, 0 ); |
439 |
} |
|
440 |
|
|
441 |
|
|
442 |
|
|
443 |
/**
|
|
444 |
* Processes the Reduction produced by Parser.methodDeclaration().
|
|
445 |
* Adds the method to the supplied class.
|
|
446 |
*/
|
|
447 |
|
|
448 | 0 |
protected MethodNode methodDeclaration( ClassNode classNode, CSTNode reduction, int extraModifiers ) throws ParserException |
449 |
{ |
|
450 | 0 |
String className = null;
|
451 | 0 |
if( classNode != null ) |
452 |
{ |
|
453 | 0 |
className = classNode.getNameWithoutPackage(); |
454 |
} |
|
455 |
|
|
456 |
|
|
457 |
//
|
|
458 |
// Get the basic method data
|
|
459 |
|
|
460 | 0 |
String name = identifier( reduction ); |
461 | 0 |
int modifiers = modifiers( reduction.get(1) ) | extraModifiers;
|
462 | 0 |
String type = resolveName( reduction.get(2) ); |
463 |
|
|
464 | 0 |
Parameter[] parameters = parameterDeclarations( reduction.get(3) ); |
465 | 0 |
BlockStatement body = statementBody( reduction.get(5) ); |
466 |
|
|
467 |
|
|
468 |
//
|
|
469 |
// Process the throws clause
|
|
470 |
|
|
471 | 0 |
CSTNode clause = reduction.get(4); |
472 | 0 |
String[] throwTypes = new String[clause.children()];
|
473 | 0 |
for( int i = 1; i < clause.size(); i++ ) |
474 |
{ |
|
475 | 0 |
throwTypes[i-1] = resolveName( clause.get(i) ); |
476 |
} |
|
477 |
|
|
478 | 0 |
if( clause.hasChildren() ) { throw new GroovyBugError( "NOT YET IMPLEMENTED: throws clause" ); } |
479 |
|
|
480 |
|
|
481 |
//
|
|
482 |
// An unnamed method is a static initializer
|
|
483 |
|
|
484 | 0 |
if( name.length() == 0 )
|
485 |
{ |
|
486 | 0 |
throw new GroovyBugError( "NOT YET IMPLEMENTED: static initializers" ); |
487 |
|
|
488 |
/*
|
|
489 |
|
|
490 |
ConstructorNode node = new ConstructorNode( modifiers | Constants.ACC_STATIC, parameters, body );
|
|
491 |
node.setCSTNode( reduction.get(0) );
|
|
492 |
|
|
493 |
classNode.addConstructor( node );
|
|
494 |
return null;
|
|
495 |
|
|
496 |
*/
|
|
497 |
} |
|
498 |
|
|
499 |
|
|
500 |
//
|
|
501 |
// A method with the class name is a constructor
|
|
502 |
|
|
503 | 0 |
else if( className != null && name.equals(className) ) |
504 |
{ |
|
505 | 0 |
ConstructorNode node = new ConstructorNode( modifiers, parameters, body );
|
506 | 0 |
node.setCSTNode( reduction.get(0) ); |
507 |
|
|
508 | 0 |
classNode.addConstructor( node ); |
509 | 0 |
return null; |
510 |
} |
|
511 |
|
|
512 |
|
|
513 |
//
|
|
514 |
// Anything else is a plain old method
|
|
515 |
|
|
516 |
else
|
|
517 |
{ |
|
518 | 0 |
MethodNode method = new MethodNode( name, modifiers, type, parameters, body );
|
519 | 0 |
method.setCSTNode( reduction.get(0) ); |
520 |
|
|
521 | 0 |
if( classNode != null ) |
522 |
{ |
|
523 | 0 |
classNode.addMethod( method ); |
524 |
} |
|
525 |
|
|
526 | 0 |
return method;
|
527 |
} |
|
528 |
|
|
529 |
} |
|
530 |
|
|
531 |
|
|
532 |
|
|
533 |
/**
|
|
534 |
* A synonym for <code>methodDeclaration( classNode, reduction, 0 )</code>.
|
|
535 |
*/
|
|
536 |
|
|
537 | 0 |
protected MethodNode methodDeclaration( ClassNode classNode, CSTNode reduction ) throws ParserException |
538 |
{ |
|
539 | 0 |
return methodDeclaration( classNode, reduction, 0 );
|
540 |
} |
|
541 |
|
|
542 |
|
|
543 |
|
|
544 |
/**
|
|
545 |
* Processes the Reduction produced by Parser.parameterDeclarationList().
|
|
546 |
*/
|
|
547 |
|
|
548 | 0 |
protected Parameter[] parameterDeclarations( CSTNode reduction ) throws ParserException |
549 |
{ |
|
550 | 0 |
Parameter[] parameters = new Parameter[ reduction.children() ];
|
551 |
|
|
552 | 0 |
for( int i = 1; i < reduction.size(); i++ ) |
553 |
{ |
|
554 | 0 |
CSTNode node = reduction.get(i); |
555 |
|
|
556 | 0 |
String identifier = identifier( node ); |
557 | 0 |
String type = resolveName( node.get(1) ); |
558 |
|
|
559 | 0 |
if( node.size() > 2 )
|
560 |
{ |
|
561 | 0 |
parameters[i-1] = new Parameter( type, identifier, expression(node.get(2)) );
|
562 |
} |
|
563 |
else
|
|
564 |
{ |
|
565 | 0 |
parameters[i-1] = new Parameter( type, identifier );
|
566 |
} |
|
567 |
} |
|
568 |
|
|
569 | 0 |
return parameters;
|
570 |
|
|
571 |
} |
|
572 |
|
|
573 |
|
|
574 |
|
|
575 |
/**
|
|
576 |
* Processes the Reduction produced by Parser.interfaceDeclaration().
|
|
577 |
*/
|
|
578 |
|
|
579 | 0 |
protected ClassNode interfaceDeclaration( ClassNode context, CSTNode reduction ) throws ParserException |
580 |
{ |
|
581 | 0 |
throw new GroovyBugError( "NOT YET IMPLEMENTED: interfaces" ); |
582 |
|
|
583 |
/*
|
|
584 |
|
|
585 |
//
|
|
586 |
// Calculate the easy stuff
|
|
587 |
|
|
588 |
String name = identifier( reduction );
|
|
589 |
int modifiers = modifiers( reduction.get(1) ) | Constants.ACC_ABSTRACT | Constants.ACC_STATIC;
|
|
590 |
String parent = null;
|
|
591 |
|
|
592 |
|
|
593 |
//
|
|
594 |
// Then process the interface list.
|
|
595 |
|
|
596 |
CSTNode interfaceReduction = reduction.get(3);
|
|
597 |
String[] interfaces = new String[interfaceReduction.children()];
|
|
598 |
for( int i = 1; i < interfaceReduction.size(); i++ )
|
|
599 |
{
|
|
600 |
interfaces[i-1] = resolveName( interfaceReduction.get(i) );
|
|
601 |
}
|
|
602 |
|
|
603 |
|
|
604 |
//
|
|
605 |
// Create the interface.
|
|
606 |
|
|
607 |
ClassNode classNode = (
|
|
608 |
context == null
|
|
609 |
? new ClassNode( dot(packageName, name), modifiers, parent, interfaces, MixinNode.EMPTY_ARRAY )
|
|
610 |
: new InnerClassNode( context, dot(packageName, name), modifiers, parent, interfaces, MixinNode.EMPTY_ARRAY )
|
|
611 |
);
|
|
612 |
|
|
613 |
classNode.setCSTNode( reduction.get(0) );
|
|
614 |
|
|
615 |
int propertyModifiers = Constants.ACC_STATIC | Constants.ACC_FINAL | Constants.ACC_PUBLIC;
|
|
616 |
int methodModifiers = Constants.ACC_ABSTRACT;
|
|
617 |
|
|
618 |
typeBody( classNode, reduction.get(4), propertyModifiers, methodModifiers );
|
|
619 |
|
|
620 |
|
|
621 |
return classNode;
|
|
622 |
|
|
623 |
*/
|
|
624 |
} |
|
625 |
|
|
626 |
|
|
627 |
|
|
628 |
|
|
629 |
//---------------------------------------------------------------------------
|
|
630 |
// STATEMENTS
|
|
631 |
|
|
632 |
|
|
633 |
/**
|
|
634 |
* Processes the Reduction that results from Parser.statementBody().
|
|
635 |
*/
|
|
636 |
|
|
637 | 0 |
protected BlockStatement statementBody( CSTNode reduction ) throws ParserException |
638 |
{ |
|
639 | 0 |
if( reduction.isEmpty() )
|
640 |
{ |
|
641 | 0 |
return new BlockStatement(); |
642 |
} |
|
643 | 0 |
else if( reduction.getMeaning() == Types.LEFT_CURLY_BRACE ) |
644 |
{ |
|
645 | 0 |
return statementBlock( reduction );
|
646 |
} |
|
647 |
else
|
|
648 |
{ |
|
649 | 0 |
Statement statement = statement( reduction ); |
650 | 0 |
statement.setCSTNode( reduction ); |
651 |
|
|
652 | 0 |
BlockStatement block = null;
|
653 | 0 |
if( statement instanceof BlockStatement ) |
654 |
{ |
|
655 | 0 |
block = (BlockStatement)statement; |
656 |
} |
|
657 |
else
|
|
658 |
{ |
|
659 | 0 |
block = new BlockStatement();
|
660 | 0 |
block.addStatement( statement ); |
661 |
} |
|
662 |
|
|
663 | 0 |
return block;
|
664 |
} |
|
665 |
} |
|
666 |
|
|
667 |
|
|
668 |
|
|
669 |
/**
|
|
670 |
* Processes any series of statements, starting at the specified offset
|
|
671 |
* and running to the end of the CSTNode.
|
|
672 |
*/
|
|
673 |
|
|
674 | 0 |
protected BlockStatement statements( CSTNode reduction, int first ) throws ParserException |
675 |
{ |
|
676 | 0 |
BlockStatement block = new BlockStatement();
|
677 |
|
|
678 | 0 |
for( int i = first; i < reduction.size(); i++ ) |
679 |
{ |
|
680 | 0 |
CSTNode statementReduction = reduction.get(i); |
681 |
|
|
682 | 0 |
Statement statement = statement( statementReduction ); |
683 | 0 |
statement.setCSTNode( statementReduction ); |
684 |
|
|
685 | 0 |
block.addStatement( statement ); |
686 |
} |
|
687 |
|
|
688 | 0 |
return block;
|
689 |
} |
|
690 |
|
|
691 |
|
|
692 |
|
|
693 |
/**
|
|
694 |
* Processes any statement block.
|
|
695 |
*/
|
|
696 |
|
|
697 | 0 |
protected BlockStatement statementBlock( CSTNode reduction ) throws ParserException |
698 |
{ |
|
699 | 0 |
return statements( reduction, 1 );
|
700 |
} |
|
701 |
|
|
702 |
|
|
703 |
|
|
704 |
/**
|
|
705 |
* Processes the Reduction produced by Parser.statement().
|
|
706 |
*/
|
|
707 |
|
|
708 | 0 |
protected Statement statement( CSTNode reduction ) throws ParserException |
709 |
{ |
|
710 | 0 |
Statement statement = null;
|
711 |
|
|
712 |
//
|
|
713 |
// Convert the statement
|
|
714 |
|
|
715 | 0 |
switch( reduction.getMeaning() )
|
716 |
{ |
|
717 |
case Types.KEYWORD_ASSERT:
|
|
718 |
{ |
|
719 | 0 |
statement = assertStatement( reduction ); |
720 | 0 |
break;
|
721 |
} |
|
722 |
|
|
723 |
case Types.KEYWORD_BREAK:
|
|
724 |
{ |
|
725 | 0 |
statement = breakStatement( reduction ); |
726 | 0 |
break;
|
727 |
} |
|
728 |
|
|
729 |
case Types.KEYWORD_CONTINUE:
|
|
730 |
{ |
|
731 | 0 |
statement = continueStatement( reduction ); |
732 | 0 |
break;
|
733 |
} |
|
734 |
|
|
735 |
case Types.KEYWORD_IF:
|
|
736 |
{ |
|
737 | 0 |
statement = ifStatement( reduction ); |
738 | 0 |
break;
|
739 |
} |
|
740 |
|
|
741 |
case Types.KEYWORD_RETURN:
|
|
742 |
{ |
|
743 | 0 |
statement = returnStatement( reduction ); |
744 | 0 |
break;
|
745 |
} |
|
746 |
|
|
747 |
case Types.KEYWORD_SWITCH:
|
|
748 |
{ |
|
749 | 0 |
statement = switchStatement( reduction ); |
750 | 0 |
break;
|
751 |
} |
|
752 |
|
|
753 |
case Types.KEYWORD_SYNCHRONIZED:
|
|
754 |
{ |
|
755 | 0 |
statement = synchronizedStatement( reduction ); |
756 | 0 |
break;
|
757 |
} |
|
758 |
|
|
759 |
case Types.KEYWORD_THROW:
|
|
760 |
{ |
|
761 | 0 |
statement = throwStatement( reduction ); |
762 | 0 |
break;
|
763 |
} |
|
764 |
|
|
765 |
case Types.KEYWORD_TRY:
|
|
766 |
{ |
|
767 | 0 |
statement = tryStatement( reduction ); |
768 | 0 |
break;
|
769 |
} |
|
770 |
|
|
771 |
case Types.KEYWORD_FOR:
|
|
772 |
{ |
|
773 | 0 |
statement = forStatement( reduction ); |
774 | 0 |
break;
|
775 |
} |
|
776 |
|
|
777 |
case Types.KEYWORD_WHILE:
|
|
778 |
{ |
|
779 | 0 |
statement = whileStatement( reduction ); |
780 | 0 |
break;
|
781 |
} |
|
782 |
|
|
783 |
case Types.KEYWORD_DO:
|
|
784 |
{ |
|
785 | 0 |
statement = doWhileStatement( reduction ); |
786 | 0 |
break;
|
787 |
} |
|
788 |
|
|
789 |
case Types.SYNTH_BLOCK:
|
|
790 |
case Types.LEFT_CURLY_BRACE:
|
|
791 |
{ |
|
792 | 0 |
statement = statementBlock( reduction ); |
793 | 0 |
break;
|
794 |
} |
|
795 |
|
|
796 |
case Types.SYNTH_LABEL:
|
|
797 |
{ |
|
798 | 0 |
statement = statement( reduction.get(1) ); |
799 | 0 |
statement.setStatementLabel( identifier(reduction) ); |
800 | 0 |
break;
|
801 |
} |
|
802 |
|
|
803 |
case Types.SYNTH_CLOSURE:
|
|
804 |
default:
|
|
805 |
{ |
|
806 | 0 |
statement = expressionStatement( reduction ); |
807 | 0 |
break;
|
808 |
} |
|
809 |
|
|
810 |
} |
|
811 |
|
|
812 |
|
|
813 | 0 |
statement.setCSTNode( reduction ); |
814 | 0 |
return statement;
|
815 |
} |
|
816 |
|
|
817 |
|
|
818 |
|
|
819 |
/**
|
|
820 |
* Processes the Reduction produced by Parser.assertStatement().
|
|
821 |
*/
|
|
822 |
|
|
823 | 0 |
protected AssertStatement assertStatement( CSTNode reduction ) throws ParserException |
824 |
{ |
|
825 | 0 |
BooleanExpression expression = new BooleanExpression( expression(reduction.get(1)) );
|
826 |
|
|
827 | 0 |
if( reduction.children() > 1 )
|
828 |
{ |
|
829 | 0 |
return new AssertStatement( expression, expression(reduction.get(2)) ); |
830 |
} |
|
831 |
|
|
832 | 0 |
return new AssertStatement( expression, ConstantExpression.NULL ); |
833 |
} |
|
834 |
|
|
835 |
|
|
836 |
|
|
837 |
/**
|
|
838 |
* Processes the Reduction produced by Parser.breakStatement().
|
|
839 |
*/
|
|
840 |
|
|
841 | 0 |
protected BreakStatement breakStatement( CSTNode reduction ) throws ParserException |
842 |
{ |
|
843 | 0 |
if( reduction.hasChildren() )
|
844 |
{ |
|
845 | 0 |
return new BreakStatement( reduction.get(1).getRootText() ); |
846 |
} |
|
847 |
|
|
848 | 0 |
return new BreakStatement(); |
849 |
} |
|
850 |
|
|
851 |
|
|
852 |
|
|
853 |
/**
|
|
854 |
* Processes the Reduction produced by Parser.continueStatement().
|
|
855 |
*/
|
|
856 |
|
|
857 | 0 |
protected ContinueStatement continueStatement( CSTNode reduction ) throws ParserException |
858 |
{ |
|
859 |
|
|
860 | 0 |
if( reduction.hasChildren() )
|
861 |
{ |
|
862 | 0 |
return new ContinueStatement( reduction.get(1).getRootText() ); |
863 |
} |
|
864 |
|
|
865 | 0 |
return new ContinueStatement(); |
866 |
} |
|
867 |
|
|
868 |
|
|
869 |
|
|
870 |
/**
|
|
871 |
* Processes the Reduction produced by Parser.ifStatement().
|
|
872 |
*/
|
|
873 |
|
|
874 | 0 |
protected IfStatement ifStatement( CSTNode reduction ) throws ParserException |
875 |
{ |
|
876 | 0 |
Expression condition = expression( reduction.get(1) ); |
877 | 0 |
BlockStatement body = statementBody( reduction.get(2) ); |
878 | 0 |
Statement elseBlock = EmptyStatement.INSTANCE; |
879 |
|
|
880 | 0 |
if( reduction.size() > 3 )
|
881 |
{ |
|
882 | 0 |
CSTNode elseReduction = reduction.get(3); |
883 | 0 |
if( elseReduction.getMeaning() == Types.KEYWORD_IF )
|
884 |
{ |
|
885 | 0 |
elseBlock = ifStatement( elseReduction ); |
886 |
} |
|
887 |
else
|
|
888 |
{ |
|
889 | 0 |
elseBlock = statementBody( elseReduction.get(1) ); |
890 |
} |
|
891 |
|
|
892 |
} |
|
893 |
|
|
894 | 0 |
return new IfStatement( new BooleanExpression(condition), body, elseBlock ); |
895 |
} |
|
896 |
|
|
897 |
|
|
898 |
|
|
899 |
/**
|
|
900 |
* Processes the Reduction produced by Parser.returnStatement().
|
|
901 |
*/
|
|
902 |
|
|
903 | 0 |
protected ReturnStatement returnStatement( CSTNode reduction ) throws ParserException |
904 |
{ |
|
905 | 0 |
if( reduction.hasChildren() )
|
906 |
{ |
|
907 | 0 |
return new ReturnStatement( expression(reduction.get(1)) ); |
908 |
} |
|
909 |
|
|
910 | 0 |
return ReturnStatement.RETURN_NULL_OR_VOID;
|
911 |
} |
|
912 |
|
|
913 |
|
|
914 |
|
|
915 |
/**
|
|
916 |
* Processes the Reduction produced by Parser.switchStatement().
|
|
917 |
*/
|
|
918 |
|
|
919 | 0 |
protected SwitchStatement switchStatement( CSTNode reduction ) throws ParserException |
920 |
{ |
|
921 | 0 |
SwitchStatement statement = new SwitchStatement( expression(reduction.get(1)) );
|
922 |
|
|
923 | 0 |
for( int i = 2; i < reduction.size(); i++ ) |
924 |
{ |
|
925 | 0 |
CSTNode child = reduction.get(i); |
926 |
|
|
927 | 0 |
switch( child.getMeaning() )
|
928 |
{ |
|
929 |
|
|
930 |
case Types.KEYWORD_CASE:
|
|
931 | 0 |
statement.addCase( caseStatement(child) ); |
932 | 0 |
break;
|
933 |
|
|
934 |
case Types.KEYWORD_DEFAULT:
|
|
935 | 0 |
statement.setDefaultStatement( statementBlock(child) ); |
936 | 0 |
break;
|
937 |
|
|
938 |
default:
|
|
939 | 0 |
throw new GroovyBugError( "invalid something in switch [" + child + "]" ); |
940 |
} |
|
941 |
} |
|
942 |
|
|
943 | 0 |
return statement;
|
944 |
} |
|
945 |
|
|
946 |
|
|
947 |
|
|
948 |
/**
|
|
949 |
* Processes the Reduction produced by Parser.switchStatement() for cases.
|
|
950 |
*/
|
|
951 |
|
|
952 | 0 |
protected CaseStatement caseStatement( CSTNode reduction ) throws ParserException |
953 |
{ |
|
954 | 0 |
return new CaseStatement( expression(reduction.get(1)), statements(reduction, 2) ); |
955 |
} |
|
956 |
|
|
957 |
|
|
958 |
|
|
959 |
/**
|
|
960 |
* Processes the Reduction produced by Parser.synchronizedStatement().
|
|
961 |
*/
|
|
962 |
|
|
963 | 0 |
protected SynchronizedStatement synchronizedStatement( CSTNode reduction ) throws ParserException |
964 |
{ |
|
965 | 0 |
return new SynchronizedStatement( expression(reduction.get(1)), statementBody(reduction.get(2)) ); |
966 |
} |
|
967 |
|
|
968 |
|
|
969 |
|
|
970 |
/**
|
|
971 |
* Processes the Reduction produced by Parser.throwStatement().
|
|
972 |
*/
|
|
973 |
|
|
974 | 0 |
protected ThrowStatement throwStatement( CSTNode reduction ) throws ParserException |
975 |
{ |
|
976 | 0 |
return new ThrowStatement( expression(reduction.get(1)) ); |
977 |
} |
|
978 |
|
|
979 |
|
|
980 |
|
|
981 |
/**
|
|
982 |
* Processes the Reduction produced by Parser.tryStatement().
|
|
983 |
*/
|
|
984 |
|
|
985 | 0 |
protected TryCatchStatement tryStatement( CSTNode reduction ) throws ParserException |
986 |
{ |
|
987 | 0 |
BlockStatement body = statementBody( reduction.get(1) ); |
988 | 0 |
BlockStatement finallyBlock = statementBody( reduction.get(3) ); |
989 |
|
|
990 | 0 |
TryCatchStatement statement = new TryCatchStatement( body, finallyBlock );
|
991 |
|
|
992 | 0 |
CSTNode catches = reduction.get(2); |
993 | 0 |
for( int i = 1; i < catches.size(); i++ ) |
994 |
{ |
|
995 | 0 |
CSTNode element = catches.get(i); |
996 | 0 |
String type = resolveName( element.get(1) ); |
997 | 0 |
String identifier = identifier( element.get(2) ); |
998 |
|
|
999 | 0 |
statement.addCatch( new CatchStatement(type, identifier, statementBody(element.get(3))) );
|
1000 |
} |
|
1001 |
|
|
1002 | 0 |
return statement;
|
1003 |
} |
|
1004 |
|
|
1005 |
|
|
1006 |
|
|
1007 |
/**
|
|
1008 |
* Processes the Reduction produced by Parser.forStatement().
|
|
1009 |
*/
|
|
1010 |
|
|
1011 | 0 |
protected ForStatement forStatement( CSTNode reduction ) throws ParserException |
1012 |
{ |
|
1013 | 0 |
CSTNode header = reduction.get(1); |
1014 | 0 |
Statement body = statementBody( reduction.get(2) ); |
1015 |
|
|
1016 |
|
|
1017 |
//
|
|
1018 |
// If the header has type Types.UNKNOWN, it's a standard for loop.
|
|
1019 |
|
|
1020 | 0 |
if( header.getMeaning() == Types.UNKNOWN )
|
1021 |
{ |
|
1022 | 0 |
Expression[] init = expressions( header.get(1) ); |
1023 | 0 |
Expression test = expression( header.get(2) ); |
1024 | 0 |
Expression[] incr = expressions( header.get(3) ); |
1025 |
|
|
1026 | 0 |
throw new GroovyBugError( "NOT YET IMPLEMENTED: standard for loop" ); |
1027 |
} |
|
1028 |
|
|
1029 |
|
|
1030 |
//
|
|
1031 |
// Otherwise, it's a for each loop.
|
|
1032 |
|
|
1033 |
else
|
|
1034 |
{ |
|
1035 |
|
|
1036 | 0 |
Type type = typeExpression( header.get(1) ); |
1037 | 0 |
String identifier = identifier( header.get(2) ); |
1038 | 0 |
Expression source = expression( header.get(3) ); |
1039 |
|
|
1040 | 0 |
return new ForStatement( identifier, type, source, body ); |
1041 |
} |
|
1042 |
} |
|
1043 |
|
|
1044 |
|
|
1045 |
|
|
1046 |
/**
|
|
1047 |
* Processes the Reduction produced by Parser.doWhileStatement().
|
|
1048 |
*/
|
|
1049 |
|
|
1050 | 0 |
protected DoWhileStatement doWhileStatement( CSTNode reduction ) throws ParserException |
1051 |
{ |
|
1052 | 0 |
Expression condition = expression( reduction.get(2) ); |
1053 | 0 |
BlockStatement body = statementBody( reduction.get(1) ); |
1054 |
|
|
1055 | 0 |
return new DoWhileStatement( new BooleanExpression(condition), body ); |
1056 |
} |
|
1057 |
|
|
1058 |
|
|
1059 |
|
|
1060 |
/**
|
|
1061 |
* Processes the Reduction produced by Parser.whileStatement().
|
|
1062 |
*/
|
|
1063 |
|
|
1064 | 0 |
protected WhileStatement whileStatement( CSTNode reduction ) throws ParserException |
1065 |
{ |
|
1066 | 0 |
Expression condition = expression( reduction.get(1) ); |
1067 | 0 |
BlockStatement body = statementBody( reduction.get(2) ); |
1068 |
|
|
1069 | 0 |
return new WhileStatement( new BooleanExpression(condition), body ); |
1070 |
|
|
1071 |
} |
|
1072 |
|
|
1073 |
|
|
1074 |
|
|
1075 |
|
|
1076 |
//---------------------------------------------------------------------------
|
|
1077 |
// EXPRESSIONS
|
|
1078 |
|
|
1079 |
|
|
1080 |
/**
|
|
1081 |
* Processes any expression that forms a complete statement.
|
|
1082 |
*/
|
|
1083 |
|
|
1084 | 0 |
protected Statement expressionStatement( CSTNode node ) throws ParserException |
1085 |
{ |
|
1086 | 0 |
return new ExpressionStatement( expression(node) ); |
1087 |
} |
|
1088 |
|
|
1089 |
|
|
1090 |
|
|
1091 |
/**
|
|
1092 |
* Processes a series of expression to an Expression[].
|
|
1093 |
*/
|
|
1094 |
|
|
1095 | 0 |
protected Expression[] expressions( CSTNode reduction ) throws ParserException |
1096 |
{ |
|
1097 | 0 |
Expression[] expressions = new Expression[ reduction.children() ];
|
1098 |
|
|
1099 | 0 |
for( int i = 1; i < reduction.size(); i++ ) |
1100 |
{ |
|
1101 | 0 |
expressions[i-1] = expression( reduction.get(i) ); |
1102 |
} |
|
1103 |
|
|
1104 | 0 |
return expressions;
|
1105 |
} |
|
1106 |
|
|
1107 |
|
|
1108 |
|
|
1109 |
/**
|
|
1110 |
* Processes the CSTNode produced by Parser.expression().
|
|
1111 |
*/
|
|
1112 |
|
|
1113 | 0 |
protected Expression expression( CSTNode reduction ) throws ParserException |
1114 |
{ |
|
1115 | 0 |
Expression expression = null;
|
1116 |
|
|
1117 | 0 |
int type = reduction.getMeaningAs( EXPRESSION_HANDLERS );
|
1118 | 0 |
switch( type )
|
1119 |
{ |
|
1120 |
case Types.SYNTHETIC:
|
|
1121 |
{ |
|
1122 | 0 |
expression = syntheticExpression( reduction ); |
1123 | 0 |
break;
|
1124 |
} |
|
1125 |
|
|
1126 |
case Types.RANGE_OPERATOR:
|
|
1127 |
{ |
|
1128 | 0 |
Expression from = expression( reduction.get(1) ); |
1129 | 0 |
Expression to = expression( reduction.get(2) ); |
1130 |
|
|
1131 | 0 |
expression = new RangeExpression( from, to, reduction.getMeaning() == Types.DOT_DOT );
|
1132 | 0 |
break;
|
1133 |
} |
|
1134 |
|
|
1135 |
|
|
1136 |
case Types.LEFT_SQUARE_BRACKET:
|
|
1137 |
case Types.INFIX_OPERATOR:
|
|
1138 |
{ |
|
1139 | 0 |
expression = infixExpression( reduction ); |
1140 | 0 |
break;
|
1141 |
} |
|
1142 |
|
|
1143 |
|
|
1144 |
case Types.REGEX_PATTERN:
|
|
1145 |
{ |
|
1146 | 0 |
expression = new RegexExpression( expression(reduction.get(1)) );
|
1147 | 0 |
break;
|
1148 |
} |
|
1149 |
|
|
1150 |
|
|
1151 |
case Types.PREFIX_OPERATOR:
|
|
1152 |
{ |
|
1153 | 0 |
expression = prefixExpression( reduction ); |
1154 | 0 |
break;
|
1155 |
} |
|
1156 |
|
|
1157 |
|
|
1158 |
case Types.POSTFIX_OPERATOR:
|
|
1159 |
{ |
|
1160 | 0 |
Expression body = expression( reduction.get(1) ); |
1161 | 0 |
expression = new PostfixExpression( body, reduction.getRoot() );
|
1162 | 0 |
break;
|
1163 |
} |
|
1164 |
|
|
1165 |
|
|
1166 |
case Types.SIMPLE_EXPRESSION:
|
|
1167 |
{ |
|
1168 | 0 |
expression = simpleExpression( reduction ); |
1169 | 0 |
break;
|
1170 |
} |
|
1171 |
|
|
1172 |
|
|
1173 |
case Types.KEYWORD_NEW:
|
|
1174 |
{ |
|
1175 | 0 |
expression = newExpression( reduction ); |
1176 | 0 |
break;
|
1177 |
} |
|
1178 |
|
|
1179 |
default:
|
|
1180 | 0 |
throw new GroovyBugError( "unhandled CST: [" + reduction.toString() + "]" ); |
1181 |
|
|
1182 |
} |
|
1183 |
|
|
1184 | 0 |
if( expression == null ) |
1185 |
{ |
|
1186 | 0 |
throw new GroovyBugError( "expression produced null: [" + reduction.toString() + "]" ); |
1187 |
} |
|
1188 |
|
|
1189 | 0 |
expression.setCSTNode( reduction ); |
1190 | 0 |
return expression;
|
1191 |
} |
|
1192 |
|
|
1193 |
|
|
1194 |
public static final int[] EXPRESSION_HANDLERS = { |
|
1195 |
Types.SYNTHETIC |
|
1196 |
, Types.RANGE_OPERATOR |
|
1197 |
, Types.LEFT_SQUARE_BRACKET |
|
1198 |
, Types.INFIX_OPERATOR |
|
1199 |
, Types.REGEX_PATTERN |
|
1200 |
, Types.PREFIX_OPERATOR |
|
1201 |
, Types.POSTFIX_OPERATOR |
|
1202 |
, Types.SIMPLE_EXPRESSION |
|
1203 |
, Types.KEYWORD_NEW |
|
1204 |
}; |
|
1205 |
|
|
1206 |
|
|
1207 |
|
|
1208 |
|
|
1209 |
/**
|
|
1210 |
* Processes most infix operators.
|
|
1211 |
*/
|
|
1212 |
|
|
1213 | 0 |
public Expression infixExpression( CSTNode reduction ) throws ParserException |
1214 |
{ |
|
1215 | 0 |
Expression expression; |
1216 |
|
|
1217 | 0 |
int type = reduction.getMeaning();
|
1218 | 0 |
switch( type )
|
1219 |
{ |
|
1220 |
case Types.DOT:
|
|
1221 |
case Types.NAVIGATE:
|
|
1222 |
{ |
|
1223 | 0 |
String name = reduction.get(2).getRootText(); |
1224 |
|
|
1225 | 0 |
Expression context = null;
|
1226 | 0 |
if( name.equals("class") ) |
1227 |
{ |
|
1228 | 0 |
CSTNode node = reduction.get(1); |
1229 | 0 |
if( node.isA(Types.LEFT_SQUARE_BRACKET) && node.children() == 1 )
|
1230 |
{ |
|
1231 | 0 |
throw new GroovyBugError( "NOT YET IMPLEMENTED: .class for array types" ); |
1232 |
// context = classExpression( reduction.get(1) );
|
|
1233 |
} |
|
1234 |
} |
|
1235 |
|
|
1236 | 0 |
if( context == null ) |
1237 |
{ |
|
1238 | 0 |
context = expression( reduction.get(1) ); |
1239 |
} |
|
1240 |
|
|
1241 | 0 |
expression = new PropertyExpression( context, name, type == Types.NAVIGATE );
|
1242 | 0 |
break;
|
1243 |
} |
|
1244 |
|
|
1245 |
|
|
1246 |
case Types.KEYWORD_INSTANCEOF:
|
|
1247 |
{ |
|
1248 | 0 |
Expression lhs = expression( reduction.get(1) ); |
1249 | 0 |
Expression rhs = classExpression( reduction.get(2) ); |
1250 | 0 |
expression = new BinaryExpression( lhs, reduction.getRoot(), rhs );
|
1251 | 0 |
break;
|
1252 |
} |
|
1253 |
|
|
1254 |
|
|
1255 |
default:
|
|
1256 |
{ |
|
1257 | 0 |
Expression lhs = expression( reduction.get(1) ); |
1258 | 0 |
Expression rhs = expression( reduction.get(2) ); |
1259 | 0 |
expression = new BinaryExpression( lhs, reduction.getRoot(), rhs );
|
1260 | 0 |
break;
|
1261 |
} |
|
1262 |
} |
|
1263 |
|
|
1264 | 0 |
return expression;
|
1265 |
} |
|
1266 |
|
|
1267 |
|
|
1268 |
|
|
1269 |
/**
|
|
1270 |
* Processes most prefix operators.
|
|
1271 |
*/
|
|
1272 |
|
|
1273 | 0 |
public Expression prefixExpression( CSTNode reduction ) throws ParserException |
1274 |
{ |
|
1275 | 0 |
Expression expression = null;
|
1276 | 0 |
CSTNode body = reduction.get(1); |
1277 |
|
|
1278 | 0 |
int type = reduction.getMeaning();
|
1279 | 0 |
switch( type )
|
1280 |
{ |
|
1281 |
case Types.PREFIX_MINUS:
|
|
1282 | 0 |
if( body.size() == 1 && body.isA(Types.NUMBER) )
|
1283 |
{ |
|
1284 | 0 |
expression = numericExpression( body, true );
|
1285 |
} |
|
1286 |
else
|
|
1287 |
{ |
|
1288 | 0 |
expression = new NegationExpression( expression(body) );
|
1289 |
} |
|
1290 | 0 |
break;
|
1291 |
|
|
1292 |
case Types.PREFIX_PLUS:
|
|
1293 | 0 |
expression = expression(body); |
1294 | 0 |
break;
|
1295 |
|
|
1296 |
case Types.NOT:
|
|
1297 | 0 |
expression = new NotExpression( expression(body) );
|
1298 | 0 |
break;
|
1299 |
|
|
1300 |
default:
|
|
1301 | 0 |
expression = new PrefixExpression( reduction.getRoot(), expression(body) );
|
1302 | 0 |
break;
|
1303 |
} |
|
1304 |
|
|
1305 | 0 |
return expression;
|
1306 |
} |
|
1307 |
|
|
1308 |
|
|
1309 |
|
|
1310 |
/**
|
|
1311 |
* Processes most simple expressions.
|
|
1312 |
*/
|
|
1313 |
|
|
1314 | 0 |
public Expression simpleExpression( CSTNode reduction ) throws ParserException |
1315 |
{ |
|
1316 | 0 |
Expression expression = null;
|
1317 |
|
|
1318 | 0 |
int type = reduction.getMeaning();
|
1319 | 0 |
switch( type )
|
1320 |
{ |
|
1321 |
case Types.KEYWORD_NULL:
|
|
1322 | 0 |
expression = ConstantExpression.NULL; |
1323 | 0 |
break;
|
1324 |
|
|
1325 |
case Types.KEYWORD_TRUE:
|
|
1326 | 0 |
expression = ConstantExpression.TRUE; |
1327 | 0 |
break;
|
1328 |
|
|
1329 |
case Types.KEYWORD_FALSE:
|
|
1330 | 0 |
expression = ConstantExpression.FALSE; |
1331 | 0 |
break;
|
1332 |
|
|
1333 |
case Types.STRING:
|
|
1334 | 0 |
expression = new ConstantExpression( reduction.getRootText() );
|
1335 | 0 |
break;
|
1336 |
|
|
1337 |
case Types.INTEGER_NUMBER:
|
|
1338 |
case Types.DECIMAL_NUMBER:
|
|
1339 | 0 |
expression = numericExpression( reduction, false );
|
1340 | 0 |
break;
|
1341 |
|
|
1342 |
case Types.KEYWORD_SUPER:
|
|
1343 |
case Types.KEYWORD_THIS:
|
|
1344 | 0 |
expression = variableExpression( reduction ); |
1345 | 0 |
break;
|
1346 |
|
|
1347 |
case Types.IDENTIFIER:
|
|
1348 | 0 |
expression = variableOrClassExpression( reduction ); |
1349 | 0 |
break;
|
1350 |
|
|
1351 |
} |
|
1352 |
|
|
1353 | 0 |
return expression;
|
1354 |
} |
|
1355 |
|
|
1356 |
|
|
1357 |
|
|
1358 |
/**
|
|
1359 |
* Processes numeric literals.
|
|
1360 |
*/
|
|
1361 |
|
|
1362 | 0 |
public Expression numericExpression( CSTNode reduction, boolean negate ) throws ParserException |
1363 |
{ |
|
1364 | 0 |
Token token = reduction.getRoot(); |
1365 | 0 |
String text = reduction.getRootText(); |
1366 | 0 |
String signed = negate ? "-" + text : text;
|
1367 |
|
|
1368 | 0 |
boolean isInteger = (token.getMeaning() == Types.INTEGER_NUMBER);
|
1369 |
|
|
1370 | 0 |
try
|
1371 |
{ |
|
1372 | 0 |
Number number = isInteger ? Numbers.parseInteger(signed) : Numbers.parseDecimal(signed); |
1373 |
|
|
1374 | 0 |
return new ConstantExpression( number ); |
1375 |
} |
|
1376 |
catch( NumberFormatException e )
|
|
1377 |
{ |
|
1378 | 0 |
error( "numeric literal [" + signed + "] invalid or out of range for its type", token ); |
1379 |
} |
|
1380 |
|
|
1381 | 0 |
throw new GroovyBugError( "this should never happen" ); |
1382 |
} |
|
1383 |
|
|
1384 |
|
|
1385 |
|
|
1386 |
/**
|
|
1387 |
* Processes most synthetic expressions.
|
|
1388 |
*/
|
|
1389 |
|
|
1390 | 0 |
public Expression syntheticExpression( CSTNode reduction ) throws ParserException |
1391 |
{ |
|
1392 | 0 |
Expression expression = null;
|
1393 |
|
|
1394 | 0 |
int type = reduction.getMeaning();
|
1395 | 0 |
switch( type )
|
1396 |
{ |
|
1397 |
case Types.SYNTH_TERNARY:
|
|
1398 |
{ |
|
1399 | 0 |
BooleanExpression condition = new BooleanExpression( expression(reduction.get(1)) );
|
1400 | 0 |
Expression trueBranch = expression( reduction.get(2) ); |
1401 | 0 |
Expression falseBranch = expression( reduction.get(3) ); |
1402 |
|
|
1403 | 0 |
expression = new TernaryExpression( condition, trueBranch, falseBranch );
|
1404 | 0 |
break;
|
1405 |
} |
|
1406 |
|
|
1407 |
|
|
1408 |
case Types.SYNTH_CAST:
|
|
1409 |
{ |
|
1410 | 0 |
String className = resolveName( reduction.get(1) ); |
1411 | 0 |
Expression body = expression( reduction.get(2) ); |
1412 |
|
|
1413 | 0 |
expression = new CastExpression( className, body );
|
1414 | 0 |
break;
|
1415 |
} |
|
1416 |
|
|
1417 |
|
|
1418 |
case Types.SYNTH_VARIABLE_DECLARATION:
|
|
1419 |
{ |
|
1420 | 0 |
expression = variableDeclarationExpression( reduction ); |
1421 | 0 |
break;
|
1422 |
} |
|
1423 |
|
|
1424 |
|
|
1425 |
case Types.SYNTH_METHOD_CALL:
|
|
1426 |
{ |
|
1427 | 0 |
expression = methodCallExpression( reduction ); |
1428 | 0 |
break;
|
1429 |
} |
|
1430 |
|
|
1431 |
|
|
1432 |
case Types.SYNTH_CLOSURE:
|
|
1433 |
{ |
|
1434 | 0 |
expression = closureExpression( reduction ); |
1435 | 0 |
break;
|
1436 |
} |
|
1437 |
|
|
1438 |
|
|
1439 |
case Types.SYNTH_GSTRING:
|
|
1440 |
{ |
|
1441 | 0 |
expression = gstringExpression( reduction ); |
1442 | 0 |
break;
|
1443 |
} |
|
1444 |
|
|
1445 |
|
|
1446 |
case Types.SYNTH_LIST:
|
|
1447 |
{ |
|
1448 | 0 |
expression = listExpression( reduction ); |
1449 | 0 |
break;
|
1450 |
} |
|
1451 |
|
|
1452 |
|
|
1453 |
case Types.SYNTH_MAP:
|
|
1454 |
{ |
|
1455 | 0 |
expression = mapExpression( reduction ); |
1456 | 0 |
break;
|
1457 |
} |
|
1458 |
} |
|
1459 |
|
|
1460 | 0 |
return expression;
|
1461 |
} |
|
1462 |
|
|
1463 |
|
|
1464 |
|
|
1465 |
|
|
1466 |
/**
|
|
1467 |
* Converts a (typically IDENTIFIER) CSTNode to a ClassExpression, if valid,
|
|
1468 |
* or a VariableExpression otherwise.
|
|
1469 |
*/
|
|
1470 |
|
|
1471 | 0 |
protected Expression variableOrClassExpression( CSTNode reduction ) throws ParserException |
1472 |
{ |
|
1473 | 0 |
String className = resolveName( reduction, false );
|
1474 |
|
|
1475 | 0 |
if( className == null ) |
1476 |
{ |
|
1477 | 0 |
return variableExpression( reduction );
|
1478 |
} |
|
1479 |
else
|
|
1480 |
{ |
|
1481 | 0 |
return new ClassExpression( className ); |
1482 |
} |
|
1483 |
} |
|
1484 |
|
|
1485 |
|
|
1486 |
|
|
1487 |
/**
|
|
1488 |
* Converts a CSTNode into a ClassExpression.
|
|
1489 |
*/
|
|
1490 |
|
|
1491 | 0 |
protected ClassExpression classExpression( CSTNode reduction ) throws ParserException |
1492 |
{ |
|
1493 | 0 |
String name = resolveName( reduction, true );
|
1494 | 0 |
return new ClassExpression( name ); |
1495 |
} |
|
1496 |
|
|
1497 |
|
|
1498 |
|
|
1499 |
/**
|
|
1500 |
* Converts a (typically IDENTIFIER) CSTNode to a VariableExpression, if
|
|
1501 |
* valid.
|
|
1502 |
*/
|
|
1503 |
|
|
1504 | 0 |
protected VariableExpression variableExpression( CSTNode reduction )
|
1505 |
{ |
|
1506 | 0 |
return new VariableExpression( reduction.getRootText(), null ); |
1507 |
} |
|
1508 |
|
|
1509 | 0 |
protected VariableExpression variableExpression( CSTNode reduction, String type )
|
1510 |
{ |
|
1511 | 0 |
return new VariableExpression( reduction.getRootText(), type ); |
1512 |
} |
|
1513 |
|
|
1514 |
|
|
1515 |
|
|
1516 |
/**
|
|
1517 |
* Converts an (possibly optional) type expression to a Type.
|
|
1518 |
*/
|
|
1519 |
|
|
1520 | 0 |
protected Type typeExpression( CSTNode reduction )
|
1521 |
{ |
|
1522 | 0 |
String name = makeName( reduction, null );
|
1523 | 0 |
if( name == null ) |
1524 |
{ |
|
1525 | 0 |
return Type.DYNAMIC_TYPE;
|
1526 |
} |
|
1527 |
else
|
|
1528 |
{ |
|
1529 | 0 |
return new Type( resolveName(name, true) ); |
1530 |
} |
|
1531 |
} |
|
1532 |
|
|
1533 |
|
|
1534 |
|
|
1535 |
/**
|
|
1536 |
* Processes the Reduction produced by parsing a typed variable
|
|
1537 |
* declaration.
|
|
1538 |
*/
|
|
1539 |
|
|
1540 | 0 |
protected Expression variableDeclarationExpression( CSTNode reduction ) throws ParserException |
1541 |
{ |
|
1542 | 0 |
String type = resolveName( reduction.get(1) ); |
1543 |
|
|
1544 |
|
|
1545 |
//
|
|
1546 |
// TEMPORARY UNTIL GENERAL SUPPORT IN PLACE
|
|
1547 |
|
|
1548 | 0 |
if( reduction.size() == 3 )
|
1549 |
{ |
|
1550 | 0 |
CSTNode node = reduction.get(2); |
1551 |
|
|
1552 | 0 |
VariableExpression name = variableExpression( node, type ); |
1553 |
//name.setType( type );
|
|
1554 |
|
|
1555 | 0 |
Token symbol = Token.newSymbol( Types.EQUAL, -1, -1 ); |
1556 |
|
|
1557 | 0 |
return new BinaryExpression( name, symbol, expression(node.get(1)) ); |
1558 |
} |
|
1559 |
|
|
1560 |
|
|
1561 | 0 |
throw new GroovyBugError( "NOT YET IMPLEMENTED: generalized variable declarations" ); |
1562 |
|
|
1563 |
/*
|
|
1564 |
|
|
1565 |
VariableDeclarationExpression expression = new VariableDeclarationExpression( type );
|
|
1566 |
|
|
1567 |
for( i = 2; i < reduction.size(); i++ )
|
|
1568 |
{
|
|
1569 |
CSTNode node = reduction.get(i);
|
|
1570 |
declaration.add( node.get(0), expression(node.get(1)) );
|
|
1571 |
}
|
|
1572 |
|
|
1573 |
return expression;
|
|
1574 |
|
|
1575 |
*/
|
|
1576 |
} |
|
1577 |
|
|
1578 |
|
|
1579 |
|
|
1580 |
/**
|
|
1581 |
* Processes a SYNTH_METHOD_CALL Reduction produced by Parser.expression().
|
|
1582 |
*/
|
|
1583 |
|
|
1584 | 0 |
protected MethodCallExpression methodCallExpression( CSTNode reduction ) throws ParserException |
1585 |
{ |
|
1586 | 0 |
MethodCallExpression call = null;
|
1587 |
|
|
1588 |
//
|
|
1589 |
// Figure out the name and context of the method call.
|
|
1590 |
|
|
1591 | 0 |
CSTNode descriptor = reduction.get(1); |
1592 | 0 |
Expression context = null;
|
1593 | 0 |
boolean implicit = false; |
1594 | 0 |
String method = "call";
|
1595 | 0 |
boolean safe = false; |
1596 |
|
|
1597 | 0 |
int type = descriptor.getMeaning();
|
1598 | 0 |
switch( type )
|
1599 |
{ |
|
1600 |
case Types.KEYWORD_SUPER:
|
|
1601 |
{ |
|
1602 | 0 |
context = variableExpression( descriptor ); |
1603 | 0 |
method = identifier( descriptor ); |
1604 | 0 |
break;
|
1605 |
} |
|
1606 |
|
|
1607 |
case Types.KEYWORD_THIS:
|
|
1608 |
{ |
|
1609 | 0 |
context = VariableExpression.THIS_EXPRESSION; |
1610 | 0 |
method = identifier( descriptor ); |
1611 | 0 |
break;
|
1612 |
} |
|
1613 |
|
|
1614 |
case Types.IDENTIFIER:
|
|
1615 |
{ |
|
1616 | 0 |
context = VariableExpression.THIS_EXPRESSION; |
1617 | 0 |
method = identifier( descriptor ); |
1618 | 0 |
implicit = true;
|
1619 | 0 |
break;
|
1620 |
} |
|
1621 |
|
|
1622 |
case Types.DOT:
|
|
1623 |
case Types.NAVIGATE:
|
|
1624 |
{ |
|
1625 | 0 |
context = expression( descriptor.get(1) ); |
1626 | 0 |
method = identifier( descriptor.get(2) ); |
1627 | 0 |
safe = type == Types.NAVIGATE; |
1628 | 0 |
break;
|
1629 |
} |
|
1630 |
|
|
1631 |
default:
|
|
1632 |
{ |
|
1633 | 0 |
context = expression( descriptor ); |
1634 | 0 |
break;
|
1635 |
} |
|
1636 |
} |
|
1637 |
|
|
1638 |
|
|
1639 |
//
|
|
1640 |
// And build the expression
|
|
1641 |
|
|
1642 | 0 |
Expression parameters = parameterList( reduction.get(2) ); |
1643 |
|
|
1644 |
// System.out.println( "method call expression: " + context + ", " + method + ", " + parameters + ", " + implicit );
|
|
1645 |
|
|
1646 | 0 |
call = new MethodCallExpression( context, method, parameters );
|
1647 | 0 |
call.setImplicitThis( implicit ); |
1648 | 0 |
call.setSafe( safe ); |
1649 |
|
|
1650 | 0 |
return call;
|
1651 |
} |
|
1652 |
|
|
1653 |
|
|
1654 |
|
|
1655 |
/**
|
|
1656 |
* Processes the Reduction produced by Parser.closureExpression().
|
|
1657 |
*/
|
|
1658 |
|
|
1659 | 0 |
protected ClosureExpression closureExpression( CSTNode reduction ) throws ParserException |
1660 |
{ |
|
1661 | 0 |
ClosureExpression expression = null;
|
1662 |
|
|
1663 | 0 |
Parameter[] parameters = parameterDeclarations( reduction.get(1) ); |
1664 | 0 |
expression = new ClosureExpression( parameters, statementBlock(reduction.get(2)) );
|
1665 |
|
|
1666 | 0 |
return expression;
|
1667 |
} |
|
1668 |
|
|
1669 |
|
|
1670 |
|
|
1671 |
/**
|
|
1672 |
* Processes the Reduction produced by Parser.parameterList().
|
|
1673 |
*/
|
|
1674 |
|
|
1675 | 0 |
protected Expression parameterList( CSTNode reduction ) throws ParserException |
1676 |
{ |
|
1677 | 0 |
TupleExpression list = new TupleExpression();
|
1678 |
|
|
1679 | 0 |
for( int i = 1; i < reduction.size(); i++ ) |
1680 |
{ |
|
1681 | 0 |
CSTNode node = reduction.get(i); |
1682 | 0 |
list.addExpression( expression(node) ); |
1683 |
} |
|
1684 |
|
|
1685 | 0 |
return list;
|
1686 |
} |
|
1687 |
|
|
1688 |
|
|
1689 |
|
|
1690 |
/**
|
|
1691 |
* Processes the Reduction produced by Parser.newExpression().
|
|
1692 |
*/
|
|
1693 |
|
|
1694 | 0 |
protected Expression newExpression( CSTNode reduction ) throws ParserException |
1695 |
{ |
|
1696 | 0 |
Expression expression = null;
|
1697 | 0 |
CSTNode typeNode = reduction.get(1); |
1698 | 0 |
String type = resolveName( typeNode ); |
1699 |
|
|
1700 |
|
|
1701 |
//
|
|
1702 |
// Array types have dimension and initialization data to handle.
|
|
1703 |
|
|
1704 | 0 |
if( typeNode.getMeaning() == Types.LEFT_SQUARE_BRACKET )
|
1705 |
{ |
|
1706 | 0 |
CSTNode dimensions = reduction.get(2); |
1707 |
|
|
1708 |
//
|
|
1709 |
// BUG: at present, ArrayExpression expects a scalar type and
|
|
1710 |
// does not support multi-dimensional arrays. In future, the
|
|
1711 |
// the latter will need to change, and that may require the
|
|
1712 |
// former to change, as well. For now, we calculate the scalar
|
|
1713 |
// type and error for multiple dimensions.
|
|
1714 |
|
|
1715 | 0 |
if( typeNode.get(1).getMeaning() == Types.LEFT_SQUARE_BRACKET )
|
1716 |
{ |
|
1717 | 0 |
throw new GroovyBugError( "NOT YET IMPLEMENTED: multidimensional arrays" ); |
1718 |
} |
|
1719 |
else
|
|
1720 |
{ |
|
1721 | 0 |
type = resolveName( typeNode.get(1) ); |
1722 |
} |
|
1723 |
|
|
1724 |
|
|
1725 |
//
|
|
1726 |
// If there are no dimensions, process a tuple initializer
|
|
1727 |
|
|
1728 | 0 |
if( dimensions.isEmpty() )
|
1729 |
{ |
|
1730 | 0 |
CSTNode data = reduction.get(3); |
1731 |
|
|
1732 | 0 |
if( data.get(1, true).getMeaning() == Types.SYNTH_TUPLE ) |
1733 |
{ |
|
1734 | 0 |
throw new GroovyBugError( "NOT YET IMPLEMENTED: multidimensional arrays" ); |
1735 |
} |
|
1736 |
|
|
1737 | 0 |
expression = new ArrayExpression( type, tupleExpression(data).getExpressions() );
|
1738 |
} |
|
1739 |
|
|
1740 |
|
|
1741 |
//
|
|
1742 |
// Otherwise, process the dimensions
|
|
1743 |
|
|
1744 |
else
|
|
1745 |
{ |
|
1746 | 0 |
if( dimensions.size() > 2 )
|
1747 |
{ |
|
1748 | 0 |
throw new GroovyBugError( "NOT YET IMPLEMENTED: multidimensional arrays" ); |
1749 |
|
|
1750 |
/*
|
|
1751 |
|
|
1752 |
expression = new ArrayExpression( type, tupleExpression(dimensions) );
|
|
1753 |
|
|
1754 |
*/
|
|
1755 |
} |
|
1756 |
else
|
|
1757 |
{ |
|
1758 | 0 |
expression = new ArrayExpression( type, expression(dimensions.get(1)) );
|
1759 |
} |
|
1760 |
} |
|
1761 |
} |
|
1762 |
|
|
1763 |
|
|
1764 |
//
|
|
1765 |
// Scalar types have a constructor parameter list and possibly a type body
|
|
1766 |
|
|
1767 |
else
|
|
1768 |
{ |
|
1769 | 0 |
Expression parameters = parameterList( reduction.get(2) ); |
1770 |
|
|
1771 | 0 |
if( reduction.size() > 3 )
|
1772 |
{ |
|
1773 | 0 |
throw new GroovyBugError( "NOT YET IMPLEMENTED: anonymous classes" ); |
1774 |
} |
|
1775 |
|
|
1776 | 0 |
expression = new ConstructorCallExpression( type, parameters );
|
1777 |
} |
|
1778 |
|
|
1779 | 0 |
return expression;
|
1780 |
} |
|
1781 |
|
|
1782 |
|
|
1783 |
|
|
1784 |
/**
|
|
1785 |
* Processes the Reduction produced by Parser.newArrayInitializer().
|
|
1786 |
*/
|
|
1787 |
|
|
1788 | 0 |
protected TupleExpression tupleExpression( CSTNode reduction ) throws ParserException |
1789 |
{ |
|
1790 | 0 |
TupleExpression tuple = new TupleExpression();
|
1791 |
|
|
1792 | 0 |
for( int i = 1; i < reduction.size(); i++ ) |
1793 |
{ |
|
1794 | 0 |
CSTNode element = reduction.get(i); |
1795 |
|
|
1796 | 0 |
if( element.getMeaning() == Types.SYNTH_TUPLE )
|
1797 |
{ |
|
1798 | 0 |
tuple.addExpression( tupleExpression(element) ); |
1799 |
} |
|
1800 |
else
|
|
1801 |
{ |
|
1802 | 0 |
tuple.addExpression( expression(element) ); |
1803 |
} |
|
1804 |
} |
|
1805 |
|
|
1806 | 0 |
return tuple;
|
1807 |
} |
|
1808 |
|
|
1809 |
|
|
1810 |
|
|
1811 |
/**
|
|
1812 |
* Processes the Reduction produced by Parser.gstring().
|
|
1813 |
*/
|
|
1814 |
|
|
1815 | 0 |
protected Expression gstringExpression( CSTNode reduction ) throws ParserException |
1816 |
{ |
|
1817 | 0 |
if( !reduction.hasChildren() )
|
1818 |
{ |
|
1819 | 0 |
return new ConstantExpression( "" ); |
1820 |
} |
|
1821 |
|
|
1822 | 0 |
if( reduction.children() == 1 && reduction.get(1).getMeaning() == Types.STRING )
|
1823 |
{ |
|
1824 | 0 |
return expression( reduction.get(1) );
|
1825 |
} |
|
1826 |
|
|
1827 |
|
|
1828 | 0 |
GStringExpression expression = new GStringExpression( reduction.getRootText() );
|
1829 | 0 |
boolean lastWasExpression = false; |
1830 |
|
|
1831 | 0 |
for( int i = 1; i < reduction.size(); i++ ) |
1832 |
{ |
|
1833 | 0 |
CSTNode element = reduction.get(i); |
1834 | 0 |
if( element.getMeaning() == Types.STRING )
|
1835 |
{ |
|
1836 | 0 |
ConstantExpression string = new ConstantExpression( element.getRootText() );
|
1837 | 0 |
string.setCSTNode( element ); |
1838 |
|
|
1839 | 0 |
expression.addString( string ); |
1840 |
|
|
1841 | 0 |
lastWasExpression = false;
|
1842 |
} |
|
1843 |
else
|
|
1844 |
{ |
|
1845 | 0 |
if( lastWasExpression )
|
1846 |
{ |
|
1847 | 0 |
expression.addString( new ConstantExpression("") ); |
1848 |
} |
|
1849 |
|
|
1850 | 0 |
lastWasExpression = true;
|
1851 | 0 |
expression.addValue( element.isEmpty() ? ConstantExpression.NULL : expression(element) ); |
1852 |
} |
|
1853 |
} |
|
1854 |
|
|
1855 | 0 |
return expression;
|
1856 |
} |
|
1857 |
|
|
1858 |
|
|
1859 |
|
|
1860 |
/**
|
|
1861 |
* Processes one of the Reductions produced by Parser.listOrMapExpression().
|
|
1862 |
*/
|
|
1863 |
|
|
1864 | 0 |
protected ListExpression listExpression( CSTNode reduction ) throws ParserException |
1865 |
{ |
|
1866 | 0 |
ListExpression list = new ListExpression();
|
1867 |
|
|
1868 | 0 |
for( int i = 1; i < reduction.size(); i++ ) |
1869 |
{ |
|
1870 | 0 |
list.addExpression( expression(reduction.get(i)) ); |
1871 |
} |
|
1872 |
|
|
1873 | 0 |
return list;
|
1874 |
} |
|
1875 |
|
|
1876 |
|
|
1877 |
|
|
1878 |
/**
|
|
1879 |
* Processes the other Reduction produced by Parser.listOrMapExpression().
|
|
1880 |
*/
|
|
1881 |
|
|
1882 | 0 |
protected MapExpression mapExpression( CSTNode reduction ) throws ParserException |
1883 |
{ |
|
1884 | 0 |
MapExpression map = new MapExpression();
|
1885 |
|
|
1886 | 0 |
for( int i = 1; i < reduction.size(); i++ ) |
1887 |
{ |
|
1888 | 0 |
CSTNode element = reduction.get(i); |
1889 | 0 |
Expression key = expression( element.get(1) ); |
1890 | 0 |
Expression value = expression( element.get(2) ); |
1891 |
|
|
1892 | 0 |
map.addMapEntryExpression( key, value ); |
1893 |
} |
|
1894 |
|
|
1895 | 0 |
return map;
|
1896 |
} |
|
1897 |
|
|
1898 |
|
|
1899 |
|
|
1900 |
|
|
1901 |
|
|
1902 |
//---------------------------------------------------------------------------
|
|
1903 |
// NAMING
|
|
1904 |
|
|
1905 |
private static HashMap resolutions = new HashMap(); // cleared on build(), to be safe |
|
1906 |
private static String NOT_RESOLVED = new String(); |
|
1907 |
|
|
1908 |
|
|
1909 |
/**
|
|
1910 |
* Converts a CSTNode representation of a type name back into
|
|
1911 |
* a string.
|
|
1912 |
*/
|
|
1913 |
|
|
1914 | 0 |
protected String makeName( CSTNode root, String defaultName )
|
1915 |
{ |
|
1916 | 0 |
if( root == null ) |
1917 |
{ |
|
1918 | 0 |
return defaultName;
|
1919 |
} |
|
1920 |
|
|
1921 | 0 |
String name = "";
|
1922 | 0 |
switch( root.getMeaning() )
|
1923 |
{ |
|
1924 |
case Types.LEFT_SQUARE_BRACKET:
|
|
1925 |
{ |
|
1926 | 0 |
name = makeName( root.get(1) ) + "[]";
|
1927 | 0 |
break;
|
1928 |
} |
|
1929 |
|
|
1930 |
case Types.DOT:
|
|
1931 |
{ |
|
1932 | 0 |
CSTNode node = root; |
1933 | 0 |
while( node.isA(Types.DOT) )
|
1934 |
{ |
|
1935 | 0 |
name = "." + node.get(2).getRootText() + name;
|
1936 | 0 |
node = node.get(1); |
1937 |
} |
|
1938 |
|
|
1939 | 0 |
name = node.getRootText() + name; |
1940 | 0 |
break;
|
1941 |
} |
|
1942 |
|
|
1943 |
case Types.UNKNOWN:
|
|
1944 |
{ |
|
1945 | 0 |
name = defaultName; |
1946 | 0 |
break;
|
1947 |
} |
|
1948 |
|
|
1949 |
default:
|
|
1950 |
{ |
|
1951 | 0 |
name = root.getRootText(); |
1952 | 0 |
break;
|
1953 |
} |
|
1954 |
|
|
1955 |
} |
|
1956 |
|
|
1957 | 0 |
return name;
|
1958 |
} |
|
1959 |
|
|
1960 |
|
|
1961 |
|
|
1962 |
/**
|
|
1963 |
* A synonym for <code>makeName( root, "java.lang.Object" )</code>.
|
|
1964 |
*/
|
|
1965 |
|
|
1966 | 0 |
protected String makeName( CSTNode root )
|
1967 |
{ |
|
1968 | 0 |
return makeName( root, "" ); // br: the default name. was "java.lang.Object" |
1969 |
} |
|
1970 |
|
|
1971 |
|
|
1972 |
|
|
1973 |
/**
|
|
1974 |
* Returns the text of an identifier.
|
|
1975 |
*/
|
|
1976 |
|
|
1977 | 0 |
protected String identifier( CSTNode identifier )
|
1978 |
{ |
|
1979 | 0 |
return identifier.getRootText();
|
1980 |
} |
|
1981 |
|
|
1982 |
|
|
1983 |
|
|
1984 |
/**
|
|
1985 |
* Returns a fully qualified name for any given potential type
|
|
1986 |
* name. Returns null if no qualified name could be determined.
|
|
1987 |
*/
|
|
1988 |
|
|
1989 | 0 |
protected String resolveName( String name, boolean safe ) |
1990 |
{ |
|
1991 |
//
|
|
1992 |
// Use our cache of resolutions, if possible
|
|
1993 |
|
|
1994 | 0 |
String resolution = (String)resolutions.get( name ); |
1995 | 0 |
if( resolution == NOT_RESOLVED )
|
1996 |
{ |
|
1997 | 0 |
return (safe ? name : null); |
1998 |
} |
|
1999 | 0 |
else if( resolution != null ) |
2000 |
{ |
|
2001 | 0 |
return (String)resolution;
|
2002 |
} |
|
2003 |
|
|
2004 |
|
|
2005 | 0 |
do
|
2006 |
{ |
|
2007 |
//
|
|
2008 |
// If the type name contains a ".", it's probably fully
|
|
2009 |
// qualified, and we don't take it to verification here.
|
|
2010 |
|
|
2011 | 0 |
if( name.indexOf(".") >= 0 ) |
2012 |
{ |
|
2013 | 0 |
resolution = name; |
2014 | 0 |
break; // <<< FLOW CONTROL <<<<<<<<< |
2015 |
} |
|
2016 |
|
|
2017 |
|
|
2018 |
//
|
|
2019 |
// Otherwise, we'll need the scalar type for checking, and
|
|
2020 |
// the postfix for reassembly.
|
|
2021 |
|
|
2022 | 0 |
String scalar = name, postfix = "";
|
2023 | 0 |
while( scalar.endsWith("[]") ) |
2024 |
{ |
|
2025 | 0 |
scalar = scalar.substring( 0, scalar.length() - 2 ); |
2026 | 0 |
postfix += "[]";
|
2027 |
} |
|
2028 |
|
|
2029 |
|
|
2030 |
//
|
|
2031 |
// Primitive types are all valid...
|
|
2032 |
|
|
2033 | 0 |
if( Types.ofType(Types.lookupKeyword(scalar), Types.PRIMITIVE_TYPE) )
|
2034 |
{ |
|
2035 | 0 |
resolution = name; |
2036 | 0 |
break; // <<< FLOW CONTROL <<<<<<<<< |
2037 |
} |
|
2038 |
|
|
2039 |
|
|
2040 |
//
|
|
2041 |
// Next, check our imports and return the qualified name,
|
|
2042 |
// if available.
|
|
2043 |
|
|
2044 | 0 |
if( this.imports.containsKey(scalar) ) |
2045 |
{ |
|
2046 | 0 |
resolution = ((String)this.imports.get(scalar)) + postfix;
|
2047 | 0 |
break; // <<< FLOW CONTROL <<<<<<<<< |
2048 |
} |
|
2049 |
|
|
2050 |
|
|
2051 |
//
|
|
2052 |
// Next, see if our class loader can resolve it in the current package.
|
|
2053 |
|
|
2054 | 0 |
if( packageName != null && packageName.length() > 0 ) |
2055 |
{ |
|
2056 | 0 |
try
|
2057 |
{ |
|
2058 | 0 |
getClassLoader().loadClass( dot(packageName, scalar) ); |
2059 | 0 |
resolution = dot(packageName, name); |
2060 |
|
|
2061 | 0 |
break; // <<< FLOW CONTROL <<<<<<<<< |
2062 |
} |
|
2063 |
catch( Throwable e )
|
|
2064 |
{ |
|
2065 |
/* ignore */
|
|
2066 |
} |
|
2067 |
} |
|
2068 |
|
|
2069 |
// search the package imports path
|
|
2070 | 0 |
List packageImports = output.getImportPackages(); |
2071 | 0 |
for (int i = 0; i < packageImports.size(); i++) { |
2072 | 0 |
String pack = (String) packageImports.get(i); |
2073 | 0 |
String clsName = pack + name; |
2074 | 0 |
try {
|
2075 | 0 |
getClassLoader().loadClass( clsName ); |
2076 | 0 |
resolution = clsName; |
2077 | 0 |
break;
|
2078 |
} catch (Throwable e) {
|
|
2079 |
//
|
|
2080 |
} |
|
2081 |
} |
|
2082 | 0 |
if (resolution != null) |
2083 | 0 |
break;
|
2084 |
|
|
2085 |
//
|
|
2086 |
// Last chance, check the default imports.
|
|
2087 |
|
|
2088 | 0 |
for( int i = 0; i < DEFAULT_IMPORTS.length; i++ ) |
2089 |
{ |
|
2090 | 0 |
try
|
2091 |
{ |
|
2092 | 0 |
String qualified = DEFAULT_IMPORTS[i] + scalar; |
2093 | 0 |
getClassLoader().loadClass( qualified ); |
2094 |
|
|
2095 | 0 |
resolution = qualified + postfix; |
2096 | 0 |
break; // <<< FLOW CONTROL <<<<<<<<< |
2097 |
} |
|
2098 |
catch( Throwable e )
|
|
2099 |
{ |
|
2100 |
/* ignore */
|
|
2101 |
} |
|
2102 |
} |
|
2103 |
|
|
2104 |
} while( false ); |
|
2105 |
|
|
2106 |
|
|
2107 |
//
|
|
2108 |
// Cache the solution and return it
|
|
2109 |
|
|
2110 | 0 |
if( resolution == null ) |
2111 |
{ |
|
2112 | 0 |
resolutions.put( name, NOT_RESOLVED ); |
2113 | 0 |
return (safe ? name : null); |
2114 |
} |
|
2115 |
else
|
|
2116 |
{ |
|
2117 | 0 |
resolutions.put( name, resolution ); |
2118 | 0 |
return resolution;
|
2119 |
} |
|
2120 |
} |
|
2121 |
|
|
2122 |
|
|
2123 |
|
|
2124 |
/**
|
|
2125 |
* Builds a name from a CSTNode, then resolves it. Returns the resolved name
|
|
2126 |
* if available, or null, unless safe is set, in which case the built name
|
|
2127 |
* is returned instead of null.
|
|
2128 |
*
|
|
2129 |
* @todo we should actually remove all resolving code from the ASTBuilder and
|
|
2130 |
* move it into the verifier / analyser
|
|
2131 |
*/
|
|
2132 |
|
|
2133 | 0 |
protected String resolveName( CSTNode root, boolean safe ) |
2134 |
{ |
|
2135 | 0 |
String name = makeName( root ); |
2136 | 0 |
if (name.length() == 0)
|
2137 | 0 |
return ""; |
2138 | 0 |
if (this.newClasses.contains(name)) { |
2139 | 0 |
return dot(packageName, name);
|
2140 |
} else {
|
|
2141 | 0 |
return resolveName( name, safe );
|
2142 |
} |
|
2143 |
} |
|
2144 |
|
|
2145 |
|
|
2146 |
|
|
2147 |
/**
|
|
2148 |
* A synonym for <code>resolveName( root, true )</code>.
|
|
2149 |
*/
|
|
2150 |
|
|
2151 | 0 |
protected String resolveName( CSTNode root )
|
2152 |
{ |
|
2153 | 0 |
return resolveName( root, true ); |
2154 |
} |
|
2155 |
|
|
2156 |
|
|
2157 |
|
|
2158 |
/**
|
|
2159 |
* Returns true if the specified name is a known type name.
|
|
2160 |
*/
|
|
2161 |
|
|
2162 | 0 |
protected boolean isDatatype( String name ) |
2163 |
{ |
|
2164 | 0 |
return resolveName( name, false ) != null; |
2165 |
} |
|
2166 |
|
|
2167 |
|
|
2168 |
|
|
2169 |
/**
|
|
2170 |
* Returns two names joined by a dot. If the base name is
|
|
2171 |
* empty, returns the name unchanged.
|
|
2172 |
*/
|
|
2173 |
|
|
2174 | 0 |
protected String dot( String base, String name )
|
2175 |
{ |
|
2176 | 0 |
if( base != null && base.length() > 0 ) |
2177 |
{ |
|
2178 | 0 |
return base + "." + name; |
2179 |
} |
|
2180 |
|
|
2181 | 0 |
return name;
|
2182 |
} |
|
2183 |
|
|
2184 |
|
|
2185 |
|
|
2186 |
/**
|
|
2187 |
* A synonym for <code>dot( base, "" )</code>.
|
|
2188 |
*/
|
|
2189 |
|
|
2190 | 0 |
protected String dot( String base )
|
2191 |
{ |
|
2192 | 0 |
return dot( base, "" ); |
2193 |
} |
|
2194 |
|
|
2195 |
|
|
2196 |
|
|
2197 |
|
|
2198 |
//---------------------------------------------------------------------------
|
|
2199 |
// ASM SUPPORT
|
|
2200 |
|
|
2201 |
|
|
2202 |
/**
|
|
2203 |
* Returns the ASM Constant bits for the specified modifiers.
|
|
2204 |
*/
|
|
2205 |
|
|
2206 | 0 |
protected int modifiers( CSTNode list ) |
2207 |
{ |
|
2208 | 0 |
int modifiers = 0;
|
2209 |
|
|
2210 | 0 |
for( int i = 1; i < list.size(); ++i ) |
2211 |
{ |
|
2212 | 0 |
SWITCH: switch( list.get(i).getMeaning() )
|
2213 |
{ |
|
2214 |
case Types.KEYWORD_PUBLIC:
|
|
2215 |
{ |
|
2216 | 0 |
modifiers |= Constants.ACC_PUBLIC; |
2217 | 0 |
break SWITCH;
|
2218 |
} |
|
2219 |
|
|
2220 |
case Types.KEYWORD_PROTECTED:
|
|
2221 |
{ |
|
2222 | 0 |
modifiers |= Constants.ACC_PROTECTED; |
2223 | 0 |
break SWITCH;
|
2224 |
} |
|
2225 |
|
|
2226 |
case Types.KEYWORD_PRIVATE:
|
|
2227 |
{ |
|
2228 | 0 |
modifiers |= Constants.ACC_PRIVATE; |
2229 | 0 |
break SWITCH;
|
2230 |
} |
|
2231 |
|
|
2232 |
|
|
2233 |
case Types.KEYWORD_ABSTRACT:
|
|
2234 |
{ |
|
2235 | 0 |
modifiers |= Constants.ACC_ABSTRACT; |
2236 | 0 |
break SWITCH;
|
2237 |
} |
|
2238 |
|
|
2239 |
case Types.KEYWORD_FINAL:
|
|
2240 |
{ |
|
2241 | 0 |
modifiers |= Constants.ACC_FINAL; |
2242 | 0 |
break SWITCH;
|
2243 |
} |
|
2244 |
|
|
2245 |
case Types.KEYWORD_NATIVE:
|
|
2246 |
{ |
|
2247 | 0 |
modifiers |= Constants.ACC_NATIVE; |
2248 | 0 |
break SWITCH;
|
2249 |
} |
|
2250 |
|
|
2251 |
case Types.KEYWORD_TRANSIENT:
|
|
2252 |
{ |
|
2253 | 0 |
modifiers |= Constants.ACC_TRANSIENT; |
2254 | 0 |
break SWITCH;
|
2255 |
} |
|
2256 |
|
|
2257 |
case Types.KEYWORD_VOLATILE:
|
|
2258 |
{ |
|
2259 | 0 |
modifiers |= Constants.ACC_VOLATILE; |
2260 | 0 |
break SWITCH;
|
2261 |
} |
|
2262 |
|
|
2263 |
|
|
2264 |
case Types.KEYWORD_SYNCHRONIZED:
|
|
2265 |
{ |
|
2266 | 0 |
modifiers |= Constants.ACC_SYNCHRONIZED; |
2267 | 0 |
break SWITCH;
|
2268 |
} |
|
2269 |
case Types.KEYWORD_STATIC:
|
|
2270 |
{ |
|
2271 | 0 |
modifiers |= Constants.ACC_STATIC; |
2272 | 0 |
break SWITCH;
|
2273 |
} |
|
2274 |
|
|
2275 |
} |
|
2276 |
} |
|
2277 |
|
|
2278 |
|
|
2279 |
//
|
|
2280 |
// If not protected or private we default to public.
|
|
2281 |
|
|
2282 | 0 |
if( (modifiers & (Constants.ACC_PROTECTED | Constants.ACC_PRIVATE)) == 0 )
|
2283 |
{ |
|
2284 | 0 |
modifiers |= Constants.ACC_PUBLIC; |
2285 |
} |
|
2286 |
|
|
2287 | 0 |
return modifiers;
|
2288 |
} |
|
2289 |
|
|
2290 |
|
|
2291 |
|
|
2292 |
|
|
2293 |
//---------------------------------------------------------------------------
|
|
2294 |
// ERROR HANDLING
|
|
2295 |
|
|
2296 |
|
|
2297 |
/**
|
|
2298 |
* Throws a <code>ParserException</code>.
|
|
2299 |
*/
|
|
2300 |
|
|
2301 | 0 |
protected void error( String description, CSTNode node ) throws ParserException |
2302 |
{ |
|
2303 | 0 |
throw new ParserException( description, node.getRoot() ); |
2304 |
} |
|
2305 |
|
|
2306 |
|
|
2307 |
} |
|
2308 |
|
|