001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
002: *
003: * The contents of this file are subject to the Netscape Public
004: * License Version 1.1 (the "License"); you may not use this file
005: * except in compliance with the License. You may obtain a copy of
006: * the License at http://www.mozilla.org/NPL/
007: *
008: * Software distributed under the License is distributed on an "AS
009: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
010: * implied. See the License for the specific language governing
011: * rights and limitations under the License.
012: *
013: * The Original Code is Rhino code, released
014: * May 6, 1999.
015: *
016: * The Initial Developer of the Original Code is Netscape
017: * Communications Corporation. Portions created by Netscape are
018: * Copyright (C) 1997-1999 Netscape Communications Corporation. All
019: * Rights Reserved.
020: *
021: * Contributor(s):
022: * Mike McCabe
023: *
024: * Alternatively, the contents of this file may be used under the
025: * terms of the GNU Public License (the "GPL"), in which case the
026: * provisions of the GPL are applicable instead of those above.
027: * If you wish to allow use of your version of this file only
028: * under the terms of the GPL and not to allow others to use your
029: * version of this file under the NPL, indicate your decision by
030: * deleting the provisions above and replace them with the notice
031: * and other provisions required by the GPL. If you do not delete
032: * the provisions above, a recipient may use your version of this
033: * file under either the NPL or the GPL.
034: */
035: // Modified by Google
036: package com.google.gwt.dev.js.rhino;
037:
038: import java.io.Reader;
039: import java.io.IOException;
040:
041: /**
042: * An input buffer that combines fast character-based access with
043: * (slower) support for retrieving the text of the current line. It
044: * also supports building strings directly out of the internal buffer
045: * to support fast scanning with minimal object creation.
046: *
047: * Note that it is customized in several ways to support the
048: * TokenStream class, and should not be considered general.
049: *
050: * Credits to Kipp Hickman and John Bandhauer.
051: *
052: * @author Mike McCabe
053: */
054: final class LineBuffer {
055: /*
056: * for smooth operation of getLine(), this should be greater than
057: * the length of any expected line. Currently, 256 is 3% slower
058: * than 4096 for large compiles, but seems safer given evaluateString.
059: * Strings for the scanner are are built with StringBuffers
060: * instead of directly out of the buffer whenever a string crosses
061: * a buffer boundary, so small buffer sizes will mean that more
062: * objects are created.
063: */
064: static final int BUFLEN = 256;
065:
066: LineBuffer(Reader in, int lineno) {
067: this .in = in;
068: this .lineno = lineno;
069: }
070:
071: int read() throws IOException {
072: for (;;) {
073: if (end == offset && !fill())
074: return -1;
075:
076: int c = buffer[offset];
077: ++offset;
078:
079: if ((c & EOL_HINT_MASK) == 0) {
080: switch (c) {
081: case '\r':
082: // if the next character is a newline, skip past it.
083: if (offset != end) {
084: if (buffer[offset] == '\n')
085: ++offset;
086: } else {
087: // set a flag for fill(), in case the first char
088: // of the next fill is a newline.
089: lastWasCR = true;
090: }
091: // NO break here!
092: case '\n':
093: case '\u2028':
094: case '\u2029':
095: prevStart = lineStart;
096: lineStart = offset;
097: lineno++;
098: return '\n';
099: }
100: }
101:
102: if (c < 128 || !formatChar(c)) {
103: return c;
104: }
105: }
106: }
107:
108: void unread() {
109: // offset can only be 0 when we're asked to unread() an implicit
110: // EOF_CHAR.
111:
112: // This would be wrong behavior in the general case,
113: // because a peek() could map a buffer.length offset to 0
114: // in the process of a fill(), and leave it there. But
115: // the scanner never calls peek() or a failed match()
116: // followed by unread()... this would violate 1-character
117: // lookahead.
118: if (offset == 0 && !hitEOF)
119: Context.codeBug();
120:
121: if (offset == 0) // Same as if (hitEOF)
122: return;
123: offset--;
124: int c = buffer[offset];
125: if ((c & EOL_HINT_MASK) == 0 && eolChar(c)) {
126: lineStart = prevStart;
127: lineno--;
128: }
129: }
130:
131: private void skipFormatChar() {
132: if (checkSelf && !formatChar(buffer[offset]))
133: Context.codeBug();
134:
135: // swap prev character with format one so possible call to
136: // startString can assume that previous non-format char is at
137: // offset - 1. Note it causes getLine to return not exactly the
138: // source LineBuffer read, but it is used only in error reporting
139: // and should not be a problem.
140: if (offset != 0) {
141: char tmp = buffer[offset];
142: buffer[offset] = buffer[offset - 1];
143: buffer[offset - 1] = tmp;
144: } else if (otherEnd != 0) {
145: char tmp = buffer[offset];
146: buffer[offset] = otherBuffer[otherEnd - 1];
147: otherBuffer[otherEnd - 1] = tmp;
148: }
149:
150: ++offset;
151: }
152:
153: int peek() throws IOException {
154: for (;;) {
155: if (end == offset && !fill()) {
156: return -1;
157: }
158:
159: int c = buffer[offset];
160: if ((c & EOL_HINT_MASK) == 0 && eolChar(c)) {
161: return '\n';
162: }
163: if (c < 128 || !formatChar(c)) {
164: return c;
165: }
166:
167: skipFormatChar();
168: }
169: }
170:
171: boolean match(int test) throws IOException {
172: // TokenStream never looks ahead for '\n', which allows simple code
173: if ((test & EOL_HINT_MASK) == 0 && eolChar(test))
174: Context.codeBug();
175: // Format chars are not allowed either
176: if (test >= 128 && formatChar(test))
177: Context.codeBug();
178:
179: for (;;) {
180: if (end == offset && !fill())
181: return false;
182:
183: int c = buffer[offset];
184: if (test == c) {
185: ++offset;
186: return true;
187: }
188: if (c < 128 || !formatChar(c)) {
189: return false;
190: }
191: skipFormatChar();
192: }
193: }
194:
195: // Reconstruct a source line from the buffers. This can be slow...
196: String getLine() {
197: // Look for line end in the unprocessed buffer
198: int i = offset;
199: while (true) {
200: if (i == end) {
201: // if we're out of buffer, let's just expand it. We do
202: // this instead of reading into a StringBuffer to
203: // preserve the stream for later reads.
204: if (end == buffer.length) {
205: char[] tmp = new char[buffer.length * 2];
206: System.arraycopy(buffer, 0, tmp, 0, end);
207: buffer = tmp;
208: }
209: int charsRead = 0;
210: try {
211: charsRead = in.read(buffer, end, buffer.length
212: - end);
213: } catch (IOException ioe) {
214: // ignore it, we're already displaying an error...
215: break;
216: }
217: if (charsRead < 0)
218: break;
219: end += charsRead;
220: }
221: int c = buffer[i];
222: if ((c & EOL_HINT_MASK) == 0 && eolChar(c))
223: break;
224: i++;
225: }
226:
227: int start = lineStart;
228: if (lineStart < 0) {
229: // the line begins somewhere in the other buffer; get that first.
230: StringBuffer sb = new StringBuffer(otherEnd - otherStart
231: + i);
232: sb.append(otherBuffer, otherStart, otherEnd - otherStart);
233: sb.append(buffer, 0, i);
234: return sb.toString();
235: } else {
236: return new String(buffer, lineStart, i - lineStart);
237: }
238: }
239:
240: // Get the offset of the current character, relative to
241: // the line that getLine() returns.
242: int getOffset() {
243: if (lineStart < 0)
244: // The line begins somewhere in the other buffer.
245: return offset + (otherEnd - otherStart);
246: else
247: return offset - lineStart;
248: }
249:
250: private boolean fill() throws IOException {
251: // fill should be caled only for emty buffer
252: if (checkSelf && !(end == offset))
253: Context.codeBug();
254:
255: // swap buffers
256: char[] tempBuffer = buffer;
257: buffer = otherBuffer;
258: otherBuffer = tempBuffer;
259:
260: // allocate the buffers lazily, in case we're handed a short string.
261: if (buffer == null) {
262: buffer = new char[BUFLEN];
263: }
264:
265: // buffers have switched, so move the newline marker.
266: if (lineStart >= 0) {
267: otherStart = lineStart;
268: } else {
269: // discard beging of the old line
270: otherStart = 0;
271: }
272:
273: otherEnd = end;
274:
275: // set lineStart to a sentinel value, unless this is the first
276: // time around.
277: prevStart = lineStart = (otherBuffer == null) ? 0 : -1;
278:
279: offset = 0;
280: end = in.read(buffer, 0, buffer.length);
281: if (end < 0) {
282: end = 0;
283:
284: // can't null buffers here, because a string might be retrieved
285: // out of the other buffer, and a 0-length string might be
286: // retrieved out of this one.
287:
288: hitEOF = true;
289: return false;
290: }
291:
292: // If the last character of the previous fill was a carriage return,
293: // then ignore a newline.
294:
295: // There's another bizzare special case here. If lastWasCR is
296: // true, and we see a newline, and the buffer length is
297: // 1... then we probably just read the last character of the
298: // file, and returning after advancing offset is not the right
299: // thing to do. Instead, we try to ignore the newline (and
300: // likely get to EOF for real) by doing yet another fill().
301: if (lastWasCR) {
302: if (buffer[0] == '\n') {
303: offset++;
304: if (end == 1)
305: return fill();
306: }
307: lineStart = offset;
308: lastWasCR = false;
309: }
310: return true;
311: }
312:
313: int getLineno() {
314: return lineno;
315: }
316:
317: boolean eof() {
318: return hitEOF;
319: }
320:
321: private static boolean formatChar(int c) {
322: return Character.getType((char) c) == Character.FORMAT;
323: }
324:
325: private static boolean eolChar(int c) {
326: return c == '\r' || c == '\n' || c == '\u2028' || c == '\u2029';
327: }
328:
329: // Optimization for faster check for eol character: eolChar(c) returns
330: // true only when (c & EOL_HINT_MASK) == 0
331: private static final int EOL_HINT_MASK = 0xdfd0;
332:
333: private Reader in;
334: private char[] otherBuffer = null;
335: private char[] buffer = null;
336:
337: // Yes, there are too too many of these.
338: private int offset = 0;
339: private int end = 0;
340: private int otherEnd;
341: private int lineno;
342:
343: private int lineStart = 0;
344: private int otherStart = 0;
345: private int prevStart = 0;
346:
347: private boolean lastWasCR = false;
348: private boolean hitEOF = false;
349:
350: // Rudimentary support for Design-by-Contract
351: private static final boolean checkSelf = true;
352: }
|