1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
|
/*
* Anarres C Preprocessor
* Copyright (c) 2007-2015, Shevek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package org.anarres.cpp;
import java.io.Closeable;
import java.io.IOException;
import java.util.Iterator;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import static org.anarres.cpp.Token.CCOMMENT;
import static org.anarres.cpp.Token.CPPCOMMENT;
import static org.anarres.cpp.Token.EOF;
import static org.anarres.cpp.Token.NL;
import static org.anarres.cpp.Token.WHITESPACE;
/**
* An input to the Preprocessor.
*
* Inputs may come from Files, Strings or other sources. The
* preprocessor maintains a stack of Sources. Operations such as
* file inclusion or token pasting will push a new source onto
* the Preprocessor stack. Sources pop from the stack when they
* are exhausted; this may be transparent or explicit.
*
* BUG: Error messages are not handled properly.
*/
public abstract class Source implements Iterable<Token>, Closeable {
private Source parent;
private boolean autopop;
private PreprocessorListener listener;
private boolean active;
private boolean werror;
/* LineNumberReader */
/*
// We can't do this, since we would lose the LexerException
private class Itr implements Iterator {
private Token next = null;
private void advance() {
try {
if (next != null)
next = token();
}
catch (IOException e) {
throw new UnsupportedOperationException(
"Failed to advance token iterator: " +
e.getMessage()
);
}
}
public boolean hasNext() {
return next.getType() != EOF;
}
public Token next() {
advance();
Token t = next;
next = null;
return t;
}
public void remove() {
throw new UnsupportedOperationException(
"Cannot remove tokens from a Source."
);
}
}
*/
public Source() {
this.parent = null;
this.autopop = false;
this.listener = null;
this.active = true;
this.werror = false;
}
/**
* Sets the parent source of this source.
*
* Sources form a singly linked list.
*/
/* pp */ void setParent(Source parent, boolean autopop) {
this.parent = parent;
this.autopop = autopop;
}
/**
* Returns the parent source of this source.
*
* Sources form a singly linked list.
*/
/* pp */ final Source getParent() {
return parent;
}
// @OverrideMustInvoke
/* pp */ void init(Preprocessor pp) {
setListener(pp.getListener());
this.werror = pp.getWarnings().contains(Warning.ERROR);
}
/**
* Sets the listener for this Source.
*
* Normally this is set by the Preprocessor when a Source is
* used, but if you are using a Source as a standalone object,
* you may wish to call this.
*/
public void setListener(PreprocessorListener pl) {
this.listener = pl;
}
/**
* Returns the File currently being lexed.
*
* If this Source is not a {@link FileLexerSource}, then
* it will ask the parent Source, and so forth recursively.
* If no Source on the stack is a FileLexerSource, returns null.
*/
@CheckForNull
public String getPath() {
Source parent = getParent();
if (parent != null)
return parent.getPath();
return null;
}
/**
* Returns the human-readable name of the current Source.
*/
@CheckForNull
public String getName() {
Source parent = getParent();
if (parent != null)
return parent.getName();
return null;
}
/**
* Returns the current line number within this Source.
*/
@Nonnegative
public int getLine() {
Source parent = getParent();
if (parent == null)
return 0;
return parent.getLine();
}
/**
* Returns the current column number within this Source.
*/
public int getColumn() {
Source parent = getParent();
if (parent == null)
return 0;
return parent.getColumn();
}
/**
* Returns true if this Source is expanding the given macro.
*
* This is used to prevent macro recursion.
*/
/* pp */ boolean isExpanding(@Nonnull Macro m) {
Source parent = getParent();
if (parent != null)
return parent.isExpanding(m);
return false;
}
/**
* Returns true if this Source should be transparently popped
* from the input stack.
*
* Examples of such sources are macro expansions.
*/
/* pp */ boolean isAutopop() {
return autopop;
}
/**
* Returns true if this source has line numbers.
*/
/* pp */ boolean isNumbered() {
return false;
}
/* This is an incredibly lazy way of disabling warnings when
* the source is not active. */
/* pp */ void setActive(boolean b) {
this.active = b;
}
/* pp */ boolean isActive() {
return active;
}
/**
* Returns the next Token parsed from this input stream.
*
* @see Token
*/
@Nonnull
public abstract Token token()
throws IOException,
LexerException;
/**
* Returns a token iterator for this Source.
*/
@Override
public Iterator<Token> iterator() {
return new SourceIterator(this);
}
/**
* Skips tokens until the end of line.
*
* @param white true if only whitespace is permitted on the
* remainder of the line.
* @return the NL token.
*/
@Nonnull
public Token skipline(boolean white)
throws IOException,
LexerException {
for (;;) {
Token tok = token();
switch (tok.getType()) {
case EOF:
/* There ought to be a newline before EOF.
* At least, in any skipline context. */
/* XXX Are we sure about this? */
warning(tok.getLine(), tok.getColumn(),
"No newline before end of file");
return new Token(NL,
tok.getLine(), tok.getColumn(),
"\n");
// return tok;
case NL:
/* This may contain one or more newlines. */
return tok;
case CCOMMENT:
case CPPCOMMENT:
case WHITESPACE:
break;
default:
/* XXX Check white, if required. */
if (white)
warning(tok.getLine(), tok.getColumn(),
"Unexpected nonwhite token");
break;
}
}
}
protected void error(int line, int column, String msg)
throws LexerException {
if (listener != null)
listener.handleError(this, line, column, msg);
else
throw new LexerException("Error at " + line + ":" + column + ": " + msg);
}
protected void warning(int line, int column, String msg)
throws LexerException {
if (werror)
error(line, column, msg);
else if (listener != null)
listener.handleWarning(this, line, column, msg);
else
throw new LexerException("Warning at " + line + ":" + column + ": " + msg);
}
public void close()
throws IOException {
}
}
|