package Scanner; import java.io.*; import java.util.*; /** * A class to parse PL142 programs. */ public class Scanner { private BufferedReader stream; private StreamTokenizer in; private boolean haveEOF; /** * Scanner constructor. *
* @param bufReader - The source of input characters. *
* Note: to be general, we have to be prepared to read from either
* a disk file or a .jar file. Opening the BufferedReader is the
* caller's responsibility, but this seems to work:
* // This bizarre looking stuff seems to work for invocations like
* // (a) java someclass
* // (b) java -jar some.jar
* // (c) double-clicking on a some.jar
* // It seems risky, though, as I don't know how to make sure I get the
* // classloader for the main class, and the user is probably going to give
* // the filename relative to that class's location.
* InputStream st = Scanner.class.getClassLoader().getResourceAsStream(filename);
* InputStreamReader stReader = new InputStreamReader( st );
* stream = new BufferedReader( stReader );
*/
public Scanner( BufferedReader bufReader ) throws ScannerException {
try {
stream = bufReader;
this.in = new StreamTokenizer(this.stream);
in.resetSyntax();
in.whitespaceChars(' ', ' ');
in.whitespaceChars('\t', '\t');
in.whitespaceChars('\r', '\r');
in.whitespaceChars('\n', '\n');
in.wordChars( 'a', 'z' );
in.wordChars( 'A', 'Z' );
in.wordChars( '0', '9' );
in.wordChars( '.', '.' );
in.wordChars( '/', '/' );
in.wordChars( '-', '-' ); // for negative numbers
in.wordChars( '<', '<' ); // for FSA keywords
in.wordChars( '>', '>' ); // for FSA keywords
in.eolIsSignificant( true );
in.commentChar('#');
//in.slashSlashComments( true );
haveEOF = false;
} catch (Exception e) {
throw new ScannerException("Unable to create scanner.");
}
}
/**
* utility routine to make EOLN not significant (won't return "EOLN")
*/
public void turnEOLNOff() {
if ( in != null ) {
in.eolIsSignificant( false );
}
}
/**
* Returns false if end-of-file has already been encountered and returned on
* a previous call to getNextToken(). Otherwise, returns true.
*/
public boolean hasNextToken() {
if ( haveEOF ) {
return false;
} else {
return true;
}
}
/**
* flushLine() - flushes to EOLN. Careful - we might already be at EOLN
*/
public void flushLine() {
if ( in != null ) {
try {
while ( in.ttype != StreamTokenizer.TT_EOL && in.ttype != StreamTokenizer.TT_EOF ) {
in.nextToken();
}
} catch (Exception e) {
}
}
}
/**
* Returns the next token from the input stream. If end-of-file is encountered,
* the String "EOF" is returned.
*/
public String getNextToken() throws ScannerException {
String result = null;
int type = 0;
if ( haveEOF ) {
throw new ScannerException( "You have tried to read past EOF" );
}
try {
type = in.nextToken();
if ( type == StreamTokenizer.TT_WORD ) {
result = in.sval;
} else if ( type == StreamTokenizer.TT_EOF ) {
result = "EOF";
haveEOF = true;
in = null;
stream.close();
} else if ( type == StreamTokenizer.TT_EOL ) {
result = "EOLN";
} else {
result = String.valueOf((char)type);
}
} catch (Exception e) {
throw new RuntimeException("Unknown input error: " + e);
}
return result;
}
/**
* readNonBlankToken - reads the next token, which must NOT be EOLN or EOF.
*/
public String readNonBlankToken() throws ScannerException {
String result = getNextToken();
if ( result.equals("EOLN") || result.equals("EOF") ) {
throw new ScannerException( "Expected token, found " + result );
}
return result;
}
/**
* readLiteral - reads the next token and compares against an ArrayList of String
* literals. Throws an error if the token doesn't match any of the literals.
* Otherwise returns the index of the literal matched.
*/
public int readLiteral( ArrayList literal ) throws ScannerException {
int index;
String token;
token = getNextToken();
for ( index=0; index