//  File: parse.C
//
//  Parse arithmetic expressions involving +, -, *, / and ().

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

// Function prototypes for this file only
//
static ETree *ParseExpression(TokenStream&) ;
static ETree *ParseTerm(TokenStream&)  ;
static ETree *ParseFactor(TokenStream&) ;


// Recursively compute the value of an expression tree
//
int Evaluate(const ETree& T) {
   int left_val, right_val ;

   if (T.root == NULL) return 0 ;

   left_val  = Evaluate(T.root->left) ;   // value of left subtree
   right_val = Evaluate(T.root->right) ;  // value of right subtree

   switch(T.root->data.kind) {

      case Token::NUMBER :
         return T.root->data.value ;

      case Token::PLUS :
         return left_val + right_val ;

      case Token::MINUS :
         return left_val - right_val ;

      case Token::TIMES :
         return left_val * right_val ;

      case Token::DIVIDE :
         return left_val / right_val ;

      default : return 0 ;
   }
}


// Parse the TokenStream into an expression tree. Catches exceptions.
//
ETree *Parse(TokenStream &tkstrm) {

   try {
      return ParseEverything(tkstrm) ;
   }
   catch(ParseError& e) {
      cerr << "ParseError: " << e.msg << endl ;
      tkstrm.error() ;
      return NULL ;
   }
   catch(MemoryError& e) {
      cerr << "MemoryError exception!" ;
      return NULL ;
   }
}


// Parse a TokenStream. Re-throws errors. Use this function to
// catch your own expressions.
//
// Everything -> Expression EOL
//
ETree *ParseEverything(TokenStream& tkstrm) {

   ETree *all=NULL ;
   Token next ;

   try {
      all = ParseExpression(tkstrm) ;

      next = tkstrm.look() ;

      if (next.kind != Token::EOL) {
         throw(ParseError("Garbage at the end")) ;
      }

      return all ;
   } 

   catch(...) {     // catch any exception

      delete all ;      // free up memory
      throw ;           // rethrow the exception
   }

}


// Parse an expression. Includes mutually recursive calls to 
//   ParseTerm and ParseFactor
//
// Expression -> Term + Expression
//            -> Term - Expression

static ETree *ParseExpression(TokenStream& tkstrm) {

   ETree *expr=NULL, *term=NULL, *temp=NULL ;
   Token  op ;

   try {
      expr = ParseTerm(tkstrm) ;

      while(1) {

         op = tkstrm.look() ;

         switch(op.kind) {

            case Token::UNDEF :         // Bogus, bogus
               cerr << "Oops, caught internal error" << endl ;
               exit(1) ;

            case Token::PLUS :
               tkstrm.eat() ; // consume the '+'
               break ;

            case Token::MINUS :
               tkstrm.eat() ; // consume the '-'
               break ;

            default :
               return expr ;
         }

         term = ParseTerm(tkstrm) ;

         temp = new ETree(op, *expr, *term) ;
         expr = temp ;
      }
   } // end of try block

   catch(...) {     // catch any exception

      delete expr ; // free up memory
      delete term ;
      delete temp ;
      throw ;           // rethrow the exception
   }

}


// Parse a term. Includes mutually recursive calls to 
//   ParseExpression and ParseFactor
//
// Term -> Factor * Term
//      -> Factor / Term

static ETree *ParseTerm(TokenStream& tkstrm) {

   ETree *term=NULL, *factor=NULL, *temp=NULL ;
   Token op ;

   try {

      term = ParseFactor(tkstrm) ;

      while(1) {

         op = tkstrm.look() ;

         switch(op.kind) {

            case Token::UNDEF :         // Bogus, bogus
               throw(ParseError("Unknown symbol")) ;

            case Token::TIMES :
               tkstrm.eat() ;   // consume the '*'
               break ;

            case Token::DIVIDE :
               tkstrm.eat() ; // consume the '/'
               break ;

            default :
               return term ;
         }

         factor = ParseFactor(tkstrm) ;

         temp = new ETree(op, *term, *factor) ;
         term = temp ;
      }
   } // end of try block

   catch (...) {        // catch any exception

      delete term ; // free up memory
      delete factor ;
      delete temp ;
      throw ;           // rethrow the exception
   }

}


// Parse a factor. Includes mutually recursive calls to 
//   ParseExpression and ParseTerm
//
// Factor -> ( Expression )
//        -> Number

static ETree *ParseFactor(TokenStream& tkstrm) {

   Token next ;
   ETree *factor=NULL ;

   try {

      next = tkstrm.look() ;

      if (next.kind == Token::NUMBER) {

         tkstrm.eat() ; // consume the number
         factor = new ETree(next) ;
         return factor ;
      }

      if (next.kind != Token::L_PAREN) {
         throw(ParseError("Expecting a number or '('")) ;
      }

      tkstrm.eat() ; // consume the '('

      factor = ParseExpression(tkstrm) ;

      next = tkstrm.look() ;

      if (next.kind != Token::R_PAREN) {
         throw(ParseError("Expecting ')'")) ;
      }

      tkstrm.eat() ; // consume ')'

      return factor ;
   }

   catch(...) {     // catch any exception

      delete factor ;   // free up memory
      throw ;           // rethrow exception
   }
}
