/* SI 413 Fall 2021
 * This is a hand-coded scanner for the simple calculator
 * language in the slides. It is based explicitly on the
 * finite automaton.
 */
#include <iostream>
using namespace std;

#include "bisoncalc.tab.hpp" // Header file generated by bison

/* This is the scanner. It reads in characters and returns
 * the type of the token, as an integer code.
 */
int yylex()
{
  int state = 0; // this is the start state.
  char opsym; // this is the operation ('*' or '/') when we OMP.
  bool negative; // true for a negative NUM
  int intval; // this is the integer value when we end in an NUM.

  while(true) {
    char c = cin.get();
    switch(state) {
      case 0:
        switch(c) {
          case '0': case '1': case '2': case '3': case '4':
          case '5': case '6': case '7': case '8': case '9':
            intval = c - '0';
            negative = false;
            state = 1;
            break;
          case '-':
            state = 2; break;
          case '+':
            state = 3; break;
          case '*': case '/':
            opsym = c; state = 4; break;
          case '(': state = 5; break;
          case ')': state = 6; break;
          case ';': state = 7; break;
          case ' ': case '\t': case '\n':
            break; // stay in state 0
          case EOF:
            return YYEOF;
          default:
            cerr << "scanner error: token cannot start with '"
              << c << "'" << endl;
            cin.putback(c);
            return YYerror;
        }
        break;

      case 1:
        switch(c) {
          case '0': case '1': case '2': case '3': case '4':
          case '5': case '6': case '7': case '8': case '9':
            intval = intval * 10 + (c - '0');
            break; // stay in state 1
          default:
            cin.putback(c);
            // finished! set yylval.val to return the number to the parser
            if (negative) yylval.val = -intval;
            else yylval.val = intval;
            return NUM;
        }
        break;

      case 2:
        switch(c) {
          case '0': case '1': case '2': case '3': case '4':
          case '5': case '6': case '7': case '8': case '9':
            intval = c - '0';
            negative = true;
            state = 1;
            break;
          default:
            cin.putback(c);
            yylval.sym = '-';
            return OPA;
        }
        break;

      case 3:
        cin.putback(c);
        yylval.sym = '+';
        return OPA;

      case 4:
        cin.putback(c);
        yylval.sym = opsym;
        return OPM;
        break;

      case 5:
        cin.putback(c);
        return LP;

      case 6:
        cin.putback(c);
        return RP;

      case 7:
        cin.putback(c);
        return STOP;
    } // end switch(state)
  } // end while

  // this should be unreachable
}