View Javadoc

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     privateong> 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     public ASTBuilder( SourceUnit sourceUnit, ClassLoader classLoader )
150     {
151         this.controller  = sourceUnit;
152         this.classLoader = classLoader;
153         this.imports     = new HashMap();
154         this.packageName = null;
155     }
156 
157 
158 
159    /***
160     *  Returns our class loader (as supplied on construction).
161     */
162 
163     public ClassLoader getClassLoader()
164     {
165         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     public ModuleNode build( CSTNode input ) throws ParserException
180     {
181         this.newClasses.clear();
182         this.output = new ModuleNode( controller );
183         resolutions.clear();
184 
185         //
186         // input structure:
187         //    1: package
188         //    2: imports
189         //   3+: statements
190 
191         packageName = packageDeclaration( input.get(1) );
192         output.setPackageName( packageName );
193 
194         importStatements( output, input.get(2) );
195 
196         for( int i = 3; i < input.size(); ++i )
197         {
198             topLevelStatement( output, input.get(i) );
199         }
200 
201         if( output.isEmpty() )
202         {
203             output.addStatement( new BlockStatement() );
204         }
205 
206         return output;
207     }
208 
209 
210 
211 
212   //---------------------------------------------------------------------------
213   // DECLARATIONS
214 
215 
216    /***
217     *  Processes the Reduction produced by Parser.packageDeclaration().
218     */
219 
220     protectedong> String packageDeclaration( CSTNode reduction )
221     {
222         if( reduction.hasChildren() )
223         {
224             return makeName( reduction.get(1) );
225         }
226 
227         return null;
228 
229     }
230 
231 
232 
233    /***
234     *  Processes the imports Reduction produced by Parser.module().
235     */
236 
237     protected void importStatements( ModuleNode module, CSTNode container )
238     {
239         for( int i = 1; i < container.size(); ++i)
240         {
241             importStatement( module, container.get(i) );
242         }
243     }
244 
245 
246 
247    /***
248     *  Processes the Reduction produced by Parser.importStatement().
249     */
250 
251     protected void importStatement( ModuleNode module, CSTNode reduction )
252     {
253         //
254         // First, get the package name (if supplied).
255 
256         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         if( reduction.get(2).isA(Types.STAR) )
264         {
265             String[] classes = module.addImportPackage( dot(importPackage) );
266             for( int i = 0; i < classes.length; i++ )
267             {
268                 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             for( int i = 2; i < reduction.size(); i++ )
279             {
280                 CSTNode clause = reduction.get(i);
281                 String  name   = identifier( clause );
282                 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                 module.addImport( as, name );  // unqualified
294 
295                 name = dot( importPackage, name );
296 
297                 // module.addImport( as, name );  // qualified
298                 imports.put( as, name );
299             }
300         }
301     }
302 
303 
304 
305    /***
306     *  Processes the Reduction produced by Parser.topLevelStatement().
307     */
308 
309     protected void topLevelStatement( ModuleNode module, CSTNode reduction ) throws ParserException
310     {
311         int type = reduction.getMeaning();
312         switch( type )
313         {
314             case Types.SYNTH_CLASS:
315                 module.addClass( classDeclaration(null, reduction) );
316                 break;
317 
318             case Types.SYNTH_INTERFACE:
319                 module.addClass( interfaceDeclaration(null, reduction) );
320                 break;
321 
322             case Types.SYNTH_METHOD:
323                 module.addMethod( methodDeclaration(null, reduction) );
324                 break;
325 
326             default:
327                 module.addStatement( statement(reduction) );
328                 break;
329         }
330 
331     }
332 
333 
334 
335    /***
336     *  Processes the Reduction produced by Parser.classDeclaration().
337     */
338 
339     protected ClassNode classDeclaration( ClassNode context, CSTNode reduction ) throws ParserException
340     {
341         //
342         // Calculate the easy stuff
343 
344         String   name = identifier( reduction );
345         this.newClasses.add(name);
346         int modifiers = modifiers( reduction.get(1) );
347         String parent = resolveName( reduction.get(2).get(1) );
348 
349 
350         //
351         // Then process the interface list.
352 
353         CSTNode  interfaceReduction = reduction.get(3);
354         String[] interfaces = new String[interfaceReduction.children()];
355         for( int i = 1; i < interfaceReduction.size(); i++ )
356         {
357             interfaces[i-1] = resolveName( interfaceReduction.get(i) );
358         }
359 
360 
361         //
362         // Create the class.
363 
364         ClassNode classNode = (
365             context == null
366                 ClassNode(               dot(packageName, name), modifiers, parent, interfaces, MixinNode/EMPTY_ARRAY )/package-summary.html">? new ClassNode(               dot(packageName, name), modifiers, parent, interfaces, MixinNode.EMPTY_ARRAY )
367                 InnerClassNode( context, dot(packageName, name), modifiers, parent, interfaces, MixinNode/EMPTY_ARRAY )/package-summary.html">: new InnerClassNode( context, dot(packageName, name), modifiers, parent, interfaces, MixinNode.EMPTY_ARRAY )
368         );
369 
370         classNode.setCSTNode( reduction.get(0) );
371         typeBody( classNode, reduction.get(4), 0, 0 );
372         return classNode;
373     }
374 
375 
376 
377    /***
378     *  Processes a type body for classDeclaration() and others.
379     */
380 
381     protected void typeBody( ClassNode classNode, CSTNode body, int propertyModifiers, int methodModifiers ) throws ParserException
382     {
383         for( int i = 1; i < body.size(); i++ )
384         {
385             CSTNode statement = body.get(i);
386             switch( statement.getMeaning() )
387             {
388                 case Types.SYNTH_PROPERTY:
389                     addPropertyDeclaration( classNode, statement, propertyModifiers );
390                     break;
391 
392                 case Types.SYNTH_METHOD:
393                     methodDeclaration( classNode, statement, methodModifiers );
394                     break;
395 
396                 case Types.SYNTH_CLASS:
397                     classDeclaration( classNode, statement );
398                     break;
399 
400                 case Types.SYNTH_INTERFACE:
401                     interfaceDeclaration( classNode, statement );
402                     break;
403 
404                 default:
405                     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     protected void addPropertyDeclaration( ClassNode classNode, CSTNode reduction, int extraModifiers ) throws ParserException
418     {
419         String   name = identifier( reduction );
420         int modifiers = modifiers( reduction.get(1) ) | extraModifiers;
421         String   type = resolveName( reduction.get(2) );
422 
423         Expression value = reduction.size() > 3 ? expression(reduction.get(3)) : null;
424 
425         PropertyNode propertyNode = classNode.addProperty( name, modifiers, type, value, null, null );
426         propertyNode.setCSTNode( reduction.get(0) );
427 
428     }
429 
430 
431 
432    /***
433     *  A synonym for <code>addPropertyDeclaration( classNode, reduction, 0 )</code>.
434     */
435 
436     protected void addPropertyDeclaration( ClassNode classNode, CSTNode reduction ) throws ParserException
437     {
438         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     protected MethodNode methodDeclaration( ClassNode classNode, CSTNode reduction, int extraModifiers ) throws ParserException
449     {
450         String className = null;
451         if( classNode != null  )
452         {
453             className = classNode.getNameWithoutPackage();
454         }
455 
456 
457         //
458         // Get the basic method data
459 
460         String   name = identifier( reduction );
461         int modifiers = modifiers( reduction.get(1) ) | extraModifiers;
462         String   type = resolveName( reduction.get(2) );
463 
464         Parameter[] parameters = parameterDeclarations( reduction.get(3) );
465         BlockStatement    body = statementBody( reduction.get(5) );
466 
467 
468         //
469         // Process the throws clause
470 
471         CSTNode  clause     = reduction.get(4);
472         String[] throwTypes = new String[clause.children()];
473         for( int i = 1; i < clause.size(); i++ )
474         {
475             throwTypes[i-1] = resolveName( clause.get(i) );
476         }
477 
478         if( clause.hasChildren() ) { throw new GroovyBugError( "NOT YET IMPLEMENTED: throws clause" ); }
479 
480 
481         //
482         // An unnamed method is a static initializer
483 
484         if( name.length() == 0 )
485         {
486             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         else if( className != null && name.equals(className) )
504         {
505             ConstructorNode node = new ConstructorNode( modifiers, parameters, body );
506             node.setCSTNode( reduction.get(0) );
507 
508             classNode.addConstructor( node );
509             return null;
510         }
511 
512 
513         //
514         // Anything else is a plain old method
515 
516         else
517         {
518             MethodNode method = new MethodNode( name, modifiers, type, parameters, body );
519             method.setCSTNode( reduction.get(0) );
520 
521             if( classNode != null )
522             {
523                 classNode.addMethod( method );
524             }
525 
526             return method;
527         }
528 
529     }
530 
531 
532 
533    /***
534     *  A synonym for <code>methodDeclaration( classNode, reduction, 0 )</code>.
535     */
536 
537     protected MethodNode methodDeclaration( ClassNode classNode, CSTNode reduction ) throws ParserException
538     {
539         return methodDeclaration( classNode, reduction, 0 );
540     }
541 
542 
543 
544    /***
545     *  Processes the Reduction produced by Parser.parameterDeclarationList().
546     */
547 
548     protected Parameter[] parameterDeclarations( CSTNode reduction ) throws ParserException
549     {
550         Parameter[] parameters = new Parameter[ reduction.children() ];
551 
552         for( int i = 1; i < reduction.size(); i++ )
553         {
554             CSTNode node = reduction.get(i);
555 
556             String identifier = identifier( node );
557             String type       = resolveName( node.get(1) );
558 
559             if( node.size() > 2 )
560             {
561                 parameters[i-1] = new Parameter( type, identifier, expression(node.get(2)) );
562             }
563             else
564             {
565                 parameters[i-1] = new Parameter( type, identifier );
566             }
567         }
568 
569         return parameters;
570 
571     }
572 
573 
574 
575    /***
576     * Processes the Reduction produced by Parser.interfaceDeclaration().
577     */
578 
579     protected ClassNode interfaceDeclaration( ClassNode context, CSTNode reduction ) throws ParserException
580     {
581         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     protected BlockStatement statementBody( CSTNode reduction ) throws ParserException
638     {
639         if( reduction.isEmpty() )
640         {
641             return new BlockStatement();
642         }
643         else if( reduction.getMeaning() == Types.LEFT_CURLY_BRACE )
644         {
645             return statementBlock( reduction );
646         }
647         else
648         {
649             Statement statement = statement( reduction );
650             statement.setCSTNode( reduction );
651 
652             BlockStatement block = null;
653             if( statement instanceof BlockStatement )
654             {
655                 block = (BlockStatement)statement;
656             }
657             else
658             {
659                 block = new BlockStatement();
660                 block.addStatement( statement );
661             }
662 
663             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     protected BlockStatement statements( CSTNode reduction, int first ) throws ParserException
675     {
676         BlockStatement block = new BlockStatement();
677 
678         for( int i = first; i < reduction.size(); i++ )
679         {
680             CSTNode statementReduction = reduction.get(i);
681 
682             Statement statement = statement( statementReduction );
683             statement.setCSTNode( statementReduction );
684 
685             block.addStatement( statement );
686         }
687 
688         return block;
689     }
690 
691 
692 
693    /***
694     *  Processes any statement block.
695     */
696 
697     protected BlockStatement statementBlock( CSTNode reduction ) throws ParserException
698     {
699         return statements( reduction, 1 );
700     }
701 
702 
703 
704    /***
705     *  Processes the Reduction produced by Parser.statement().
706     */
707 
708     protected Statement statement( CSTNode reduction ) throws ParserException
709     {
710         Statement statement = null;
711 
712         //
713         // Convert the statement
714 
715         switch( reduction.getMeaning() )
716         {
717             case Types.KEYWORD_ASSERT:
718             {
719                 statement = assertStatement( reduction );
720                 break;
721             }
722 
723             case Types.KEYWORD_BREAK:
724             {
725                 statement = breakStatement( reduction );
726                 break;
727             }
728 
729             case Types.KEYWORD_CONTINUE:
730             {
731                 statement = continueStatement( reduction );
732                 break;
733             }
734 
735             case Types.KEYWORD_IF:
736             {
737                 statement = ifStatement( reduction );
738                 break;
739             }
740 
741             case Types.KEYWORD_RETURN:
742             {
743                 statement = returnStatement( reduction );
744                 break;
745             }
746 
747             case Types.KEYWORD_SWITCH:
748             {
749                 statement = switchStatement( reduction );
750                 break;
751             }
752 
753             case Types.KEYWORD_SYNCHRONIZED:
754             {
755                 statement = synchronizedStatement( reduction );
756                 break;
757             }
758 
759             case Types.KEYWORD_THROW:
760             {
761                 statement = throwStatement( reduction );
762                 break;
763             }
764 
765             case Types.KEYWORD_TRY:
766             {
767                 statement = tryStatement( reduction );
768                 break;
769             }
770 
771             case Types.KEYWORD_FOR:
772             {
773                 statement = forStatement( reduction );
774                 break;
775             }
776 
777             case Types.KEYWORD_WHILE:
778             {
779                 statement = whileStatement( reduction );
780                 break;
781             }
782 
783             case Types.KEYWORD_DO:
784             {
785                 statement = doWhileStatement( reduction );
786                 break;
787             }
788 
789             case Types.SYNTH_BLOCK:
790             case Types.LEFT_CURLY_BRACE:
791             {
792                 statement = statementBlock( reduction );
793                 break;
794             }
795 
796             case Types.SYNTH_LABEL:
797             {
798                 statement = statement( reduction.get(1) );
799                 statement.setStatementLabel( identifier(reduction) );
800                 break;
801             }
802 
803             case Types.SYNTH_CLOSURE:
804             default:
805             {
806                 statement = expressionStatement( reduction );
807                 break;
808             }
809 
810         }
811 
812 
813         statement.setCSTNode( reduction );
814         return statement;
815     }
816 
817 
818 
819    /***
820     *  Processes the Reduction produced by Parser.assertStatement().
821     */
822 
823     protected AssertStatement assertStatement( CSTNode reduction ) throws ParserException
824     {
825         BooleanExpression expression = new BooleanExpression( expression(reduction.get(1)) );
826 
827         if( reduction.children() > 1 )
828         {
829             return new AssertStatement( expression, expression(reduction.get(2)) );
830         }
831 
832         return new AssertStatement( expression, ConstantExpression.NULL );
833     }
834 
835 
836 
837    /***
838     *  Processes the Reduction produced by Parser.breakStatement().
839     */
840 
841     protected BreakStatement breakStatement( CSTNode reduction ) throws ParserException
842     {
843         if( reduction.hasChildren() )
844         {
845             return new BreakStatement( reduction.get(1).getRootText() );
846         }
847 
848         return new BreakStatement();
849     }
850 
851 
852 
853    /***
854     *  Processes the Reduction produced by Parser.continueStatement().
855     */
856 
857     protected ContinueStatement continueStatement( CSTNode reduction ) throws ParserException
858     {
859 
860         if( reduction.hasChildren() )
861         {
862             return new ContinueStatement( reduction.get(1).getRootText() );
863         }
864 
865         return new ContinueStatement();
866     }
867 
868 
869 
870    /***
871     *  Processes the Reduction produced by Parser.ifStatement().
872     */
873 
874     protected IfStatement ifStatement( CSTNode reduction ) throws ParserException
875     {
876         Expression condition = expression( reduction.get(1) );
877         BlockStatement  body = statementBody( reduction.get(2) );
878         Statement  elseBlock = EmptyStatement.INSTANCE;
879 
880         if( reduction.size() > 3 )
881         {
882             CSTNode elseReduction = reduction.get(3);
883             if( elseReduction.getMeaning() == Types.KEYWORD_IF )
884             {
885                 elseBlock = ifStatement( elseReduction );
886             }
887             else
888             {
889                 elseBlock = statementBody( elseReduction.get(1) );
890             }
891 
892         }
893 
894         return new IfStatement( new BooleanExpression(condition), body, elseBlock );
895     }
896 
897 
898 
899    /***
900     *  Processes the Reduction produced by Parser.returnStatement().
901     */
902 
903     protected ReturnStatement returnStatement( CSTNode reduction ) throws ParserException
904     {
905         if( reduction.hasChildren() )
906         {
907             return new ReturnStatement( expression(reduction.get(1)) );
908         }
909 
910         return ReturnStatement.RETURN_NULL_OR_VOID;
911     }
912 
913 
914 
915    /***
916     *  Processes the Reduction produced by Parser.switchStatement().
917     */
918 
919     protected SwitchStatement switchStatement( CSTNode reduction ) throws ParserException
920     {
921         SwitchStatement statement = new SwitchStatement( expression(reduction.get(1)) );
922 
923         for( int i = 2; i < reduction.size(); i++ )
924         {
925             CSTNode child = reduction.get(i);
926 
927             switch( child.getMeaning() )
928             {
929 
930                 case Types.KEYWORD_CASE:
931                     statement.addCase( caseStatement(child) );
932                     break;
933 
934                 case Types.KEYWORD_DEFAULT:
935                     statement.setDefaultStatement( statementBlock(child) );
936                     break;
937 
938                 default:
939                     throw new GroovyBugError( "invalid something in switch [" + child + "]" );
940             }
941         }
942 
943         return statement;
944     }
945 
946 
947 
948    /***
949     *  Processes the Reduction produced by Parser.switchStatement() for cases.
950     */
951 
952     protected CaseStatement caseStatement( CSTNode reduction ) throws ParserException
953     {
954         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     protected SynchronizedStatement synchronizedStatement( CSTNode reduction ) throws ParserException
964     {
965         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     protected ThrowStatement throwStatement( CSTNode reduction ) throws ParserException
975     {
976         return new ThrowStatement( expression(reduction.get(1)) );
977     }
978 
979 
980 
981    /***
982     *  Processes the Reduction produced by Parser.tryStatement().
983     */
984 
985     protected TryCatchStatement tryStatement( CSTNode reduction ) throws ParserException
986     {
987         BlockStatement         body = statementBody( reduction.get(1) );
988         BlockStatement finallyBlock = statementBody( reduction.get(3) );
989 
990         TryCatchStatement statement = new TryCatchStatement( body, finallyBlock );
991 
992         CSTNode catches = reduction.get(2);
993         for( int i = 1; i < catches.size(); i++ )
994         {
995             CSTNode   element = catches.get(i);
996             String       type = resolveName( element.get(1) );
997             String identifier = identifier( element.get(2) );
998 
999             statement.addCatch( new CatchStatement(type, identifier, statementBody(element.get(3))) );
1000         }
1001 
1002         return statement;
1003     }
1004 
1005 
1006 
1007    /***
1008     *  Processes the Reduction produced by Parser.forStatement().
1009     */
1010 
1011     protected ForStatement forStatement( CSTNode reduction ) throws ParserException
1012     {
1013         CSTNode header = reduction.get(1);
1014         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         if( header.getMeaning() == Types.UNKNOWN )
1021         {
1022             Expression[] init = expressions( header.get(1) );
1023             Expression   test = expression(  header.get(2) );
1024             Expression[] incr = expressions( header.get(3) );
1025 
1026             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             Type         type = typeExpression( header.get(1) );
1037             String identifier = identifier(  header.get(2) );
1038             Expression source = expression(  header.get(3) );
1039 
1040             return new ForStatement( identifier, type, source, body );
1041         }
1042     }
1043 
1044 
1045 
1046    /***
1047     *  Processes the Reduction produced by Parser.doWhileStatement().
1048     */
1049 
1050     protected DoWhileStatement doWhileStatement( CSTNode reduction ) throws ParserException
1051     {
1052         Expression condition = expression( reduction.get(2) );
1053         BlockStatement  body = statementBody( reduction.get(1) );
1054 
1055         return new DoWhileStatement( new BooleanExpression(condition), body );
1056     }
1057 
1058 
1059 
1060    /***
1061     *  Processes the Reduction produced by Parser.whileStatement().
1062     */
1063 
1064     protected WhileStatement whileStatement( CSTNode reduction ) throws ParserException
1065     {
1066         Expression condition = expression( reduction.get(1) );
1067         BlockStatement  body = statementBody( reduction.get(2) );
1068 
1069         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     protected Statement expressionStatement( CSTNode node ) throws ParserException
1085     {
1086         return new ExpressionStatement( expression(node) );
1087     }
1088 
1089 
1090 
1091    /***
1092     *  Processes a series of expression to an Expression[].
1093     */
1094 
1095     protected Expression[] expressions( CSTNode reduction ) throws ParserException
1096     {
1097         Expression[] expressions = new Expression[ reduction.children() ];
1098 
1099         for( int i = 1; i < reduction.size(); i++ )
1100         {
1101             expressions[i-1] = expression( reduction.get(i) );
1102         }
1103 
1104         return expressions;
1105     }
1106 
1107 
1108 
1109    /***
1110     *  Processes the CSTNode produced by Parser.expression().
1111     */
1112 
1113     protected Expression expression( CSTNode reduction ) throws ParserException
1114     {
1115         Expression expression = null;
1116 
1117         int type = reduction.getMeaningAs( EXPRESSION_HANDLERS );
1118         switch( type )
1119         {
1120             case Types.SYNTHETIC:
1121             {
1122                 expression = syntheticExpression( reduction );
1123                 break;
1124             }
1125 
1126             case Types.RANGE_OPERATOR:
1127             {
1128                 Expression from = expression( reduction.get(1) );
1129                 Expression   to = expression( reduction.get(2) );
1130 
1131                 expression = new RangeExpression( from, to, reduction.getMeaning() == Types.DOT_DOT );
1132                 break;
1133             }
1134 
1135 
1136             case Types.LEFT_SQUARE_BRACKET:
1137             case Types.INFIX_OPERATOR:
1138             {
1139                 expression = infixExpression( reduction );
1140                 break;
1141             }
1142 
1143 
1144             case Types.REGEX_PATTERN:
1145             {
1146                 expression = new RegexExpression( expression(reduction.get(1)) );
1147                 break;
1148             }
1149 
1150 
1151             case Types.PREFIX_OPERATOR:
1152             {
1153                 expression = prefixExpression( reduction );
1154                 break;
1155             }
1156 
1157 
1158             case Types.POSTFIX_OPERATOR:
1159             {
1160                 Expression body = expression( reduction.get(1) );
1161                 expression = new PostfixExpression( body, reduction.getRoot() );
1162                 break;
1163             }
1164 
1165 
1166             case Types.SIMPLE_EXPRESSION:
1167             {
1168                 expression = simpleExpression( reduction );
1169                 break;
1170             }
1171 
1172 
1173             case Types.KEYWORD_NEW:
1174             {
1175                 expression = newExpression( reduction );
1176                 break;
1177             }
1178 
1179             default:
1180                 throw new GroovyBugError( "unhandled CST: [" + reduction.toString() + "]" );
1181 
1182         }
1183 
1184         if( expression == null )
1185         {
1186             throw new GroovyBugError( "expression produced null: [" + reduction.toString() + "]" );
1187         }
1188 
1189         expression.setCSTNode( reduction );
1190         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     public Expression infixExpression( CSTNode reduction ) throws ParserException
1214     {
1215         Expression expression;
1216 
1217         int type = reduction.getMeaning();
1218         switch( type )
1219         {
1220             case Types.DOT:
1221             case Types.NAVIGATE:
1222             {
1223                 String name = reduction.get(2).getRootText();
1224 
1225                 Expression context = null;
1226                 if( name.equals("class") )
1227                 {
1228                     CSTNode node = reduction.get(1);
1229                     if( node.isA(Types.LEFT_SQUARE_BRACKET) && node.children() == 1 )
1230                     {
1231                         throw new GroovyBugError( "NOT YET IMPLEMENTED: .class for array types" );
1232                         // context = classExpression( reduction.get(1) );
1233                     }
1234                 }
1235 
1236                 if( context == null )
1237                 {
1238                     context = expression( reduction.get(1) );
1239                 }
1240 
1241                 expression = new PropertyExpression( context, name, type == Types.NAVIGATE );
1242                 break;
1243             }
1244 
1245 
1246             case Types.KEYWORD_INSTANCEOF:
1247             {
1248                 Expression   lhs = expression(  reduction.get(1) );
1249                 Expression   rhs = classExpression( reduction.get(2) );
1250                 expression = new BinaryExpression( lhs, reduction.getRoot(), rhs );
1251                 break;
1252             }
1253 
1254 
1255             default:
1256             {
1257                 Expression lhs = expression( reduction.get(1) );
1258                 Expression rhs = expression( reduction.get(2) );
1259                 expression = new BinaryExpression( lhs, reduction.getRoot(), rhs );
1260                 break;
1261             }
1262         }
1263 
1264         return expression;
1265     }
1266 
1267 
1268 
1269    /***
1270     *  Processes most prefix operators.
1271     */
1272 
1273     public Expression prefixExpression( CSTNode reduction ) throws ParserException
1274     {
1275         Expression expression = null;
1276         CSTNode    body       = reduction.get(1);
1277 
1278         int type = reduction.getMeaning();
1279         switch( type )
1280         {
1281             case Types.PREFIX_MINUS:
1282                 if( body.size() == 1 && body.isA(Types.NUMBER) )
1283                 {
1284                     expression = numericExpression( body, true );
1285                 }
1286                 else
1287                 {
1288                     expression = new NegationExpression( expression(body) );
1289                 }
1290                 break;
1291 
1292             case Types.PREFIX_PLUS:
1293                 expression = expression(body);
1294                 break;
1295 
1296             case Types.NOT:
1297                 expression = new NotExpression( expression(body) );
1298                 break;
1299 
1300             default:
1301                 expression = new PrefixExpression( reduction.getRoot(), expression(body) );
1302                 break;
1303         }
1304 
1305         return expression;
1306     }
1307 
1308 
1309 
1310    /***
1311     *  Processes most simple expressions.
1312     */
1313 
1314     public Expression simpleExpression( CSTNode reduction ) throws ParserException
1315     {
1316         Expression expression = null;
1317 
1318         int type = reduction.getMeaning();
1319         switch( type )
1320         {
1321             case Types.KEYWORD_NULL:
1322                 expression = ConstantExpression.NULL;
1323                 break;
1324 
1325             case Types.KEYWORD_TRUE:
1326                 expression = ConstantExpression.TRUE;
1327                 break;
1328 
1329             case Types.KEYWORD_FALSE:
1330                 expression = ConstantExpression.FALSE;
1331                 break;
1332 
1333             case Types.STRING:
1334                 expression = new ConstantExpression( reduction.getRootText() );
1335                 break;
1336 
1337             case Types.INTEGER_NUMBER:
1338             case Types.DECIMAL_NUMBER:
1339                 expression = numericExpression( reduction, false );
1340                 break;
1341 
1342             case Types.KEYWORD_SUPER:
1343             case Types.KEYWORD_THIS:
1344                 expression = variableExpression( reduction );
1345                 break;
1346 
1347             case Types.IDENTIFIER:
1348                 expression = variableOrClassExpression( reduction );
1349                 break;
1350 
1351         }
1352 
1353         return expression;
1354     }
1355 
1356 
1357 
1358    /***
1359     *  Processes numeric literals.
1360     */
1361 
1362     public Expression numericExpression( CSTNode reduction, boolean negate ) throws ParserException
1363     {
1364         Token  token  = reduction.getRoot();
1365         String text   = reduction.getRootText();
1366         String signed = negate ? "-" + text : text;
1367 
1368         boolean isInteger = (token.getMeaning() == Types.INTEGER_NUMBER);
1369 
1370         try
1371         {
1372             Number number = isInteger ? Numbers.parseInteger(signed) : Numbers.parseDecimal(signed);
1373 
1374             return new ConstantExpression( number );
1375         }
1376         catch( NumberFormatException e )
1377         {
1378             error( "numeric literal [" + signed + "] invalid or out of range for its type", token );
1379         }
1380 
1381         throw new GroovyBugError( "this should never happen" );
1382     }
1383 
1384 
1385 
1386    /***
1387     *  Processes most synthetic expressions.
1388     */
1389 
1390     public Expression syntheticExpression( CSTNode reduction ) throws ParserException
1391     {
1392         Expression expression = null;
1393 
1394         int type = reduction.getMeaning();
1395         switch( type )
1396         {
1397             case Types.SYNTH_TERNARY:
1398             {
1399                 BooleanExpression condition   = new BooleanExpression( expression(reduction.get(1)) );
1400                 Expression        trueBranch  = expression( reduction.get(2) );
1401                 Expression        falseBranch = expression( reduction.get(3) );
1402 
1403                 expression = new TernaryExpression( condition, trueBranch, falseBranch );
1404                 break;
1405             }
1406 
1407 
1408             case Types.SYNTH_CAST:
1409             {
1410                 String className = resolveName( reduction.get(1) );
1411                 Expression  body = expression(  reduction.get(2) );
1412 
1413                 expression = new CastExpression( className, body );
1414                 break;
1415             }
1416 
1417 
1418             case Types.SYNTH_VARIABLE_DECLARATION:
1419             {
1420                 expression = variableDeclarationExpression( reduction );
1421                 break;
1422             }
1423 
1424 
1425             case Types.SYNTH_METHOD_CALL:
1426             {
1427                 expression = methodCallExpression( reduction );
1428                 break;
1429             }
1430 
1431 
1432             case Types.SYNTH_CLOSURE:
1433             {
1434                 expression = closureExpression( reduction );
1435                 break;
1436             }
1437 
1438 
1439             case Types.SYNTH_GSTRING:
1440             {
1441                 expression = gstringExpression( reduction );
1442                 break;
1443             }
1444 
1445 
1446             case Types.SYNTH_LIST:
1447             {
1448                 expression = listExpression( reduction );
1449                 break;
1450             }
1451 
1452 
1453             case Types.SYNTH_MAP:
1454             {
1455                 expression = mapExpression( reduction );
1456                 break;
1457             }
1458         }
1459 
1460         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     protected Expression variableOrClassExpression( CSTNode reduction ) throws ParserException
1472     {
1473         String className = resolveName( reduction, false );
1474 
1475         if( className == null )
1476         {
1477             return variableExpression( reduction );
1478         }
1479         else
1480         {
1481             return new ClassExpression( className );
1482         }
1483     }
1484 
1485 
1486 
1487    /***
1488     *  Converts a CSTNode into a ClassExpression.
1489     */
1490 
1491     protected ClassExpression classExpression( CSTNode reduction ) throws ParserException
1492     {
1493         String name = resolveName( reduction, true );
1494         return new ClassExpression( name );
1495     }
1496 
1497 
1498 
1499    /***
1500     *  Converts a (typically IDENTIFIER) CSTNode to a VariableExpression, if
1501     *  valid.
1502     */
1503 
1504    protected VariableExpression variableExpression( CSTNode reduction )
1505    {
1506        return new VariableExpression( reduction.getRootText(), null );
1507    }
1508 
1509     protected VariableExpression variableExpression( CSTNode reduction, String type )
1510     {
1511         return new VariableExpression( reduction.getRootText(), type );
1512     }
1513 
1514 
1515 
1516    /***
1517     *  Converts an (possibly optional) type expression to a Type.
1518     */
1519 
1520     protected Type typeExpression( CSTNode reduction )
1521     {
1522         String name = makeName( reduction, null );
1523         if( name == null )
1524         {
1525             return Type.DYNAMIC_TYPE;
1526         }
1527         else
1528         {
1529             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     protected Expression variableDeclarationExpression( CSTNode reduction ) throws ParserException
1541     {
1542         String type = resolveName( reduction.get(1) );
1543 
1544 
1545         //
1546         // TEMPORARY UNTIL GENERAL SUPPORT IN PLACE
1547 
1548         if( reduction.size() == 3 )
1549         {
1550             CSTNode node = reduction.get(2);
1551 
1552             VariableExpression name = variableExpression( node, type );
1553             //name.setType( type );
1554 
1555             Token symbol = Token.newSymbol( Types.EQUAL, -1, -1 );
1556 
1557             return new BinaryExpression( name, symbol, expression(node.get(1)) );
1558         }
1559 
1560 
1561         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     protected MethodCallExpression methodCallExpression( CSTNode reduction ) throws ParserException
1585     {
1586         MethodCallExpression call = null;
1587 
1588         //
1589         // Figure out the name and context of the method call.
1590 
1591         CSTNode descriptor = reduction.get(1);
1592         Expression context = null;
1593         boolean   implicit = false;
1594         String      method = "call";
1595         boolean       safe = false;
1596 
1597         int type = descriptor.getMeaning();
1598         switch( type )
1599         {
1600             case Types.KEYWORD_SUPER:
1601             {
1602                 context  = variableExpression( descriptor );
1603                 method   = identifier( descriptor );
1604                 break;
1605             }
1606 
1607             case Types.KEYWORD_THIS:
1608             {
1609                 context  = VariableExpression.THIS_EXPRESSION;
1610                 method   = identifier( descriptor );
1611                 break;
1612             }
1613 
1614             case Types.IDENTIFIER:
1615             {
1616                 context  = VariableExpression.THIS_EXPRESSION;
1617                 method   = identifier( descriptor );
1618                 implicit = true;
1619                 break;
1620             }
1621 
1622             case Types.DOT:
1623             case Types.NAVIGATE:
1624             {
1625                 context = expression( descriptor.get(1) );
1626                 method  = identifier( descriptor.get(2) );
1627                 safe    = type == Types.NAVIGATE;
1628                 break;
1629             }
1630 
1631             default:
1632             {
1633                 context = expression( descriptor );
1634                 break;
1635             }
1636         }
1637 
1638 
1639         //
1640         // And build the expression
1641 
1642         Expression parameters = parameterList( reduction.get(2) );
1643 
1644         // System.out.println( "method call expression: " + context + ", " + method + ", " + parameters + ", " + implicit );
1645 
1646         call = new MethodCallExpression( context, method, parameters );
1647         call.setImplicitThis( implicit );
1648         call.setSafe( safe );
1649 
1650         return call;
1651     }
1652 
1653 
1654 
1655    /***
1656     *  Processes the Reduction produced by Parser.closureExpression().
1657     */
1658 
1659     protected ClosureExpression closureExpression( CSTNode reduction ) throws ParserException
1660     {
1661         ClosureExpression expression = null;
1662 
1663         Parameter[] parameters = parameterDeclarations( reduction.get(1) );
1664         expression = new ClosureExpression( parameters, statementBlock(reduction.get(2)) );
1665 
1666         return expression;
1667     }
1668 
1669 
1670 
1671    /***
1672     *  Processes the Reduction produced by Parser.parameterList().
1673     */
1674 
1675     protected Expression parameterList( CSTNode reduction ) throws ParserException
1676     {
1677         TupleExpression list = new TupleExpression();
1678 
1679         for( int i = 1; i < reduction.size(); i++ )
1680         {
1681             CSTNode node = reduction.get(i);
1682             list.addExpression( expression(node) );
1683         }
1684 
1685         return list;
1686     }
1687 
1688 
1689 
1690    /***
1691     *  Processes the Reduction produced by Parser.newExpression().
1692     */
1693 
1694     protected Expression newExpression( CSTNode reduction ) throws ParserException
1695     {
1696         Expression expression = null;
1697         CSTNode      typeNode = reduction.get(1);
1698         String           type = resolveName( typeNode );
1699 
1700 
1701         //
1702         // Array types have dimension and initialization data to handle.
1703 
1704         if( typeNode.getMeaning() == Types.LEFT_SQUARE_BRACKET )
1705         {
1706             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             if( typeNode.get(1).getMeaning() == Types.LEFT_SQUARE_BRACKET )
1716             {
1717                 throw new GroovyBugError( "NOT YET IMPLEMENTED: multidimensional arrays" );
1718             }
1719             else
1720             {
1721                 type = resolveName( typeNode.get(1) );
1722             }
1723 
1724 
1725             //
1726             // If there are no dimensions, process a tuple initializer
1727 
1728             if( dimensions.isEmpty() )
1729             {
1730                 CSTNode data = reduction.get(3);
1731 
1732                 if( data.get(1, true).getMeaning() == Types.SYNTH_TUPLE )
1733                 {
1734                     throw new GroovyBugError( "NOT YET IMPLEMENTED: multidimensional arrays" );
1735                 }
1736 
1737                 expression = new ArrayExpression( type, tupleExpression(data).getExpressions() );
1738             }
1739 
1740 
1741             //
1742             // Otherwise, process the dimensions
1743 
1744             else
1745             {
1746                 if( dimensions.size() > 2 )
1747                 {
1748                     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                     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             Expression parameters = parameterList( reduction.get(2) );
1770 
1771             if( reduction.size() > 3 )
1772             {
1773                 throw new GroovyBugError( "NOT YET IMPLEMENTED: anonymous classes" );
1774             }
1775 
1776             expression = new ConstructorCallExpression( type, parameters );
1777         }
1778 
1779         return expression;
1780     }
1781 
1782 
1783 
1784    /***
1785     *  Processes the Reduction produced by Parser.newArrayInitializer().
1786     */
1787 
1788     protected TupleExpression tupleExpression( CSTNode reduction ) throws ParserException
1789     {
1790         TupleExpression tuple = new TupleExpression();
1791 
1792         for( int i = 1; i < reduction.size(); i++ )
1793         {
1794             CSTNode element = reduction.get(i);
1795 
1796             if( element.getMeaning() == Types.SYNTH_TUPLE )
1797             {
1798                 tuple.addExpression( tupleExpression(element) );
1799             }
1800             else
1801             {
1802                 tuple.addExpression( expression(element) );
1803             }
1804         }
1805 
1806         return tuple;
1807     }
1808 
1809 
1810 
1811    /***
1812     *  Processes the Reduction produced by Parser.gstring().
1813     */
1814 
1815     protected Expression gstringExpression( CSTNode reduction ) throws ParserException
1816     {
1817         if( !reduction.hasChildren() )
1818         {
1819             return new ConstantExpression( "" );
1820         }
1821 
1822         if( reduction.children() == 1 && reduction.get(1).getMeaning() == Types.STRING )
1823         {
1824             return expression( reduction.get(1) );
1825         }
1826 
1827 
1828         GStringExpression expression = new GStringExpression( reduction.getRootText() );
1829         boolean lastWasExpression = false;
1830 
1831         for( int i = 1; i < reduction.size(); i++ )
1832         {
1833             CSTNode element = reduction.get(i);
1834             if( element.getMeaning() == Types.STRING )
1835             {
1836                 ConstantExpression string = new ConstantExpression( element.getRootText() );
1837                 string.setCSTNode( element );
1838 
1839                 expression.addString( string );
1840 
1841                 lastWasExpression = false;
1842             }
1843             else
1844             {
1845                 if( lastWasExpression )
1846                 {
1847                     expression.addString( new ConstantExpression("") );
1848                 }
1849 
1850                 lastWasExpression = true;
1851                 expression.addValue( element.isEmpty() ? ConstantExpression.NULL : expression(element) );
1852             }
1853         }
1854 
1855         return expression;
1856     }
1857 
1858 
1859 
1860    /***
1861     *  Processes one of the Reductions produced by Parser.listOrMapExpression().
1862     */
1863 
1864     protected ListExpression listExpression( CSTNode reduction ) throws ParserException
1865     {
1866         ListExpression list = new ListExpression();
1867 
1868         for( int i = 1; i < reduction.size(); i++ )
1869         {
1870             list.addExpression( expression(reduction.get(i)) );
1871         }
1872 
1873         return list;
1874     }
1875 
1876 
1877 
1878    /***
1879     *  Processes the other Reduction produced by Parser.listOrMapExpression().
1880     */
1881 
1882     protected MapExpression mapExpression( CSTNode reduction ) throws ParserException
1883     {
1884         MapExpression map = new MapExpression();
1885 
1886         for( int i = 1; i < reduction.size(); i++ )
1887         {
1888             CSTNode  element = reduction.get(i);
1889             Expression   key = expression( element.get(1) );
1890             Expression value = expression( element.get(2) );
1891 
1892             map.addMapEntryExpression( key, value );
1893         }
1894 
1895         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     protected String makeName( CSTNode root, String defaultName )
1915     {
1916         if( root == null )
1917         {
1918             return defaultName;
1919         }
1920 
1921         String name = "";
1922         switch( root.getMeaning() )
1923         {
1924             case Types.LEFT_SQUARE_BRACKET:
1925             {
1926                 name = makeName( root.get(1) ) + "[]";
1927                 break;
1928             }
1929 
1930             case Types.DOT:
1931             {
1932                 CSTNode node = root;
1933                 while( node.isA(Types.DOT) )
1934                 {
1935                     name = "." + node.get(2).getRootText() + name;
1936                     node = node.get(1);
1937                 }
1938 
1939                 name = node.getRootText() + name;
1940                 break;
1941             }
1942 
1943             case Types.UNKNOWN:
1944             {
1945                 name = defaultName;
1946                 break;
1947             }
1948 
1949             default:
1950             {
1951                 name = root.getRootText();
1952                 break;
1953             }
1954 
1955         }
1956 
1957         return name;
1958     }
1959 
1960 
1961 
1962    /***
1963     *  A synonym for <code>makeName( root, "java.lang.Object" )</code>.
1964     */
1965 
1966     protected String makeName( CSTNode root )
1967     {
1968         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     protected String identifier( CSTNode identifier )
1978     {
1979         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     protected String resolveName( String name, boolean safe )
1990     {
1991         //
1992         // Use our cache of resolutions, if possible
1993 
1994         String resolution = (String)resolutions.get( name );
1995         if( resolution == NOT_RESOLVED )
1996         {
1997             return (safe ? name : null);
1998         }
1999         else if( resolution != null )
2000         {
2001             return (String)resolution;
2002         }
2003 
2004 
2005         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             if( name.indexOf(".") >= 0 )
2012             {
2013                 resolution = name;
2014                 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             String scalar = name, postfix = "";
2023             while( scalar.endsWith("[]") )
2024             {
2025                 scalar = scalar.substring( 0, scalar.length() - 2 );
2026                 postfix += "[]";
2027             }
2028 
2029 
2030             //
2031             // Primitive types are all valid...
2032 
2033             if( Types.ofType(Types.lookupKeyword(scalar), Types.PRIMITIVE_TYPE) )
2034             {
2035                 resolution = name;
2036                 break;                                            // <<< FLOW CONTROL <<<<<<<<<
2037             }
2038 
2039 
2040             //
2041             // Next, check our imports and return the qualified name,
2042             // if available.
2043 
2044             if( this.imports.containsKey(scalar) )
2045             {
2046                 resolution = ((String)this.imports.get(scalar)) + postfix;
2047                 break;                                            // <<< FLOW CONTROL <<<<<<<<<
2048             }
2049 
2050 
2051             //
2052             // Next, see if our class loader can resolve it in the current package.
2053 
2054             if( packageName != null && packageName.length() > 0 )
2055             {
2056                 try
2057                 {
2058                     getClassLoader().loadClass( dot(packageName, scalar) );
2059                     resolution = dot(packageName, name);
2060 
2061                     break;                                        // <<< FLOW CONTROL <<<<<<<<<
2062                 }
2063                 catch( Throwable e )
2064                 {
2065                     /* ignore */
2066                 }
2067             }
2068 
2069             // search the package imports path
2070             List packageImports = output.getImportPackages();
2071             for (int i = 0; i < packageImports.size(); i++) {
2072                 String pack = (String) packageImports.get(i);
2073                 String clsName = pack + name;
2074                 try {
2075                     getClassLoader().loadClass( clsName );
2076                     resolution  = clsName;
2077                     break;
2078                 } catch (Throwable e) {
2079                     //
2080                 }
2081             }
2082             if (resolution != null)
2083                 break;
2084 
2085             //
2086             // Last chance, check the default imports.
2087 
2088             for( int i = 0; i < DEFAULT_IMPORTS.length; i++ )
2089             {
2090                 try
2091                 {
2092                     String qualified = DEFAULT_IMPORTS[i] + scalar;
2093                     getClassLoader().loadClass( qualified );
2094 
2095                     resolution = qualified + postfix;
2096                     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         if( resolution == null )
2111         {
2112             resolutions.put( name, NOT_RESOLVED );
2113             return (safe ? name : null);
2114         }
2115         else
2116         {
2117             resolutions.put( name, resolution );
2118             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     protected String resolveName( CSTNode root, boolean safe )
2134     {
2135         String name = makeName( root );
2136         if (name.length() == 0)
2137             return "";
2138         if (this.newClasses.contains(name)) {
2139             return dot(packageName, name);
2140         } else {
2141             return resolveName( name, safe );
2142         }
2143     }
2144 
2145 
2146 
2147    /***
2148     *  A synonym for <code>resolveName( root, true )</code>.
2149     */
2150 
2151     protected String resolveName( CSTNode root )
2152     {
2153         return resolveName( root, true );
2154     }
2155 
2156 
2157 
2158    /***
2159     *  Returns true if the specified name is a known type name.
2160     */
2161 
2162     protected boolean isDatatype( String name )
2163     {
2164         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     protected String dot( String base, String name )
2175     {
2176         if( base != null && base.length() > 0 )
2177         {
2178             return base + "." + name;
2179         }
2180 
2181         return name;
2182     }
2183 
2184 
2185 
2186    /***
2187     *  A synonym for <code>dot( base, "" )</code>.
2188     */
2189 
2190     protected String dot( String base )
2191     {
2192         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     protected int modifiers( CSTNode list )
2207     {
2208         int modifiers = 0;
2209 
2210         for( int i = 1; i < list.size(); ++i )
2211         {
2212             SWITCH: switch( list.get(i).getMeaning() )
2213             {
2214                 case Types.KEYWORD_PUBLIC:
2215                 {
2216                     modifiers |= Constants.ACC_PUBLIC;
2217                     break SWITCH;
2218                 }
2219 
2220                 case Types.KEYWORD_PROTECTED:
2221                 {
2222                     modifiers |= Constants.ACC_PROTECTED;
2223                     break SWITCH;
2224                 }
2225 
2226                 case Types.KEYWORD_PRIVATE:
2227                 {
2228                     modifiers |= Constants.ACC_PRIVATE;
2229                     break SWITCH;
2230                 }
2231 
2232 
2233                 case Types.KEYWORD_ABSTRACT:
2234                 {
2235                     modifiers |= Constants.ACC_ABSTRACT;
2236                     break SWITCH;
2237                 }
2238 
2239                 case Types.KEYWORD_FINAL:
2240                 {
2241                     modifiers |= Constants.ACC_FINAL;
2242                     break SWITCH;
2243                 }
2244 
2245                 case Types.KEYWORD_NATIVE:
2246                 {
2247                     modifiers |= Constants.ACC_NATIVE;
2248                     break SWITCH;
2249                 }
2250 
2251                 case Types.KEYWORD_TRANSIENT:
2252                 {
2253                     modifiers |= Constants.ACC_TRANSIENT;
2254                     break SWITCH;
2255                 }
2256 
2257                 case Types.KEYWORD_VOLATILE:
2258                 {
2259                     modifiers |= Constants.ACC_VOLATILE;
2260                     break SWITCH;
2261                 }
2262 
2263 
2264                 case Types.KEYWORD_SYNCHRONIZED:
2265                 {
2266                     modifiers |= Constants.ACC_SYNCHRONIZED;
2267                     break SWITCH;
2268                 }
2269                 case Types.KEYWORD_STATIC:
2270                 {
2271                     modifiers |= Constants.ACC_STATIC;
2272                     break SWITCH;
2273                 }
2274 
2275             }
2276         }
2277 
2278 
2279         //
2280         // If not protected or private we default to public.
2281 
2282         if( (modifiers & (Constants.ACC_PROTECTED | Constants.ACC_PRIVATE)) == 0 )
2283         {
2284             modifiers |= Constants.ACC_PUBLIC;
2285         }
2286 
2287         return modifiers;
2288     }
2289 
2290 
2291 
2292 
2293   //---------------------------------------------------------------------------
2294   // ERROR HANDLING
2295 
2296 
2297    /***
2298     *  Throws a <code>ParserException</code>.
2299     */
2300 
2301     protected void error( String description, CSTNode node ) throws ParserException
2302     {
2303         throw new ParserException( description, node.getRoot() );
2304     }
2305 
2306 
2307 }