/* File: parse.c

   Parse arithmetic expressions
   involving +, -, *, / and ().
*/

#include <stdio.h>
#include "token.h"

/* Local Types */

enum expr_type {EXPR, TERM, FACTOR, ERROR} ;

typedef struct{
   enum expr_type type ;
   int value ;
   char *err_mesg ;
} result_t ;


/* Function Prototypes */

result_t ParseEverything(input_t *) ;
result_t ParseExpression(input_t *) ;
result_t ParseTerm(input_t *)  ;
result_t ParseFactor(input_t *) ;


/* Everything -> Expression EOL */

result_t ParseEverything(input_t *input) {
   result_t result ;
   token_t next ;

   result = ParseExpression(input) ;
   if (result.type == ERROR) return result ;

   next = LookAhead(input) ;

   if (next.kind != EOL) {
      result.type = ERROR ;
      result.err_mesg = "Garbage at the end" ;
      return result ;
   } 
   
   return result ;
}



/* Expression -> Term + Expression
	      -> Term - Expression
*/

result_t ParseExpression(input_t *input) {
   int expr_value = 0, sign = 1 ;
   token_t  next ;
   result_t result ;

   while(1) {
      result = ParseTerm(input) ;
      if (result.type == ERROR) return result ;

      expr_value = expr_value + sign * result.value ;

      next = LookAhead(input) ;

      switch(next.kind) {
	 case UNDEF : /* Bogus, bogus */
	    fprintf(stderr,"Oops, caught internal error\n") ;
	    exit(1) ;
            
	 case PLUS :
	    EatToken(input) ; /* consume the '+' */
	    sign = 1 ;
	    break ;

	 case MINUS :
	    EatToken(input) ; /* consume the '-' */
	    sign = -1 ;
	    break ;

	 default :
	    result.type = EXPR ;
	    result.value = expr_value ;
	    return result ;
      }
   }
}



/* Term -> Factor * Term
	-> Factor / Term
*/

result_t ParseTerm(input_t *input) {
   int term_value = 1, power = 1 ;
   token_t next ;
   result_t result ;  

   while(1) {
      result = ParseFactor(input) ;
      if (result.type == ERROR) return result ;
       
      if (power == 1) {
	 term_value *= result.value ;
      } else {
	 term_value /= result.value ;
      }

      next = LookAhead(input) ;

      switch(next.kind) {
	 case UNDEF : /* Bogus, bogus */
	    fprintf(stderr,"Yikes, caught internal error\n") ;
	    exit(1) ;
            
	 case TIMES :
	    EatToken(input) ; /* consume the '*' */
	    power = 1 ;
	    break ;

	 case DIVIDE :
	    EatToken(input) ; /* consume the '/' */
	    power = -1 ;
	    break ;

	 default :
	    result.type = TERM ;
	    result.value = term_value ;
	    return result ;
      }
   }
}



/* Factor -> ( Expression )
	  -> Number
*/

result_t ParseFactor(input_t *input) {
   int factor_value ;
   token_t next ;
   result_t result ;

   next = LookAhead(input) ;

   if (next.kind == NUMBER) {
      EatToken(input) ; /* consume the number */
      result.type = FACTOR ;
      result.value = next.value ;
      return result ; 
   }

   if (next.kind != L_PAREN) {
      result.type = ERROR ;
      result.err_mesg = "Expecting a number or '('" ;
      return result ;
   }

   EatToken(input) ; /* consume the '(' */

   result = ParseExpression(input) ;
   if (result.type == ERROR) return result ;

   next = LookAhead(input) ;

   if (next.kind != R_PAREN) {
      result.type = ERROR ;
      result.err_mesg = "Expecting ')'" ;
      return result ;
   }

   EatToken(input) ; /* consume ')' */

   result.type = FACTOR ;
   return result ;
}


main() {
   result_t result ;
   input_t *input ;

   printf("Enter an expression (<255 char): ") ;
   input = ReadInput() ;
   result = ParseEverything(input) ;

   if (result.type == ERROR) {
      PrintError(input) ;
      printf("%s\n", result.err_mesg) ;
   } else {
      printf("Value = %d\n", result.value) ;
   }
}
