// a scanner class for the bitter programming language
import java.io.*;

// a scanner error class
class scannerError extends Exception{
    public scannerError(token t){
	super("unexpected_character: " + t.name);
    } // end of constructor
} // end of class scannerError

public class scanner{
    private StringReader input;
    private int ch; // the current lookahead character
    
    public static final int START = 0;
    public static final int BAR = 1;
    public static final int LPAR = 2;
    public static final int RPAR = 3;
    public static final int EQ = 4;
    public static final int PLUS = 5;
    public static final int MINUS = 6;
    public static final int COMMA = 7;
    public static final int STRING = 8;
    public static final int ID = 9;
    public static final int COMMENT = 10;
    public static final int _START = 11;
    public static final int _STOP = 12;
    public static final int _CLEAR = 13;
   
    public static final int ERROR = -2;
    public static final int EOF = -1;
    public static final int EPSILON = -3;

    public scanner(String s){
	input = new StringReader(s);
    } // end of constructor
    
    
    // return a token
    public token getToken() throws scannerError{
	token currentToken;
	String result = "";
	char ch = '\0';
	int state = START;
	while(true){
	    switch(state){
	    case START:
		ch = readchar();
		switch(ch){
		case '|':
		    state = BAR; break;
		case '(':
		    state = LPAR; break;
		case ')':
		    state = RPAR; break;
		case '=':
		    state = EQ; break;
		case '+':
		    state = PLUS; break;
		case '-':
		    state = MINUS; break;
		case ',':
		    state = COMMA; break;
		case '.':
		case '!':
		    state = STRING; break;
		case '[':
		    state = COMMENT; break;
		case '_':
		    state = ID; break;
		case '\0':
		    state = EOF; break;
		default:
		    if(!Character.isWhitespace((char)ch))
			state = ERROR;
		    break;
		} // end of START transitions
		break;
	    case BAR: return( new token(BAR, "|"));
	    case LPAR: return(new token(LPAR, "("));
	    case RPAR: return(new token(RPAR, ")"));
	    case EQ: return(new token(EQ,"="));
	    case PLUS: return(new token(PLUS, "+"));
	    case MINUS: return(new token(MINUS, "-"));
	    case COMMA: return(new token(COMMA, ","));
	    case EOF: return(new token(EOF, result + ch));
	    case STRING: 
		result = result + ch;
		ch = readchar();
		switch(ch){
		case '.':
		case '!':
		    state = STRING; break;
		default:
		    unread(ch);
		    return(new token(STRING, result));
		} // end of transitions from STRING
		break;
	    case ID:
		result = result + ch;
		ch = readchar();
		if(Character.isDigit((char)ch) || Character.isLetter((char)ch)) 
		    state = ID;
		else{
		    unread(ch); 
		    if (result.equals("_start"))  
			return (new token(_START,result));
		    else if (result.equals("_stop")) 
			return (new token(_STOP,result));
		    else if (result.equals("_clear"))
			return (new token(_CLEAR,result));
		    else return(new token(ID, result));
		} // end of ID
		break;
	    case COMMENT:
		result = result + ch;
		ch = readchar();
		switch(ch){
		case '\n':
		case '\0':
		    return(new token(COMMENT,result));
		default:
		    state = COMMENT;
		break;
		}
	    } // end of states
	} // end of loop
    } // end of getToken
    
    
    char readchar(){
	int ch;
	try{input.mark(1); ch = input.read();}
	catch(IOException e){ch = EOF;}
	return (char)ch;
    }
    
    void unread(char ch) {
	try{input.reset();}
	catch(IOException e) {}
    }

} // end of class scanner	


	
