View Javadoc

    1   /*
    2    $Id: Parser.java,v 1.85 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 org.codehaus.groovy.GroovyBugError;
    49  import org.codehaus.groovy.control.CompilationFailedException;
    50  import org.codehaus.groovy.control.SourceUnit;
    51  import org.codehaus.groovy.control.messages.SimpleMessage;
    52  import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
    53  import org.codehaus.groovy.syntax.CSTNode;
    54  import org.codehaus.groovy.syntax.ReadException;
    55  import org.codehaus.groovy.syntax.Reduction;
    56  import org.codehaus.groovy.syntax.SyntaxException;
    57  import org.codehaus.groovy.syntax.Token;
    58  import org.codehaus.groovy.syntax.TokenStream;
    59  import org.codehaus.groovy.syntax.Types;
    60
    61  /***
    62   *  Reads the source text and produces a Concrete Syntax Tree.  Exceptions
    63   *  are collected during processing, and parsing will continue for while
    64   *  possible, in order to report as many problems as possible.
    65   *  <code>module()</code> is the primary entry point.
    66   *
    67   *  @author Bob McWhirter
    68   *  @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    69   *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
    70    */
    71
    72  public class Parser
    73  {
    74      private SourceUnit  controller  = null;  // The controller to which we report errors
    75      private TokenStream tokenStream = null;  // Our token source
    76      private int         nestCount   = 1;     // Simplifies tracing of nested calls
    77
    78
    79
    80    //---------------------------------------------------------------------------
    81    // CONSTRUCTION AND DATA ACCESS
    82
    83     /***
    84      *  Sets the <code>Parser</code> to process a <code>TokenStream</code>,
    85      *  under control of the specified <code>SourceUnit</code>.
    86      */
    87
    88      public Parser( SourceUnit controller, TokenStream tokenStream )
    89      {
    90          this.controller  = controller;
    91          this.tokenStream = tokenStream;
    92      }
    93
    94
    95     /***
    96      *  Synonym for module(), the primary entry point.
    97      */
    98
    99      public Reduction parse() throws CompilationFailedException
    100     {
    101         try
    102         {
    103             return module();
    104         }
    105         catch( SyntaxException e )
    106         {
    107             controller.addFatalError( new SyntaxErrorMessage(e) );
    108         }
    109
    110         throw new GroovyBugError( "this will never happen" );
    111     }
    112
    113
    114
    115    /***
    116     *  Returns the <code>TokenStream</code> being parsed.
    117     */
    118
    119     public TokenStream getTokenStream()
    120     {
    121         return this.tokenStream;
    122     }
    123
    124
    125
    126
    127   //---------------------------------------------------------------------------
    128   // PRODUCTION SUPPORT
    129
    130
    131    /***
    132     *  Eats any optional newlines.
    133     */
    134
    135     public void optionalNewlines() throws SyntaxException, CompilationFailedException
    136     {
    137         while( lt(false) == Types.NEWLINE)
    138         {
    139             consume( Types.NEWLINE );
    140         }
    141     }
    142
    143
    144
    145    /***
    146     *  Eats a required end-of-statement (semicolon or newline) from the stream.
    147     *  Throws an <code>UnexpectedTokenException</code> if anything else is found.
    148     */
    149
    150     public void endOfStatement( boolean allowRightCurlyBrace ) throws SyntaxException, CompilationFailedException
    151     {
    152         Token next = la( true );
    153
    154         if( next.isA(Types.GENERAL_END_OF_STATEMENT) )
    155         {
    156             consume( true );
    157         }
    158         else
    159         {
    160             if( allowRightCurlyBrace )
    161             {
    162                 if( !next.isA(Types.RIGHT_CURLY_BRACE) )
    163                 {
    164                     error( new int[] { Types.SEMICOLON, Types.NEWLINE, Types.RIGHT_CURLY_BRACE } );
    165                 }
    166             }
    167             else
    168             {
    169                 error( new int[] { Types.SEMICOLON, Types.NEWLINE } );
    170             }
    171         }
    172     }
    173
    174
    175
    176    /***
    177     *  A synonym for <code>endOfStatement( true )</code>.
    178     */
    179
    180     public void endOfStatement() throws SyntaxException, CompilationFailedException
    181     {
    182         endOfStatement( true );
    183     }
    184
    185
    186
    187
    188   //---------------------------------------------------------------------------
    189   // PRODUCTIONS: PRIMARY STRUCTURES
    190
    191
    192    /***
    193     *  Processes a dotted identifer.  Used all over the place.
    194     *  <p>
    195     *  Grammar: <pre>
    196     *     dottedIdentifier = <identifier> ("." <identifier>)*
    197     *  </pre>
    198     *  <p>
    199     *  CST: <pre>
    200     *     dotted = { "." dotted <identifier> } | <identifier>
    201     *  </pre>
    202     */
    203
    204     public CSTNode dottedIdentifier() throws SyntaxException, CompilationFailedException
    205     {
    206         CSTNode identifier = consume(Types.IDENTIFIER);
    207
    208         while (lt() == Types.DOT)
    209         {
    210             identifier = consume(Types.DOT).asReduction( identifier, consume(Types.IDENTIFIER) );
    211         }
    212
    213         return identifier;
    214     }
    215
    216
    217
    218    /***
    219     *  The primary file-level parsing entry point.  The returned CST
    220     *  represents the content in a single class file.  Collects most
    221     *  exceptions and attempts to continue.
    222     *  <p>
    223     *  Grammar: <pre>
    224     *     module = [packageStatement]
    225     *              (usingStatement)*
    226     *              (topLevelStatement)*
    227     *              <eof>
    228     *  </pre>
    229     *  <p>
    230     *  CST: <pre>
    231     *     module = { <null> package imports (topLevelStatement)* }
    232     *
    233     *     package           see packageDeclaration()
    234     *     imports           see importStatement()
    235     *     topLevelStatement see topLevelStatement()
    236     *  </pre>
    237     *
    238     */
    239
    240     public Reduction module() throws SyntaxException, CompilationFailedException
    241     {
    242         Reduction module = Reduction.newContainer();
    243
    244         //
    245         // First up, the package declaration
    246
    247         // XXX br: this is where i can do macro processing
    248
    249         Reduction packageDeclaration = null/package-summary.html">Reduction packageDeclaration = null;
    250
    251         if( lt() == Types.KEYWORD_PACKAGE )
    252         {
    253             try
    254             {
    255                 packageDeclaration = packageDeclaration();
    256             }
    257
    258             catch (SyntaxException e)
    259             {
    260                 controller.addError(e);
    261                 recover();
    262             }
    263         }
    264
    265         if( packageDeclaration == null )/package-summary.html">strong>( packageDeclaration == null )
    266         {
    267             packageDeclaration = Reduction.EMPTY;
    268         }
    269
    270         module.add( packageDeclaration );
    271
    272
    273         //
    274         // Next, handle import statements
    275
    276         Reduction imports = (Reduction)module.add( Reduction.newContainer() );
    277         Object collector;
    278
    279         while( lt() == Types.KEYWORD_IMPORT )
    280         {
    281             try
    282             {
    283                 imports.add( importStatement() );
    284             }
    285
    286             catch( SyntaxException e )
    287             {
    288                 controller.addError(e);
    289                 recover();
    290             }
    291         }
    292
    293
    294         //
    295         // With that taken care of, process everything else.
    296
    297         while( lt() != Types.EOF )
    298         {
    299             try
    300             {
    301                 module.add( topLevelStatement() );
    302             }
    303             catch (SyntaxException e)
    304             {
    305                 controller.addError(e);
    306                 recover();
    307             }
    308         }
    309
    310         return module;
    311     }
    312
    313
    314
    315    /***
    316     *  Processes a package declaration.  Called by <code>module()</code>.
    317     *  <p>
    318     *  Grammar: <pre>
    319     *     packageDeclaration = "package" dottedIdentifier <eos>
    320     *  </pre>
    321     *  <p>
    322     *  CST: <pre>
    323     *     package = { "package" dottedIdentifier }
    324     *
    325     *     see dottedIdentifier()
    326     *  </pre>
    327     */
    328
    329     publicReduction packageDeclaration() throws SyntaxException, CompilationFailedException/package-summary.html">ong> Reduction packageDeclaration() throws SyntaxException, CompilationFailedException
    330     {
    331         Reduction packageDeclaration = consume(Types/KEYWORD_PACKAGE)/asReduction( dottedIdentifier() )/package-summary.html">Reduction packageDeclaration = consume(Types.KEYWORD_PACKAGE).asReduction( dottedIdentifier() );
    332         endOfStatement( false );
    333
    334         return</strong> packageDeclaration;
    335     }
    336
    337
    338
    339    /***
    340     *  Processes an import statement.  Called by <code>module()</code>.
    341     *  <p>
    342     *  Grammar: <pre>
    343     *     importStatement = "import" (all|specific) <eos>
    344     *
    345     *     all      = package "." (package ".")* "*"
    346     *
    347     *     specific = (package "." (package ".")*)? classes
    348     *     classes  = class ["as" alias] ("," class ["as" alias])*
    349     *
    350     *     package  = <identifier>
    351     *     class    = <identifier>
    352     *     alias    = <identifier>
    353     *  </pre>
    354     *  <p>
    355     *  CST: <pre>
    356     *     import   = { "import" (package|{}) ({"*"} | clause+) }
    357     *
    358     *     package  = { "." package <identifier> } | <identifier>
    359     *     clause   = { <identifier> <alias>? }
    360     *  </pre>
    361     */
    362
    363     public Reduction importStatement() throws SyntaxException, CompilationFailedException
    364     {
    365         Reduction importStatement = consume(Types.KEYWORD_IMPORT).asReduction();
    366
    367         //
    368         // First, process any package name.
    369
    370         CSTNode packageNode = null/package-summary.html">CSTNode packageNode = null;
    371         if( lt(2) == Types.DOT )
    372         {
    373             packageNode = consume(Types.IDENTIFIER).asReduction();
    374
    375             while( lt(3) == Types.DOT )
    376             {
    377                 packageNode = consume(Types.DOT).asReduction( packageNode );
    378                 packageNode.add( consume(Types.IDENTIFIER) );
    379             }
    380
    381             consume( Types.DOT );
    382         }
    383
    384         if( packageNode == null )/package-summary.html">strong>( packageNode == null )
    385         {
    386             packageNode = Reduction.EMPTY;
    387         }
    388
    389         importStatement.add( packageNode );
    390
    391
    392         //
    393         // Then process the class list.
    394
    395         if( !packageNode.isEmpty() && lt() == Types.STAR )
    396         {
    397             importStatement.add( consume(Types.STAR) );
    398         }
    399         else
    400         {
    401            boolean done = false;
    402            while( !done )
    403            {
    404                Reduction clause = consume(Types.IDENTIFIER).asReduction();
    405                if( lt() == Types.KEYWORD_AS )
    406                {
    407                    consume( Types.KEYWORD_AS );
    408                    clause.add( consume(Types.IDENTIFIER) );
    409                }
    410
    411                importStatement.add( clause );
    412
    413                if( lt() == Types.COMMA )
    414                {
    415                    consume( Types.COMMA );
    416                }
    417                else
    418                {
    419                    done = true;
    420                }
    421            }
    422
    423         }
    424
    425         //
    426         // End the statement and return.
    427
    428         endOfStatement( false );
    429         return importStatement;
    430     }
    431
    432
    433
    434    /***
    435     *  Processes a top level statement (classes, interfaces, unattached methods, and
    436     *  unattached code).  Called by <code>module()</code>.
    437     *  <p>
    438     *  Grammar: <pre>
    439     *     topLevelStatement
    440     *       = methodDeclaration
    441     *       | typeDeclaration
    442     *       | statement
    443     *
    444     *     typeDeclaration = classDeclaration | interfaceDeclaration
    445     *  </pre>
    446     *  <p>
    447     *  Recognition: <pre>
    448     *     "def"                    => methodDeclaration
    449     *     "synchronized" "("       => synchronizedStatement
    450     *     modifierList "class"     => classDeclaration
    451     *     modifierList "interface" => interfaceDeclaration
    452     *     modifierList             => <error>
    453     *     *                        => statement
    454     *  </pre>
    455     *  <p>
    456     *  CST: <pre>
    457     *     see methodDeclaration()
    458     *     see classDeclaration()
    459     *     see interfaceDeclaration()
    460     *     see statement()
    461     *     see synchronizedStatement()
    462     *  </pre>
    463     */
    464
    465     public CSTNode topLevelStatement() throws SyntaxException, CompilationFailedException
    466     {
    467         CSTNode result = null;
    468
    469         //
    470         // If it starts "def", it's a method declaration.  Methods
    471         // declared this way cannot be abstract.  Note that "def"
    472         // is required because the return type is not, and it would
    473         // be very hard to tell the difference between a function
    474         // def and a function invokation with closure...
    475
    476         if (lt() == Types.KEYWORD_DEF)
    477         {
    478             consume();
    479
    480             Reduction modifiers  = modifierList( false, false );
    481             CSTNode   type       = optionalDatatype( false, true );
    482             Token     identifier = nameDeclaration( false );
    483
    484             result = methodDeclaration(modifiers, type, identifier, false);
    485         }
    486
    487         else if (lt() == Types.KEYWORD_DEFMACRO)
    488         {
    489         	// XXX add my logic here
    490         }
    491
    492         //
    493         // If it starts "synchronized(", it's a statement.  This check
    494         // is necessary because "synchronized" is also a class modifier.
    495
    496         else if( lt() == Types.KEYWORD_SYNCHRONIZED && lt(2) == Types.LEFT_PARENTHESIS )
    497         {
    498             result = synchronizedStatement();
    499         }
    500
    501         //
    502         // If it starts with a modifier, "class", or "interface",
    503         // it's a type declaration.
    504
    505         else if( la().isA(Types.DECLARATION_MODIFIER) || la().isA(Types.TYPE_DECLARATION) )
    506         {
    507             Reduction modifiers = modifierList( true, true );
    508
    509             switch( lt() )
    510             {
    511                 case Types.KEYWORD_CLASS:
    512                 {
    513                     result = classDeclaration( modifiers );
    514                     break;
    515                 }
    516
    517                 case Types.KEYWORD_INTERFACE:
    518                 {
    519                     result = interfaceDeclaration( modifiers );
    520                     break;
    521                 }
    522
    523                 default:
    524                 {
    525                     error( new int[] { Types.KEYWORD_CLASS, Types.KEYWORD_INTERFACE } );
    526                     break;
    527                 }
    528             }
    529         }
    530
    531         //
    532         // Otherwise, it's a statement.
    533
    534         else
    535         {
    536             result = statement();
    537         }
    538
    539         return result;
    540     }
    541
    542
    543
    544    /***
    545     *  A synomym for <code>topLevelStatement()</code>.
    546     */
    547
    548     public CSTNode typeDeclaration() throws SyntaxException, CompilationFailedException
    549     {
    550         return topLevelStatement();
    551     }
    552
    553
    554
    555    /***
    556     *  Processes the modifiers list that can appear on top- and class-level
    557     *  method and class-level variable names (public, private, abstract, etc.).
    558     *  <p>
    559     *  Grammar: <pre>
    560     *     modifierList = <modifier>*
    561     *  </pre>
    562     *  <p>
    563     *  CST: <pre>
    564     *     modifiers = { <null> <modifier>* }
    565     *  </pre>
    566     */
    567
    568     public Reduction modifierList(boolean allowStatic, boolean allowAbstract) throws CompilationFailedException, SyntaxException
    569     {
    570         Reduction modifiers = Reduction.newContainer();
    571
    572         while( la().isA(Types.DECLARATION_MODIFIER) )
    573         {
    574             if( lt() == Types.KEYWORD_ABSTRACT && !allowAbstract)
    575             {
    576                 controller.addError( "keyword 'abstract' not valid in this setting", la() );
    577             }
    578             else if (lt() == Types.KEYWORD_STATIC && !allowStatic)
    579             {
    580                 controller.addError( "keyword 'static' not valid in this setting", la() );
    581             }
    582             modifiers.add( consume() );
    583         }
    584
    585         return modifiers;
    586     }
    587
    588
    589
    590    /***
    591     *  Processes a class declaration.  Caller has already processed the declaration
    592     *  modifiers, and passes them in.
    593     *  <p>
    594     *  Grammar: <pre>
    595     *     classDeclaration = <modifier>* "class" <identifier>
    596     *                        ["extends" datatype]
    597     *                        ["implements" datatype (, datatype)*]
    598     *                        typeBody
    599     *  </pre>
    600     *  <p>
    601     *  CST: <pre>
    602     *     class      = { <identifier>:SYNTH_CLASS modifiers extends implements body }
    603     *     extends    = { "extends"    datatype  } | {}
    604     *     implements = { "implements" datatype* } | {}
    605     *
    606     *     modifiers see modifierList()
    607     *     datatype  see datatype()
    608     *     body      see typeBody()
    609     *  </pre>
    610     */
    611
    612     public Reduction classDeclaration( Reduction modifiers ) throws SyntaxException, CompilationFailedException
    613     {
    614         consume( Types.KEYWORD_CLASS );
    615
    616         Reduction classDeclaration = consume(Types.IDENTIFIER).asReduction( modifiers );
    617         classDeclaration.setMeaning( Types.SYNTH_CLASS );
    618
    619
    620         //
    621         // Process any extends clause.
    622
    623         try
    624         {
    625             classDeclaration.add( typeList(Types.KEYWORD_EXTENDS, true, 1) );
    626         }
    627         catch (SyntaxException e)
    628         {
    629             controller.addError(e);
    630             classDeclaration.add( Reduction.EMPTY );
    631         }
    632
    633
    634         //
    635         // Process any implements clause.
    636
    637         try
    638         {
    639             classDeclaration.add( typeList(Types.KEYWORD_IMPLEMENTS, true, 0) );
    640         }
    641         catch (SyntaxException e)
    642         {
    643             controller.addError(e);
    644             classDeclaration.add( Reduction.EMPTY );
    645         }
    646
    647
    648         //
    649         // Process the declaration body.  We currently ignore the abstract keyword.
    650
    651         classDeclaration.add( typeBody(true, true, false) );
    652
    653         return classDeclaration;
    654     }
    655
    656
    657
    658    /***
    659     *  Processes a interface declaration.  Caller has already processed the
    660     *  declaration modifiers, and passes them in.
    661     *  <p>
    662     *  Grammar: <pre>
    663     *     interfaceDeclaration = <modifier>* "interface" <identifier>
    664     *                            ["extends" typeList]
    665     *                            typeBody
    666     *  </pre>
    667     *  <p>
    668     *  CST: <pre>
    669     *     interface  = { <identifier>:SYNTH_INTERFACE modifiers {} extends body }
    670     *     extends    = { "extends" datatype* } | {}
    671     *
    672     *     modifiers see modifierList()
    673     *     datatype  see datatype()
    674     *     body      see typeBody()
    675     *  </pre>
    676     */
    677
    678     public Reduction interfaceDeclaration( Reduction modifiers ) throws SyntaxException, CompilationFailedException
    679     {
    680         consume( Types.KEYWORD_INTERFACE );
    681
    682         Reduction interfaceDeclaration = consume(Types.IDENTIFIER).asReduction( modifiers, Reduction.EMPTY );
    683         interfaceDeclaration.setMeaning( Types.SYNTH_INTERFACE );
    684
    685
    686         //
    687         // Process any extends clause.
    688
    689         try
    690         {
    691             interfaceDeclaration.add( typeList(Types.KEYWORD_EXTENDS, true, 0) );
    692         }
    693         catch (SyntaxException e)
    694         {
    695             controller.addError(e);
    696             interfaceDeclaration.add( Reduction.EMPTY );
    697         }
    698
    699
    700         //
    701         // Process the declaration body.  All methods must be abstract.
    702         // Static methods are not allowed.
    703
    704         interfaceDeclaration.add( typeBody(false, true, true) );
    705         return interfaceDeclaration;
    706     }
    707
    708
    709
    710    /***
    711     *  Processes a type list, like the ones that occur after "extends" or
    712     *  implements.  If the list is optional, the returned CSTNode will
    713     *  be empty.
    714     *  <p>
    715     *  Grammar: <pre>
    716     *     typeList = <declarator> datatype (, datatype)*
    717     *  </pre>
    718     *  <p>
    719     *  CST: <pre>
    720     *     typeList = { <declarator> datatype+ } | {}
    721     *
    722     *     datatype see datatype()
    723     *  </pre>
    724     */
    725
    726     public Reduction typeList(int declarator, boolean optional, int limit) throws SyntaxException, CompilationFailedException
    727     {
    728         Reduction typeList = null;
    729
    730         if( lt() == declarator )
    731         {
    732             typeList = consume(declarator).asReduction();
    733
    734             //
    735             // Loop, reading one datatype at a time.  On error, attempt
    736             // recovery until the end of the clause is found.
    737
    738             while( limit == 0 || typeList.children() < limit )
    739             {
    740                 //
    741                 // Try for a list entry, and correct if missing
    742
    743                 try
    744                 {
    745                     if( typeList.children() > 0)
    746                     {
    747                         consume( Types.COMMA );
    748                     }
    749
    750                     typeList.add( datatype(false) );
    751                 }
    752                 catch (SyntaxException e)
    753                 {
    754                     controller.addError(e);
    755                     recover( Types.TYPE_LIST_TERMINATORS );
    756                 }
    757
    758                 //
    759                 // Check if we have reached the end point.  It is
    760                 // done at the bottom of the loop to ensure that there
    761                 // is at least one datatype in the list
    762
    763                 if( !la().isA(Types.COMMA) )
    764                 {
    765                     break;
    766                 }
    767             }
    768         }
    769
    770         else
    771         {
    772             if (optional)
    773             {
    774                 typeList = Reduction.EMPTY;
    775             }
    776             else
    777             {
    778                 error( declarator );
    779             }
    780         }
    781
    782         return typeList;
    783     }
    784
    785
    786
    787    /***
    788     *  Processes the body of an interface or class.
    789     *  <p>
    790     *  Grammar: <pre>
    791     *     typeBody = "{" typeBodyStatement* "}"
    792     *  </pre>
    793     *  <p>
    794     *  CST: <pre>
    795     *     body = { <null> typeBodyStatement* }
    796     *
    797     *     typeBodyStatement see typeBodyStatement()
    798     *  </pre>
    799     */
    800
    801     public Reduction typeBody(boolean allowStatic, boolean allowAbstract, boolean requireAbstract) throws SyntaxException, CompilationFailedException
    802     {
    803         Reduction body = Reduction.newContainer();
    804
    805         consume( Types.LEFT_CURLY_BRACE );
    806
    807         while( lt() != Types.EOF && lt() != Types.RIGHT_CURLY_BRACE )
    808         {
    809             try
    810             {
    811                 body.add( typeBodyStatement(allowStatic, allowAbstract, requireAbstract) );
    812             }
    813             catch( SyntaxException e )
    814             {
    815                 controller.addError(e);
    816                 recover();
    817             }
    818         }
    819
    820         consume( Types.RIGHT_CURLY_BRACE );
    821
    822         return body;
    823     }
    824
    825
    826
    827    /***
    828     *  Processes a single entry in the the body of an interface or class.
    829     *  Valid objects are constructors, methods, properties, static initializers,
    830     *  and inner classes or interfaces.
    831     *  <p>
    832     *  Grammar: <pre>
    833     *     typeBodyStatement
    834     *       = staticInitializer
    835     *       | classDeclaration
    836     *       | interfaceDeclaration
    837     *       | propertyDeclaration
    838     *       | methodDeclaration
    839     *
    840     *     staticInitializer = ("static" "{" statement* "}")
    841     *  </pre>
    842     *  <p>
    843     *  Recognition: <pre>
    844     *     "static" "{"             => staticInitializer
    845     *     modifierList "class"     => classDeclaration
    846     *     modifierList "interface" => interfaceDeclaration
    847     *     modifierList ["property"] optionalDatatype identifier "("               => methodDeclaration
    848     *     modifierList ["property"] optionalDatatype identifier ("="|";"|"\n"|"}" => propertyDeclaration
    849     *     *                        => <error>
    850     *  </pre>
    851     *  <p>
    852     *  CST: <pre>
    853     *     see classDeclaration()
    854     *     see interfaceDeclaration()
    855     *     see methodDeclaration()
    856     *     see propertyDeclaration()
    857     *  </pre>
    858     */
    859
    860     public Reduction typeBodyStatement(boolean allowStatic, boolean allowAbstract, boolean requireAbstract) throws SyntaxException, CompilationFailedException
    861     {
    862         Reduction statement = null;
    863
    864         //
    865         // As "static" can be both a modifier and a static initializer, we
    866         // handle the static initializer first.
    867
    868         if( lt() == Types.KEYWORD_STATIC && lt(2) == Types.LEFT_CURLY_BRACE )
    869         {
    870             if (!allowStatic)
    871             {
    872                 controller.addError( "static initializers not valid in this context", la() );
    873             }
    874
    875             Reduction modifiers  = modifierList( true, false );
    876             Token     identifier = Token.NULL;
    877             statement = methodDeclaration( modifiers, Reduction.EMPTY, identifier, false );
    878         }
    879
    880         //
    881         // Otherwise, it is a property, constructor, method, class, or interface.
    882
    883         else
    884         {
    885             Reduction modifiers = modifierList( allowStatic, allowAbstract );
    886
    887             //
    888             // Check for inner types
    889
    890             if( lt() == Types.KEYWORD_CLASS )
    891             {
    892                 statement = classDeclaration( modifiers );
    893             }
    894
    895             else if( lt() == Types.KEYWORD_INTERFACE )
    896             {
    897                 statement = interfaceDeclaration( modifiers );
    898             }
    899
    900             //
    901             // Otherwise, it is a property, constructor, or method.
    902
    903             else
    904             {
    905                 //
    906                 // Ignore any property keyword, if present (it's deprecated)
    907
    908                 if( lt() == Types.KEYWORD_PROPERTY )
    909                 {
    910                     consume();
    911                 }
    912
    913                 //
    914                 // All processing here is whitespace sensitive, in order
    915                 // to be consistent with the way "def" functions work (due
    916                 // to the optionality of the semicolon).  One of the
    917                 // consequences is that the left parenthesis of a
    918                 // method declaration /must/ appear on the same line.
    919
    920                 while( lt(true) == Types.NEWLINE)
    921                 {
    922                     consume( Types.NEWLINE );
    923                 }
    924
    925                 //
    926                 // We don't yet know about void, so we err on the side of caution
    927
    928                 CSTNode   type       = optionalDatatype( true, true );
    929                 Token     identifier = nameDeclaration( true );
    930
    931                 switch( lt(true) )
    932                 {
    933                     case Types.LEFT_PARENTHESIS :
    934                     {
    935                         //
    936                         // We require abstract if specified on call or the
    937                         // "abstract" modifier was supplied.
    938
    939                         boolean methodIsAbstract = requireAbstract;
    940
    941                         if( !methodIsAbstract )
    942                         {
    943                             for( int i = 1; i < modifiers.size(); i++ )
    944                             {
    945                                 if( modifiers.get(i).getMeaning() == Types.KEYWORD_ABSTRACT )
    946                                 {
    947                                     methodIsAbstract = true;
    948                                     break;
    949                                 }
    950                             }
    951                         }
    952
    953                         statement = methodDeclaration( modifiers, type, identifier, methodIsAbstract );
    954                         break;
    955                     }
    956
    957                     case Types.EQUAL:
    958                     case Types.SEMICOLON:
    959                     case Types.NEWLINE:
    960                     case Types.RIGHT_CURLY_BRACE:
    961                     case Types.EOF:
    962                         statement = propertyDeclaration( modifiers, type, identifier );
    963                         break;
    964
    965                     default:
    966                         error( new int[] { Types.LEFT_PARENTHESIS, Types.EQUAL, Types.SEMICOLON, Types.NEWLINE, Types.RIGHT_CURLY_BRACE } );
    967                 }
    968             }
    969         }
    970
    971         return statement;
    972     }
    973
    974
    975
    976    /***
    977     *  A synonym for <code>typeBodyStatement( true, true, false )</code>.
    978     */
    979
    980     public Reduction bodyStatement() throws SyntaxException, CompilationFailedException
    981     {
    982         return typeBodyStatement( true, true, false );
    983     }
    984
    985
    986
    987    /***
    988     *  Processes a name that is valid for declarations.  Newlines can be made
    989     *  significant, if required for disambiguation.
    990     *  <p>
    991     *  Grammar: <pre>
    992     *     nameDeclaration = <identifier>
    993     *  </pre>
    994     *  <p>
    995     *  CST: <pre>
    996     *     name = <identifier>
    997     *  </pre>
    998     */
    999
    1000     protected Token nameDeclaration( boolean significantNewlines ) throws SyntaxException, CompilationFailedException
    1001     {
    1002         return consume( Types.IDENTIFIER, significantNewlines );
    1003     }
    1004
    1005
    1006
    1007    /***
    1008     *  Processes a reference to a declared name.  Newlines can be made significant,
    1009     *  if required for disambiguation.
    1010     *  <p>
    1011     *  Grammar: <pre>
    1012     *     nameReference = <identifier> | <various keywords>
    1013     *  </pre>
    1014     *  <p>
    1015     *  CST: <pre>
    1016     *     name = <identifier>
    1017     *  </pre>
    1018     */
    1019
    1020     protected Token nameReference( boolean significantNewlines ) throws SyntaxException, CompilationFailedException
    1021     {
    1022
    1023         Token token = la( significantNewlines );
    1024         if( !token.canMean(Types.IDENTIFIER) )
    1025         {
    1026             error( Types.IDENTIFIER );
    1027         }
    1028
    1029         consume();
    1030         token.setMeaning( Types.IDENTIFIER );
    1031
    1032         return token;
    1033
    1034     }
    1035
    1036
    1037
    1038    /***
    1039     *  Processes an optional data type marker (for a parameter, method return type,
    1040     *  etc.).  Newlines can be made significant, if required for disambiguation.
    1041     *  <p>
    1042     *  Grammar: <pre>
    1043     *     optionalDatatype = datatype? (?=<identifier>)
    1044     *  </pre>h
    1045     *  <p>
    1046     *  CST: <pre>
    1047     *     result = datatype | {}
    1048     *
    1049     *     see datatype()
    1050     *  </pre>
    1051     */
    1052
    1053     protected CSTNode optionalDatatype( boolean significantNewlines, boolean allowVoid ) throws SyntaxException, CompilationFailedException
    1054     {
    1055         CSTNode type = Reduction.EMPTY;
    1056         Token   next = la(significantNewlines);
    1057
    1058         //
    1059         // If the next token is an identifier, it could be an untyped
    1060         // variable/method name.  If it is followed by another identifier,
    1061         // we'll assume type.  Otherwise, we'll attempt a datatype and
    1062         // restore() the stream if there is a problem.
    1063
    1064         if( next.isA(Types.IDENTIFIER) )
    1065         {
    1066             if( lt(2, significantNewlines) == Types.IDENTIFIER )
    1067             {
    1068                 type = datatype( allowVoid );
    1069             }
    1070             else
    1071             {
    1072                 getTokenStream().checkpoint();
    1073
    1074                 try
    1075                 {
    1076                     type = datatype( allowVoid );
    1077                     if( lt(significantNewlines) != Types.IDENTIFIER )
    1078                     {
    1079                         throw new Exception();
    1080                     }
    1081                 }
    1082                 catch( Exception e )
    1083                 {
    1084                     getTokenStream().restore();
    1085                     type = Reduction.EMPTY;
    1086                 }
    1087             }
    1088         }
    1089
    1090         //
    1091         // If it is a primitive type name, it must be a datatype.  If void
    1092         // is present but not allowed, it is an error, and we let datatype()
    1093         // catch it.
    1094
    1095         else if( next.isA(Types.PRIMITIVE_TYPE) )
    1096         {
    1097             type = datatype( allowVoid );
    1098         }
    1099
    1100         return type;
    1101     }
    1102
    1103
    1104
    1105
    1106    /***
    1107     *  Processes a class/interface property, including the optional initialization
    1108     *  clause.  The modifiers, type, and identifier have already been identified
    1109     *  by the caller, and are passed in.
    1110     *  <p>
    1111     *  Grammar: <pre>
    1112     *     propertyDeclaration = (modifierList optionalDatatype nameDeclaration ["=" expression]) <eos>
    1113     *  </pre>
    1114     *  <p>
    1115     *  CST: <pre>
    1116     *     property = { <identifier>:SYNTH_PROPERTY modifierList optionalDatatype expression? }
    1117     *
    1118     *     see modifierList()
    1119     *     see optionalDatatype()
    1120     *     see expression()
    1121     *  </pre>
    1122     */
    1123
    1124     public Reduction propertyDeclaration( Reduction modifiers, CSTNode type, Token identifier ) throws SyntaxException, CompilationFailedException
    1125     {
    1126         Reduction property = identifier.asReduction( modifiers, type );
    1127         property.setMeaning( Types.SYNTH_PROPERTY );
    1128
    1129         if( lt() == Types.EQUAL )
    1130         {
    1131             consume();
    1132             property.add( expression() );
    1133         }
    1134
    1135         endOfStatement();
    1136         return property;
    1137     }
    1138
    1139
    1140
    1141    /***
    1142     *  Processes a class/interface method.  The modifiers, type, and identifier have
    1143     *  already been identified by the caller, and are passed in.  If <code>emptyOnly</code>
    1144     *  is set, no method body will be allowed.
    1145     *  <p>
    1146     *  Grammar: <pre>
    1147     *     methodDeclaration = modifierList optionalDatatype identifier
    1148     *                         "(" parameterDeclarationList ")"
    1149     *                         [ "throws" typeList ]
    1150     *                         ( statementBody | <eos> )
    1151     *  </pre>
    1152     *  <p>
    1153     *  CST: <pre>
    1154     *     method = { <identifier>:SYNTH_METHOD modifierList optionalDatatype
    1155     *                 parameterDeclarationList throwsClause statementBody }
    1156     *
    1157     *     throwsClause = { "throws" datatype+ } | {}
    1158     *
    1159     *     see modifierList()
    1160     *     see optionalDatatype()
    1161     *     see parameterDeclarationList()
    1162     *     see statementBody()
    1163     *  </pre>
    1164     */
    1165
    1166     public Reduction methodDeclaration( Reduction modifiers, CSTNode type, Token identifier, boolean emptyOnly) throws SyntaxException, CompilationFailedException
    1167     {
    1168         Reduction method = identifier.asReduction( modifiers, type );
    1169         method.setMeaning( Types.SYNTH_METHOD );
    1170
    1171         //
    1172         // Process the parameter list
    1173
    1174         consume(Types.LEFT_PARENTHESIS);
    1175         method.add( parameterDeclarationList() );
    1176         consume(Types.RIGHT_PARENTHESIS);
    1177
    1178         //
    1179         // Process the optional "throws" clause
    1180
    1181         try
    1182         {
    1183             method.add( typeList( Types.KEYWORD_THROWS, true, 0 ) );
    1184         }
    1185         catch (SyntaxException e)
    1186         {
    1187             controller.addError(e);
    1188             method.add( Reduction.EMPTY );
    1189         }
    1190
    1191         //
    1192         // And the body.  If it isn't supposed to be there, report the
    1193         // error, but process it anyway, for the point of recovering.
    1194
    1195         CSTNode body = null;
    1196
    1197         if( emptyOnly )
    1198         {
    1199             if( lt() == Types.LEFT_CURLY_BRACE )
    1200             {
    1201                 controller.addError( "abstract and interface methods cannot have a body", la() );
    1202             }
    1203             else
    1204             {
    1205                 body = Reduction.EMPTY;
    1206                 endOfStatement();
    1207             }
    1208
    1209         }
    1210
    1211         if( body == null )
    1212         {
    1213             body = statementBody(true);
    1214         }
    1215
    1216         method.add( body );
    1217
    1218
    1219         return method;
    1220     }
    1221
    1222
    1223
    1224    /***
    1225     *  Processes a parameter declaration list, which can occur on methods and closures.
    1226     *  It loops as long as it finds a comma as the next token.
    1227     *  <p>
    1228     *  Grammar: <pre>
    1229     *     parameterDeclarationList
    1230     *        = (parameterDeclaration ("," parameterDeclaration)* ("," parameterDeclaration "=" expression)* )?
    1231     *        | (parameterDeclaration "=" expression ("," parameterDeclaration "=" expression)* )?
    1232     *  </pre>
    1233     *  <p>
    1234     *  CST: <pre>
    1235     *     parameters = { <null> parameter* }
    1236     *     parameter  = { <identifier>:SYNTH_PARAMETER_DECLARATION optionalDatatype default? }
    1237     *     default    = expression
    1238     *  </pre>
    1239     */
    1240
    1241     protected Reduction parameterDeclarationList() throws SyntaxException, CompilationFailedException
    1242     {
    1243         Reduction list = Reduction.newContainer();
    1244
    1245         boolean expectDefaults = false;
    1246         while( la().isA(Types.TYPE_NAME) )  // TYPE_NAME includes <identifier>, and so does double duty
    1247         {
    1248
    1249             //
    1250             // Get the declaration
    1251
    1252             Reduction parameter = (Reduction)list.add( parameterDeclaration() );
    1253
    1254             //
    1255             // Process any default parameter (it is required on every parameter
    1256             // after the first occurrance).
    1257
    1258             if( expectDefaults || lt() == Types.EQUAL )
    1259             {
    1260                 expectDefaults = true;
    1261                 consume( Types.EQUAL );
    1262
    1263                 parameter.add( expression() );
    1264             }
    1265
    1266             //
    1267             // Check if we are done.
    1268
    1269             if( lt() == Types.COMMA )
    1270             {
    1271                 consume( Types.COMMA );
    1272             }
    1273             else
    1274             {
    1275                 break;
    1276             }
    1277         }
    1278
    1279         return list;
    1280     }
    1281
    1282
    1283
    1284    /***
    1285     *  Processes a single parameter declaration, which can occur on methods and closures.
    1286     *  <p>
    1287     *  Grammar: <pre>
    1288     *     parameterDeclaration = optionalDatatype nameDeclaration
    1289     *  </pre>
    1290     *  <p>
    1291     *  CST: <pre>
    1292     *     parameter = { <identifier>:SYNTH_PARAMETER_DECLARATION optionalDatatype }
    1293     *
    1294     *     see optionalDatatype()
    1295     *  </pre>
    1296     */
    1297
    1298     protected Reduction parameterDeclaration() throws SyntaxException, CompilationFailedException
    1299     {
    1300         CSTNode   type      = optionalDatatype( false, false );
    1301         Reduction parameter = nameDeclaration( false ).asReduction( type );
    1302         parameter.setMeaning( Types.SYNTH_PARAMETER_DECLARATION );
    1303
    1304         return parameter;
    1305     }
    1306
    1307
    1308
    1309    /***
    1310     *  Processes a datatype specification.  For reasons of disambiguation,
    1311     *  the array marker ([]) must never be on a separate line from the
    1312     *  base datatype.
    1313     *  <p>
    1314     *  Grammar: <pre>
    1315     *     datatype = scalarDatatype ( "[" "]" )*
    1316     *
    1317     *     scalarDatatype = dottedIdentifier | "void" | "int" | ...
    1318     *  </pre>
    1319     *  <p>
    1320     *  CST: <pre>
    1321     *     datatype  = { "[" datatype } | scalar
    1322     *     scalar    = dottedIdentifier | primitive
    1323     *     primitive = "void" | "int" | ...
    1324     *
    1325     *     see dottedIdentifier()
    1326     *  </pre>
    1327     */
    1328
    1329     protected CSTNode datatype( boolean allowVoid ) throws SyntaxException, CompilationFailedException
    1330     {
    1331         CSTNode datatype = scalarDatatype(allowVoid);
    1332
    1333         while( lt(true) == Types.LEFT_SQUARE_BRACKET )
    1334         {
    1335             datatype = consume(Types.LEFT_SQUARE_BRACKET).asReduction( datatype );
    1336             consume( Types.RIGHT_SQUARE_BRACKET );
    1337         }
    1338
    1339         return datatype;
    1340     }
    1341
    1342
    1343
    1344    /***
    1345     *  A synonym for <code>datatype( true )</code>.
    1346     */
    1347
    1348     protected CSTNode datatype() throws SyntaxException, CompilationFailedException
    1349     {
    1350         return datatype(true);
    1351     }
    1352
    1353
    1354
    1355    /***
    1356     *  Processes a scalar datatype specification.
    1357     *  <p>
    1358     *  Grammar: <pre>
    1359     *     scalarDatatype = dottedIdentifier | "void" | "int" | ...
    1360     *  </pre>
    1361     *  <p>
    1362     *  CST: <pre>
    1363     *     scalar    = dottedIdentifier | primitive
    1364     *     primitive = "void" | "int" | ...
    1365     *
    1366     *     see dottedIdentifier()
    1367     *  </pre>
    1368     */
    1369
    1370     protected CSTNode scalarDatatype( boolean allowVoid ) throws SyntaxException, CompilationFailedException
    1371     {
    1372         CSTNode datatype = null;
    1373
    1374         if( la().isA(allowVoid ? Types.PRIMITIVE_TYPE : Types.CREATABLE_PRIMITIVE_TYPE) )
    1375         {
    1376             datatype = consume();
    1377         }
    1378         else
    1379         {
    1380             datatype = dottedIdentifier();
    1381         }
    1382
    1383         return datatype;
    1384     }
    1385
    1386
    1387
    1388    /***
    1389     *  Processes the body of a complex statement (like "if", "for", etc.).
    1390     *  Set <code>requireBraces</code> if the body must not be just a single
    1391     *  statement.
    1392     *  <p>
    1393     *  Grammar: <pre>
    1394     *     statementBody = ("{" statement* "}")
    1395     *                   | statement
    1396     *  </pre>
    1397     *  <p>
    1398     *  CST: <pre>
    1399     *     complex = { "{" statement* }
    1400     *     simple  = statement
    1401     *
    1402     *     see statement()
    1403     *  </pre>
    1404     */
    1405
    1406     protected CSTNode statementBody( boolean requireBraces ) throws SyntaxException, CompilationFailedException
    1407     {
    1408         CSTNode body = null;
    1409
    1410         if (lt() == Types.LEFT_CURLY_BRACE)
    1411         {
    1412             Token brace = consume( Types.LEFT_CURLY_BRACE );
    1413             brace.setMeaning( Types.SYNTH_BLOCK );
    1414
    1415             body = statementsUntilRightCurly();
    1416             body.set( 0, brace );
    1417
    1418             consume( Types.RIGHT_CURLY_BRACE );
    1419         }
    1420         else
    1421         {
    1422             if( requireBraces )
    1423             {
    1424                 error( Types.LEFT_CURLY_BRACE );
    1425             }
    1426             else
    1427             {
    1428                body = statement();
    1429             }
    1430         }
    1431
    1432         return body;
    1433     }
    1434
    1435
    1436
    1437    /***
    1438     *  Reads statements until a "}" is met.
    1439     *  <p>
    1440     *  Grammar: <pre>
    1441     *     statementsUntilRightCurly = statement* (?= "}")
    1442     *  </pre>
    1443     *  <p>
    1444     *  CST: <pre>
    1445     *     statements = { <null> statement* }
    1446     *  </pre>
    1447     */
    1448
    1449     protected Reduction statementsUntilRightCurly( ) throws SyntaxException, CompilationFailedException
    1450     {
    1451         Reduction block = Reduction.newContainer();
    1452
    1453         while( lt() != Types.EOF && lt() != Types.RIGHT_CURLY_BRACE )
    1454         {
    1455             try
    1456             {
    1457                 block.add( statement() );
    1458             }
    1459             catch( SyntaxException e )
    1460             {
    1461                 controller.addError( e );
    1462                 recover();
    1463             }
    1464         }
    1465
    1466
    1467         return block;
    1468     }
    1469
    1470
    1471
    1472   //---------------------------------------------------------------------------
    1473   // PRODUCTIONS: STATEMENTS
    1474
    1475
    1476    /***
    1477     *  Processes a single statement.  Statements include: loop constructs, branch
    1478     *  constructs, flow control constructs, exception constructs, expressions of
    1479     *  a variety of types, and pretty much anything you can put inside a method.
    1480     *  <p>
    1481     *  Grammar: <pre>
    1482     *     statement      = (label ":")? bareStatement
    1483     *     bareStatement  = (emptyStatement|basicStatement|blockStatement)
    1484     *
    1485     *     basicStatement = forStatement
    1486     *                    | whileStatement
    1487     *                    | doStatement
    1488     *                    | continueStatement
    1489     *                    | breakStatement
    1490     *                    | ifStatement
    1491     *                    | tryStatement
    1492     *                    | throwStatement
    1493     *                    | synchronizedStatement
    1494     *                    | switchStatement
    1495     *                    | returnStatement
    1496     *                    | assertStatement
    1497     *                    | expression <eos>
    1498     *
    1499     *     label          = <identifier>
    1500     *     blockStatement = "{" statement* "}"
    1501     *     emptyStatement = ";"
    1502     *  </pre>
    1503     *  <p>
    1504     *  Recognition: <pre>
    1505     *     ";"       => emptyStatement
    1506     *     <keyword> => <keyword>Statement
    1507     *     "{"       => expression, then:
    1508     *                    if it is a closureExpression and has no parameters => blockStatement
    1509     *
    1510     *     *         => expression
    1511     *  </pre>
    1512     *  <p>
    1513     *  CST: <pre>
    1514     *     labelled       = { <identifier>:SYNTH_LABEL bareStatement }
    1515     *     bareStatement  = emptyStatement | blockStatement | basicStatement
    1516     *     emptyStatement = { "{" }
    1517     *     blockStatement = { "{" statement* }
    1518     *
    1519     *     see forStatement()
    1520     *     see whileStatement()
    1521     *     see doStatement()
    1522     *     see continueStatement()
    1523     *     see breakStatement()
    1524     *     see ifStatement()
    1525     *     see tryStatement()
    1526     *     see throwStatement()
    1527     *     see synchronizedStatement()
    1528     *     see switchStatement()
    1529     *     see returnStatement()
    1530     *     see assertStatement()
    1531     *     see expression()
    1532     *  </pre>
    1533     */
    1534
    1535     protected CSTNode statement( boolean allowUnlabelledBlocks ) throws SyntaxException, CompilationFailedException
    1536     {
    1537         CSTNode statement = null;
    1538
    1539         //
    1540         // Check for and grab any label for the statement
    1541
    1542         CSTNode label = null;
    1543         if( lt() == Types.IDENTIFIER && lt(2) == Types.COLON )
    1544         {
    1545             label = consume( Types.IDENTIFIER ).asReduction();
    1546             label.setMeaning( Types.SYNTH_LABEL );
    1547
    1548             consume( Types.COLON );
    1549         }
    1550
    1551         //
    1552         // Process the statement
    1553
    1554         switch( lt() )
    1555         {
    1556             case Types.KEYWORD_ASSERT:
    1557             {
    1558                 statement = assertStatement();
    1559                 break;
    1560             }
    1561
    1562             case Types.KEYWORD_BREAK:
    1563             {
    1564                 statement = breakStatement();
    1565                 break;
    1566             }
    1567
    1568             case Types.KEYWORD_CONTINUE:
    1569             {
    1570                 statement = continueStatement();
    1571                 break;
    1572             }
    1573
    1574             case Types.KEYWORD_IF:
    1575             {
    1576                 statement = ifStatement();
    1577                 break;
    1578             }
    1579
    1580             case Types.KEYWORD_RETURN:
    1581             {
    1582                 statement = returnStatement();
    1583                 break;
    1584             }
    1585
    1586             case Types.KEYWORD_SWITCH:
    1587             {
    1588                 statement = switchStatement();
    1589                 break;
    1590             }
    1591
    1592             case Types.KEYWORD_SYNCHRONIZED:
    1593             {
    1594                 statement = synchronizedStatement();
    1595                 break;
    1596             }
    1597
    1598             case Types.KEYWORD_THROW:
    1599             {
    1600                 statement = throwStatement();
    1601                 break;
    1602             }
    1603
    1604             case Types.KEYWORD_TRY:
    1605             {
    1606                 statement = tryStatement();
    1607                 break;
    1608             }
    1609
    1610             case Types.KEYWORD_FOR:
    1611             {
    1612                 statement = forStatement();
    1613                 break;
    1614             }
    1615
    1616             case Types.KEYWORD_DO:
    1617             {
    1618                 statement = doWhileStatement();
    1619                 break;
    1620             }
    1621
    1622             case Types.KEYWORD_WHILE:
    1623             {
    1624                 statement = whileStatement();
    1625                 break;
    1626             }
    1627
    1628             case Types.SEMICOLON:
    1629             {
    1630                 statement = consume().asReduction();
    1631                 statement.setMeaning( Types.SYNTH_BLOCK );
    1632                 break;
    1633             }
    1634
    1635             case Types.LEFT_CURLY_BRACE:
    1636             {
    1637
    1638                 //
    1639                 // Bare blocks are no longer generally supported, due to the ambiguity
    1640                 // with closures.  Further, closures and blocks can look identical
    1641                 // until after parsing, so we process first as a closure expression,
    1642                 // then, if the expression is a parameter-less, bare closure, rebuild
    1643                 // it as a block (which generally requires a label).  Joy.
    1644
    1645                 statement = expression();
    1646                 if( statement.isA(Types.SYNTH_CLOSURE) )
    1647                 {
    1648                     if( !statement.get(1).hasChildren() )
    1649                     {
    1650                         Reduction block = statement.getRoot().asReduction();
    1651                         block.setMeaning( Types.SYNTH_BLOCK );
    1652                         block.addChildrenOf( statement.get(2) );
    1653
    1654                         if( label == null && !allowUnlabelledBlocks )
    1655                         {
    1656                             controller.addError( "groovy does not support anonymous blocks; please add a label", statement.getRoot() );
    1657                         }
    1658
    1659                         statement = block;
    1660                     }
    1661                 }
    1662                 else
    1663                 {
    1664                    //
    1665                    // It's a closure expression, and must be a statement
    1666
    1667                    endOfStatement();
    1668                 }
    1669
    1670                 break;
    1671             }
    1672
    1673             default:
    1674             {
    1675                 try
    1676                 {
    1677                     statement = expression();
    1678                     endOfStatement();
    1679                 }
    1680                 catch (SyntaxException e)
    1681                 {
    1682                     controller.addError(e);
    1683                     recover();
    1684                 }
    1685             }
    1686         }
    1687
    1688
    1689         //
    1690         // Wrap the statement in the label, if necessary.
    1691
    1692         if( label != null )
    1693         {
    1694             label.add( statement );
    1695             statement = label;
    1696         }
    1697
    1698         return statement;
    1699     }
    1700
    1701
    1702
    1703    /***
    1704     *  Synonym for <code>statement( false )</code>.
    1705     */
    1706
    1707     protected CSTNode statement( ) throws SyntaxException, CompilationFailedException
    1708     {
    1709         return statement( false );
    1710     }
    1711
    1712
    1713
    1714    /***
    1715     *  Processes an assert statement.
    1716     *  <p>
    1717     *  Grammar: <pre>
    1718     *     assertStatement = "assert" expression (":" expression) <eos>
    1719     *  </pre>
    1720     *  <p>
    1721     *  CST: <pre>
    1722     *     assert = { "assert" expression expression? }
    1723     *
    1724     *     see expression()
    1725     *  </pre>
    1726     */
    1727
    1728     protected Reduction assertStatement() throws SyntaxException, CompilationFailedException
    1729     {
    1730         Reduction statement = consume( Types.KEYWORD_ASSERT ).asReduction( expression() );
    1731
    1732         if( lt() == Types.COLON )
    1733         {
    1734             consume( Types.COLON );
    1735             statement.add( expression() );
    1736         }
    1737
    1738         endOfStatement();
    1739
    1740         return statement;
    1741     }
    1742
    1743
    1744
    1745    /***
    1746     *  Processes a break statement.  We require the label on the same line.
    1747     *  <p>
    1748     *  Grammar: <pre>
    1749     *     breakStatement = "break" label? <eos>
    1750     *
    1751     *     label = <identifier>
    1752     *  </pre>
    1753     *  <p>
    1754     *  CST: <pre>
    1755     *     statement = { "break" label? }
    1756     *     label     = <identifier>
    1757     *  </pre>
    1758     */
    1759
    1760     protected Reduction breakStatement() throws SyntaxException, CompilationFailedException
    1761     {
    1762         Reduction statement = consume(Types.KEYWORD_BREAK).asReduction();
    1763         if( lt(true) == Types.IDENTIFIER )
    1764         {
    1765             statement.add( consume() );
    1766         }
    1767
    1768         endOfStatement();
    1769         return statement;
    1770
    1771     }
    1772
    1773
    1774
    1775    /***
    1776     *  Processes a continue statement.  We require the label on the same line.
    1777     *  <p>
    1778     *  Grammar: <pre>
    1779     *     continueStatement = "continue" label? <eos>
    1780     *
    1781     *     label = <identifier>
    1782     *  </pre>
    1783     *  <p>
    1784     *  CST: <pre>
    1785     *     statement = { "continue" label? }
    1786     *     label     = <identifier>
    1787     *  </pre>
    1788     */
    1789
    1790     protected Reduction continueStatement() throws SyntaxException, CompilationFailedException
    1791     {
    1792         Reduction statement = consume(Types.KEYWORD_CONTINUE).asReduction();
    1793         if( lt(true) == Types.IDENTIFIER )
    1794         {
    1795             statement.add( consume() );
    1796         }
    1797
    1798         endOfStatement();
    1799         return statement;
    1800
    1801     }
    1802
    1803
    1804
    1805    /***
    1806     *  Processes a throw statement.
    1807     *  <p>
    1808     *  Grammar: <pre>
    1809     *     throwStatement = "throw" expression <eos>
    1810     *  </pre>
    1811     *  <p>
    1812     *  CST: <pre>
    1813     *     statement = { "throw" expression }
    1814     *
    1815     *     see expression()
    1816     *  </pre>
    1817     */
    1818
    1819     protected Reduction throwStatement() throws SyntaxException, CompilationFailedException
    1820     {
    1821         Reduction statement = consume(Types.KEYWORD_THROW).asReduction( expression() );
    1822         endOfStatement();
    1823         return statement;
    1824
    1825     }
    1826
    1827
    1828
    1829    /***
    1830     *  Processes an if statement.
    1831     *  <p>
    1832     *  Grammar: <pre>
    1833     *     ifStatement  = ifClause elseIfClause* elseClause?
    1834     *
    1835     *     ifClause     = "if" "(" expression ")" statementBody
    1836     *     elseIfClause = "else" "if" "(" expression ")" statementBody
    1837     *     elseClause   = "else" statementBody
    1838     *  </pre>
    1839     *  <p>
    1840     *  CST: <pre>
    1841     *     if   = { "if" expression statementBody else? }
    1842     *     else = if | { "else" statementBody }
    1843     *
    1844     *     see expression()
    1845     *     see statementBody()
    1846     *  </pre>
    1847     */
    1848
    1849     protected Reduction ifStatement() throws SyntaxException, CompilationFailedException
    1850     {
    1851         //
    1852         // Process the if clause
    1853
    1854         Reduction statement = consume(Types.KEYWORD_IF).asReduction();
    1855
    1856         consume( Types.LEFT_PARENTHESIS );
    1857
    1858         try
    1859         {
    1860             statement.add( expression() );
    1861         }
    1862         catch( SyntaxException e )
    1863         {
    1864             controller.addError( e );
    1865             recover( Types.RIGHT_PARENTHESIS );
    1866         }
    1867
    1868         consume( Types.RIGHT_PARENTHESIS );
    1869
    1870         statement.add( statementBody(false) );
    1871
    1872
    1873         //
    1874         // If the else clause is present:
    1875         //   if it is an else if, recurse
    1876         //   otherwise, build the else node directly.
    1877
    1878         if( lt() == Types.KEYWORD_ELSE )
    1879         {
    1880             if( lt(2) == Types.KEYWORD_IF )
    1881             {
    1882                 consume( Types.KEYWORD_ELSE );
    1883                 statement.add( ifStatement() );
    1884             }
    1885             else
    1886             {
    1887                 Reduction last = (Reduction)statement.add( consume(Types.KEYWORD_ELSE).asReduction() );
    1888                 last.add( statementBody(false) );
    1889             }
    1890         }
    1891
    1892         return statement;
    1893     }
    1894
    1895
    1896
    1897    /***
    1898     *  Processes a return statement.  Any expression must start on the same line
    1899     *  as the "return".
    1900     *  <p>
    1901     *  Grammar: <pre>
    1902     *     returnStatement = "return" expression? <eos>
    1903     *  </pre>
    1904     *  <p>
    1905     *  CST: <pre>
    1906     *     statement = { "return" expression? }
    1907     *
    1908     *     see expression()
    1909     *  </pre>
    1910     */
    1911
    1912     protected Reduction returnStatement() throws SyntaxException, CompilationFailedException
    1913     {
    1914         Reduction statement = consume(Types.KEYWORD_RETURN).asReduction();
    1915
    1916         if( !la(true).isA(Types.ANY_END_OF_STATEMENT) )
    1917         {
    1918             statement.add( expression() );
    1919         }
    1920
    1921         endOfStatement();
    1922         return statement;
    1923
    1924     }
    1925
    1926
    1927
    1928    /***
    1929     *  Processes a switch statement.
    1930     *  <p>
    1931     *  Grammar: <pre>
    1932     *     switchStatment = "switch" "(" expression ")" "{" switchBody "}"
    1933     *
    1934     *     switchBody = caseSet*
    1935     *     caseSet = (("case" expression ":")+ | ("default" ":")) statement+
    1936     *  </pre>
    1937     *  <p>
    1938     *  CST: <pre>
    1939     *     switch = { "switch" expression case* }
    1940     *     case   = { "case" expression statement* }
    1941     *            | { "default" statement* }
    1942     *
    1943     *     see expression()
    1944     *     see statement()
    1945     *  </pre>
    1946     */
    1947
    1948     protected Reduction switchStatement() throws SyntaxException, CompilationFailedException
    1949     {
    1950         Reduction statement = consume(Types.KEYWORD_SWITCH).asReduction();
    1951         consume( Types.LEFT_PARENTHESIS );
    1952         statement.add( expression() );
    1953         consume( Types.RIGHT_PARENTHESIS );
    1954
    1955         //
    1956         // Process the switch body.  Labels can be pretty much anything,
    1957         // but we'll duplicate-check for default.
    1958
    1959         consume( Types.LEFT_CURLY_BRACE );
    1960
    1961         boolean defaultFound = false;
    1962         while( lt() == Types.KEYWORD_CASE || lt() == Types.KEYWORD_DEFAULT )
    1963         {
    1964             //
    1965             // Read the label
    1966
    1967             Reduction caseBlock = null;
    1968             if( lt() == Types.KEYWORD_CASE )
    1969             {
    1970                 caseBlock = consume( Types.KEYWORD_CASE ).asReduction( expression() );
    1971             }
    1972             else if( lt() == Types.KEYWORD_DEFAULT )
    1973             {
    1974                 if( defaultFound )
    1975                 {
    1976                     controller.addError( "duplicate default entry in switch", la() );
    1977                 }
    1978
    1979                 caseBlock = consume( Types.KEYWORD_DEFAULT ).asReduction();
    1980                 defaultFound = true;
    1981             }
    1982             else
    1983             {
    1984                 error( new int[] { Types.KEYWORD_DEFAULT, Types.KEYWORD_CASE } );
    1985                 recover( Types.SWITCH_ENTRIES );
    1986             }
    1987
    1988             consume( Types.COLON );
    1989
    1990
    1991             //
    1992             // Process the statements, if any
    1993
    1994             boolean first = true;
    1995             while( !la().isA(Types.SWITCH_BLOCK_TERMINATORS) )
    1996             {
    1997                 caseBlock.add( statement(first) );
    1998                 first = false;
    1999             }
    2000
    2001             statement.add( caseBlock );
    2002         }
    2003
    2004         consume( Types.RIGHT_CURLY_BRACE );
    2005
    2006         return statement;
    2007     }
    2008
    2009
    2010
    2011    /***
    2012     *  Processes a synchronized statement.
    2013     *  <p>
    2014     *  Grammar: <pre>
    2015     *     synchronizedStatement = "synchronized" "(" expression ")" statementBody
    2016     *  </pre>
    2017     *  <p>
    2018     *  CST: <pre>
    2019     *     statement = { "synchronized" expression statementBody }
    2020     *
    2021     *     see expression()
    2022     *     see statementBody()
    2023     *  </pre>
    2024     */
    2025
    2026     protected Reduction synchronizedStatement() throws SyntaxException, CompilationFailedException
    2027     {
    2028         Reduction statement = consume(Types.KEYWORD_SYNCHRONIZED).asReduction();
    2029
    2030         consume( Types.LEFT_PARENTHESIS );
    2031         statement.add( expression() );
    2032         consume( Types.RIGHT_PARENTHESIS );
    2033
    2034         statement.add( statementBody(true) );
    2035
    2036         return statement;
    2037
    2038     }
    2039
    2040
    2041
    2042    /***
    2043     *  Processes a try statement.
    2044     *  <p>
    2045     *  Grammar: <pre>
    2046     *     tryStatement  = "try" statementBody catchClause* finallyClause?
    2047     *
    2048     *     catchClause   = "catch" "(" datatype identifier ")" statementBody
    2049     *     finallyClause = "finally" statementBody
    2050     *  </pre>
    2051     *  <p>
    2052     *  CST: <pre>
    2053     *     try     = { "try" statementBody catches finally }
    2054     *
    2055     *     catches = { <null> catch* }
    2056     *
    2057     *     catch   = { "catch" datatype <identifier> statementBody }
    2058     *
    2059     *     finally = {} | statementBody
    2060     *
    2061     *     see datatype()
    2062     *     see identifier()
    2063     *     see statementBody()
    2064     *  </pre>
    2065     */
    2066
    2067     protected Reduction tryStatement() throws SyntaxException, CompilationFailedException
    2068     {
    2069
    2070         //
    2071         // Set up the statement with the try clause
    2072
    2073         Reduction statement = consume(Types.KEYWORD_TRY).asReduction();
    2074         statement.add( statementBody(true) );
    2075
    2076
    2077         //
    2078         // Process the catch clauses
    2079
    2080         Reduction catches = (Reduction)statement.add( Reduction.newContainer() );
    2081         while( lt() == Types.KEYWORD_CATCH )
    2082         {
    2083             try
    2084             {
    2085                 Reduction catchBlock = (Reduction)catches.add( consume(Types.KEYWORD_CATCH).asReduction() );
    2086
    2087                 consume( Types.LEFT_PARENTHESIS );
    2088                 try
    2089                 {
    2090                     catchBlock.add( datatype(false) );
    2091                     catchBlock.add( nameDeclaration(false) );
    2092                 }
    2093                 catch( SyntaxException e )
    2094                 {
    2095                     controller.addError( e );
    2096                     recover( Types.RIGHT_PARENTHESIS );
    2097                 }
    2098                 consume( Types.RIGHT_PARENTHESIS );
    2099
    2100                 catchBlock.add( statementBody(true) );
    2101             }
    2102             catch( SyntaxException e )
    2103             {
    2104                 controller.addError( e );
    2105                 recover();
    2106             }
    2107         }
    2108
    2109         //
    2110         // Process the finally clause, if available.
    2111
    2112         if( lt() == Types.KEYWORD_FINALLY )
    2113         {
    2114             consume( Types.KEYWORD_FINALLY );
    2115             statement.add( statementBody(true) );
    2116         }
    2117         else
    2118         {
    2119             statement.add( Reduction.EMPTY );
    2120         }
    2121
    2122         return statement;
    2123     }
    2124
    2125
    2126
    2127   //---------------------------------------------------------------------------
    2128   // PRODUCTIONS: LOOP STATEMENTS
    2129
    2130
    2131    /***
    2132     *  Processes a for statement.
    2133     *  <p>
    2134     *  Grammar: <pre>
    2135     *     forStatement = "for" "(" normal | each ")" statementBody
    2136     *
    2137     *     normal = multi ";" expression ";" multi
    2138     *     multi  = (expression ["," expression]*)
    2139     *
    2140     *     each   = optionalDatatype nameDeclaration ("in"|":") expression
    2141     *  </pre>
    2142     *  <p>
    2143     *  CST: <pre>
    2144     *     for    = { "for" header statementBody }
    2145     *
    2146     *     header = normal | each
    2147     *     each   = { ("in"|":") optionalDatatype nameDeclaration expression }
    2148     *
    2149     *     normal = { <null> init test incr }
    2150     *     init   = { <null> expression* }
    2151     *     test   = expression
    2152     *     incr   = { <null> expression* }
    2153     *
    2154     *     see expression()
    2155     *     see nameDeclaration()
    2156     *     see statementBody()
    2157     *  </pre>
    2158     */
    2159
    2160     protected Reduction forStatement() throws SyntaxException, CompilationFailedException
    2161     {
    2162         Reduction statement = consume( Types.KEYWORD_FOR ).asReduction();
    2163
    2164         //
    2165         // The for loop is a little tricky.  There are three forms,
    2166         // and the first two can't be processed with expression().
    2167         // In order to avoid complications, we are going to checkpoint()
    2168         // the stream before processing optionalDatatype(), then restore
    2169         // it if we need to use expression().
    2170         //
    2171         // Anyway, after processing the optionalDatatype(), if KEYWORD_IN
    2172         // or a COLON is at la(2), it's an each loop.  Otherwise, it's the
    2173         // standard for loop.
    2174
    2175         consume( Types.LEFT_PARENTHESIS );
    2176
    2177         getTokenStream().checkpoint();
    2178
    2179         Reduction header   = null;
    2180         CSTNode   datatype = optionalDatatype( false, false );
    2181
    2182         if( lt(2) == Types.KEYWORD_IN || lt(2) == Types.COLON )
    2183         {
    2184             Token name = nameDeclaration( false );
    2185             header = consume().asReduction( datatype, name, expression() );
    2186         }
    2187         else
    2188         {
    2189             getTokenStream().restore();
    2190             header = Reduction.newContainer();
    2191
    2192             Reduction init = Reduction.newContainer();
    2193             while( lt() != Types.SEMICOLON && lt() != Types.EOF )
    2194             {
    2195                 init.add( expression() );
    2196
    2197                 if( lt() != Types.SEMICOLON )
    2198                 {
    2199                     consume( Types.COMMA );
    2200                 }
    2201             }
    2202
    2203             consume( Types.SEMICOLON );
    2204
    2205             header.add( init );
    2206
    2207
    2208             //
    2209             // Next up, a single expression is the test clause, followed
    2210             // by a semicolon.
    2211
    2212             header.add( expression() );
    2213             consume( Types.SEMICOLON );
    2214
    2215
    2216             //
    2217             // Finally, the increment section is a (possibly empty) comma-
    2218             // separated list of expressions followed by the RIGHT_PARENTHESIS.
    2219
    2220             Reduction incr = (Reduction)header.add( Reduction.newContainer() );
    2221
    2222             while( lt() != Types.RIGHT_PARENTHESIS && lt() != Types.EOF )
    2223             {
    2224                 incr.add( expression() );
    2225
    2226                 if( lt() != Types.RIGHT_PARENTHESIS )
    2227                 {
    2228                     consume( Types.COMMA );
    2229                 }
    2230             }
    2231         }
    2232
    2233         consume( Types.RIGHT_PARENTHESIS );
    2234
    2235         statement.add( header );
    2236         statement.add( statementBody(false) );
    2237
    2238         return statement;
    2239     }
    2240
    2241
    2242
    2243    /***
    2244     *  Processes a do ... while statement.
    2245     *  <p>
    2246     *  Grammar: <pre>
    2247     *     doWhileStatement = "do" statementBody "while" "(" expression ")" <eos>
    2248     *  </pre>
    2249     *  <p>
    2250     *  CST: <pre>
    2251     *     do = { "do" statementBody expression }
    2252     *
    2253     *     see expression()
    2254     *     see statementBody()
    2255     *  </pre>
    2256     */
    2257
    2258     protected Reduction doWhileStatement() throws SyntaxException, CompilationFailedException
    2259     {
    2260         Reduction statement = consume(Types.KEYWORD_DO).asReduction();
    2261         statement.add( statementBody(false) );
    2262         consume( Types.KEYWORD_WHILE );
    2263
    2264         consume( Types.LEFT_PARENTHESIS );
    2265         try
    2266         {
    2267             statement.add( expression() );
    2268         }
    2269         catch( SyntaxException e )
    2270         {
    2271             controller.addError( e );
    2272             recover( Types.RIGHT_PARENTHESIS );
    2273         }
    2274         consume( Types.RIGHT_PARENTHESIS );
    2275
    2276         return statement;
    2277
    2278     }
    2279
    2280
    2281
    2282    /***
    2283     *  Processes a while statement.
    2284     *  <p>
    2285     *  Grammar: <pre>
    2286     *     whileStatement = "while" "(" expression ")" statementBody
    2287     *  </pre>
    2288     *  <p>
    2289     *  CST: <pre>
    2290     *     while = { "while" expression statementBody }
    2291     *
    2292     *     see expression()
    2293     *     see statementBody()
    2294     *  </pre>
    2295     */
    2296
    2297     protected Reduction whileStatement() throws SyntaxException, CompilationFailedException
    2298     {
    2299         Reduction statement = consume(Types.KEYWORD_WHILE).asReduction();
    2300
    2301         consume( Types.LEFT_PARENTHESIS );
    2302
    2303         try
    2304         {
    2305             statement.add( expression() );
    2306         }
    2307         catch( SyntaxException e )
    2308         {
    2309             controller.addError( e );
    2310             recover( Types.RIGHT_PARENTHESIS );
    2311         }
    2312         consume( Types.RIGHT_PARENTHESIS );
    2313
    2314         statement.add( statementBody(false) );
    2315         return statement;
    2316
    2317     }
    2318
    2319
    2320
    2321
    2322   //---------------------------------------------------------------------------
    2323   // PRODUCTIONS: EXPRESSIONS
    2324
    2325
    2326    /***
    2327     *  Processes a single (sub-)expression into a CSTNode.  No assumption is
    2328     *  made about what follows the expression.
    2329     *  <p>
    2330     *  Note that the expression parser is rather stupid, in that it cannot
    2331     *  resolve names.  Therefore it is little more than a pre-filter, removing
    2332     *  statements that can't possibly be right, but leaving everything that
    2333     *  might be right for semantic analysis by the <code>Analyzer</code> (which
    2334     *  has access to the symbol table.  There was some thought given to eliminating
    2335     *  the CSTs and going right to ASTs, but that option was rejected because
    2336     *  inner classes mean that class name resolution won't work before parsing
    2337     *  is complete.
    2338     */
    2339
    2340     protected CSTNode expression( ) throws SyntaxException, CompilationFailedException
    2341     {
    2342         // int id = nestCount++;
    2343         // System.out.println( "ENTERING EXPRESSION " + id );
    2344
    2345         ExpressionStack stack = new ExpressionStack( this );
    2346         CSTNode expression = null;
    2347
    2348         boolean bareMode = false;
    2349
    2350         MAIN_LOOP: do
    2351         {
    2352             //
    2353             // Valid at the start of an (sub)expression, a typed variable declaration
    2354             // is handled separately.  It has the form
    2355
    2356             //
    2357             // In the SHIFT phase, we move stuff onto the stack that can have
    2358             // multiple meanings and/or precedence issues, and leave the interpretation
    2359             // for a later REDUCE.  No lookahead is used.  When structures are found that
    2360             // have a consistent form, we use LL techniques (the new operator, for instance).
    2361
    2362             Token next = la(stack);
    2363             int type = next.getMeaningAs( EXPRESSION_SHIFT_HANDLERS );
    2364
    2365             // System.out.println( "expression() status:" );
    2366             // System.out.println( stack.toString() );
    2367             // System.out.println( "next: " );
    2368             // System.out.println( next.toString() );
    2369             // System.out.println( la(2).toString() );
    2370
    2371             SHIFT: switch( type )
    2372             {
    2373                 case Types.GSTRING_START:
    2374                 {
    2375                     if( stack.topIsAnExpression() )
    2376                     {
    2377                         error( "gstring cannot directly follow another expression" );
    2378                     }
    2379
    2380                     stack.push( gstring() );
    2381                     break;
    2382                 }
    2383
    2384
    2385                 case Types.CREATABLE_PRIMITIVE_TYPE:
    2386                 {
    2387                     stack.shiftIf( stack.atStartOfExpression(), "type name not valid in this context" );
    2388                     break;
    2389                 }
    2390
    2391
    2392                 case Types.SIMPLE_EXPRESSION:
    2393                 {
    2394
    2395                     //
    2396                     // Method parameters don't make it here (see REDUCE)...
    2397
    2398                     stack.shiftUnlessTopIsAnExpression( "literal cannot directly follow another expression" );
    2399                     break;
    2400                 }
    2401
    2402
    2403                 case Types.KEYWORD_IDENTIFIER:
    2404                 {
    2405                     if( stack.top().isA(Types.DEREFERENCE_OPERATOR) && stack.topIsAnOperator() )
    2406                     {
    2407                         la().setMeaning( Types.IDENTIFIER );
    2408                         stack.shift();
    2409                     }
    2410                     else
    2411                     {
    2412                         error( "not valid as an identifier in this context" );
    2413                     }
    2414                     break;
    2415                 }
    2416
    2417
    2418                 case Types.ASSIGNMENT_OPERATOR:
    2419                 {
    2420                     stack.shiftIf( stack.topIsAModifiableExpression(), "left-hand-side of assignment must be modifiable" );
    2421                     break;
    2422                 }
    2423
    2424
    2425                 case Types.PREFIX_OR_INFIX_OPERATOR:
    2426                 {
    2427                     if( stack.topIsAnOperator(0, true) )
    2428                     {
    2429                         Types.makePrefix( next, false );
    2430                     }
    2431                     stack.shift( );
    2432                     break;
    2433                 }
    2434
    2435
    2436                 case Types.PREFIX_OPERATOR:
    2437                 {
    2438                     Types.makePrefix( next, false );
    2439                     stack.shift( );
    2440                     break;
    2441                 }
    2442
    2443
    2444                 case Types.QUESTION:
    2445                 case Types.INFIX_OPERATOR:
    2446                 {
    2447                     stack.shiftIfTopIsAnExpression( "infix operators may only follow expressions" );
    2448                     break;
    2449                 }
    2450
    2451
    2452                 case Types.LEFT_PARENTHESIS:
    2453                 {
    2454                     //
    2455                     // Method calls don't make it here (see REDUCE).  It is
    2456                     // either a sub-expression or a cast.
    2457
    2458                     boolean condition = stack.atStartOfExpression() || (stack.topIsAnOperator() && !stack.top().isA(Types.DEREFERENCE_OPERATOR));
    2459                     stack.shiftIf( condition, "sub-expression not valid at this position" );
    2460                     break;
    2461                 }
    2462
    2463
    2464
    2465                 case Types.LEFT_CURLY_BRACE:
    2466                 {
    2467                     if( stack.atStartOfExpression() || stack.topIsAnOperator() || stack.top().isA(Types.SYNTH_METHOD_CALL) )
    2468                     {
    2469                         stack.push( closureExpression() );
    2470                     }
    2471                     else
    2472                     {
    2473                         error( "closure not valid in this context" );
    2474                     }
    2475                     break;
    2476                 }
    2477
    2478
    2479                 case Types.LEFT_SQUARE_BRACKET:
    2480                 {
    2481                     boolean isMap = false, insist = false;
    2482                     if( stack.topIsAnExpression() )
    2483                     {
    2484                         insist = true;
    2485                     }
    2486                     stack.push( listOrMapExpression(isMap, insist) );
    2487                     break;
    2488                 }
    2489
    2490
    2491                 case Types.KEYWORD_NEW:
    2492                 {
    2493                     if( stack.atStartOfExpression() || stack.topIsAnOperator() )
    2494                     {
    2495                         stack.push( newExpression() );
    2496                     }
    2497                     else
    2498                     {
    2499                         error( "new can follow the start of an expression or another operator" );
    2500                     }
    2501                     break;
    2502                 }
    2503
    2504
    2505                 case Types.KEYWORD_INSTANCEOF:
    2506                 {
    2507                     stack.shiftIf( stack.topIsAnExpression(), "instanceof may only follow an expression" );
    2508                     break;
    2509                 }
    2510
    2511
    2512                 default:
    2513                 {
    2514
    2515                     //
    2516                     // All other operators are caught during REDUCE, so if it makes
    2517                     // it here, it's either the end of the expression, or an error.
    2518
    2519                     if( stack.size() == 1 && stack.topIsAnExpression() )
    2520                     {
    2521                         break MAIN_LOOP;                          // <<< FLOW CONTROL <<<<<<<<<
    2522                     }
    2523                     else
    2524                     {
    2525                         error();
    2526                     }
    2527                 }
    2528
    2529
    2530             }
    2531
    2532
    2533
    2534             //
    2535             // In the REDUCE phase, we try to find ways to convert several stack
    2536             // elements (and maybe one lookahead token) into a single expression.
    2537             // We retry the REDUCE as long as it succeeds.  Note that reductions
    2538             // are ONLY possible when the top of the stack is an expression.
    2539
    2540             boolean checkAgain = false, skipPatterns = false;
    2541             CSTNode top0 = null, top1 = null, top2 = null;
    2542             int nextPrecedence = 0, top1Precedence = 0;
    2543
    2544             REDUCE: do
    2545             {
    2546                 if( !stack.topIsAnExpression() && !ExpressionSupport.isAPotentialTypeName(stack.top(), false) )
    2547                 {
    2548                     break;
    2549                 }
    2550
    2551
    2552                 //
    2553                 // We reduce at most once per iteration, so we collect info here.
    2554
    2555                 checkAgain   = false;
    2556                 skipPatterns = false;
    2557
    2558                 top0 = stack.top();
    2559                 top1 = stack.top(1);
    2560                 top2 = stack.top(2);
    2561
    2562                 next = la( stack );
    2563
    2564                 // System.out.println( "expression() stack for reduce: " + stack );
    2565                 // System.out.println( "expression() next token for reduce: " + next );
    2566
    2567                 nextPrecedence = Types.getPrecedence( next.getMeaning(), false );
    2568                 top1Precedence = Types.getPrecedence( top1.getMeaning(), false );
    2569
    2570
    2571
    2572               //---------------------------------------------------------------
    2573               // UNUSUAL STUFF FIRST
    2574
    2575
    2576                 //
    2577                 // Not really an operator at all, if top1 is a "(" and next is an ")",
    2578                 // we should reduce.  Extra processing is needed because the "(" is not
    2579                 // the type of an expression.
    2580
    2581                 if( top1.isA(Types.LEFT_PARENTHESIS) )
    2582                 {
    2583                     if( next.isA(Types.RIGHT_PARENTHESIS) )
    2584                     {
    2585                         consume();
    2586
    2587                         //
    2588                         // To simplify things, cast operators MUST appear on the same line
    2589                         // as the start of their operands.  Without name lookup, we can't
    2590                         // be sure that even things that look like casts are, but we assume
    2591                         // they are and let later phases correct, where necessary.
    2592
    2593                         next = la(true); // XXX the precludes is true for GString. Seems wrong
    2594                         boolean castPrecluded = next.isA(Types.NEWLINE) || next.isA(Types.PRECLUDES_CAST_OPERATOR);
    2595
    2596                         if( ExpressionSupport.isAPotentialTypeName(top0, false) && !castPrecluded )
    2597                         {
    2598                             CSTNode   name = stack.pop();
    2599                             Reduction cast = ((Token)stack.pop()).asReduction( name );
    2600                             cast.setMeaning( Types.SYNTH_CAST );
    2601                             stack.push( cast );
    2602                         }
    2603                         else
    2604                         {
    2605                             CSTNode subexpression = stack.pop();
    2606                             stack.pop();
    2607                             stack.push( subexpression );
    2608                         }
    2609
    2610                         checkAgain = true;
    2611                         continue;                             // <<< LOOP CONTROL <<<<<<<<<
    2612                     }
    2613                     else
    2614                     {
    2615                         skipPatterns = true;
    2616                     }
    2617                 }
    2618
    2619
    2620                 //
    2621                 // Highest precedence: "new".  If it is preceeded on the stack by
    2622                 // a ".", what preceeds the "." is the context for the new, and
    2623                 // we'll have to do some rewriting....  Note that SHIFT will only
    2624                 // shift a "new" if it is preceeded by nothing or an operator,
    2625                 // and it will only shift a "." if it is preceeded by an expression.
    2626                 // Therefore, we can assume any preceeding "." is an operator.
    2627
    2628                 if( top0.isA(Types.KEYWORD_NEW) && !top0.isAnExpression() )
    2629                 {
    2630                     top0.markAsExpression();
    2631
    2632                     if( top1.isA(Types.DOT) )
    2633                     {
    2634                         CSTNode theNew  = stack.pop();
    2635                         CSTNode theDot  = stack.pop();
    2636                         CSTNode context = stack.pop();
    2637
    2638                         theNew.set( 1, context );
    2639                         stack.push( theNew );
    2640
    2641                         checkAgain = true;
    2642                         continue;                             // <<< LOOP CONTROL <<<<<<<<<
    2643                     }
    2644                 }
    2645
    2646
    2647                 //
    2648                 // Not unusual, but handled here to simplify precendence handling for
    2649                 // the rest of the unusual stuff: dereference operators are left-associative.
    2650
    2651                 if( top1.isA(Types.DEREFERENCE_OPERATOR) && !top0.hasChildren() )
    2652                 {
    2653                     stack.reduce( 3, 1, true );
    2654
    2655                     checkAgain = true;
    2656                     continue;                                 // <<< LOOP CONTROL <<<<<<<<<
    2657                 }
    2658
    2659
    2660
    2661                 //
    2662                 // Next precedence, array offsets.  Because we allow lists and ranges
    2663                 // and such inside list expressions, all lists will have been processed
    2664                 // to a SYNTH_LISTH during SHIFT.  Here we do some rewriting, where
    2665                 // necessary.  Empty array offsets are only allowed on types, and we
    2666                 // run the appropriate conversion in that case.
    2667
    2668                 if( top0.isA(Types.SYNTH_LIST) && top1.isAnExpression() || ExpressionSupport.isAPotentialTypeName(top1, false) )
    2669                 {
    2670                     //
    2671                     // Empty list is an array type
    2672
    2673                     if( !top0.hasChildren() )
    2674                     {
    2675                         boolean typePreceeds   = ExpressionSupport.isAPotentialTypeName(top1, false);
    2676                         boolean potentialCast  = top2.isA(Types.LEFT_PARENTHESIS);
    2677                         boolean potentialDecl  = top2.isA(Types.LEFT_PARENTHESIS) || top2.isA(Types.UNKNOWN);
    2678                         boolean classReference = next.isA(Types.DOT) && la(2).isA(Types.KEYWORD_CLASS);
    2679                         if( !(typePreceeds && (potentialCast || potentialDecl || classReference)) )
    2680                         {
    2681                             error( "empty square brackets are only valid on type names" );
    2682                         }
    2683
    2684                         //
    2685                         // Okay, we have an array type.  We now convert the list and
    2686                         // expression to an array type, and slurp any further dimensions
    2687                         // off the lookahead.
    2688
    2689                         Reduction array = stack.pop().asReduction();
    2690                         array.setMeaning( Types.LEFT_SQUARE_BRACKET );
    2691                         array.add( stack.pop() );
    2692
    2693                         while( lt(true) == Types.LEFT_SQUARE_BRACKET )
    2694                         {
    2695                             array = consume( Types.LEFT_SQUARE_BRACKET ).asReduction( array );
    2696                             consume( Types.RIGHT_SQUARE_BRACKET );
    2697                         }
    2698
    2699
    2700                         //
    2701                         // One last decision: variable type declaration, or
    2702                         // cast, or class reference...
    2703
    2704                         if( classReference )
    2705                         {
    2706                             CSTNode reference = consume(Types.DOT).asReduction(array, consume(Types.KEYWORD_CLASS));
    2707                             reference.markAsExpression();
    2708                             stack.push( reference );
    2709
    2710                         }
    2711                         else if( lt(true) == Types.IDENTIFIER && lt(2) == Types.EQUAL )
    2712                         {
    2713                             stack.push( variableDeclarationExpression(array) );
    2714                         }
    2715                         else if( stack.top().isA(Types.LEFT_PARENTHESIS) && la(true).isA(Types.RIGHT_PARENTHESIS) )
    2716                         {
    2717                             CSTNode cast = ((Token)stack.pop()).asReduction( array );
    2718                             cast.setMeaning( Types.SYNTH_CAST );
    2719                             stack.push( cast );
    2720                             consume( Types.RIGHT_PARENTHESIS );
    2721                         }
    2722                         else
    2723                         {
    2724                             error( "found array type where none expected" );
    2725                         }
    2726                     }
    2727
    2728
    2729                     //
    2730                     // Non-empty list is an offset (probably)
    2731
    2732                     else
    2733                     {
    2734                         CSTNode list = stack.pop();
    2735                         CSTNode base = stack.pop();
    2736
    2737                         Reduction result = ((Token)list.get(0)).dup().asReduction();
    2738                         result.setMeaning( Types.LEFT_SQUARE_BRACKET );
    2739                         result.add( base );
    2740
    2741                         if( list.children() == 1 )
    2742                         {
    2743                             result.add( list.get(1) );
    2744                         }
    2745                         else
    2746                         {
    2747                             result.add( list );
    2748                         }
    2749
    2750                         result.markAsExpression();
    2751                         stack.push( result );
    2752                     }
    2753
    2754                     checkAgain = true;
    2755                     continue;                                 // <<< LOOP CONTROL <<<<<<<<<
    2756
    2757                 }
    2758
    2759
    2760
    2761                 //
    2762                 // Next precedence: typed variable declarations.  If the top of stack
    2763                 // isAPotentialTypeName(), la(true) is an identifier, and la(2) is
    2764                 // an "=", it's a declaration.
    2765
    2766                 if( la(true).isA(Types.IDENTIFIER) && lt(2) == Types.EQUALS && ExpressionSupport.isAPotentialTypeName(top0, false) )
    2767                 {
    2768                     stack.push( variableDeclarationExpression(stack.pop()) );
    2769
    2770                     checkAgain = true;
    2771                     continue;                                 // <<< LOOP CONTROL <<<<<<<<<
    2772                 }
    2773
    2774
    2775                 //
    2776                 // Before getting to method call handling proper, we should check for any
    2777                 // pending bookkeeping.  If the top of stack is a closure and the element
    2778                 // before it is a method call, the closure is either one of its parameters
    2779                 // or an error.
    2780
    2781                 if( top1.isA(Types.SYNTH_METHOD_CALL) && top0.isA(Types.SYNTH_CLOSURE) )
    2782                 {
    2783                     CSTNode parameters = top1.get(2);
    2784
    2785                     int last = parameters.size() - 1;
    2786                     if( last > 0 && parameters.get(last).isA(Types.SYNTH_CLOSURE) )
    2787                     {
    2788                         error( "you may only pass one closure to a method implicitly" );
    2789                     }
    2790
    2791                     parameters.add( stack.pop() );
    2792
    2793                     checkAgain = true;
    2794                     continue;                                 // <<< LOOP CONTROL <<<<<<<<<
    2795                 }
    2796
    2797
    2798                 //
    2799                 // Next precedence: method calls and typed declarations.  If the top of stack
    2800                 // isInvokable() and la(stack) is an "(", an "{", or a simple expression, it's
    2801                 // a method call.  We leave the closure for the next SHIFT.
    2802
    2803                 if( ExpressionSupport.isInvokable(top0) && (next.isA(Types.LEFT_CURLY_BRACE) || la(true).isA(Types.METHOD_CALL_STARTERS)) )
    2804                 {
    2805                     // System.out.println( "making a method call of " + top0 );
    2806
    2807                     CSTNode name = stack.pop();
    2808
    2809                     Reduction method = null;
    2810                     switch( next.getMeaning() )
    2811                     {
    2812                         case Types.LEFT_PARENTHESIS:
    2813                             method = consume().asReduction();
    2814                             method.add( name );
    2815                             method.add( la().isA(Types.RIGHT_PARENTHESIS) ? Reduction.newContainer() : parameterList() );
    2816                             consume( Types.RIGHT_PARENTHESIS );
    2817                             break;
    2818
    2819                         case Types.LEFT_CURLY_BRACE:
    2820                             method = Token.newSymbol( Types.LEFT_PARENTHESIS, next.getStartLine(), next.getStartColumn() ).asReduction();
    2821                             method.add( name );
    2822                             method.add( Reduction.newContainer() );
    2823                             break;
    2824
    2825                         default:
    2826                             method = Token.newSymbol( Types.LEFT_PARENTHESIS, next.getStartLine(), next.getStartColumn() ).asReduction();
    2827                             method.add( name );
    2828                             method.add( parameterList() );
    2829                             break;
    2830                     }
    2831
    2832                     method.setMeaning( Types.SYNTH_METHOD_CALL );
    2833                     method.markAsExpression();
    2834
    2835                     stack.push( method );
    2836
    2837                     if( lt() != Types.LEFT_CURLY_BRACE )
    2838                     {
    2839                         checkAgain = true;
    2840                     }
    2841
    2842                     continue;                                 // <<< LOOP CONTROL <<<<<<<<<
    2843                 }
    2844
    2845
    2846                 //
    2847                 // Handle postfix operators next.  We have to check for acceptable
    2848                 // precedence before doing it.  All the higher precedence reductions
    2849                 // have already been checked.
    2850
    2851                 if( next.isA(Types.POSTFIX_OPERATOR) && stack.topIsAnExpression() )
    2852                 {
    2853                     if( !ExpressionSupport.isAVariable(stack.top()) )
    2854                     {
    2855                         error( "increment/decrement operators can only be applied to variables" );
    2856                     }
    2857
    2858                     Types.makePostfix( next, true );
    2859
    2860                     stack.shift();
    2861                     stack.reduce( 2, 0, true );
    2862
    2863                     checkAgain = true;
    2864                     continue;                                 // <<< LOOP CONTROL <<<<<<<<<
    2865                 }
    2866
    2867
    2868                 //
    2869                 // The ternary operator will be seen twice.  The first reduction is
    2870                 // infix when there is a ":" on lookahed.  The second reduction is
    2871                 // prefix when there is a lower-precedence operator on lookahed.
    2872                 // The ternary operator is right-associative.  Note that
    2873                 // Types.getPrecedence() on a ternary operator returns 10.
    2874
    2875                 if( top1.isA(Types.QUESTION) )
    2876                 {
    2877                     boolean reduce = false;
    2878
    2879                     if( la().isA(Types.COLON) )
    2880                     {
    2881                         if( top1.hasChildren() )
    2882                         {
    2883                             error( "ternary operator can have only three clauses" );
    2884                         }
    2885
    2886                         consume();
    2887                         stack.reduce( 3, 1, false );
    2888                         checkAgain = true;
    2889                     }
    2890                     else if( Types.getPrecedence(next.getMeaning(), false) < 10 )
    2891                     {
    2892                         stack.reduce( 2, 1, false );
    2893                         stack.top().setMeaning( Types.SYNTH_TERNARY );
    2894                         checkAgain = true;
    2895                     }
    2896
    2897
    2898                     continue;                                 // <<< LOOP CONTROL <<<<<<<<<
    2899                 }
    2900
    2901
    2902
    2903
    2904               //---------------------------------------------------------------
    2905               // PATTERN STUFF SECOND
    2906
    2907
    2908                 //
    2909                 // Note that because of the loop control above, we get here only if none
    2910                 // of the above options matched.
    2911                 //
    2912                 // So, everything else we handle generically: top1 will be an operator, and
    2913                 // will be reduced or not with top0 and possibly top2, depending on the
    2914                 // cardinality and associativity of the operator, and the type of la().
    2915
    2916                 if( skipPatterns || !ExpressionSupport.isAnOperator(top1, false) )
    2917                 {
    2918                     break;                                    // <<< LOOP CONTROL <<<<<<<<<
    2919                 }
    2920
    2921
    2922                 switch( top1.getMeaningAs(EXPRESSION_REDUCE_HANDLERS) )
    2923                 {
    2924                     //
    2925                     // Prefix increment/decrement operators aren't always valid, so we
    2926                     // handle the separately from the other prefix operators.
    2927
    2928                     case Types.PREFIX_PLUS_PLUS:
    2929                     case Types.PREFIX_MINUS_MINUS:
    2930                     {
    2931                         if( nextPrecedence < top1Precedence )
    2932                         {
    2933                             if( !ExpressionSupport.isAVariable(stack.top()) )
    2934                             {
    2935                                 error( "increment/decrement operators can only be applied to variables" );
    2936                             }
    2937
    2938                             stack.reduce( 2, 1, true );
    2939                             checkAgain = true;
    2940                        }
    2941
    2942                        break;
    2943                     }
    2944
    2945
    2946                     //
    2947                     // All other prefix operators.  They are all right-associative.
    2948
    2949                     case Types.PURE_PREFIX_OPERATOR:
    2950                     {
    2951                         if( nextPrecedence < top1Precedence )
    2952                         {
    2953                             stack.reduce( 2, 1, true );
    2954                             checkAgain = true;
    2955                         }
    2956
    2957                         break;
    2958                     }
    2959
    2960
    2961                     //
    2962                     // Handle the assignment operators.  They are all right-associative.
    2963
    2964                     case Types.ASSIGNMENT_OPERATOR:
    2965                     {
    2966                         if( nextPrecedence < top1Precedence )
    2967                         {
    2968                             stack.reduce( 3, 1, true );
    2969                             checkAgain = true;
    2970                         }
    2971
    2972                         break;
    2973                     }
    2974
    2975
    2976                     //
    2977                     // Handle the instenceof keyword.  The rhs has to be a potential type.
    2978
    2979                     case Types.KEYWORD_INSTANCEOF:
    2980                     {
    2981                         if( nextPrecedence < top1Precedence )
    2982                         {
    2983                             if( !ExpressionSupport.isAPotentialTypeName(top0, false) )
    2984                             {
    2985                                 error( "instanceof right-hand side must be a valid type name" );
    2986                             }
    2987
    2988                             stack.reduce( 3, 1, true );
    2989                             checkAgain = true;
    2990                         }
    2991
    2992                         break;
    2993                     }
    2994
    2995
    2996                     //
    2997                     // Handle all other infix operators.  They are all left-associative.
    2998
    2999                     case Types.INFIX_OPERATOR:
    3000                     {
    3001                         if( nextPrecedence <= top1Precedence )
    3002                         {
    3003                             stack.reduce( 3, 1, true );
    3004                             checkAgain = true;
    3005                         }
    3006
    3007                         break;
    3008                     }
    3009
    3010
    3011                     //
    3012                     // Anything else in top1 is an bug.
    3013
    3014                     default:
    3015                     {
    3016                         throw new GroovyBugError( "found unexpected token during REDUCE [" + top1.getMeaning() + "]" );
    3017                     }
    3018                 }
    3019
    3020             } while( checkAgain );
    3021
    3022         } while( true );
    3023
    3024
    3025         if( stack.size() == 1 && stack.topIsAnExpression() )
    3026         {
    3027             expression = stack.pop();
    3028         }
    3029         else
    3030         {
    3031             error( "expression incomplete" );
    3032         }
    3033
    3034
    3035         // System.out.println( "EXITING EXPRESSION " + id );
    3036         return expression;
    3037     }
    3038
    3039
    3040     private static final int EXPRESSION_SHIFT_HANDLERS[] = {
    3041           Types.GSTRING_START
    3042         , Types.CREATABLE_PRIMITIVE_TYPE
    3043         , Types.SIMPLE_EXPRESSION
    3044         , Types.KEYWORD_IDENTIFIER
    3045         , Types.ASSIGNMENT_OPERATOR
    3046         , Types.PREFIX_OR_INFIX_OPERATOR
    3047         , Types.PREFIX_OPERATOR
    3048         , Types.QUESTION
    3049         , Types.INFIX_OPERATOR
    3050         , Types.LEFT_PARENTHESIS
    3051         , Types.LEFT_CURLY_BRACE
    3052         , Types.LEFT_SQUARE_BRACKET
    3053         , Types.KEYWORD_NEW
    3054         , Types.KEYWORD_INSTANCEOF
    3055     };
    3056
    3057     private static final int EXPRESSION_REDUCE_HANDLERS[] = {
    3058           Types.PREFIX_PLUS_PLUS
    3059         , Types.PREFIX_MINUS_MINUS
    3060         , Types.PURE_PREFIX_OPERATOR
    3061         , Types.ASSIGNMENT_OPERATOR
    3062         , Types.KEYWORD_INSTANCEOF
    3063         , Types.INFIX_OPERATOR
    3064     };
    3065
    3066
    3067
    3068    /***
    3069     *  Processes a typed variable declaration.  Without the type, it's a
    3070     *  assignment expression instead (no comma support).  The datatype
    3071     *  has already been identified and is passed in.
    3072     *  <p>
    3073     *  Grammar: <pre>
    3074     *     variableDeclarationExpression
    3075     *        = datatype (nameDeclaration "=" expression)
    3076     *                   ("," nameDeclaration "=" expression)*
    3077     *  </pre>
    3078     *  <p>
    3079     *  CST: <pre>
    3080     *     statement   = { :<SYNTH_VARIABLE_DECLARATION> datatype declaration+ }
    3081     *     declaration = { <identifier> expression }
    3082     *
    3083     *     see expression()
    3084     *  </pre>
    3085     */
    3086
    3087     protected Reduction variableDeclarationExpression( CSTNode datatype ) throws SyntaxException, CompilationFailedException
    3088     {
    3089         Reduction expression = ((Token)datatype.get(0)).dup().asReduction( datatype );  // done for line number on SYNTH
    3090         expression.setMeaning( Types.SYNTH_VARIABLE_DECLARATION );
    3091
    3092         boolean done = false;
    3093         do
    3094         {
    3095             try
    3096             {
    3097                 Reduction declaration = (Reduction)expression.add( nameDeclaration(false).asReduction() );
    3098                 consume( Types.EQUAL );
    3099                 declaration.add( expression() );
    3100             }
    3101             catch( SyntaxException e )
    3102             {
    3103                 controller.addError( e );
    3104                 recover( Types.ANY_END_OF_STATEMENT );
    3105             }
    3106
    3107             if( lt() == Types.COMMA )
    3108             {
    3109                 consume( Types.COMMA );
    3110             }
    3111             else
    3112             {
    3113                 done = true;
    3114             }
    3115
    3116         } while( !done );
    3117
    3118
    3119         return expression;
    3120     }
    3121
    3122
    3123
    3124    /***
    3125     *  Processes a GString.
    3126     *  <p>
    3127     *  Grammar: <pre>
    3128     *     gstring = (<text>? "$" "{" expression "}" <text>?)*
    3129     *  </pre>
    3130     *  <p>
    3131     *  CST: <pre>
    3132     *     gstring = { <full-text>:SYNTH_GSTRING (segment|expression)* }
    3133     *
    3134     *     see expression()
    3135     *  </pre>
    3136     */
    3137
    3138     protected Reduction gstring() throws SyntaxException, CompilationFailedException
    3139     {
    3140         // int id = nestCount++;
    3141         // System.out.println( "ENTERING GSTRING " + id );
    3142
    3143         Reduction data = Reduction.newContainer();
    3144
    3145         consume( Types.GSTRING_START );
    3146
    3147         while( lt() != Types.GSTRING_END && lt() != Types.EOF )
    3148         {
    3149             switch( lt() )
    3150             {
    3151                 case Types.STRING:
    3152                     data.add( consume() );
    3153                     break;
    3154
    3155                 case Types.GSTRING_EXPRESSION_START:
    3156                     consume();
    3157                     data.add( expression() );
    3158                     consume( Types.GSTRING_EXPRESSION_END );
    3159                     break;
    3160
    3161                 default:
    3162                     throw new GroovyBugError( "gstring found invalid token: " + la() );
    3163             }
    3164         }
    3165
    3166         Reduction complete = consume( Types.GSTRING_END ).asReduction();
    3167         complete.addChildrenOf( data );
    3168
    3169         complete.setMeaning( Types.SYNTH_GSTRING );
    3170
    3171         // System.out.println( "EXITING GSTRING " + id );
    3172         return complete;
    3173     }
    3174
    3175
    3176
    3177
    3178    /***
    3179     *  Processes a NON-EMPTY parameter list, as supplied on either a method invokation or
    3180     *  a closure invokation.  Reads parameters until something that doesn't belong
    3181     *  is found.
    3182     *  <p>
    3183     *  Grammar: <pre>
    3184     *     parameterList = (regular "," named) | named
    3185     *     regular = parameter ("," parameter)*
    3186     *     named   = nameReference ":" parameter ("," nameReference ":" parameter)*
    3187     *
    3188     *     parameter = expression
    3189     *  </pre>
    3190     *  <p>
    3191     *  CST: <pre>
    3192     *     parameterList = { <null> regular* named* }
    3193     *     regular = expression
    3194     *     named   = { ":" <identifier> expression }
    3195     *
    3196     *     see expression()
    3197     *  </pre>
    3198     */
    3199
    3200     protected Reduction parameterList() throws SyntaxException, CompilationFailedException
    3201     {
    3202         // int id = nestCount++;
    3203         // System.out.println( "ENTERING PARAMETER LIST " + id );
    3204
    3205         Reduction list  = Reduction.newContainer();
    3206         Reduction named = null;
    3207
    3208         boolean done = false;
    3209
    3210         do
    3211         {
    3212             if( la().canMean(Types.IDENTIFIER) && la(2).isA(Types.COLON) )
    3213             {
    3214                 if( named == null )
    3215                 {
    3216                     named = Token.newPlaceholder(Types.SYNTH_MAP).asReduction();
    3217                     list.add( named );
    3218                 }
    3219
    3220                 Token name = nameReference(false);
    3221                 name.setMeaning( Types.STRING );
    3222
    3223                 named.add( consume(Types.COLON).asReduction(name, expression()) );
    3224             }
    3225             else
    3226             {
    3227                 list.add( expression() );
    3228             }
    3229
    3230
    3231             if( lt() == Types.COMMA )
    3232             {
    3233                 consume();
    3234             }
    3235             else
    3236             {
    3237                 done = true;
    3238             }
    3239
    3240
    3241         } while( !done );
    3242
    3243         // System.out.println( "EXITING PARAMETER LIST " + id );
    3244         return list;
    3245     }
    3246
    3247
    3248
    3249    /***
    3250     *  Processes a "new" expression.  Handles optional constructors, array
    3251     *  initializations, closure arguments, and anonymous classes.  In order
    3252     *  to support anonymous classes, anonymous closures are not allowed.
    3253     *  <p>
    3254     *  Grammar: <pre>
    3255     *     newExpression = "new" scalarDatatype (array|init)?
    3256     *     array = ( "[" expression "]" )+ | ( ("[" "]")+ newArrayInitializer )
    3257     *     init  = "(" parameterList? ")" (typeBody | closureExpression)?
    3258     *  </pre>
    3259     *  <p>
    3260     *  CST: <pre>
    3261     *     new = { "new" arrayType     dimensions newArrayInitializer? }
    3262     *         | { "new" scalarDataype (parameterList|{<null>}) typeBody? }
    3263     *
    3264     *     arrayType  = { "{" (arrayType | scalarDatatype) }
    3265     *     dimensions = { <null> expression+ } | {}
    3266     *
    3267     *     see expression()
    3268     *     see scalarDatatype()
    3269     *     see typeBody()
    3270     *  </pre>
    3271     */
    3272
    3273     protected Reduction newExpression() throws SyntaxException, CompilationFailedException
    3274     {
    3275         // int id = nestCount++;
    3276         // System.out.println( "ENTERING NEW " + id );
    3277
    3278         Reduction expression = consume(Types.KEYWORD_NEW).asReduction();
    3279         CSTNode   scalarType = scalarDatatype(false);
    3280
    3281         if( lt(true) == Types.LEFT_SQUARE_BRACKET )
    3282         {
    3283             //
    3284             // First up, figure out the actual type and any
    3285             // stated dimensions.
    3286
    3287             boolean   implicit   = (lt(2) == Types.RIGHT_SQUARE_BRACKET);
    3288             Reduction dimensions = implicit ? Reduction.EMPTY : Reduction.newContainer();
    3289             int       count      = 0;
    3290             CSTNode   arrayType  = scalarType;
    3291
    3292             while( lt(true) == Types.LEFT_SQUARE_BRACKET )
    3293             {
    3294                 arrayType = consume(Types.LEFT_SQUARE_BRACKET).asReduction( arrayType );
    3295                 count++;
    3296
    3297                 if( !implicit )
    3298                 {
    3299                     dimensions.add( expression() );
    3300                 }
    3301
    3302                 consume(Types.RIGHT_SQUARE_BRACKET);
    3303             }
    3304
    3305             expression.add( arrayType );
    3306             expression.add( dimensions );
    3307
    3308             //
    3309             // If implicit, there must be initialization data
    3310
    3311             if( implicit )
    3312             {
    3313                 expression.add( tupleExpression(0, count) );
    3314             }
    3315
    3316         }
    3317
    3318         else
    3319         {
    3320             expression.add( scalarType );
    3321
    3322
    3323             //
    3324             // Process the constructor call
    3325
    3326             Reduction parameters = null;
    3327
    3328             consume( Types.LEFT_PARENTHESIS );
    3329             parameters = (lt() == Types.RIGHT_PARENTHESIS ? Reduction.newContainer() : parameterList());
    3330             consume( Types.RIGHT_PARENTHESIS );
    3331
    3332             expression.add( parameters );
    3333
    3334
    3335             //
    3336             // If a "{" follows, it's a class body or a closure...
    3337
    3338             if( lt() == Types.LEFT_CURLY_BRACE )
    3339             {
    3340                 if( lt(2) == Types.PIPE || lt(2) == Types.DOUBLE_PIPE )
    3341                 {
    3342                     parameters.add( closureExpression() );
    3343                 }
    3344                 else
    3345                 {
    3346                     expression.add( typeBody(true, false, false) );
    3347                 }
    3348             }
    3349         }
    3350
    3351         // System.out.println( "EXITING NEW " + id );
    3352         return expression;
    3353     }
    3354
    3355
    3356
    3357    /***
    3358     *  Processes a "new" array initializer expression.
    3359     *  <p>
    3360     *  Grammar: <pre>
    3361     *     tupleExpression = "{" (tupleExpression | (expression ("," expression))? "}"
    3362     *  </pre>
    3363     *  <p>
    3364     *  CST: <pre>
    3365     *     initializer = { "{":SYNTH_TUPLE (initializer*|expression*) }
    3366     *
    3367     *     see expression()
    3368     *  </pre>
    3369     */
    3370
    3371     protected Reduction tupleExpression( int level, int depth ) throws SyntaxException, CompilationFailedException
    3372     {
    3373         Reduction data = consume(Types.LEFT_CURLY_BRACE).asReduction();
    3374         data.setMeaning( Types.SYNTH_TUPLE );
    3375
    3376         if( lt() != Types.RIGHT_CURLY_BRACE )
    3377         {
    3378             int    child = level + 1;
    3379             boolean leaf = (child == depth);
    3380
    3381             do
    3382             {
    3383                 data.add( leaf ? expression() : tupleExpression(child, depth) );
    3384
    3385             } while( lt() == Types.COMMA && (consume() != null) );
    3386         }
    3387
    3388         consume( Types.RIGHT_CURLY_BRACE );
    3389
    3390         return data;
    3391     }
    3392
    3393
    3394
    3395    /***
    3396     *  Processes a closure expression.
    3397     *  <p>
    3398     *  Grammar: <pre>
    3399     *     closureExpression = "{" parameters statement* "}"
    3400     *     parameters = ("|" parameterDeclarationList "|")?
    3401     *  </pre>
    3402     *  <p>
    3403     *  CST: <pre>
    3404     *     initializer = { "{":SYNTH_CLOSURE parameters statements }
    3405     *     parameters  = parameterDeclarationList | { <null> }
    3406     *     statements  = { <null> statement* }
    3407     *
    3408     *     see parameterDeclarationList()
    3409     *     see statement()
    3410     *  </pre>
    3411     */
    3412
    3413     protected Reduction closureExpression( ) throws SyntaxException, CompilationFailedException
    3414     {
    3415         // int id = nestCount++;
    3416         // System.out.println( "ENTERING CLOSURE EXPRESSION " + id );
    3417
    3418         Reduction closure = consume(Types.LEFT_CURLY_BRACE).asReduction();
    3419         closure.setMeaning( Types.SYNTH_CLOSURE );
    3420         boolean specified = (lt() == Types.PIPE) || (lt() == Types.DOUBLE_PIPE);
    3421
    3422         //
    3423         // DEPRECATED: the old syntax for parameters had a | only
    3424         // at the end of the parameter list.  The new syntax has
    3425         // two pipes or none.  For now, we attempt to support the
    3426         // old syntax.  It can mistake a variable declaration
    3427         // for a parameter declaration, though, so it may cause more
    3428         // trouble than it's worth.  This if() and the one below
    3429         // (also marked) should be removed before v1.0.
    3430
    3431         if( !specified )
    3432         {
    3433             getTokenStream().checkpoint();
    3434             CSTNode type = optionalDatatype( true, false );
    3435             if( lt() == Types.IDENTIFIER && (lt(2) == Types.PIPE || lt(2) == Types.COMMA) )
    3436             {
    3437                 specified = true;
    3438             }
    3439
    3440             getTokenStream().restore();
    3441         }
    3442
    3443
    3444         //
    3445         // If the parameter list is specified, process it.
    3446
    3447         if( specified )
    3448         {
    3449             if( lt() == Types.DOUBLE_PIPE )
    3450             {
    3451                 consume( Types.DOUBLE_PIPE );
    3452                 closure.add( Reduction.newContainer() );
    3453             }
    3454             else
    3455             {
    3456                 //
    3457                 // DEPRECATED: further support for note above, this consume()
    3458                 // should not be conditional after the above code is removed.
    3459
    3460                 if( lt() == Types.PIPE )
    3461                 {
    3462                     consume(Types.PIPE);
    3463                 }
    3464
    3465                 closure.add( parameterDeclarationList() );
    3466                 consume(Types.PIPE);
    3467             }
    3468         }
    3469         else
    3470         {
    3471             closure.add( Reduction.newContainer() );
    3472         }
    3473
    3474
    3475         //
    3476         // Finally, process the statements.
    3477
    3478         closure.add( statementsUntilRightCurly() );
    3479         consume( Types.RIGHT_CURLY_BRACE );
    3480
    3481         // System.out.println( "EXITING CLOSURE EXPRESSION " + id );
    3482         return closure;
    3483     }
    3484
    3485
    3486
    3487    /***
    3488     *  Processes a list or map expression.
    3489     *  <p>
    3490     *  Grammar: <pre>
    3491     *     listOrMapExpression = list | map
    3492     *
    3493     *     list = "[" (expression ("," expression)*)? "]"
    3494     *
    3495     *     map     = "[" (":" | mapping+) "]"
    3496     *     mapping = expression ":" expression
    3497     *  </pre>
    3498     *  <p>
    3499     *  CST: <pre>
    3500     *     list    = { "[":SYNTH_LIST expression* }
    3501     *     map     = { "[":SYNTH_MAP  mapping* }
    3502     *     mapping = { ":" expression expression }
    3503     *
    3504     *     see expression()
    3505     *  </pre>
    3506     */
    3507
    3508     protected Reduction listOrMapExpression( boolean isMap, boolean insist ) throws SyntaxException, CompilationFailedException
    3509     {
    3510         Reduction expression = consume(Types.LEFT_SQUARE_BRACKET).asReduction();
    3511         expression.setMeaning( Types.SYNTH_LIST );
    3512
    3513         if( lt() == Types.COLON )
    3514         {
    3515             if( !isMap && insist )
    3516             {
    3517                 error( "expected list" );
    3518             }
    3519
    3520             isMap = true;
    3521             expression.setMeaning( Types.SYNTH_MAP );
    3522             consume();
    3523             if( lt() != Types.RIGHT_SQUARE_BRACKET )
    3524             {
    3525                 error( "expected empty map" );
    3526             }
    3527         }
    3528
    3529
    3530         //
    3531         // Process the data.  On the first one, check if we are
    3532         // processing a map.  We assume not going in, as the empty
    3533         // map isn't relevant...
    3534
    3535         boolean done = (lt() == Types.RIGHT_SQUARE_BRACKET);
    3536
    3537         while( !done )
    3538         {
    3539             CSTNode element = expression();
    3540
    3541             if( !insist )
    3542             {
    3543                 insist = true;
    3544                 if( lt() == Types.COLON )
    3545                 {
    3546                     isMap = true;
    3547                     expression.setMeaning(Types.SYNTH_MAP);
    3548                 }
    3549             }
    3550
    3551             if( isMap )
    3552             {
    3553                 element = consume(Types.COLON).asReduction( element, expression() );
    3554             }
    3555
    3556             expression.add( element );
    3557
    3558             if( lt() == Types.COMMA ) { consume(); } else { done = true; }
    3559         }
    3560
    3561         consume(Types.RIGHT_SQUARE_BRACKET);
    3562
    3563         return expression;
    3564     }
    3565
    3566
    3567
    3568    /***
    3569     *  Synonym for <code>listOrMapExpression( false, false )</code>.
    3570     */
    3571
    3572     protected Reduction listOrMapExpression( ) throws SyntaxException, CompilationFailedException
    3573     {
    3574         return listOrMapExpression( false, false );
    3575     }
    3576
    3577
    3578
    3579
    3580
    3581
    3582   //---------------------------------------------------------------------------
    3583   // ERROR REPORTING
    3584
    3585
    3586    /***
    3587     *  Reports an error assembled from parts.
    3588     */
    3589
    3590     protected UnexpectedTokenException error( Token found, int[] expectedTypes, boolean throwIt, String comment ) throws SyntaxException
    3591     {
    3592         UnexpectedTokenException e = new UnexpectedTokenException( found, expectedTypes, comment );
    3593
    3594         if( throwIt )
    3595         {
    3596             throw e;
    3597         }
    3598
    3599         return e;
    3600     }
    3601
    3602
    3603    /***
    3604     *  Reports an error by generating and optionally throwing an
    3605     *  <code>UnexpectedTokenException</code>.
    3606     */
    3607
    3608     protected UnexpectedTokenException error( int[] expectedTypes, boolean throwIt, int k, String comment ) throws SyntaxException, CompilationFailedException
    3609     {
    3610         return error( la(k), expectedTypes, throwIt, comment );
    3611     }
    3612
    3613
    3614
    3615    /***
    3616     *  A synonym for <code>error( expectedTypes, throwIt, k, null )</code>.
    3617     */
    3618
    3619     protected UnexpectedTokenException error( int[] expectedTypes, boolean throwIt, int k ) throws SyntaxException, CompilationFailedException
    3620     {
    3621         return error( expectedTypes, throwIt, k, null );
    3622     }
    3623
    3624
    3625
    3626    /***
    3627     *  A synonym for <code>error( expectedTypes, true, 1, null )</code>.
    3628     */
    3629
    3630     protected void error( int[] expectedTypes ) throws SyntaxException, CompilationFailedException
    3631     {
    3632         throw error( expectedTypes, false, 1, null );
    3633     }
    3634
    3635
    3636
    3637    /***
    3638     *  A synonym for <code>error( null, true, 1, null )</code>.
    3639     */
    3640
    3641     protected void error() throws SyntaxException, CompilationFailedException
    3642     {
    3643         throw error( null, true, 1, null );
    3644     }
    3645
    3646
    3647
    3648    /***
    3649     *  A synonym for <code>error( null, true, 1, comment )</code>.
    3650     */
    3651
    3652     protected void error( String comment ) throws SyntaxException, CompilationFailedException
    3653     {
    3654         throw error( null, true, 1, comment );
    3655     }
    3656
    3657
    3658
    3659    /***
    3660     *  A synonym for <code>error( found, null, true, comment )</code>.
    3661     */
    3662
    3663     protected void error( Token found, String comment ) throws SyntaxException
    3664     {
    3665         throw error( found, null, true, comment );
    3666     }
    3667
    3668
    3669
    3670    /***
    3671     *  A scalar synonym of <code>error( expectedTypes )</code>.
    3672     */
    3673
    3674     protected void error( int expectedType ) throws SyntaxException, CompilationFailedException
    3675     {
    3676         error( new int[] { expectedType } );
    3677     }
    3678
    3679
    3680
    3681
    3682   //---------------------------------------------------------------------------
    3683   // ERROR RECOVERY
    3684
    3685
    3686    /***
    3687     *  Attempts to recover from an error by discarding input until a
    3688     *  known token is found.  It further guarantees that /at least/
    3689     *  one token will be eaten.
    3690     */
    3691
    3692     public void recover( int[] safe, boolean ignoreNewlines ) throws SyntaxException, CompilationFailedException
    3693     {
    3694         Token leading = la( ignoreNewlines );
    3695
    3696         while( true )
    3697         {
    3698             Token next = la( ignoreNewlines );
    3699             if( next.isA(Types.EOF) || next.isOneOf(safe) )
    3700             {
    3701                 break;
    3702             }
    3703             else
    3704             {
    3705                 consume( ignoreNewlines );
    3706             }
    3707         }
    3708
    3709         if( la(ignoreNewlines) == leading )
    3710         {
    3711             consume( ignoreNewlines );
    3712         }
    3713     }
    3714
    3715
    3716
    3717    /***
    3718     *  A scalar version of <code>recover( int[], boolean )</code>.
    3719     */
    3720
    3721     public void recover( int safe, boolean ignoreNewlines ) throws SyntaxException, CompilationFailedException
    3722     {
    3723         Token leading = la( ignoreNewlines );
    3724
    3725         while( true )
    3726         {
    3727             Token next = la( ignoreNewlines );
    3728             if( next.isA(Types.EOF) || next.isA(safe) )
    3729             {
    3730                 break;
    3731             }
    3732             else
    3733             {
    3734                 consume( ignoreNewlines );
    3735             }
    3736         }
    3737
    3738         if( la(ignoreNewlines) == leading )
    3739         {
    3740             consume( ignoreNewlines );
    3741         }
    3742     }
    3743
    3744
    3745
    3746    /***
    3747     *  A synonym for <code>recover( safe, false )</code>.
    3748     */
    3749
    3750     public void recover( int[] safe ) throws SyntaxException, CompilationFailedException
    3751     {
    3752         recover( safe, false );
    3753     }
    3754
    3755
    3756
    3757    /***
    3758     *  A synonm for the scalar <code>recover( safe, false )</code>.
    3759     */
    3760
    3761     public void recover( int safe ) throws SyntaxException, CompilationFailedException
    3762     {
    3763         recover( safe, false );
    3764     }
    3765
    3766
    3767
    3768    /***
    3769     *  A synonym for <code>recover( Types.ANY_END_OF_STATMENT, true )</code>.
    3770     */
    3771
    3772     public void recover( ) throws SyntaxException, CompilationFailedException
    3773     {
    3774         recover( Types.ANY_END_OF_STATEMENT, true );
    3775     }
    3776
    3777
    3778
    3779
    3780   //---------------------------------------------------------------------------
    3781   // TOKEN LOOKAHEAD
    3782
    3783
    3784    /***
    3785     *  Returns (without consuming) the next kth token in the underlying
    3786     *  token stream.  You can make newlines significant as needed.
    3787     *  Returns Token.EOF on end of stream.  k is counted from 1.
    3788     */
    3789
    3790     protected Token la( int k, boolean significantNewlines ) throws SyntaxException, CompilationFailedException
    3791     {
    3792         Token token = Token.NULL;
    3793
    3794         //
    3795         // Count down on k while counting up on streamK.
    3796         // NOTE: k starting at less than 1 is a mistake...
    3797         // This routine will reliably NOT return Token.NULL
    3798         // /unless/ it is actually in the stream.
    3799
    3800         try
    3801         {
    3802             int streamK = 1;
    3803             while( k > 0 && token.getMeaning() != Types.EOF )
    3804             {
    3805                 token = getTokenStream().la( streamK );
    3806                 streamK += 1;
    3807
    3808                 if( token == null  )
    3809                 {
    3810                     token = Token.EOF;
    3811                 }
    3812                 else if( token.getMeaning() == Types.NEWLINE )
    3813                 {
    3814                     if( significantNewlines )
    3815                     {
    3816                         k -= 1;
    3817                     }
    3818                 }
    3819                 else
    3820                 {
    3821                     k -= 1;
    3822                 }
    3823             }
    3824         }
    3825         catch( ReadException e )
    3826         {
    3827             controller.addFatalError( new SimpleMessage(e.getMessage()) );
    3828         }
    3829
    3830         return token;
    3831     }
    3832
    3833
    3834
    3835    /***
    3836     *  Synonym for <code>la( k, false )</code>.
    3837     */
    3838
    3839     protected Token la( int k ) throws SyntaxException, CompilationFailedException
    3840     {
    3841         return la( k, false );
    3842     }
    3843
    3844
    3845
    3846    /***
    3847     *  Synonym for <code>la( 1, significantNewlines )</code>.
    3848     */
    3849
    3850     protected Token la( boolean significantNewlines ) throws SyntaxException, CompilationFailedException
    3851     {
    3852         return la( 1, significantNewlines );
    3853     }
    3854
    3855
    3856
    3857    /***
    3858     * Synonym for <code>la( 1, false )</code>.
    3859     */
    3860
    3861     protected Token la() throws SyntaxException, CompilationFailedException
    3862     {
    3863         return la( 1, false );
    3864     }
    3865
    3866
    3867
    3868    /***
    3869     *  Special <code>la()</code> used by the expression parser.  It will get the next token
    3870     *  in the current statement.  If the next token is past a line boundary and might be
    3871     *  the start of the next statement, it won't cross the line to get it.
    3872     */
    3873
    3874     protected Token la( ExpressionStack stack ) throws SyntaxException, CompilationFailedException
    3875     {
    3876         Token next = la();
    3877
    3878         if( stack.canComplete() && next.isA(Types.UNSAFE_OVER_NEWLINES) )
    3879         {
    3880             if( la(true).getMeaning() == Types.NEWLINE )
    3881             {
    3882                 next = la(true);
    3883             }
    3884         }
    3885
    3886         return next;
    3887     }
    3888
    3889
    3890
    3891
    3892    /***
    3893     *  Returns the meaning of the <code>la( k, significantNewlines )</code> token.
    3894     */
    3895
    3896     protected int lt( int k, boolean significantNewlines ) throws SyntaxException, CompilationFailedException
    3897     {
    3898         return la(k, significantNewlines).getMeaning();
    3899     }
    3900
    3901
    3902
    3903    /***
    3904     *  Returns the meaning of the <code>la( k )</code> token.
    3905     */
    3906
    3907     protected int lt( int k ) throws SyntaxException, CompilationFailedException
    3908     {
    3909         return la(k).getMeaning();
    3910     }
    3911
    3912
    3913
    3914    /***
    3915     *  Returns the meaning of the <code>la( significantNewlines )</code> token.
    3916     */
    3917
    3918     protected int lt( boolean significantNewlines ) throws SyntaxException, CompilationFailedException
    3919     {
    3920         return la(significantNewlines).getMeaning();
    3921     }
    3922
    3923
    3924
    3925    /***
    3926     *  Returns the meaning of the <code>la()</code> token.
    3927     */
    3928
    3929     protected int lt() throws SyntaxException, CompilationFailedException
    3930     {
    3931         return la().getMeaning();
    3932     }
    3933
    3934
    3935
    3936
    3937   //---------------------------------------------------------------------------
    3938   // TOKEN CONSUMPTION
    3939
    3940
    3941    /***
    3942     *  Consumes (and returns) the next token if it is of the specified type.
    3943     *  If <code>significantNewlines</code> is set, newlines will not automatically
    3944     *  be consumed; otherwise they will.  Throws <code>UnexpectedTokenException</code>
    3945     *  if the type doesn't match.
    3946     */
    3947
    3948     protected Token consume( int type, boolean significantNewlines ) throws SyntaxException, CompilationFailedException
    3949     {
    3950         try
    3951         {
    3952             if( !la(significantNewlines).isA(type) )
    3953             {
    3954                 error( type );
    3955             }
    3956
    3957             if( !significantNewlines )
    3958             {
    3959                 while( lt(true) == Types.NEWLINE )
    3960                 {
    3961                     getTokenStream().consume(Types.NEWLINE);
    3962                 }
    3963             }
    3964
    3965             return getTokenStream().consume(type);
    3966         }
    3967         catch( ReadException e )
    3968         {
    3969             controller.addFatalError( new SimpleMessage(e.getMessage()) );
    3970         }
    3971
    3972         throw new GroovyBugError( "this should never happen" );
    3973     }
    3974
    3975
    3976
    3977    /***
    3978     *  A synonym for <code>consume( type, false )</code>.  If type is Types.NEWLINE,
    3979     *  equivalent to <code>consume( Types.NEWLINE, true )</code>.
    3980     */
    3981
    3982     protected Token consume( int type ) throws SyntaxException, CompilationFailedException
    3983     {
    3984         return consume( type, type == Types.NEWLINE );
    3985     }
    3986
    3987
    3988
    3989    /***
    3990     *  A synonym for <code>consume( Types.ANY, false )</code>.
    3991     */
    3992
    3993     protected Token consume() throws SyntaxException, CompilationFailedException
    3994     {
    3995         return consume( lt(), false );
    3996     }
    3997
    3998
    3999
    4000    /***
    4001     *  A synonym for <code>consume( Types.ANY, significantNewlines )</code>.
    4002     *  If you pass true, it will consume exactly the next token from the
    4003     *  stream.
    4004     */
    4005
    4006     protected Token consume( boolean significantNewlines ) throws SyntaxException, CompilationFailedException
    4007     {
    4008         return consume( lt(significantNewlines), significantNewlines );
    4009     }
    4010
    4011 }