1 package org.codehaus.groovy.syntax;
2
3 import org.codehaus.groovy.GroovyBugError;
4
5
6 /***
7 * Provides the common code for <code>{@link TokenStream}</code> implementations.
8 */
9
10 public abstract class AbstractTokenStream
11 implements TokenStream
12 {
13 private Token[] buf;
14 private int first;
15 private int avail;
16
17 private int checkpoint_first;
18 private int checkpoint_avail;
19
20 private String sourceLocator;
21
22
23 /***
24 * Default constructor.
25 */
26
27 public AbstractTokenStream()
28 {
29 this( "<unknown>" );
30 }
31
32
33 /***
34 * Initializes the stream with information about the source.
35 */
36
37 public AbstractTokenStream(String sourceLocator)
38 {
39 this.buf = new Token[8 * 1024];
40 this.first = -1;
41 this.avail = 0;
42 this.sourceLocator = sourceLocator;
43 }
44
45
46 /***
47 * Returns a description of the source (typically a file name).
48 */
49
50 public String getSourceLocator()
51 {
52 return this.sourceLocator;
53 }
54
55
56 /***
57 * Implemented by concrete subtypes, provides access to the next
58 * token in the underlying stream.
59 */
60
61 protected abstract Token nextToken()
62 throws ReadException, SyntaxException;
63
64
65 /***
66 * Returns the next token in the stream without consuming it.
67 */
68
69 public Token la()
70 throws ReadException, SyntaxException
71 {
72 return la( 1 );
73 }
74
75
76 /***
77 * Returns the <code>k</code>th token in the stream without consuming
78 * it (or any other unconsumed tokens).
79 */
80
81 public Token la(int k)
82 throws ReadException, SyntaxException
83 {
84 if ( k > buf.length )
85 {
86 throw new GroovyBugError( "Lookahead [" + k + "] is larger than lookahead buffer [" + buf.length + "]" );
87 }
88
89
90
91
92
93 if ( k > this.avail )
94 {
95 int numToPopulate = k - this.avail;
96
97 for ( int i = 0 ; i < numToPopulate ; ++i )
98 {
99 if ( this.first < 0 )
100 {
101 this.first = 0;
102 }
103 int pop = ( ( this.first + this.avail ) % this.buf.length );
104 this.buf[ pop ] = nextToken();
105 ++this.avail;
106 ++this.checkpoint_avail;
107 }
108 }
109
110
111
112
113
114 int pos = ( ( k + this.first - 1 ) % this.buf.length );
115
116 return this.buf[ pos ];
117 }
118
119
120 /***
121 * Removes and returns the first token in the stream, provided it
122 * matches the specified type.
123 */
124
125 public Token consume(int type)
126 throws ReadException, SyntaxException
127 {
128 Token token = la();
129
130 if( token == null )
131 {
132 return null;
133 }
134
135 if( !token.isA(type) )
136 {
137 throw new TokenMismatchException( token, type );
138 }
139
140 ++this.first;
141 --this.avail;
142
143 this.first %= this.buf.length;
144
145 return token;
146 }
147
148
149
150 /***
151 * Removes and returns the first token in the stream, provided it
152 * isn't the EOF.
153 */
154
155 public Token consume() throws ReadException, SyntaxException
156 {
157 return consume( Types.NOT_EOF );
158 }
159
160
161
162 /***
163 * Saves the look-ahead state for <code>restore()</code>ing later.
164 */
165
166 public void checkpoint() {
167 checkpoint_first = first;
168 checkpoint_avail = avail;
169 }
170
171
172 /***
173 * Restores the look-ahead state saved by <code>checkpoint()</code>.
174 */
175
176 public void restore() {
177 first = checkpoint_first;
178 avail = checkpoint_avail;
179 }
180
181
182 /***
183 * Returns true if the stream is out of tokens.
184 */
185
186 public boolean atEnd( boolean ignoringWhitespace ) {
187
188 try {
189 int offset = 1;
190
191 if( ignoringWhitespace ) {
192 try {
193 while( la(offset).isA(Types.NEWLINE) ) {
194 offset++;
195 }
196 }
197 catch( Exception e ) {}
198 }
199
200 if( la(offset) == null ) {
201 return true;
202 }
203 }
204 catch( Exception e ) {}
205
206 return false;
207 }
208
209
210 /***
211 * A synonym for <code>atEnd(true)</code>.
212 */
213
214 public boolean atEnd() {
215 return atEnd(true);
216 }
217
218 }