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 {
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 );
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
197 return token;
198 }
199 else
200 {
201
202 return Token.newString( segment.toString(), getStartLine(), getStartColumn() );
203 }
204
205 }
206
207
208
209
210
211
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
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