/* PARSING AND LEXICAL ANALYSIS FOR E 
   (excluding '-' and if0-then-else) */

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

/* TOKENS ------------------------------------------------------------- */

/* Token values: 
    EOF = -1  (defined in stdio.h)
    Single-character tokens are represented by character's ASCII code. 
    Other token values are arbitrary (but must all be below smallest
           single-character tokens ASCII code).
*/

static char operators[] = "={}()+;";

/* Following implicitly defines type token to be int */
typedef enum {VAR=0,ID,NUM} token;

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

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

static FILE *fp;
static token tok;
static char lexeme[MAXCHAR+1];
static int lineno;

/* ERROR REPORTING ---------------------------------------------------- */

static void error(char *msg, ...) {
  va_list args;
  va_start(args,msg);
  fprintf(stderr, "Line %d: ", lineno);
  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) 
    lexeme[i] = x;
  else {
    lexeme[MAXCHAR] = '\0';
    error("Lexical error: Symbol or number too long: \"%s\"",lexeme);
  }
}

static void lex() {
  lexeme[0] = '\0';
  while (1) {
    int c = getc(fp);
    if (c == EOF) {
      tok = EOF;
      return;
    } else if ( c == '\n') {
      lineno++;
      continue;
    } else if (isspace(c)) 
      continue;
    else if (strchr(operators,c) != NULL) {
      tok = c;
      store(c,0);
      lexeme[1] = '\0';
      return;
    } else if (isalpha(c)) {
      int i = 0;
      store(c,i++);
      c = getc(fp);
      while (isalnum(c)) {
	store(c,i++);
	c = getc(fp);
      }
      lexeme[i] = '\0';
      ungetc(c,fp);
      /* check for keywords */
      if (strcmp(lexeme,"var") == 0)
	tok = VAR;      
      else 
	tok = ID;
      return;
    } else if (isdigit(c)) {
      int i = 0;
      store(c,i++);
      c = getc(fp);
      while (isdigit(c)) {
	store(c,i++);
	c = getc(fp);
      };
      lexeme[i] = '\0';
      ungetc(c,fp);
      tok = NUM;
      return;
    } else 
      error("Lexical error: Invalid character: \"%c\"", c);      
  }
}

/* PARSING ------------------------------------------------------------ */

/* Support routines --------------------------------------------------- */

static void expect_symbol(char t) {
  if (tok != t) 
     error("Syntax error: expected: \"%c\" found: \"%s\"",t,lexeme);
  lex();
}

static void expected_error(char *expected) {
  error("Syntax error: expected: %s found: \"%s\"",
	expected,lexeme);
}

/* Main recursive-descent parsing routines ---------------------------- */ 

static id parse_id() {
  id i;
  if (tok != ID) 
    expected_error("identifier");
  i = strsave(lexeme);
  lex();
  return i;
}

static ids parse_ids() {
  id i = parse_id();
  if (tok != ';') {
    ids is = parse_ids();
    return mk_ids(i,is);
  } else
    return mk_ids(i,NULL);
}

/* Since next three functions are mutually recursive, must
   declare two of them before defining any. */
static Exp parse_term();
static Exps parse_exps();

static Exp parse_exp() {
  Exp el;
  if (tok == ID) { 
    id i = parse_id();
    if (tok == '=') {
      Exp e;
      lex();
      e = parse_exp(e);
      return mk_AsgnExp(i,e);
   } else 
      el = mk_VarExp(i);
  } else 
    el = parse_term();
  if (tok == '+') {
    Exp er;
    lex();
    er = parse_term();
    return mk_PlusExp(el,er);
  } else 
    return el;
}
 
static Exp parse_term() {
  if (tok == ID) {
    id  i = parse_id();
    return mk_VarExp(i);
  } else if (tok == NUM) {
    int n = atoi(lexeme);
    lex();
    return mk_ConstExp(n);
  } else if (tok == '{') {
    ids is = NULL;
    Exps es;
    lex();
    if (tok == VAR) {
      lex();
      is = parse_ids();
      expect_symbol(';');
      es = parse_exps();
    } else 
      es = parse_exps();
    expect_symbol('}');
    return mk_BlockExp(is,es);
  } else if (tok == '(') {
    Exp e;
    lex();
    e = parse_exp();
    expect_symbol(')');
    return e;
  } else 
    expected_error("expression");
}

static Exps parse_exps() {
  Exp e = parse_exp();
  if (tok == ';') {
    Exps es; 
    lex();
    es = parse_exps();
    return mk_Exps(e,es);
  } else
    return mk_Exps(e,NULL);
}

/* Top-level parsing routine ------------------------------------------ */

Exp parse(FILE *fp0) {
  Exp e;
  fp = fp0;
  lineno = 1;
  lex();
  e = parse_exp();
  if (e == NULL) 
    expected_error("expression");
  if (tok != EOF)
    expected_error("end of file");
  return e;
}

