/* SI 413 Fall 2021
 * This is a table-driven top-down parser for the calculator language.
 *
 * Here is the grammar:
        stmt -> exp STOP
         exp -> term exptail
     exptail -> OPA term exptail| e
        term -> factor termtail
    termtail -> OPM factor termtail | e
      factor -> NUM | LP exp RP
 */
#include <iostream>
#include <cstdlib>
#include <string>
#include <stack>
#include "bisoncalc.tab.hpp"
using std::cout;
using std::cerr;
using std::endl;
using std::flush;
using std::stack;


/**************************************
 * Non-terminals are
 * numbered consecutively from startSym
 * up, where startSym is some number
 * greater than any token number.
 **************************************/
enum NonTerminal {
  stmt=500,
  exp,
  exptail,
  term,
  termtail,
  factor
};
const NonTerminal startSym = stmt;
const int error = -1;

//-- These are the grammar rules. -----------------//
const int rule[][10] = {
  { exp, STOP, -1 },             // Rule 0  (stmt)
  { term, exptail, -1 },         // Rule 1  (exp)
  { OPA, term, exptail, -1 },    // Rule 2  (exptail)
  { -1 },                        // Rule 3  (exptail)
  { factor, termtail, -1 },      // Rule 4  (term)
  { OPM, factor, termtail, -1 }, // Rule 5  (termtail)
  { -1 },                        // Rule 6  (termtail)
  { NUM, -1 },                   // Rule 7  (factor)
  { LP, exp, RP, -1 }            // Rule 8  (factor)
};

//-- parseTable[symbol][token] = predicted rule. --//
const int parseTable[][10] = {
  /*                NUM OPA OPM LP  RP  STOP */
  /* stmt     */ {   0,  0, -1,  0, -1, -1 },
  /* exp      */ {   1,  1, -1,  1, -1, -1 },
  /* exptail  */ {  -1,  2, -1, -1,  3,  3 },
  /* term     */ {   4,  4, -1,  4, -1, -1 },
  /* termtail */ {  -1,  6,  5, -1,  6,  6 },
  /* factor   */ {   7, -1, -1,  8, -1, -1 }
};

int next = -1;

//-- Helper functions

int yylex();

void perror(const char* msg) {
  cerr << "Parse error: " << msg << endl;
  exit(1);
}

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

void match(int tok) {
  if (tok == peek())
    next = -1;
  else
    perror("match");
}

//-- Table-driven parser ... pretty darn simple
void parse() {
  stack<int> S;
  S.push(startSym);
  while(true)
  {
    int sym = S.top();
    S.pop();
    if (sym < startSym)
    {
      match(sym);
      if (sym == STOP) return;
    }
    else
    {
      int action = parseTable[sym - startSym][peek()-NUM];
      if (action == error) perror("Non-terminal expansion");
      int k = 0;
      while(rule[action][k] != -1) ++k;
      while(--k >= 0) S.push(rule[action][k]);
    }
  }
}

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