View Javadoc

    1   package org.codehaus.groovy.syntax.lexer;
    2
    3   import org.codehaus.groovy.syntax.ReadException;
    4   import org.codehaus.groovy.syntax.Token;
    5
    6   /***
    7    *  Lexes Groovy, counting braces.  Considers itself at end of stream
    8    *  when the } count exceeds the { count.
    9    *
    10   *  @author Chris Poirier
    11   */
    12
    13  public class GroovyExpressionLexer extends GroovyLexerBase implements Delimiter
    14  {
    15
    16      protected boolean delimited = true;   // When true, the lexer can do its delimiting
    17      protected boolean finished  = false;  // Set when we reach the delimiter
    18      protected int     balance   = 0;      // The current number of unmatched open-braces
    19
    20
    21     /***
    22      *  Finds and returns (and consumes) the next token from the underlying stream.
    23      *  Returns null when out of tokens.  We let the GroovyLexerBase version deal
    24      *  with delegation stuff.
    25      */
    26
    27      public Token nextToken() throws ReadException, LexerException
    28      {
    29          if( finished )
    30          {
    31              return null;
    32          }
    33          else
    34          {
    35              return super.nextToken();
    36          }
    37
    38      }
    39
    40
    41
    42    //---------------------------------------------------------------------------
    43    // DELIMITER ROUTINES
    44
    45
    46     /***
    47      *  Turns delimiting on or off.  This should affect <code>la()</code>
    48      *  and <code>consume()</code>.  However, once the delimiter has been
    49      *  reached, this routine should have no effect.
    50      */
    51
    52      public void delimit( boolean delimited )
    53      {
    54          this.delimited = delimited;
    55      }
    56
    57
    58
    59     /***
    60      *  Returns true if the lexer is applying its delimiter policy.
    61      */
    62
    63      public boolean isDelimited()
    64      {
    65          return this.delimited;
    66      }
    67
    68
    69
    70     /***
    71      *  Returns true if the lexer stream is dry.
    72      */
    73
    74      public boolean isFinished()
    75      {
    76          return finished;
    77      }
    78
    79
    80
    81     /***
    82      *  Restarts the lexer stream after a <code>finish()</code>
    83      *  and some intevening act (like a new source).
    84      */
    85
    86      protected void restart()
    87      {
    88          finished = false;
    89      }
    90
    91
    92
    93     /***
    94      *  Stops the lexer stream.
    95      */
    96
    97      protected void finish()
    98      {
    99          finished = true;
    100     }
    101
    102
    103
    104
    105   //---------------------------------------------------------------------------
    106   // DELEGATION
    107
    108
    109    /***
    110     *  Delegates our duties to another Lexer.
    111     */
    112
    113     public void delegate( Lexer to )
    114     {
    115         this.delegate = to;
    116         delimit( false );
    117         to.setSource( this );
    118     }
    119
    120
    121
    122    /***
    123     *  Retakes responsibility for our duties.
    124     */
    125
    126     public void undelegate()
    127     {
    128         if( delegate != null )
    129         {
    130             delegate.unsetSource( );
    131             delegate = null;
    132             delimit( true );
    133         }
    134     }
    135
    136
    137
    138   //---------------------------------------------------------------------------
    139   // STREAM ROUTINES
    140
    141
    142
    143    /***
    144     *  Returns the next <code>k</code>th character, without consuming any.
    145     */
    146
    147     public char la(int k) throws LexerException, ReadException
    148     {
    149         if( source != null )
    150         {
    151             if( delimited )
    152             {
    153                 char c = ' ';
    154                 int balance = this.balance;
    155                 for( int i = 1; i <= k && balance >= 0; i++ )
    156                 {
    157                     c = source.la(k);
    158                     switch( c )
    159                     {
    160                         case '{':
    161                             balance++;
    162                             break;
    163                         case '}':
    164                             balance--;
    165                             break;
    166                     }
    167                 }
    168
    169                 if( balance >= 0 )
    170                 {
    171                     return c;
    172                 }
    173                 else
    174                 {
    175                     return CharStream.EOS;
    176                 }
    177
    178             }
    179             else
    180             {
    181                 return source.la(k);
    182             }
    183
    184         }
    185         else
    186         {
    187             return CharStream.EOS;
    188         }
    189     }
    190
    191
    192
    193    /***
    194     *  Eats a character from the input stream.
    195     */
    196
    197     public char consume() throws LexerException, ReadException
    198     {
    199         if( source != null )
    200         {
    201             if( delimited )
    202             {
    203                 char c = source.la(1);
    204                 switch( c )
    205                 {
    206                     case '{':
    207                         balance++;
    208                         break;
    209                     case '}':
    210                         balance--;
    211                         break;
    212                 }
    213
    214                 if( balance >= 0 )
    215                 {
    216                     return source.consume();
    217                 }
    218                 else
    219                 {
    220                     finish();
    221                 }
    222             }
    223             else
    224             {
    225                 return source.consume();
    226             }
    227         }
    228
    229         return CharStream.EOS;
    230     }
    231
    232 }