/* 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;
}