View Javadoc

    1   package org.codehaus.groovy.syntax.lexer;
    2
    3   //{{{ imports
    4   import org.codehaus.groovy.syntax.ReadException;
    5   import org.codehaus.groovy.syntax.Types;
    6   import org.codehaus.groovy.syntax.Token;
    7   //}}}
    8
    9   /***
    10   *  A lexer for GStrings, usually run on a LexerFilter base.
    11   *
    12   *  @author Chris Poirier
    13   */
    14
    15  public class GStringLexer extends LexerBase
    16  {
    17
    18      protected boolean sentStartToken      = false;
    19      protected boolean sentEndToken        = false;
    20
    21      protected StringBuffer       fullText = new StringBuffer();
    22      protected int       fullTextStartLine = 0;
    23      protected int     fullTextStartColumn = 0;
    24
    25      protected GroovyExpressionLexer child = null;
    26      protected boolean        inExpression = false;
    27
    28
    29     /***
    30      *  Finds and returns (consuming) the next token from the underlying stream.
    31      *  Returns null when out of tokens.
    32      */
    33
    34      protected Token undelegatedNextToken() throws ReadException, LexerException
    35      {
    36          // System.out.println( "" + this + "undelegatedNextToken()" );
    37          Token token = null;
    38
    39
    40          //
    41          // Handle bracketing tokens and EOS
    42
    43          if( !sentStartToken )
    44          {
    45              mark();
    46              fullTextStartLine   = getStartLine();
    47              fullTextStartColumn = getStartColumn();
    48              sentStartToken      = true;
    49
    50              // System.out.println( "" + this + "returning GSTRING_START" );
    51              return symbol( Types.GSTRING_START );
    52          }
    53          else if( la(1) == CharStream.EOS )
    54          {
    55              if( !sentEndToken )
    56              {
    57                  sentEndToken = true;
    58                  token = Token.newSymbol( Types.GSTRING_END, fullTextStartLine, fullTextStartColumn );
    59                  token.setText( fullText.toString() );
    60              }
    61
    62              // System.out.println( "" + this + "returning " + token );
    63              return token;
    64          }
    65
    66
    67          //
    68          // If we get this far, we are no longer delegated.  If
    69          // we just processed an expression, the next character
    70          // had better be a '}'...
    71
    72          if( inExpression && la(1) != '}' )
    73          {
    74              mark();
    75              unexpected( la(1), 0 );
    76          }
    77
    78
    79          //
    80          // Otherwise, it's a lex...
    81
    82          mark();
    83          StringBuffer segment = new StringBuffer();
    84
    85          char c;
    86          MAIN_LOOP: while( true )
    87          {
    88              c = la(1);
    89
    90              ROOT_SWITCH: switch( c )
    91              {
    92                  case CharStream.EOS:
    93                  {
    94                      break MAIN_LOOP;
    95                  }
    96
    97                  case '\r':
    98                  case '\n':
    99                  {
    100                     readEOL( segment );
    101                     break ROOT_SWITCH;
    102                 }
    103
    104                 case '//':
    105                 {
    106                     ESCAPE_SWITCH: switch( la(2) )
    107                     {
    108                         case '//':
    109                         case '$':
    110                         {
    111                             consume();
    112                             segment.append( consume() );
    113                             break ESCAPE_SWITCH;
    114                         }
    115
    116                         default:
    117                         {
    118                             segment.append( consume() );
    119                             break ESCAPE_SWITCH;
    120                         }
    121                     }
    122
    123                     break ROOT_SWITCH;
    124                 }
    125
    126                 case '$':
    127                 {
    128                     if( la(2) == '{' )
    129                     {
    130                         if( segment.length() == 0 )
    131                         {
    132                             sourceDelimiting( false );  // ensures ${"..."} works
    133
    134                             mark();
    135                             consume();
    136                             consume();
    137
    138                             token = symbol( Types.GSTRING_EXPRESSION_START );
    139                             inExpression = true;
    140
    141                             if( child == null )
    142                             {
    143                                 child = new GroovyExpressionLexer();
    144                             }
    145                             else
    146                             {
    147                                 child.reset();
    148                             }
    149
    150                             delegate( child );
    151
    152                             break MAIN_LOOP;
    153                         }
    154                         else
    155                         {
    156                             break MAIN_LOOP;
    157                         }
    158                     }
    159                     else
    160                     {
    161                         segment.append( consume() );
    162                     }
    163
    164                     break ROOT_SWITCH;
    165                 }
    166
    167                 case '}':
    168                 {
    169                     if( inExpression )
    170                     {
    171                         mark();
    172                         consume();
    173                         token = symbol( Types.GSTRING_EXPRESSION_END );
    174
    175                         inExpression = false;
    176
    177                         break MAIN_LOOP;
    178                     }
    179                     else
    180                     {
    181                         segment.append( consume() );
    182                         break ROOT_SWITCH;
    183                     }
    184                 }
    185
    186                 default:
    187                 {
    188                     segment.append( consume() );
    189                     break ROOT_SWITCH;
    190                 }
    191             }
    192         }
    193
    194
    195         if( token != null )
    196         {
    197             // System.out.println( "" + this + "returning " + token );
    198             return token;
    199         }
    200         else
    201         {
    202             // System.out.println( "" + this + "returning string of " + segment );
    203             return Token.newString( segment.toString(), getStartLine(), getStartColumn() );
    204         }
    205
    206     }
    207
    208
    209
    210
    211   //---------------------------------------------------------------------------
    212   // DELEGATION
    213
    214
    215    /***
    216     *  Coordinates with our source about delimiting.  When
    217     *  entering or processing sub-expressions, source delimiting
    218     *  should be off.
    219     */
    220
    221     protected void sourceDelimiting( boolean delimit )
    222     {
    223         if( source instanceof Delimiter )
    224         {
    225             ((Delimiter)source).delimit( delimit );
    226         }
    227     }
    228
    229
    230
    231    /***
    232     *  Delegates our duties to another Lexer.
    233     */
    234
    235     public void delegate( Lexer to )
    236     {
    237         this.delegate = to;
    238         sourceDelimiting( false );
    239         to.setSource( this );
    240     }
    241
    242
    243
    244    /***
    245     *  Retakes responsibility for our duties.
    246     */
    247
    248     public void undelegate()
    249     {
    250         if( delegate != null )
    251         {
    252             delegate.unsetSource( );
    253             delegate = null;
    254             sourceDelimiting( true );
    255         }
    256     }
    257
    258
    259
    260    /***
    261     *  Sets the source lexer.
    262     */
    263
    264     public void setSource( Lexer source )
    265     {
    266         super.setSource( source );
    267
    268         sentStartToken = false;
    269         sentEndToken   = false;
    270
    271         fullTextStartLine   = getStartLine();
    272         fullTextStartColumn = getStartColumn();
    273         fullText            = new StringBuffer();
    274
    275         inExpression = false;
    276     }
    277
    278
    279
    280    /***
    281     *  Unsets the source lexer.
    282     */
    283
    284     public void unsetSource()
    285     {
    286         super.unsetSource();
    287
    288         sentStartToken = false;
    289         sentEndToken   = false;
    290         fullText       = null;
    291         inExpression   = false;
    292     }
    293
    294
    295
    296
    297   //---------------------------------------------------------------------------
    298   // STREAM ROUTINES
    299
    300
    301    /***
    302     *  Eats a character from the input stream.
    303     */
    304
    305     public char consume() throws LexerException, ReadException
    306     {
    307         char c = super.consume();
    308
    309         if( c != CharStream.EOS )
    310         {
    311             fullText.append(c);
    312         }
    313
    314         return c;
    315     }
    316
    317
    318 }
    319