/*
* 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 LispFilterStream
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 LispFilterStream
with the specified underlying
* InputStream
.
*
* @param in
* the underlying input stream (must not be null
)
*/
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 int
in the range 0
to
* 255
. If no byte is available because the end of the stream
* has been reached, the value -1
is returned.
*
* @return
* the next byte of data, or -1
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
* -1
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
* null
)
* @param off
* the start offset in b
at which the data is written (must
* be >= 0
and < b.length
)
* @param len
* the maximum number of bytes to read into b
(len +
* off
must be < b.length
)
* @return
* the total number of bytes read into the buffer, or -1
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 b
* for some reason other than reaching the end of the stream.
* @throws IndexOutOfBoundsException
* Indicates that this method attempted to access an index in
* b
that was either negative or greater than or equal to
* b.length
as a result of the given parameters.
* @throws NullPointerException
* Indicates that b
is null
.
*/
@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 0
. 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;
}
}