1 package org.codehaus.groovy.syntax.lexer;
2
3 import org.codehaus.groovy.syntax.ReadException;
4 import org.codehaus.groovy.syntax.Token;
5
6 /***
7 * Lexes Groovy, counting braces. Considers itself at end of stream
8 * when the } count exceeds the { count.
9 *
10 * @author Chris Poirier
11 */
12
13 public class GroovyExpressionLexer extends GroovyLexerBase implements Delimiter
14 {
15
16 protected boolean delimited = true;
17 protected boolean finished = false;
18 protected int balance = 0;
19
20
21 /***
22 * Finds and returns (and consumes) the next token from the underlying stream.
23 * Returns null when out of tokens. We let the GroovyLexerBase version deal
24 * with delegation stuff.
25 */
26
27 public Token nextToken() throws ReadException, LexerException
28 {
29 if( finished )
30 {
31 return null;
32 }
33 else
34 {
35 return super.nextToken();
36 }
37
38 }
39
40
41
42
43
44
45
46 /***
47 * Turns delimiting on or off. This should affect <code>la()</code>
48 * and <code>consume()</code>. However, once the delimiter has been
49 * reached, this routine should have no effect.
50 */
51
52 public void delimit( boolean delimited )
53 {
54 this.delimited = delimited;
55 }
56
57
58
59 /***
60 * Returns true if the lexer is applying its delimiter policy.
61 */
62
63 public boolean isDelimited()
64 {
65 return this.delimited;
66 }
67
68
69
70 /***
71 * Returns true if the lexer stream is dry.
72 */
73
74 public boolean isFinished()
75 {
76 return finished;
77 }
78
79
80
81 /***
82 * Restarts the lexer stream after a <code>finish()</code>
83 * and some intevening act (like a new source).
84 */
85
86 protected void restart()
87 {
88 finished = false;
89 }
90
91
92
93 /***
94 * Stops the lexer stream.
95 */
96
97 protected void finish()
98 {
99 finished = true;
100 }
101
102
103
104
105
106
107
108
109 /***
110 * Delegates our duties to another Lexer.
111 */
112
113 public void delegate( Lexer to )
114 {
115 this.delegate = to;
116 delimit( false );
117 to.setSource( this );
118 }
119
120
121
122 /***
123 * Retakes responsibility for our duties.
124 */
125
126 public void undelegate()
127 {
128 if( delegate != null )
129 {
130 delegate.unsetSource( );
131 delegate = null;
132 delimit( true );
133 }
134 }
135
136
137
138
139
140
141
142
143 /***
144 * Returns the next <code>k</code>th character, without consuming any.
145 */
146
147 public char la(int k) throws LexerException, ReadException
148 {
149 if( source != null )
150 {
151 if( delimited )
152 {
153 char c = ' ';
154 int balance = this.balance;
155 for( int i = 1; i <= k && balance >= 0; i++ )
156 {
157 c = source.la(k);
158 switch( c )
159 {
160 case '{':
161 balance++;
162 break;
163 case '}':
164 balance--;
165 break;
166 }
167 }
168
169 if( balance >= 0 )
170 {
171 return c;
172 }
173 else
174 {
175 return CharStream.EOS;
176 }
177
178 }
179 else
180 {
181 return source.la(k);
182 }
183
184 }
185 else
186 {
187 return CharStream.EOS;
188 }
189 }
190
191
192
193 /***
194 * Eats a character from the input stream.
195 */
196
197 public char consume() throws LexerException, ReadException
198 {
199 if( source != null )
200 {
201 if( delimited )
202 {
203 char c = source.la(1);
204 switch( c )
205 {
206 case '{':
207 balance++;
208 break;
209 case '}':
210 balance--;
211 break;
212 }
213
214 if( balance >= 0 )
215 {
216 return source.consume();
217 }
218 else
219 {
220 finish();
221 }
222 }
223 else
224 {
225 return source.consume();
226 }
227 }
228
229 return CharStream.EOS;
230 }
231
232 }