/* SI 413 Fall 2021
 * Lab 7
 * Parser for SPL that just shows the parse tree
 * This parser uses a simpler grammar with assoc/prec specifications.
 * YOUR NAME HERE
 */

// This code is included in the spl.tab.hpp header file
%code requires {

#include <cstdlib>
#include <iostream>
using namespace std;

#include "ast.hpp"

int yylex();
int yylex_destroy();

} // end header file part

// This code is only included in the parser file spl.tab.cpp
%code {

// These are the colored output streams to make things all pretty.
colorout resout(1, 'u');
colorout errout(2, 'r');

// Global variable will be set to the entire AST.
Stmt* tree;

// Global variable to indicate if an error has occurred.
bool error;

// Global variable to indicate that terminal input is "live" and
// so prompts should be displayed.
bool showPrompt;

// This is the C file that flex reads from for scanning.
extern FILE* yyin;

void yyerror(const char *p) {
  if (! error) {
    errout << "Parser error: " << p << endl;
    error = true;
  }
}

} // end top of parser part

  /* Tell bison to give descriptive error mesages. */
%define parse.error verbose

%union {
  Block* block;
  Stmt* stmt;
  Exp* exp;
  Id* id;
  Oper op;
};

%left<op> BOP
%right<op> NOTTOK
%left<op> COMP
%left<op> OPA
%left<op> OPM
%right POSNEG
%left FUNARG

%token LC RC LP RP LAMBDA IF IFELSE WHILE READ WRITE NEW ASN STOP
%token<id> ID
%token<exp> NUM BOOL
%type<stmt> stmt stmtlist
%type<block> block
%type<exp> exp

%destructor { delete $$; } <block>
%destructor { delete $$; } <stmt>
%destructor { delete $$; } <exp>
%destructor { delete $$; } <id>

%%
  /*Note: YYACCEPT is a bison macro that just tells it to quit parsing.*/
res: stmt { tree = $1; YYACCEPT; }
|         { tree = nullptr; }

block: LC stmtlist RC { $$ = new Block($2); }

stmtlist: stmtlist stmt { $$ = Stmt::append($1,$2); }
|                       { $$ = new NullStmt; }

stmt: NEW ID ASN exp STOP    {$$ = new NewStmt($2,$4);}
|     ID ASN exp STOP        {$$ = new Asn($1,$3);}
|     WRITE exp STOP         {$$ = new Write($2);}
|     IF exp block           {$$ = new IfStmt($2,$3,new NullStmt());}
|     IFELSE exp block block {$$ = new IfStmt($2,$3,$4);}
|     WHILE exp block        {$$ = new WhileStmt($2,$3);}
|     block                  {$$ = $1;}

exp: exp BOP exp          {$$ = new BoolOp($1,$2,$3);}
|    NOTTOK exp           {$$ = new NotOp($2);}
|    exp COMP exp         {$$ = new CompOp($1,$2,$3);}
|    exp OPA exp          {$$ = new ArithOp($1,$2,$3);}
|    exp OPM exp          {$$ = new ArithOp($1,$2,$3);}
|    OPA exp %prec POSNEG {$$ = ($1 == ADD ? $2 : new NegOp($2));}
|    READ                 {$$ = new Read();}
|    LAMBDA ID block      {$$ = new Lambda($2,$3);}
|    exp FUNARG exp       {$$ = new Funcall($1,$3);}
|    LP exp RP            {$$ = $2;}
|    ID                   {$$ = $1;}
|    NUM                  {$$ = $1;}
|    BOOL                 {$$ = $1;}

%%
int main(int argc, char** argv) {
  // 0, 1, and 2 correspond to stdin, stdout (plus resout), and stderr (plus errout)
  showPrompt = isatty(0) && isatty(2);
  bool interactive = isatty(1) && isatty(2);

  if (argc >= 2) {
    if (!(yyin = fopen(argv[1],"r"))) {
      cerr << "Could not open input file \"" << argv[1] << "\"!" << endl;
      exit(2);
    }
    interactive = false;
  }

  int exitcode = 0;

  if (interactive) {
    bool showAST = true; // set to false to stop the AST from popping up.
    // This is the "interactive" version of the interpreter.
    // It keeps going, even if there are errors, and prints out
    // prompts and such.
    while(true) {
      tree = nullptr;
      error = false;
      cerr << "spl> ";
      yyparse();
      if (tree == nullptr && ! error) break;
      else if (tree != nullptr) {
        if (showAST) {
          tree->writeDot("spl.dot");
          if (system("dot -Tpdf spl.dot -ospl.pdf") != 0
              || system("xdg-open spl.pdf >/dev/null 2>&1 &") != 0)
          {
            errout << "ERROR displaying AST. Please install the graphviz package." << endl;
            showAST = false;
          }
        }
        tree->exec();
        delete tree;
      }
    }
    cerr << "Goodbye" << endl;
  }
  else {
    // This is the non-interactive version of the interpreter.
    // It exits with return code 5 if there is any kind of error,
    // and doesn't display prompts or other niceties.
    error = false;
    while(! error) {
      tree = nullptr;
      if (yyparse() != 0 || error || tree == nullptr) {
        if (tree) delete tree;
        break;
      }
      tree->exec();
      delete tree;
    }
    if (error) exitcode = 5;
  }

  // cleanup
  yylex_destroy();

  return exitcode;
}