import java.util.*;
import org.w3c.dom.*;

/**
 * This class parses an arithmetic expression and creates an XML document that represent
 * the expression.  (note: "Parser" doesn't refer to XML parsing in this context)
 */
public class Parser {
    Parser() {
	org.apache.xerces.dom.DOMImplementationImpl DOM = new
	    org.apache.xerces.dom.DOMImplementationImpl();

	// this is the dtd pointer for the message.  It will be used by the receiver
	// to validate the XML.  Only the 3rd argument really matters here.  The other
	// two should be unique, but don't really mean anything, and the 2nd one should
	// be some sort of URL.
	DocumentType docType = DOM.createDocumentType("calc", "http://localhost/calc.dtd", "calc.dtd");

	// create the document
	doc = new org.apache.xerces.dom.DocumentImpl(docType);
    }
    
    /**
     * the XML document that will be returned.
     */
    private Document doc;

    /**
     * element (Token) of the expression
     */
    class Tok {
	// type: 0=num, 1=plus, 2=minus 3=mult 4=div  10=lparen 11=rparen, -1=end
	int type;
	Element node;


	Tok(int type, double val) {
	    this.type = type;

	    switch(type) {
	    case 0:
		node = doc.createElement("number");
		node.setAttribute("val", String.valueOf(val));
		break;
	    case 1:
		node = doc.createElement("plus");
		break;
	    case 2:
		node = doc.createElement("minus");
		break;
	    case 3:
		node = doc.createElement("mult");
		break;
	    case 4:
		node = doc.createElement("div");
		break;
	    }
	}
	public String toString() {
	    return "<" + type + ", " + node + ">";
	}
    }


    /**
     * simple stack based arithmetic parser
     */
    Document eval(String exp) {
	exp = exp + "$";	// sentinel character
	ArrayList l = new ArrayList();
	int len = exp.length();
	int i = 0;
	while (i<len) {
	    char ch = exp.charAt(i);
	    //System.out.println("got char: " + ch);

	    switch(ch) {
	    case '$':
		l.add(new Tok(-1, 0));
		//System.out.println("Eval: " + l);
		try {
		    return eval(l);
		} catch (Exception ex) {
		    throw new IllegalArgumentException("Invalid expression: " + exp);
		}
	    case ' ':
	    case '\t':
		++i;
		continue;
	    case '+': 
		l.add(new Tok(1, 0));
		++i;
		continue;
	    case '*': 
		l.add(new Tok(3, 0));
		++i;
		continue;
	    case '/': 
		l.add(new Tok(4, 0));
		++i;
		continue;
	    case '(': 
		l.add(new Tok(10, 0));
		++i;
		continue;
	    case ')': 
		l.add(new Tok(11, 0));
		++i;
		continue;
	    case '-':
		++i;
		if (l.size() == 0) break;
		int t = ((Tok)l.get(l.size()-1)).type;
		if (t == 11 || t==0) {
		    l.add(new Tok(2, 0));
		    continue;
		}
		break;
	    }
	    // its a number

	    boolean neg = (ch == '-');

	    int j = i;
	    while (Character.isDigit(ch = exp.charAt(j++)) || ch == '.' || ch=='E')
		;
	    double num = Double.parseDouble(exp.substring(i,--j));
	    if (neg) num = -num;
	    l.add(new Tok(0, num));
	    i = j;
    	}
	
	throw new IllegalArgumentException("Invalid expression: " + exp);

    }

    Document eval(ArrayList l) {
	Stack vs = new Stack();
	Stack os = new Stack();
	int len = l.size();
	for (int i=0; i<len; ++i) {
	    Tok t = (Tok) l.get(i);
	    Tok o;
	    if (t.type == 0) {	// value
		vs.push(t);
	    }
	    if (t.type == 1 || t.type == 2) { // add/sub
		while (!os.empty() && (o = (Tok) os.peek()).type > 2 && o.type < 10) {
		    os.pop();
		    eval(o, vs);
		}
		os.push(t);
	    }
	    if (t.type == 3 || t.type == 4) { // mul/div
		while (!os.empty() && (o = (Tok) os.peek()).type > 4 && o.type < 10) {
		    os.pop();
		    eval(o, vs);
		}
		os.push(t);
	    }
	    
	    if (t.type == 5 || t.type == 10) { // pow or lparen
		os.push(t);
	    }

	    if (t.type == 11) {	// rparen
		while ((o = (Tok) os.pop()).type != 10) {
		    eval(o, vs);
		}
	    }
	    if (t.type == -1) {
		while (!os.empty()) {
		    o = (Tok) os.pop();
		    eval(o, vs);
		}
		Node calc = doc.createElement("calc");
		calc.appendChild(((Tok)vs.pop()).node);
		doc.appendChild(calc);
		return doc;
	    }
	}
	throw new IllegalArgumentException();
    }

    /**
     * Combine two nodes in the DOM tree
     */
    static void eval(Tok t, Stack vs) {
        
	Tok b = ((Tok)vs.pop());
	Tok a = ((Tok)vs.pop());
	
	t.type = 0;
	t.node.appendChild(a.node);
	t.node.appendChild(b.node);
	vs.push(t);
    }
}
