/* SI 413 Fall 2021
 * Recursive descent parser and interpreter
 * for a simple calculator language.
 */

#include <iostream>
#include <cstdlib>
#include "bisoncalc.tab.hpp"
using std::cout;
using std::cerr;
using std::endl;
using std::flush;

//-- Prototypes and globals
int yylex();

void stmt();
int exp();  int exptail(int lhs);
int term(); int termtail(int lhs);
int factor();
int next = -1;
YYSTYPE nextval;

//-- Helper functions
void parse_error(const char* nt) {
  cerr << "Parse error in " << nt << endl;
  exit(1);
}

int peek() {
  if (next == -1) {
    next = yylex();
    nextval = yylval;
  }
  return next;
}

// Returns the value of the matched token
YYSTYPE match(int tok) {
  if (peek() != tok) parse_error("match");
  next = -1;
  return nextval;
}

//-- Grammar rule functions
void stmt() {
  int result = exp();
  match(STOP);
  cout << result << endl;
}

int exp() {
  int first = term();
  return exptail(first);
}

int exptail(int lhs) {
  char op;
  int next;
  switch(peek()) {
    case OPA:
      op = match(OPA).sym;
      next = term();
      if (op == '+') return exptail(lhs + next);
      else return exptail(lhs - next);
    case RP: case STOP:
      return lhs; break;
    default:
      parse_error("exptail"); return -1;
  }
}

int term() {
  int first = factor();
  return termtail(first);
}

int termtail(int lhs) {
  char op;
  int next;
  switch(peek()) {
    case OPM:
      op = match(OPM).sym;
      next = factor();
      if (op == '*') return termtail(lhs * next);
      else return termtail(lhs / next);
    case RP: case STOP: case OPA:
      return lhs;
      break;
    default:
      parse_error("term");
      return -1;
  }
}

int factor() {
  int val;
  switch(peek()) {
    case NUM:
      return match(NUM).val; break;
    case LP:
      match(LP);
      val = exp();
      match(RP);
      return val;
      break;
    default: parse_error("factor"); return -1;
  }
}

int main() {
  while(true) {
    cout << "> " << flush;
    if (peek() == 0) break;
    stmt();
  }
  cout << endl;
  return 0;
}