import java.io.*;
import java.util.Stack;
/**
Parses the input stream (e.g., System.in) into the tokens used for
processing PostScript: Double, Boolean, Identifier, Symbol, and Procedure.
*/
public class StreamIterator implements java.util.Iterator<Object>, java.lang.Iterable<Object> {
private StreamTokenizer st;
/** */
public StreamIterator(InputStream in) {
// Reader r = new BufferedReader(new InputStreamReader(in));
Reader r = new InputStreamReader(in);
st = new StreamTokenizer(r);
st.parseNumbers();
st.slashSlashComments(false);
st.slashStarComments(false);
st.lowerCaseMode(true);
// Make '/' not a comment, which was the Java default
st.wordChars('/', '/');
st.commentChar('%');
// Mark the PostScript delayed execution operators
st.ordinaryChar('{');
st.ordinaryChar('}');
}
/** You can use this class either as an iterator or as a container
to iterate over. Because it is a stream, tokens that are
iterated over are consumed, so iteration changes the object.
<p>
Allows the syntax:
<pre>
for (Object value : stream) {
... process value
}
</pre>
*/
public java.util.Iterator<Object> iterator() {
return this;
}
public boolean hasNext() {
return st.ttype != StreamTokenizer.TT_EOF;
}
private static class CloseBrace {}
private final static CloseBrace CLOSE_BRACE = new CloseBrace();
/** Returns Double, Boolean, Symbol, Identifier, or Procedure.
Returns null when the stream is done. */
public Object next() {
Object value = null;
try {
// Read until EOF or a useful token
do {
st.nextToken();
} while (st.ttype == StreamTokenizer.TT_EOL);
} catch (Exception e) {
System.err.println(e);
}
switch (st.ttype) {
case '{':
value = readProcedure();
break;
case '}':
value = CLOSE_BRACE;
break;
case StreamTokenizer.TT_NUMBER:
value = new Double(st.nval);
break;
case StreamTokenizer.TT_WORD:
if (st.sval.equals("true")) {
value = new Boolean(true);
} else if (st.sval.equals("false")) {
value = new Boolean(false);
} else if ((st.sval.length() > 1) && (st.sval.charAt(0) == '/')){
// Strip the leading slash
value = new Symbol(st.sval.substring(1));
} else if ((st.sval.length() > 0) && (st.sval.charAt(0) == '(')) {
assert false : "This implementation does not support Postscript strings in parentheses.";
} else {
value = new Identifier(st.sval);
}
break;
default:
//System.out.println("End of File");
}
return value;
}
/** Assumes that the current token is '{'. Reads until the
matching '}' and returns the resulting Procedure. Will
recursively handle procedures within procedures.
Called from next(). */
private Procedure readProcedure() {
assert st.ttype == '{';
// Begin a new procedure
Procedure procedure = new Procedure();
// Read until that procedure is done
for (Object token = next(); token != CLOSE_BRACE; token = next()) {
procedure.add(token);
}
return procedure;
}
/** Returns true if obj is not null and is one of the
types of values that can be returned from next(). */
static public boolean isLegalPostScriptValue(Object obj) {
return
(obj != null) &&
((obj instanceof Procedure) ||
(obj instanceof Boolean) ||
(obj instanceof Double) ||
(obj instanceof Symbol) ||
(obj instanceof Identifier));
}
public void remove() {
assert false : "Not supported";
}
}