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