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