package plume;
import java.util.*;
import java.io.*;
/**
* Provides a somewhat simpler interface for tokenizing strings than
* does StreamTokenizer. All tokenizing is done by StreamTokenizer. <p>
*
* The major difference from StreamTokenizer is that all tokens are
* returned as strings. EOF returns a null, EOL returns an empty string.
* A delimiters is returned as a one-character string. Words and numbers
* are returned as strings. Quoted strings are also returned as strings
* including their quote characters (so they can easily be differentiated
* from words and numbers).
*
* Other differences are: <ul>
* <li> Automatic setup for tokenizing strings.
* <li> Underscores are included in identifiers (words).
* <li> I/O errors (which should be impossible when tokenizing strings) are
* converted to RuntimeExceptions so that every call doesn't have to
* be included in a try block.
* <li> Convenience functions isWord(), isQString(), and need().
* <li> Returned string tokens are interned for easier comparisons.
* </ul>
*/
public class StrTok {
Reader reader;
public StreamTokenizer stok;
Error err = new Error();
/**
* Creates a tokenizer for the specified string.
*/
public StrTok (String s) {
reader = new StringReader (s);
stok = new StreamTokenizer (reader);
stok.wordChars ('_', '_');
}
/**
* Creates a tokenizer for the specified string with the specified
* error handler
*/
public StrTok (String s, Error e) {
this(s);
set_error_handler (e);
}
/**
* Default class for error handling. Throws a RuntimeException when an
* error occurs.
*
* @see #set_error_handler(Error)
*/
public static class Error {
/**
* Called when an unexpected token is found (see {@link #need(String)}).
*/
public void tok_error (String s) {
throw new RuntimeException ("StrTok error: " + s);
}
}
/**
* Returns the next token as a string. EOF returns a null, EOL
* returns an empty string. Delimiters are returned as one character
* strings. Quoted strings and words are returned as strings.
*/
public /*@Nullable*/ /*@Interned*/ String nextToken() {
// Get the next token. Turn IO exceptions into runtime exceptions
// so that callers don't have to catch them.
int ttype;
try {
ttype = stok.nextToken();
} catch (Exception e) {
throw new RuntimeException ("StreamTokenizer exception: ", e);
}
return (token());
}
/**
* Causes the next call to nextToken() to return the current token
*/
public void pushBack() {
stok.pushBack();
}
/**
* Returns the current token.
* @see #nextToken()
*/
public /*@Nullable*/ /*@Interned*/ String token() {
int ttype = stok.ttype;
// Null indicates eof
if (ttype == StreamTokenizer.TT_EOF)
return (null);
// Return end of line as an empty string
if (ttype == StreamTokenizer.TT_EOL)
return ("");
// Return identifiers (words) and quoted strings. Quoted strings
// include their quote characters (for recognition)
if (stok.sval != null) {
if (ttype > 0) {
String s = ((char) ttype) + stok.sval + ((char) ttype);
return (s.intern());
}
return (stok.sval.intern());
}
// Other tokens are delimiters
if (ttype > 0) {
String s = "" + (char)ttype;
return (s.intern());
}
throw new RuntimeException ("Unexpected return " + ttype +
" from StreamTokenizer");
}
/**
* Specifies the single line comment character.
* @see StreamTokenizer#commentChar(int)
*/
public void commentChar (int ch) {
stok.commentChar (ch);
}
/**
* Specifies that matching pairs of this character delimit string constants.
* @see StreamTokenizer#quoteChar(int)
*/
public void quoteChar (int ch) {
stok.quoteChar (ch);
}
/**
* Returns the type of the current token.
* @see StreamTokenizer#ttype
*/
public int ttype() {
return stok.ttype;
}
/** Returns true if the current token is a word (identifier) */
public boolean isWord() {
return (stok.ttype == StreamTokenizer.TT_WORD);
}
/** Returns true if the current token is a quoted string */
public boolean isQString() {
return ((stok.sval != null) && (stok.ttype > 0));
}
/**
* Sets the error handler. The default error handler will throw a
* runtime exception on errors.
* @see Error
*/
public void set_error_handler (Error err) {
this.err = err;
}
/**
* Reads the next token and checks that it matches tok. If it does
* not match, calls the current error handling routine (see {@link
* #set_error_handler(StrTok.Error) set_error_handler()}).
* If it does match, just returns.
*/
public void need (String tok) {
String t = nextToken();
if (tok.equals(t))
return;
err.tok_error (String.format ("Token %s found where %s expected", t, tok));
}
/**
* Reads the next token and checks to make sure that it is a word (id).
* If it is not a word, calls the error handling routine. If it is,
* returns the string of the word.
*/
public String need_word() {
String t = nextToken();
if (!isWord()) {
err.tok_error (String.format ("'%s' found where identifier expected", t));
}
assert t != null : "@SuppressWarnings(nullness): dependent: because of isWord check";
return t;
}
}