Assignment-3/parser.y
2019-05-27 21:57:41 -07:00

176 lines
4.9 KiB
Plaintext

%{
#include <iostream>
#include <set>
#include "tree.hpp"
#include "parser.hpp"
extern int yylex();
void yyerror(YYLTYPE* loc, const char* err);
std::string* translate_boolean_str(std::string* boolean_str);
/*
* Here, target_program is a string that will hold the target program being
* generated, and symbols is a simple symbol table.
*/
stmt_ptr target_program;
%}
/* Enable location tracking. */
%locations
/*
* All program constructs will be represented as strings, specifically as
* their corresponding C/C++ translation.
*/
%union {
expr_ptr ex;
stmt_ptr st;
std::string* str;
}
/*
* Because the lexer can generate more than one token at a time (i.e. DEDENT
* tokens), we'll use a push parser.
*/
%define api.pure full
%define api.push-pull push
/*
* These are all of the terminals in our grammar, i.e. the syntactic
* categories that can be recognized by the lexer.
*/
%token IDENTIFIER
%token FLOAT INTEGER BOOLEAN
%token INDENT DEDENT NEWLINE
%token AND BREAK DEF ELIF ELSE FOR IF NOT OR RETURN WHILE
%token ASSIGN PLUS MINUS TIMES DIVIDEDBY
%token EQ NEQ GT GTE LT LTE
%token LPAREN RPAREN COMMA COLON
/*
* Here, we're defining the precedence of the operators. The ones that appear
* later have higher precedence. All of the operators are left-associative
* except the "not" operator, which is right-associative.
*/
%left OR
%left AND
%left PLUS MINUS
%left TIMES DIVIDEDBY
%left EQ NEQ GT GTE LT LTE
%right NOT
%type <str> IDENTIFIER FLOAT INTEGER BOOLEAN
%type <st> program statements statement if_statement while_statement break_statement block elif_blocks else_block
%type <ex> assign_statement primary_expression negated_expression expression condition
/* This is our goal/start symbol. */
%start program
%%
/*
* Each of the CFG rules below recognizes a particular program construct in
* Python and creates a new string containing the corresponding C/C++
* translation. Since we're allocating strings as we go, we also free them
* as we no longer need them. Specifically, each string is freed after it is
* combined into a larger string.
*/
program
: statements { target_program = $1; }
;
statements
: statement { $$ = new stmt_block(); ((stmt_block*)$$)->children.push_back($1); }
| statements statement { $$ = $1; ((stmt_block*)$$)->children.push_back($2); }
;
statement
: assign_statement { $$ = new stmt_expr($1); }
| if_statement { $$ = $1; }
| while_statement { $$ = $1; }
| break_statement { $$ = $1; }
;
primary_expression
: IDENTIFIER { $$ = new expr_id(*$1); delete $1; }
| FLOAT { $$ = new expr_float(std::stod(*$1)); delete $1; }
| INTEGER { $$ = new expr_int(std::stoi(*$1)); delete $1; }
| BOOLEAN { $$ = new expr_bool(*$1 == "True"); delete $1; }
| LPAREN expression RPAREN { $$ = $2; }
;
negated_expression
: NOT primary_expression { $$ = new expr_unop(unop::lnot, $2); }
;
expression
: primary_expression { $$ = $1; }
| negated_expression { $$ = $1; }
| expression PLUS expression { $$ = new expr_binop(binop::plus, $1, $3); }
| expression MINUS expression { $$ = new expr_binop(binop::minus, $1, $3); }
| expression TIMES expression { $$ = new expr_binop(binop::times, $1, $3); }
| expression DIVIDEDBY expression { $$ = new expr_binop(binop::divide, $1, $3); }
| expression EQ expression { $$ = new expr_binop(binop::eq, $1, $3); }
| expression NEQ expression { $$ = new expr_binop(binop::neq, $1, $3); }
| expression GT expression { $$ = new expr_binop(binop::gt, $1, $3); }
| expression GTE expression { $$ = new expr_binop(binop::gte, $1, $3); }
| expression LT expression { $$ = new expr_binop(binop::lt, $1, $3); }
| expression LTE expression { $$ = new expr_binop(binop::lte, $1, $3); }
;
assign_statement
: IDENTIFIER ASSIGN expression NEWLINE { $$ = new expr_assign(*$1, $3); delete $1; }
;
block
: INDENT statements DEDENT { $$ = $2; }
;
condition
: expression { $$ = $1; }
| condition AND condition { $$ = new expr_binop(binop::land, $1, $3); }
| condition OR condition { $$ = new expr_binop(binop::lor, $1, $3); }
;
if_statement
: IF condition COLON NEWLINE block elif_blocks { $$ = new stmt_if($2, $5, $6); }
;
elif_blocks
: %empty { $$ = nullptr; }
| else_block { $$ = $1; }
| ELIF condition COLON NEWLINE block elif_blocks { $$ = new stmt_if($2, $5, $6); }
;
else_block
: ELSE COLON NEWLINE block { $$ = $4; }
;
while_statement
: WHILE condition COLON NEWLINE block { $$ = new stmt_while($2, $5); }
;
break_statement
: BREAK NEWLINE { $$ = new stmt_break(); }
;
%%
void yyerror(YYLTYPE* loc, const char* err) {
std::cerr << "Error (line " << loc->first_line << "): " << err << std::endl;
}
/*
* This function translates a Python boolean value into the corresponding
* C++ boolean value.
*/
std::string* translate_boolean_str(std::string* boolean_str) {
if (*boolean_str == "True") {
return new std::string("true");
} else {
return new std::string("false");
}
}