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                         {
110                             consume();
111                             segment.append( consume() );
112                             break ESCAPE_SWITCH;
113                         }
114 
115                         default:
116                         {
117                             segment.append( consume() );
118                             break ESCAPE_SWITCH;
119                         }
120                     }
121 
122                     break ROOT_SWITCH;
123                 }
124 
125                 case '$':
126                 {
127                     if( la(2) == '{' )
128                     {
129                         if( segment.length() == 0 )
130                         {
131                             sourceDelimiting( false );  // ensures ${"..."} works
132 
133                             mark();
134                             consume();
135                             consume();
136 
137                             token = symbol( Types.GSTRING_EXPRESSION_START );
138                             inExpression = true;
139 
140                             if( child == null )
141                             {
142                                 child = new GroovyExpressionLexer();
143                             }
144                             else
145                             {
146                                 child.reset();
147                             }
148 
149                             delegate( child );
150 
151                             break MAIN_LOOP;
152                         }
153                         else
154                         {
155                             break MAIN_LOOP;
156                         }
157                     }
158                     else
159                     {
160                         segment.append( consume() );
161                     }
162 
163                     break ROOT_SWITCH;
164                 }
165 
166                 case '}':
167                 {
168                     if( inExpression )
169                     {
170                         mark();
171                         consume();
172                         token = symbol( Types.GSTRING_EXPRESSION_END );
173 
174                         inExpression = false;
175 
176                         break MAIN_LOOP;
177                     }
178                     else
179                     {
180                         segment.append( consume() );
181                         break ROOT_SWITCH;
182                     }
183                 }
184 
185                 default:
186                 {
187                     segment.append( consume() );
188                     break ROOT_SWITCH;
189                 }
190             }
191         }
192 
193 
194         if( token != null )
195         {
196             // System.out.println( "" + this + "returning " + token );
197             return token;
198         }
199         else
200         {
201             // System.out.println( "" + this + "returning string of " + segment );
202             return Token.newString( segment.toString(), getStartLine(), getStartColumn() );
203         }
204 
205     }
206 
207 
208 
209 
210   //---------------------------------------------------------------------------
211   // DELEGATION
212 
213 
214    /***
215     *  Coordinates with our source about delimiting.  When
216     *  entering or processing sub-expressions, source delimiting
217     *  should be off.
218     */
219 
220     protected void sourceDelimiting( boolean delimit )
221     {
222         if( source instanceof Delimiter )
223         {
224             ((Delimiter)source).delimit( delimit );
225         }
226     }
227 
228 
229 
230    /***
231     *  Delegates our duties to another Lexer.
232     */
233 
234     public void delegate( Lexer to )
235     {
236         this.delegate = to;
237         sourceDelimiting( false );
238         to.setSource( this );
239     }
240 
241 
242 
243    /***
244     *  Retakes responsibility for our duties.
245     */
246 
247     public void undelegate()
248     {
249         if( delegate != null )
250         {
251             delegate.unsetSource( );
252             delegate = null;
253             sourceDelimiting( true );
254         }
255     }
256 
257 
258 
259    /***
260     *  Sets the source lexer.
261     */
262 
263     public void setSource( Lexer source )
264     {
265         super.setSource( source );
266 
267         sentStartToken = false;
268         sentEndToken   = false;
269 
270         fullTextStartLine   = getStartLine();
271         fullTextStartColumn = getStartColumn();
272         fullText            = new StringBuffer();
273 
274         inExpression = false;
275     }
276 
277 
278 
279    /***
280     *  Unsets the source lexer.
281     */
282 
283     public void unsetSource()
284     {
285         super.unsetSource();
286 
287         sentStartToken = false;
288         sentEndToken   = false;
289         fullText       = null;
290         inExpression   = false;
291     }
292 
293 
294 
295 
296   //---------------------------------------------------------------------------
297   // STREAM ROUTINES
298 
299 
300    /***
301     *  Eats a character from the input stream.
302     */
303 
304     public char consume() throws LexerException, ReadException
305     {
306         char c = super.consume();
307 
308         if( c != CharStream.EOS )
309         {
310             fullText.append(c);
311         }
312 
313         return c;
314     }
315 
316 
317 }
318