%{
open Wai.Ast
%}

%token <int> Number
%token <string> Identifier
%token EOF

%token SEMICOLON
%token SKIP
%token IF
%token ELSE
%token WHILE
%token VAR

// Arithmetic operators
%token PLUS
%token MINUS
%token MULT
%token DIV

// Boolean operators and comparison operators.
%token TRUE
%token FALSE
%token NOT
%token AND
%token OR
%token LT
%token LEQ
%token EQ
%token GEQ
%token GT
%token NEQ
%token ASS

%token LPAREN
%token RPAREN
%token LCURLPAREN
%token RCURLPAREN

%nonassoc THEN ELSE ASS
%left SEMICOLON
%left OR
%left AND
%left EQ LT GT LEQ GEQ NEQ
%left PLUS MINUS
%left MULT DIV


%start prog
%type <Wai.Ast.Stm> prog
%%

prog: seqStatement EOF                            { $1 }

seqStatement
  : statement                                     { $1 }
  | statement seqStatement                        { Seq ($1, $2) }

statement
  : VAR Identifier ASS arithmExpr SEMICOLON       { VarDec ($2, $4) }
  | Identifier ASS arithmExpr SEMICOLON           { VarAss ($1, $3) }
  | SKIP SEMICOLON                                { Skip }
  | ifStmt                                        { $1 }
  | whileStmt                                     { $1 }
  | block                                         { $1 }

block: LCURLPAREN seqStatement RCURLPAREN         { $2 }

ifStmt
  : IF LPAREN boolExpr RPAREN block ELSE block    { IfThenElse ($3, $5, Some $7) }
  | IF LPAREN boolExpr RPAREN block               { IfThenElse ($3, $5, None) }

whileStmt: WHILE LPAREN boolExpr RPAREN block     { While ($3, $5) }

arithmExpr
  : Number                                        { Constant ($1) }
  | MINUS arithmExpr                              { UnOp ("-", $2) }
  | Identifier                                    { Variable ($1) }
  | arithmExpr PLUS arithmExpr                    { BinOp ($1, "+", $3) }
  | arithmExpr MINUS arithmExpr                   { BinOp ($1, "-", $3) }
  | arithmExpr MULT arithmExpr                    { BinOp ($1, "*", $3) }
  | arithmExpr DIV arithmExpr                     { BinOp ($1, "/", $3) }
  | LPAREN arithmExpr RPAREN                      { $2 }

boolExpr
  : TRUE                                          { Boolean (true) }
  | FALSE                                         { Boolean (false) }
  | NOT boolExpr                                  { UnOp ("!", $2) }
  | boolExpr AND boolExpr                         { BinOp ($1, "&&", $3) }
  | boolExpr OR boolExpr                          { BinOp ($1, "||", $3) }
  | arithmExpr LT arithmExpr                      { BinOp ($1, "<", $3) }
  | arithmExpr LEQ arithmExpr                     { BinOp ($1, "<=", $3) }
  | arithmExpr GEQ arithmExpr                     { BinOp ($1, ">=", $3) }
  | arithmExpr GT arithmExpr                      { BinOp ($1, ">", $3) }
  | arithmExpr EQ arithmExpr                      { BinOp ($1, "==", $3) }
  | arithmExpr NEQ arithmExpr                     { BinOp ($1, "!=", $3) }
  | LPAREN boolExpr RPAREN                        { $2 }
