/*
* Nathaniel Lim
* CS 136 Williams College
* Lab 6: Postscript
* Mar 31, 2008
* njl2@williams.edu
*/
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
public class PS {
/*
* This PS class is an interpreter for reading postscript commands.
* It keeps track of the operands using java.util.Stack,
* and the variables usings java.util.Map, mapping Identifier objects
* to general objects.
* The various operators are stored in a Map, mapping operator string to the
* specific Operator object. The operator strings are stored in a set so that
* operatorMap.keySet() does not have to be called everytime the interpreter wants to
* check whether an Identifier is the name of an operator.
*
* If the object pushed is an Operator, the apply method is
* performed. Many implementations of Operator perform casts assuming that
* the Objects on the stack are of a certain type. If they are not the expected
* type, ClassCastException will be thrown at runtime (failing fast).
*/
private Map<Identifier, Object> vars = new HashMap<Identifier, Object>();
private Stack<Object> operands = new Stack<Object>();
private Map<String, Operator> operatorMap = new HashMap<String, Operator>();
private Set<String> operators;
public PS (){
operatorMap.put("def", new DefOperator());
operatorMap.put("exec", new ExecOperator());
operatorMap.put("add", new AddOperator());
operatorMap.put("mul", new MulOperator());
operatorMap.put("sub", new SubOperator());
operatorMap.put("div", new DivOperator());
operatorMap.put("dup", new DupOperator());
operatorMap.put("ifelse", new IfElseOperator());
operatorMap.put("if", new IfOperator());
operatorMap.put("exch", new ExchOperator());
operatorMap.put("pop", new PopOperator());
operatorMap.put("eq", new EqOperator());
operatorMap.put("ne", new NeOperator());
operatorMap.put("gt", new GtOperator());
operatorMap.put("lt", new LtOperator());
operatorMap.put("clear", new ClearOperator());
operatorMap.put("pstack", new PstackOperator());
operatorMap.put("quit", new QuitOperator());
operators = operatorMap.keySet();
}
public static void main (String [] args){
PS interpreter = new PS();
StreamIterator stream = new StreamIterator(System.in);
for(Object val: stream){
interpreter.push(val);
}
}
public Object pop(){
assert operands.peek() != null : "Stack is empty";
return operands.pop();
}
public void push(Object obj){
if (obj instanceof Identifier){
//If the obj is a defined variable
//execute its value
if (vars.containsKey(obj)){
obj = vars.get(obj);
exec(obj);
} else if ( operators.contains(((Identifier)obj).getValue())){
//If the Identifier is a name of an operator
String opName = ((Identifier)obj).getValue();
Operator o = operatorMap.get(opName);
o.apply(this);
}
} else {
operands.push(obj);
}
}
public void exec (Object obj){
if (obj instanceof Procedure){
//Push all the steps of the procedure on the stack
Procedure p = (Procedure)obj;
for (Object o: p){
push(o);
}
} else {
operands.push(obj);
}
}
/*
* Clears all the values on the stack and
* and clears all the defined variables.
*/
public void clear(){
operands = new Stack<Object>();
vars = new HashMap<Identifier, Object>();
}
/*
* Associate an Identifier to the Object value in a Map
* in order to define the variable
*/
public void define(Identifier name, Object val){
vars.put(name, val);
}
/*
* Prints out the elements that are on the stack,
* so that the first line is the first item to be
* popped of the stack.
*/
public void pstack() {
for (int i = operands.size() -1; i >= 0; i--){
System.out.println(operands.get(i));
}
}
/*
* Prints out a table of the defined variable names and their values.
*/
public void ptable () {
for (Identifier x : vars.keySet()){
System.out.println(x + ": " + vars.get(x));
}
}
public void quit() {
System.exit(0);
}
}