|
|||||||||||||||||||
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover | |||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
StringLexer.java | 0% | 0% | 0% | 0% |
|
1 |
package org.codehaus.groovy.syntax.lexer;
|
|
2 |
|
|
3 |
//{{{ imports
|
|
4 |
import org.codehaus.groovy.syntax.ReadException;
|
|
5 |
import org.codehaus.groovy.syntax.Token;
|
|
6 |
import org.codehaus.groovy.GroovyBugError;
|
|
7 |
//}}}
|
|
8 |
|
|
9 |
/**
|
|
10 |
* A Lexer for processing standard strings.
|
|
11 |
*
|
|
12 |
* @author Chris Poirier
|
|
13 |
*/
|
|
14 |
|
|
15 |
public class StringLexer extends TextLexerBase |
|
16 |
{ |
|
17 |
|
|
18 |
protected String delimiter = null; |
|
19 |
protected char watchFor; |
|
20 |
protected boolean allowGStrings = false; |
|
21 |
protected boolean emptyString = true; // If set, we need to send an empty string |
|
22 |
|
|
23 |
|
|
24 |
/**
|
|
25 |
* If set true, the filter will allow \\ and \$ to pass through unchanged.
|
|
26 |
* You should set this appropriately BEFORE setting source!
|
|
27 |
*/
|
|
28 |
|
|
29 | 0 |
public void allowGStrings( boolean allow ) |
30 |
{ |
|
31 | 0 |
allowGStrings = allow; |
32 |
} |
|
33 |
|
|
34 |
|
|
35 |
|
|
36 |
/**
|
|
37 |
* Returns a single STRING, then null. The STRING is all of the processed
|
|
38 |
* input. Backslashes are stripped, with the \r, \n, and \t converted
|
|
39 |
* appropriately.
|
|
40 |
*/
|
|
41 |
|
|
42 | 0 |
public Token undelegatedNextToken( ) throws ReadException, LexerException |
43 |
{ |
|
44 | 0 |
if( emptyString )
|
45 |
{ |
|
46 | 0 |
emptyString = false;
|
47 | 0 |
return Token.newString( "", getStartLine(), getStartColumn() ); |
48 |
} |
|
49 | 0 |
else if( finished ) |
50 |
{ |
|
51 | 0 |
return null; |
52 |
} |
|
53 |
else
|
|
54 |
{ |
|
55 | 0 |
StringBuffer string = new StringBuffer();
|
56 |
|
|
57 | 0 |
while( la(1) != CharStream.EOS )
|
58 |
{ |
|
59 | 0 |
string.append( consume() ); |
60 |
} |
|
61 |
|
|
62 | 0 |
if( la(1) == CharStream.EOS && string.length() == 0 )
|
63 |
{ |
|
64 | 0 |
finished = true;
|
65 |
} |
|
66 |
|
|
67 | 0 |
return Token.newString( string.toString(), getStartLine(), getStartColumn() );
|
68 |
} |
|
69 |
} |
|
70 |
|
|
71 |
|
|
72 |
|
|
73 |
/**
|
|
74 |
* Controls delimiter search. When turned on, the first thing we do
|
|
75 |
* is check for and eat our delimiter.
|
|
76 |
*/
|
|
77 |
|
|
78 | 0 |
public void delimit( boolean delimit ) |
79 |
{ |
|
80 | 0 |
super.delimit( delimit );
|
81 |
|
|
82 | 0 |
if( delimit )
|
83 |
{ |
|
84 | 0 |
try
|
85 |
{ |
|
86 | 0 |
if( !finished && la(1) == CharStream.EOS )
|
87 |
{ |
|
88 | 0 |
finishUp(); |
89 |
|
|
90 |
//
|
|
91 |
// The GStringLexer will correctly handle the empty string.
|
|
92 |
// We don't. In order to ensure that an empty string is
|
|
93 |
// supplied, we set a flag that is checked during
|
|
94 |
// undelegatedNextToken().
|
|
95 |
|
|
96 | 0 |
if( !allowGStrings )
|
97 |
{ |
|
98 | 0 |
emptyString = true;
|
99 |
} |
|
100 |
} |
|
101 |
} |
|
102 |
catch( Exception e )
|
|
103 |
{ |
|
104 | 0 |
finished = true;
|
105 |
} |
|
106 |
} |
|
107 |
} |
|
108 |
|
|
109 |
|
|
110 |
|
|
111 |
|
|
112 |
/**
|
|
113 |
* Sets the source lexer and identifies and consumes the opening delimiter.
|
|
114 |
*/
|
|
115 |
|
|
116 | 0 |
public void setSource( Lexer source ) |
117 |
{ |
|
118 | 0 |
super.setSource( source );
|
119 |
|
|
120 | 0 |
emptyString = false;
|
121 |
|
|
122 | 0 |
try
|
123 |
{ |
|
124 | 0 |
char c = source.la();
|
125 | 0 |
switch( c )
|
126 |
{ |
|
127 |
case '\'':
|
|
128 |
case '"': |
|
129 | 0 |
mark(); |
130 | 0 |
source.consume(); |
131 |
|
|
132 | 0 |
if( source.la() == c && source.la(2) == c )
|
133 |
{ |
|
134 | 0 |
source.consume(); source.consume(); |
135 | 0 |
delimiter = new StringBuffer().append(c).append(c).append(c).toString();
|
136 |
} |
|
137 |
else
|
|
138 |
{ |
|
139 | 0 |
delimiter = new StringBuffer().append(c).toString();
|
140 |
} |
|
141 |
|
|
142 | 0 |
watchFor = delimiter.charAt(0); |
143 | 0 |
break;
|
144 |
|
|
145 |
|
|
146 |
default:
|
|
147 |
{ |
|
148 | 0 |
throw new GroovyBugError( "at the time of StringLexer.setSource(), the source must be on a single or double quote" ); |
149 |
} |
|
150 |
} |
|
151 |
|
|
152 | 0 |
restart(); |
153 | 0 |
delimit( true );
|
154 |
} |
|
155 |
catch( Exception e )
|
|
156 |
{ |
|
157 |
//
|
|
158 |
// If we couldn't read our delimiter, we'll just
|
|
159 |
// cancel our source. nextToken() will return null.
|
|
160 |
|
|
161 | 0 |
e.printStackTrace(); |
162 | 0 |
unsetSource( ); |
163 |
} |
|
164 |
} |
|
165 |
|
|
166 |
|
|
167 |
|
|
168 |
/**
|
|
169 |
* Unsets our source.
|
|
170 |
*/
|
|
171 |
|
|
172 | 0 |
public void unsetSource() |
173 |
{ |
|
174 | 0 |
super.unsetSource();
|
175 | 0 |
delimiter = null;
|
176 | 0 |
finished = true;
|
177 | 0 |
emptyString = false;
|
178 |
} |
|
179 |
|
|
180 |
|
|
181 |
|
|
182 |
|
|
183 |
//---------------------------------------------------------------------------
|
|
184 |
// STREAM ROUTINES
|
|
185 |
|
|
186 |
private int lookahead = 0; // the number of characters identified |
|
187 |
private char[] characters = new char[3]; // the next characters identified by la() |
|
188 |
private int[] widths = new int[3]; // the source widths of the next characters |
|
189 |
|
|
190 |
|
|
191 |
|
|
192 |
/**
|
|
193 |
* Returns the next <code>k</code>th character, without consuming any.
|
|
194 |
*/
|
|
195 |
|
|
196 | 0 |
public char la(int k) throws LexerException, ReadException |
197 |
{ |
|
198 |
|
|
199 | 0 |
if( !finished && source != null ) |
200 |
{ |
|
201 |
|
|
202 | 0 |
if( delimited )
|
203 |
{ |
|
204 |
|
|
205 | 0 |
if( k > characters.length )
|
206 |
{ |
|
207 | 0 |
throw new GroovyBugError( "StringLexer lookahead tolerance exceeded" ); |
208 |
} |
|
209 |
|
|
210 | 0 |
if( lookahead >= k )
|
211 |
{ |
|
212 | 0 |
return characters[k-1];
|
213 |
} |
|
214 |
|
|
215 | 0 |
lookahead = 0; |
216 |
|
|
217 | 0 |
char c = ' ', c1 = ' ', c2 = ' ';
|
218 | 0 |
int offset = 1, width = 0;
|
219 | 0 |
for( int i = 1; i <= k; i++ ) |
220 |
{ |
|
221 | 0 |
c1 = source.la(offset); |
222 | 0 |
C1_SWITCH: switch( c1 )
|
223 |
{ |
|
224 |
case CharStream.EOS:
|
|
225 |
{ |
|
226 | 0 |
return c1;
|
227 |
} |
|
228 |
|
|
229 |
case '\\':
|
|
230 |
{ |
|
231 | 0 |
c2 = source.la( offset + 1 ); |
232 |
|
|
233 | 0 |
ESCAPE_SWITCH: switch( c2 )
|
234 |
{ |
|
235 |
|
|
236 |
case CharStream.EOS:
|
|
237 | 0 |
return c2;
|
238 |
|
|
239 |
case '\\':
|
|
240 |
case '$':
|
|
241 |
{ |
|
242 | 0 |
if( allowGStrings )
|
243 |
{ |
|
244 | 0 |
c = c1; |
245 | 0 |
width = 1; |
246 |
} |
|
247 |
else
|
|
248 |
{ |
|
249 | 0 |
c = c2; |
250 | 0 |
width = 2; |
251 |
} |
|
252 | 0 |
break ESCAPE_SWITCH;
|
253 |
} |
|
254 |
|
|
255 |
case 'r':
|
|
256 | 0 |
c = '\r'; |
257 | 0 |
width = 2; |
258 | 0 |
break ESCAPE_SWITCH;
|
259 |
|
|
260 |
case 't':
|
|
261 | 0 |
c = '\t'; |
262 | 0 |
width = 2; |
263 | 0 |
break ESCAPE_SWITCH;
|
264 |
|
|
265 |
case 'n':
|
|
266 | 0 |
c = '\n'; |
267 | 0 |
width = 2; |
268 | 0 |
break ESCAPE_SWITCH;
|
269 |
|
|
270 |
|
|
271 |
default:
|
|
272 | 0 |
c = c2; |
273 | 0 |
width = 2; |
274 | 0 |
break ESCAPE_SWITCH;
|
275 |
} |
|
276 | 0 |
break C1_SWITCH;
|
277 |
} |
|
278 |
|
|
279 |
default:
|
|
280 |
{ |
|
281 | 0 |
if( c1 == watchFor )
|
282 |
{ |
|
283 | 0 |
boolean atEnd = true; |
284 | 0 |
for( int j = 1; j < delimiter.length(); j++ ) |
285 |
{ |
|
286 | 0 |
if( source.la(offset+j) != delimiter.charAt(j) )
|
287 |
{ |
|
288 | 0 |
atEnd = false;
|
289 | 0 |
break;
|
290 |
} |
|
291 |
} |
|
292 |
|
|
293 | 0 |
if( atEnd )
|
294 |
{ |
|
295 | 0 |
return CharStream.EOS;
|
296 |
} |
|
297 |
} |
|
298 |
|
|
299 | 0 |
c = c1; |
300 | 0 |
width = 1; |
301 | 0 |
break C1_SWITCH;
|
302 |
} |
|
303 |
} |
|
304 |
|
|
305 |
|
|
306 | 0 |
characters[lookahead] = c; |
307 | 0 |
widths[lookahead] = width; |
308 |
|
|
309 | 0 |
offset += width; |
310 | 0 |
lookahead += 1; |
311 |
} |
|
312 |
|
|
313 | 0 |
return c; // <<< FLOW CONTROL <<<<<<<<< |
314 |
} |
|
315 |
|
|
316 | 0 |
lookahead = 0; |
317 | 0 |
return source.la(k);
|
318 |
} |
|
319 |
|
|
320 | 0 |
return CharStream.EOS;
|
321 |
|
|
322 |
} |
|
323 |
|
|
324 |
|
|
325 |
|
|
326 |
/**
|
|
327 |
* Eats a character from the input stream. Searches for the delimiter if
|
|
328 |
* delimited. Note that turning delimiting on also checks if we are at the
|
|
329 |
* delimiter, so if we aren't finished, there is something to consume.
|
|
330 |
*/
|
|
331 |
|
|
332 | 0 |
public char consume() throws LexerException, ReadException |
333 |
{ |
|
334 | 0 |
if( !finished && source != null ) |
335 |
{ |
|
336 | 0 |
char c = CharStream.EOS;
|
337 |
|
|
338 | 0 |
if( delimited )
|
339 |
{ |
|
340 | 0 |
if( lookahead < 1 )
|
341 |
{ |
|
342 | 0 |
la( 1 ); |
343 |
} |
|
344 |
|
|
345 | 0 |
if( lookahead >= 1 )
|
346 |
{ |
|
347 | 0 |
c = characters[0]; |
348 | 0 |
for( int i = 0; i < widths[0]; i++ ) |
349 |
{ |
|
350 | 0 |
source.consume(); |
351 |
} |
|
352 |
|
|
353 | 0 |
lookahead = 0; |
354 |
} |
|
355 |
|
|
356 | 0 |
if( la(1) == CharStream.EOS )
|
357 |
{ |
|
358 | 0 |
finishUp(); |
359 |
} |
|
360 |
} |
|
361 |
else
|
|
362 |
{ |
|
363 | 0 |
c = source.consume(); |
364 |
} |
|
365 |
|
|
366 | 0 |
lookahead = 0; |
367 | 0 |
return c;
|
368 |
} |
|
369 |
|
|
370 | 0 |
return CharStream.EOS;
|
371 |
} |
|
372 |
|
|
373 |
|
|
374 |
|
|
375 |
/**
|
|
376 |
* Eats our delimiter from the stream and marks us finished.
|
|
377 |
*/
|
|
378 |
|
|
379 | 0 |
protected void finishUp() throws LexerException, ReadException |
380 |
{ |
|
381 | 0 |
for( int i = 0; i < delimiter.length(); i++ ) |
382 |
{ |
|
383 | 0 |
char c = source.la(1);
|
384 | 0 |
if( c == CharStream.EOS )
|
385 |
{ |
|
386 | 0 |
throw new UnterminatedStringLiteralException(getStartLine(), getStartColumn()); |
387 |
} |
|
388 | 0 |
else if( c == delimiter.charAt(i) ) |
389 |
{ |
|
390 | 0 |
source.consume(); |
391 |
} |
|
392 |
else
|
|
393 |
{ |
|
394 | 0 |
throw new GroovyBugError( "la() said delimiter [" + delimiter + "], finishUp() found [" + c + "]" ); |
395 |
} |
|
396 |
} |
|
397 |
|
|
398 | 0 |
finish(); |
399 |
} |
|
400 |
|
|
401 |
} |
|
402 |
|
|