/* File: token.c
   Support utilities for the parser.
*/

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

/* Internal function */
static int SkipSpaces(char *, int) ;

/* Initialize input 
*/
input_t *ReadInput(void) {
    input_t *rec ;
    int length ;
 
    /* make space for rec */
    rec = (input_t *) malloc(sizeof(input_t)) ;
    if (rec == NULL) {
	printf("ReadInput: could not malloc!\n") ;
	exit(1) ;
    }

    /* Get user input */
    fgets(rec->str, BUFLEN, stdin) ; /* includes '\n' */
    length = strlen(rec->str) ;
    rec->str[length-1] = '\0' ; /* kill trailing '\n' */

    /* Initialize input indices */
    rec->pos = SkipSpaces(rec->str, 0) ; /* skip leading spaces */
    rec->last = length - 2 ;

    /* Haven't looked ahead */
    rec->looked = 0 ;
    rec->lookahead_pos = rec->pos ;
    rec->lookahead.kind = UNDEF ;

    return rec ;
}


/* Return index of next non-space character */

int SkipSpaces(char *str, int i) {

   while(isspace(str[i])) i++ ;
   return i ;
}


/* Return the next token in the input string.  
   Just looking, do not consume the token.
   Assumptions:
     input->pos is the index of the beginning of the next token
	(leading spaces should have been skipped)

     input->last is the index of the last character of the input
	string ('\0' is beyond the last character).

     input->looked says whether we've looked ahead before.

     input->lookahead_pos, if we've looked ahead, points to
	the beginning of the token after the look ahead token

     input->lookahead is the lookahead token, if we've looked ahead
        before.
	
*/

token_t LookAhead(input_t *rec) {
   token_t token ;
   char *str ;
   int val, i ;

   /* Previously looked ahead? */
   if (rec->looked) return rec->lookahead ;

   /* Look for the next token, mark that we looked ahead */
   rec->looked = 1 ;
   i = rec->pos ;
   str = rec->str ;

   /* Bogus input position */
   if (i < 0) {
      token.kind = UNDEF ;
      rec->lookahead = token ;
      rec->looked = 0 ;
      return token ;
   }

   /* Past end of input ? */
   if (rec->pos > rec->last) {
      token.kind = EOL ;
      rec->lookahead = token ;
      rec->lookahead_pos = i ;
      return token ;
   }

   /* it's a decimal number */
   if (isdigit(str[i])) {
      val = 0 ;
      do {
	  val = 10*val + str[i] - '0' ;
	  i++ ;
      } while (isdigit(str[i])) ;

      token.kind = NUMBER ;
      token.value = val ;
      rec->lookahead = token ;
      rec->lookahead_pos = SkipSpaces(str,i) ;
      return token ;
   }

   /* Single character cases */
   switch(str[i]) {
      case '+' : token.kind = PLUS ; break ;

      case '-' : token.kind = MINUS ; break ;

      case '*' : token.kind = TIMES ; break  ;

      case '/' : token.kind = DIVIDE ; break ;

      case '(' : token.kind = L_PAREN ; break ;

      case ')' : token.kind = R_PAREN ; break ;

      default :
	 token.kind = UNDEF ;
	 rec->lookahead = token ;
	 rec->lookahead_pos = i ;
	 return token ;
   }

   rec->lookahead = token ;
   rec->lookahead_pos = SkipSpaces(str, i+1) ;
   return token ;

}


/* Consume the next token.
   Read documentation for LookAhead().
*/

void EatToken(input_t *rec) {

   /* Must have looked ahead previously.  Update input accordingly */
   if (rec->looked) {
      rec->pos = rec->lookahead_pos ;
      rec->looked = 0 ;
      return ;
   } else {
      printf ("EatToken: Internal error, must call LookAhead() first\n") ;
      exit(1) ;
   }
}   



/* Print error message and point to where the syntax error occurred. */

void PrintError(input_t *rec) {
   int i ;

   printf("%s\n", rec->str) ;
   for (i = 0 ;  i < rec->pos ; i++) {
      printf(" ") ;
   }
   printf("^--- syntax error\n") ;
}
