%code top {
    #include "TestLexer.h"
}

%require "3.2" // suppress generating stack.hh
%language "c++"
%define api.parser.class { TestParser }
%header "yyTestParser.h"

%param {TestLexer* scanner}
%code {
    #define yylex(L, S) ((S)->yylex())

    #define vt100GREEN "\x1b[32m"
    #define DEBUG(X) std::cout << vt100GREEN << " " << #X << std::endl
}


%token COMMENT INT IF ELSE WHILE INPUT OUTPUT
%token OPENPAREN CLOSEPAREN OPENBRACE CLOSEBRACE COMMA SEMICOLON
%token ASSIGN PLUS MINUS MULT DIV MOD
%token LESS LESSEQUAL EQUAL UNEQUAL GREATEREQUAL GREATER
%token NOT OR AND
%token NUMBER STRING IDENTIFIER

%nterm S
%nterm statements
%nterm statement
%nterm rvalue
%nterm lvalue
%nterm boolexp
%nterm relexp
%nterm arithexp
%nterm type

%start S

%left ASSIGN
%left OR
%left AND
%left EQUAL UNEQUAL
%left LESS LESSEQUAL GREATER GREATEREQUAL
%left PLUS MINUS
%left MULT DIV MOD
%right NOT



%%

S : statements YYEOF
    {
        DEBUG(S : statements YYEOF);
    }
;

statements
    : %empty
    {
        DEBUG( statements: %empty );
    }
    | statements statement
    {
        DEBUG( statements: statements statement);
    }
;

statement
    : OPENBRACE statements CLOSEBRACE
    {
        DEBUG( statement: OPENBRACE statements CLOSEBRACE );
    }
    | type lvalue ASSIGN rvalue SEMICOLON
    {
        DEBUG( statement: type lvalue ASSIGN rvalue SEMICOLON );
    }
    | lvalue ASSIGN rvalue SEMICOLON
    {
        DEBUG( statement: lvalue ASSIGN rvalue SEMICOLON );
    }
    | OUTPUT rvalue SEMICOLON
    {
        DEBUG( statement: OUTPUT rvalue SEMICOLON );
    }
    | INPUT lvalue SEMICOLON
    {
        DEBUG( statement: INPUT lvalue SEMICOLON );
    }
    | IF OPENPAREN boolexp CLOSEPAREN statement ELSE statement
    {
        DEBUG( statement: IF OPENPAREN boolexp CLOSEPAREN statement ELSE statement );
    }
    | WHILE OPENPAREN boolexp CLOSEPAREN statement
    {
        DEBUG( statement: WHILE OPENPAREN boolexp CLOSEPAREN statement );
    }
;

type
    : INT
    {
        DEBUG( type: INT );
    }
;

rvalue
    : arithexp
    {
        DEBUG( rvalue: arithexp );
    }
;

lvalue
    : IDENTIFIER
    {
        DEBUG( lvalue: IDENTIFIER );
    }
;

boolexp
    : OPENPAREN boolexp CLOSEPAREN
    {
        DEBUG( boolexp: OPENPAREN boolexp CLOSEPAREN );
    }
    | relexp
    {
        DEBUG( boolexp: relexp );
    }
    | NOT boolexp
    {
        DEBUG( boolexp: NOT boolexp );
    }
    | boolexp AND boolexp
    {
        DEBUG( boolexp: boolexp AND boolexp );
    }
    | boolexp OR boolexp
    {
        DEBUG( boolexp: boolexp OR boolexp );
    }
;

relexp
    : arithexp LESS arithexp
    {
        DEBUG( relexp: arithexp LESS arithexp );
    }
    | arithexp LESSEQUAL arithexp
    {
        DEBUG( relexp: arithexp LESSEQUAL arithexp );
    }
    | arithexp EQUAL arithexp
    {
        DEBUG( relexp: arithexp EQUAL arithexp );
    }
    | arithexp UNEQUAL arithexp
    {
        DEBUG( relexp: arithexp UNEQUAL arithexp );
    }
    | arithexp GREATEREQUAL arithexp
    {
        DEBUG( relexp: arithexp GREATEREQUAL arithexp );
    }
    | arithexp GREATER arithexp
    {
        DEBUG( relexp: arithexp GREATER arithexp );
    }
;

arithexp
    : OPENPAREN arithexp CLOSEPAREN
    {
        DEBUG( arithexp: OPENPAREN arithexp CLOSEPAREN );
    }
    | NUMBER
    {
        DEBUG( arithexp: NUMBER );
    }
    | lvalue
    {
        DEBUG( arithexp: lvalue );
    }
    | arithexp PLUS arithexp
    {
        DEBUG( arithexp: arithexp PLUS arithexp );
    }
    | arithexp MINUS arithexp
    {
        DEBUG( arithexp: arithexp MINUS arithexp );
    }
    | arithexp MULT arithexp
    {
        DEBUG( arithexp: arithexp MULT arithexp );
    }
    | arithexp DIV arithexp
    {
        DEBUG( arithexp: arithexp DIV arithexp );
    }
    | arithexp MOD arithexp
    {
        DEBUG( arithexp: arithexp MOD arithexp );
    }
;

%%

void yy::TestParser::error(const std::string& msg)
{
    std::cerr << msg << std::endl;
}
