%{ open Ast %}

%token SEMI LPAREN RPAREN LBRACE RBRACE LBRACK RBRACK COMMA
%token BAR LMATBRACK RMATBRACK
%token TRUE FALSE
%token PLUS MINUS TIMES DIVIDE EXP FLOATCAST
%token EQ NEQ LT LEQ GT GEQ
%token ASSIGN RETURN INT BOOL STRING VOID NULL FLOAT
%token IF ELSE WHILE FOR
%token AND OR NOT NEW
%token IMG ARRAY MATRIX

%token <int> INTLITERAL
%token <string> STRINGLITERAL
%token <string> ID
%token <float> FLOATLITERAL

%token DOT AMP PLUSPLUS STARSTAR
%token EOF

%nonassoc NOELSE
%nonassoc ELSE

%right ASSIGN
%right NEG NOT
%right EXP

%left PLUS MINUS
%left TIMES DIVIDE
%left EQ NEQ
%left LT GT LEQ GEQ
%left OR AND

%start program
%type <Ast.program> program

%%
program:
    decls EOF { $1 }

decls:
    /* nothing */      { [], []}
  | decls fdecl        {fst $1, ($2 :: snd $1) }
  | decls vdecl { ($2 :: fst $1), snd $1 }

vdecl:
   typ ID SEMI { ($1, $2) }

vdecl_list:
    /* nothing */    { [] }
  | vdecl_list vdecl { $2 :: $1 }

fdecl:
  typ ID LPAREN formals_opt RPAREN LBRACE vdecl_list stmt_list RBRACE
    { { typ = $1; fname = $2; formals = $4;
      locals = List.rev $7; body = List.rev $8 } }


formals_opt:
    /* nothing */ { [] }
  | formal_list   { List.rev $1 }

formal_list:
    typ ID                   { [($1,$2)] }
  | formal_list COMMA typ ID { ($3,$4) :: $1 }

array_typ:
    ARRAY typ LBRACK INTLITERAL RBRACK { ArrayTyp($2,$4) }

mat:
   MATRIX typ LBRACK INTLITERAL RBRACK LBRACK INTLITERAL RBRACK { MatrixTyp($2, $4, $7) }

typ:
    INT    { Int }
  | FLOAT  { Float }
  | BOOL   { Bool }
  | VOID   { Void }
  | IMG    { Img }
  | STRING { String }
  | array_typ { $1 }
  | mat	   {$1}

stmt_list:
    /* nothing */  { [] }
  | stmt_list stmt { $2 :: $1 }

stmt:
    expr SEMI { Expr $1 }
  | RETURN SEMI { Return Noexpr }
  | RETURN expr SEMI { Return $2 }
  | LBRACE stmt_list RBRACE { Block(List.rev $2) }
  | IF LPAREN expr RPAREN stmt %prec NOELSE { If($3, $5, Block([])) }
  | IF LPAREN expr RPAREN stmt ELSE stmt    { If($3, $5, $7) }
  | WHILE LPAREN expr RPAREN stmt { While($3, $5) }
  | FOR LPAREN expr_opt SEMI expr SEMI expr_opt RPAREN stmt { For($3, $5, $7, $9) }

expr_opt:
    /* nothing */ { Noexpr }
  | expr          { $1 }

expr:
    literals         { $1 }
  | ID               { ID($1) }
  | ID LPAREN actuals_opt RPAREN { Call($1, $3) }
  | LPAREN expr RPAREN { $2 }
  | expr PLUS   expr { Binop($1, Add,   $3) }
  | expr MINUS  expr { Binop($1, Sub,   $3) }
  | expr TIMES  expr { Binop($1, Mult,  $3) }
  | expr DIVIDE expr { Binop($1, Div,   $3) }
  | expr EQ     expr { Binop($1, Equal, $3) }
  | expr NEQ    expr { Binop($1, Neq,   $3) }
  | expr LT     expr { Binop($1, Less,  $3) }
  | expr LEQ    expr { Binop($1, Leq,   $3) }
  | expr GT     expr { Binop($1, Greater, $3) }
  | expr GEQ    expr { Binop($1, Geq,   $3) }
  | expr EXP    expr { Binop($1, Exp,   $3) }
  | expr AND    expr { Binop($1, And,   $3) }
  | expr OR     expr { Binop($1, Or,    $3) }
  | expr ASSIGN   expr  { Assign($1, $3)}
  | MINUS expr %prec NEG { Unop(Neg, $2) }
  | NOT expr         { Unop(Not, $2) }
  | TRUE             { BoolLit(true) }
  | FALSE            { BoolLit(false) }
  | NULL             { Null }
  | ID LBRACK expr RBRACK { ArrayAccess($1, $3) }
  | ID LBRACK expr RBRACK LBRACK expr RBRACK { MatrixAccess($1, $3, $6) }
  | AMP ID		   { Dereference($2) }
  | PLUSPLUS ID   { MovePointer($2) }
  | STARSTAR ID			{Reference($2) }

primitives:
	INTLITERAL { IntLiteral($1) }
  | STRINGLITERAL { StringLiteral($1) }
  | FLOATLITERAL  { FloatLiteral($1) }

literals:
  primitives { $1 }
  | LBRACK primitive_arraylit RBRACK { ArrayLit(List.rev $2) }
  | LMATBRACK primitive_matrixlit RMATBRACK { MatrixLit(List.rev $2) }

primitive_arraylit:
  primitives { [$1] }
  | primitive_arraylit COMMA primitives { $3 :: $1 }

primitive_matrixlit:
    primitive_arraylit { [$1] }
  | primitive_matrixlit BAR primitive_arraylit { $3 :: $1 }

actuals_opt:
    /* nothing */                   { [] }
  | actuals_list                    { List.rev $1 }

actuals_list:
    expr                            { [$1] }
	| actuals_list COMMA expr       { $3 :: $1 }
