/* LEXICAL ANALYSIS FOR E */

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "ast.h"
#include "y.tab.h"

/* Maps keywords to corresponding token values. */
static struct {
  char *name;
  int tok;
} keywords[] = {{"var",VAR},{"if0",IF0},{"then",THEN},{"else",ELSE}};

/* Single-character operators are their own token values. */
static char operators[] = "={}()+-;";

/* VARIABLES SHARED BY LEXICAL ANALYSIS AND PARSING ------------------- */

extern YYSTYPE yylval;

/* GLOBAL VARIABLES USED BY LEXICAL ANALYSIS -------------------------- */

static FILE *fp;
static int yylineno = 1;

#define MAXCHAR 64 /* maximum length of a lexeme */

static char yytext[MAXCHAR+1];

/* ERROR REPORTING (ALSO USED BY PARSER) ------------------------------ */

void yyerror(char *msg, ...) {
  va_list args;
  va_start(args,msg);
  fprintf(stderr, "Line %d near '%s':", yylineno,yytext);
  vfprintf(stderr, msg,args);
  fprintf(stderr,"\n");
  va_end(args);
  exit(1);
}

/* LEXICAL ANALYSIS --------------------------------------------------- */

/* Store character into current lexeme, checking for overflow.*/
static void store(char x,int i)
{
  if (i < MAXCHAR) 
    yytext[i] = x;
  else {
    yytext[MAXCHAR] = '\0';
    yyerror("Lexical error: Symbol or number too long: \"%s\"",yytext);
  }
}

int yylex() {
  yytext[0] = '\0';
  while (1) {
    int c = getc(fp);
    if (c == EOF) {
      return 0;
    } else if ( c == '\n') {
      yylineno++;
      continue;
    } else if (isspace(c)) 
      continue;
    else if (strchr(operators,c) != NULL) {
      store(c,0);
      yytext[1] = '\0';
      return c;
    } else if (isalpha(c)) {
      int i = 0;
      store(c,i++);
      c = getc(fp);
      while (isalnum(c)) {
	store(c,i++);
	c = getc(fp);
      }
      yytext[i] = '\0';
      ungetc(c,fp);
      for (i = 0; i < (sizeof(keywords)/sizeof(keywords[0])); i++)
	if (!strcmp(yytext,keywords[i].name)) {
	  return keywords[i].tok; 
	}
      yylval.svalue = strsave(yytext);
      return ID;
    } else if (isdigit(c)) {
      int i = 0;
      store(c,i++);
      c = getc(fp);
      while (isdigit(c)) {
	store(c,i++);
	c = getc(fp);
      };
      yytext[i] = '\0';
      ungetc(c,fp);
      yylval.ivalue = atoi(yytext);  /* don't worry about conversion errors */
      return NUM;
    } else 
      yyerror("Lexical error: Invalid character: \"%c\"", c);      
  }
}

void init_lex(FILE *fp0) {
  fp = fp0;
  yylineno = 1;
}

  
