transcendental-lisp/scanner/LispFilterStream.java

161 lines
5.1 KiB
Java

/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
*/
package scanner;
import java.io.InputStream;
import java.io.FilterInputStream;
import java.io.IOException;
/**
* A <code>LispFilterStream</code> is an input stream that returns the bytes
* from an underlying input stream with all Lisp comments removed and replaced
* with newlines.
*/
public class LispFilterStream extends FilterInputStream {
private boolean inQuote;
/**
* Creates a <code>LispFilterStream</code> with the specified underlying
* <code>InputStream</code>.
*
* @param in
* the underlying input stream (must not be <code>null</code>)
*/
public LispFilterStream(InputStream in) {
super(in);
inQuote = false;
}
/**
* Reads the next byte of data from this input stream. The value byte is
* returned as an <code>int</code> in the range <code>0</code> to
* <code>255</code>. If no byte is available because the end of the stream
* has been reached, the value <code>-1</code> is returned.
*
* @return
* the next byte of data, or <code>-1</code> if the end of the stream has
* been reached.
* @throws IOException
* Indicates that an I/O error has occurred.
*/
@Override
public int read() throws IOException {
int next = super.read();
if ((next == ';') && (! inQuote)) {
// we have entered a comment, consume all the bytes from the
// underlying input stream until we reach a newline character or
// the end of the stream
while ((next != '\n') && (next != -1)) {
next = super.read();
}
} else if (next == '\n') {
inQuote = false;
} else if (next == '\"') { // we have entered or left a quoted string
inQuote = (! inQuote);
}
return next;
}
/**
* Reads up to the specified number of data bytes from this input stream
* into an array of bytes starting at the specified offset. If no bytes are
* available because the end of this stream has been reached then
* <code>-1</code> is returned. Also, If the specified number of bytes is
* more than the number of remaining data bytes in this input stream, then
* only the number of remaining data bytes are copied into the byte array.
*
* @param b
* the buffer into which the data bytes are read (must not be
* <code>null</code>)
* @param off
* the start offset in <code>b</code> at which the data is written (must
* be <code>&gt;= 0</code> and <code>&lt; b.length</code>)
* @param len
* the maximum number of bytes to read into <code>b</code> (<code>len +
* off</code> must be <code>&lt; b.length</code>)
* @return
* the total number of bytes read into the buffer, or <code>-1</code> if
* there is no more data because the end of the stream has been reached
* @throws IOException
* Indicates that the first byte could not be read into <code>b</code>
* for some reason other than reaching the end of the stream.
* @throws IndexOutOfBoundsException
* Indicates that this method attempted to access an index in
* <code>b</code> that was either negative or greater than or equal to
* <code>b.length</code> as a result of the given parameters.
* @throws NullPointerException
* Indicates that <code>b</code> is <code>null</code>.
*/
@Override
public int read(byte[] b, int off, int len) throws IOException {
int bytesRead = 0;
// make sure we are supposed to read at least one byte into 'b'
if (len > 0) {
int next = read();
if (next == -1) {
// there are no more bytes to read from this input stream
return -1;
}
int i = off;
while (next != -1) {
++bytesRead;
b[i++] = (byte) next;
if (i >= (off + len)) { // we have read 'len' bytes into 'b'
break;
}
try {
next = read();
} catch (IOException e) {
// treat this exception like an end of stream
break;
}
}
}
return bytesRead;
}
/**
* Skip over and discard the specified number of bytes from this input
* stream. This method may, for a variety of reasons, end up skipping some
* smaller number of bytes, possibly <code>0</code>. The actual number of
* bytes skipped is returned.
*
* @param n
* the number of bytes to be skipped
* @return
* the actual number of bytes skipped
* @throws IOException
* Indicates that an I/O error has occurred.
*/
@Override
public long skip (long n) throws IOException {
long bytesSkipped = 0;
while ((n > 0) && (read() != -1)) {
++bytesSkipped;
--n;
}
return bytesSkipped;
}
}