001: // Copyright (c) 1999 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.text;
005:
006: import java.io.*;
007:
008: /**
009: * Framework for implementing lexical scanners.
010: * @author Per Bothner
011: */
012:
013: public class Lexer extends Reader {
014: protected LineBufferedReader port;
015:
016: public Lexer(LineBufferedReader port) {
017: this .port = port;
018: }
019:
020: public Lexer(LineBufferedReader port, SourceMessages messages) {
021: this .port = port;
022: this .messages = messages;
023: }
024:
025: public final LineBufferedReader getPort() {
026: return port;
027: }
028:
029: public void close() throws java.io.IOException {
030: port.close();
031: }
032:
033: public int read() throws java.io.IOException {
034: return port.read();
035: }
036:
037: public int read(char[] buf, int offset, int length)
038: throws java.io.IOException {
039: return port.read(buf, offset, length);
040: }
041:
042: public void unread(int ch) throws java.io.IOException {
043: if (ch >= 0)
044: port.unread();
045: }
046:
047: public int peek() throws java.io.IOException {
048: return port.peek();
049: }
050:
051: public void skip() throws java.io.IOException {
052: port.skip();
053: }
054:
055: protected void unread() throws java.io.IOException {
056: port.unread();
057: }
058:
059: protected void unread_quick() throws java.io.IOException {
060: port.unread_quick();
061: }
062:
063: protected void skip_quick() throws java.io.IOException {
064: port.skip_quick();
065: }
066:
067: SourceMessages messages = null;
068:
069: public SourceMessages getMessages() {
070: return messages;
071: }
072:
073: public void setMessages(SourceMessages messages) {
074: this .messages = messages;
075: }
076:
077: /** Returns true if any error were seen. Prints and clears the errors.
078: * @param out where to write the error message to
079: * @param max maximum number of messages to print (can be 0) */
080: public boolean checkErrors(PrintWriter out, int max) {
081: return messages != null && messages.checkErrors(out, max);
082: }
083:
084: public SourceError getErrors() {
085: return messages == null ? null : messages.getErrors();
086: }
087:
088: public boolean seenErrors() {
089: return messages != null && messages.seenErrors();
090: }
091:
092: public void clearErrors() {
093: if (messages != null)
094: messages.clearErrors();
095: }
096:
097: public void error(char severity, String filename, int line,
098: int column, String message) {
099: if (messages == null)
100: messages = new SourceMessages();
101: messages.error(severity, filename, line, column, message);
102: }
103:
104: public void error(char severity, String message) {
105: int line = port.getLineNumber();
106: int column = port.getColumnNumber();
107: error(severity, port.getName(), line + 1,
108: column >= 0 ? column + 1 : 0, message);
109: }
110:
111: public void error(String message) {
112: error('e', message);
113: }
114:
115: public void fatal(String message) throws SyntaxException {
116: error('f', message);
117: throw new SyntaxException(messages);
118: }
119:
120: public void eofError(String msg) throws SyntaxException {
121: fatal(msg);
122: }
123:
124: /** Read an optional signed integer.
125: * If there is no integer in the input stream, return 1.
126: * For excessively large exponents, return Integer.MIN_VALUE
127: * or Integer.MAX_VALUE.
128: */
129: public int readOptionalExponent() throws java.io.IOException {
130: int sign = read();
131: boolean neg = false;
132: boolean overflow = false;
133: int c;
134: if (sign == '+' || sign == '-')
135: c = read();
136: else {
137: c = sign;
138: sign = 0;
139: }
140: int value;
141: if (c < 0 || (value = Character.digit((char) c, 10)) < 0) {
142: if (sign != 0)
143: error("exponent sign not followed by digit");
144: value = 1;
145: } else {
146: int max = (Integer.MAX_VALUE - 9) / 10;
147: for (;;) {
148: c = read();
149: int d = Character.digit((char) c, 10);
150: if (d < 0)
151: break;
152: if (value > max)
153: overflow = true;
154: value = 10 * value + d;
155: }
156: }
157: if (c >= 0)
158: unread(c);
159: if (sign == '-')
160: value = -value;
161: if (overflow)
162: return sign == '-' ? Integer.MIN_VALUE : Integer.MAX_VALUE;
163: return value;
164: }
165:
166: /** Read digits, up to the first non-digit or the buffer limit
167: * @return the digits seen as a non-negative long, or -1 on overflow
168: */
169: public static long readDigitsInBuffer(LineBufferedReader port,
170: int radix) {
171: long ival = 0;
172: boolean overflow = false;
173: long max_val = Long.MAX_VALUE / radix;
174: int i = port.pos;
175: if (i >= port.limit)
176: return 0;
177: for (;;) {
178: char c = port.buffer[i];
179: int dval = Character.digit(c, radix);
180: if (dval < 0)
181: break;
182: if (ival > max_val)
183: overflow = true;
184: else
185: ival = ival * radix + dval;
186: if (ival < 0)
187: overflow = true;
188: if (++i >= port.limit)
189: break;
190: }
191: port.pos = i;
192: return overflow ? -1 : ival;
193: }
194:
195: public String getName() {
196: return port.getName();
197: }
198:
199: public int getLineNumber() {
200: return port.getLineNumber();
201: }
202:
203: public int getColumnNumber() {
204: return port.getColumnNumber();
205: }
206:
207: /** For building tokens of various kinds. */
208: public char[] tokenBuffer = new char[100];
209:
210: /** The number of chars of tokenBuffer that are used. */
211: public int tokenBufferLength = 0;
212:
213: /** Append one character to tokenBuffer, resizing it if need be. */
214: public void tokenBufferAppend(int ch) {
215: if (ch > 0x10000) {
216: // append surrogates - fixme.
217: }
218: int len = tokenBufferLength;
219: char[] buffer = tokenBuffer;
220: if (len == tokenBuffer.length) {
221: tokenBuffer = new char[2 * len];
222: System.arraycopy(buffer, 0, tokenBuffer, 0, len);
223: buffer = tokenBuffer;
224: }
225: buffer[len] = (char) ch;
226: tokenBufferLength = len + 1;
227: }
228: }
|