#ifdef GOT_STRTAB
#include <strtab.h>
#else
#define st_GetIdent(a,b)  (b)
#endif

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#include "ibm2.tab.h"

#include "error.h"

struct reserved_pair {
  char *str;
  int token;
};

struct reserved_pair reserved[] = {
  { "MODULE", MODULE },
  { "BEGIN", BEGIN },
  { "END", END },
  { "ARRAY", ARRAY },
  { "OF", OF },
  { "POINTER", POINTER },
  { "TO", TO },
  { "RECORD", RECORD },
  { "TRUE", TRUE },
  { "FALSE", FALSE },
  { "IF", IF },
  { "THEN", THEN },
  { "ELSE", ELSE },
  { "WHILE", WHILE },
  { "DO", DO },
  { "RETURN", RETURN },
  { "INTEGER", INTEGER },
  { "BOOLEAN", BOOLEAN },
  { "TYPE", TYPE },
  { "VAR", VAR },
  { "PROCEDURE", PROCEDURE },
  { "OR", OR },
  { "AND", AND },
  { "NOT", NOT }
};
#define NUM_RESERVED_WORDS (sizeof(reserved)/sizeof(*reserved))

/* ---------- Macros ---------- */
#define MAX_IDENT_LENGTH 250
#define INIT_STATE 0
#define IN_IDENT 1
#define IN_NUMBER 2

/* ---------- Function prototypes ---------- */
extern int ident_to_token(char *);

extern int next_char(void);
extern void eat_char(void);
extern int peek_char(void);

int yylex () 
{
  char buf[MAX_IDENT_LENGTH];
  char *ch = buf;
  int state = INIT_STATE;

  while (1) {
    switch(state) {
    case INIT_STATE:
      if (isalpha(peek_char())) {
	state = IN_IDENT;
	*ch++ = next_char();
      }
      else if (isdigit(peek_char())) {
	*ch++ = next_char();
	state = IN_NUMBER;
      }
      break;
    case IN_IDENT:
      if (isalnum(peek_char())) {
	*ch++ = next_char();
      }
      else {
	*ch++ = '\0';
	yylval.id = st_GetIdent(stp, buf);
	return ident_to_token(buf);
      }
      break;
    case IN_NUMBER:
      if (isdigit(peek_char())) {
	*ch++ = next_char();
      }
      else {
	*ch++ = '\0';
	yylval.number = atoi(buf);
	return NUMBER;
      }
      break;
    default:
      error(SCANNER, INTERNAL, INVALID_STATE);
      abort();
      break;
    }
  }
}

int ident_to_token(char *id)
{
  int i;
  for (i = 0; i < NUM_RESERVED_WORDS; i++) {
    if (!strcmp(id, reserved[i].str)) return reserved[i].token;
  }
  return IDENTIFIER;
}

/* ------------------------------------------------------------------------ */
/* get_char interface */

/* next_char - return the next character and move forward one. */
/* eat_char - move forward one character */
/* peek_char - return the next character, but don't move forward */

static int spoof_char = 0;

int next_char()
{
  int ret;

  if (spoof_char == 0) {
    return getchar();
  }
  else {
    ret = spoof_char;
    spoof_char = 0;
    return ret;
  }
}

void eat_char()
{
  if (spoof_char == 0) {
    getchar();
  }
  else {
    spoof_char = 0;
  }
}

int peek_char()
{
  if (!spoof_char) {
    spoof_char = getchar();
  }
  return spoof_char;
}
