1 package org.codehaus.groovy.syntax.lexer;
2
3
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
37 Token token = null;
38
39
40
41
42
43 if( !sentStartToken )
44 {
45 mark();
46 fullTextStartLine = getStartLine();
47 fullTextStartColumn = getStartColumn();
48 sentStartToken = true;
49
50
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
63 return token;
64 }
65
66
67
68
69
70
71
72 if( inExpression && la(1) != '}' )
73 {
74 mark();
75 unexpected( la(1), 0 );
76 }
77
78
79
80
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 );
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
198 return token;
199 }
200 else
201 {
202
203 return Token.newString( segment.toString(), getStartLine(), getStartColumn() );
204 }
205
206 }
207
208
209
210
211
212
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
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