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     private</str<a href="../../../../../ong> String      packageName/package-summary.html">ong> 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     protected</str<a href="../../../../../ong> String packageDeclaration( CSTNode reduction )/package-summary.html">ong> 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 }