// a parser for the bitter language
import java.io.*;

class parserError extends Exception{
    public parserError(token t){
	super("bad_syntax: " + t.name);
    } // end of constructor
} // end of class parserError

class parser{
    private scanner scan;
    private token currentToken;
    
    public parser(scanner s){
	scan = s;
    } // end of constructor
    
    // scanner utility functions
    private void lookahead() throws scannerError{
	currentToken = scan.getToken();
    } // end of lookahead
    
    private token match(int tokType) throws parserError, scannerError{
	token t = currentToken;
	if(tokType != t.type)
	    throw new parserError(currentToken);
	currentToken = scan.getToken();
	return(t);
    } // end of match
    
    // build the syntax tree
    public token buildTree() throws parserError, scannerError{
	currentToken = scan.getToken();
	return(prog());
    } // end of buildTree

    private token prog() throws parserError, scannerError{
	match(scanner._START);
	token t = new token(scanner._START, "_start");
	t.addChild(slist());
	match(scanner._STOP);
	return t;
    }
    
    private token slist() throws parserError, scannerError{
	switch(currentToken.type){
	case scanner.ID:
	case scanner._CLEAR:
	    token t1 = stat();
	    token t2 = match(scanner.BAR);
	    token t3 = stail();
	    t2.addChild(t1);
	    t2.addChild(t3);
	    return t2;
	default: throw new parserError(currentToken);
	}
    } // end of statlist
    
    private token stail() throws parserError, scannerError{
	switch (currentToken.type) {
	case scanner.ID:
	case scanner._CLEAR: 
	    token t1 = stat();
	    token t2 = match(scanner.BAR);
	    token t3 = stail();
	    t2.addChild(t1);
	    t2.addChild(t3);
	    return t2;
	case scanner._STOP:
	    return (new token(scanner.EPSILON,"epsilon"));
	default: throw new parserError(currentToken);
	}
    } // end of stail

    private token stat() throws parserError, scannerError{
	token t1, t2, t3;
	switch(currentToken.type){
	case scanner.ID:
	    t1 = match(scanner.ID);
	    t2 = match(scanner.EQ);
	    t3 = expr();
	    t2.addChild(t1);
	    t2.addChild(t3);
	    return t2;
	case scanner._CLEAR:
	    match(scanner._CLEAR);
	    match(scanner.LPAR);
	    t1 = idlist();
	    match(scanner.RPAR);
	    return t1;
	default: throw new parserError(currentToken);
	}
    } // end of stat
    
    private token expr() throws parserError, scannerError{
	switch (currentToken.type) {
	case scanner.ID:
	case scanner.STRING: 
	case scanner.MINUS:
	case scanner.LPAR:
	    token t1 = atom();
	    return exprtail(t1);
	default: throw new parserError(currentToken);
	}
    } // end of expr
    
    private token exprtail(token t) throws parserError, scannerError{
	switch (currentToken.type) {
	case scanner.PLUS:
	    token t1 = match(scanner.PLUS);
	    token t2 = atom();
	    t1.addChild(t);
	    t1.addChild(t2);
	    return exprtail(t1);
	case scanner.BAR: 
	case scanner.RPAR:
	    return t;
	default: throw new parserError(currentToken);
	}
    } // end of exprtail
	
    private token atom() throws parserError, scannerError{
	token t1, t2;
	switch(currentToken.type){ 
	case scanner.ID:
	    return match(scanner.ID);
	case scanner.STRING:
	    return match(scanner.STRING);
	case scanner.MINUS:
	    t1 = match(scanner.MINUS);
	    t2 = atom();
	    t1.addChild(t2);
	    return t1;
	case scanner.LPAR:
	    match(scanner.LPAR);
	    t1 = expr();
	    match(scanner.RPAR);
	    return t1;
	default:
	    throw new parserError(currentToken);
	} // end of lookahead cases
    } // end of atom
    
    private token idlist() throws parserError, scannerError{
	switch (currentToken.type) {
	case scanner.ID:
	    token t1 = match(scanner.ID);
	    token t2 = idtail();
	    token t3 = new token(scanner._CLEAR,"_clear");
	    t3.addChild(t1);
	    t3.addChild(t2);
	    return t3;
	default: throw new parserError(currentToken);
	}
    } // end of idlist
    
    private token idtail() throws parserError, scannerError{
	switch (currentToken.type) {
	case scanner.COMMA:
	    match(scanner.COMMA);
	    token t1 = match(scanner.ID);
	    token t2 = idtail();
	    token t3 = new token(scanner._CLEAR,"_clear");
	    t3.addChild(t1);
	    t3.addChild(t2);
	    return t3;
	case scanner.RPAR:
	    return (new token(scanner.EPSILON,"epsilon"));
	default: throw new parserError(currentToken);
	}
    } // end of idtail
    
} // end of the class parser

