%{
    (* open Symbols *)
    open Ast
%}

%token SYM_EOF
%token SYM_VOID SYM_CHAR SYM_INT SYM_STRUCT SYM_POINT SYM_BOOL_NOT SYM_BOOL_AND SYM_BOOL_OR
%token SYM_ARROW SYM_BITWISE_OR SYM_BITWISE_AND SYM_BIT_NOT SYM_XOR SYM_LBRACKET SYM_RBRACKET
%token SYM_ALLOC SYM_EXTERN SYM_AMPERSAND
%token<char> SYM_CHARACTER
%token<string> SYM_STRING
%token<string> SYM_INCLUDE
%token<string> SYM_IDENTIFIER
%token<int> SYM_INTEGER
%token SYM_PLUS SYM_MINUS SYM_ASTERISK SYM_DIV SYM_MOD
%token SYM_LPARENTHESIS SYM_RPARENTHESIS SYM_LBRACE SYM_RBRACE
%token SYM_ASSIGN SYM_SEMICOLON SYM_RETURN SYM_IF SYM_WHILE SYM_ELSE SYM_COMMA SYM_PRINT
%token SYM_EQUALITY SYM_NOTEQ SYM_LT SYM_LEQ SYM_GT SYM_GEQ

%left SYM_EQUALITY SYM_NOTEQ
%left SYM_GEQ SYM_LEQ SYM_LT SYM_GT
%left SYM_PLUS SYM_MINUS
%left SYM_ASTERISK SYM_DIV SYM_MOD
%nonassoc UMINUS

%start main
%type <Ast.tree> main

%%

  main:
    | fundefs SYM_EOF { Node(Tlistglobdef, $1) }
    ;
      fundefs:
        | fundef fundefs { $1 :: $2 }
        | { [] }
    ;
      fundef:
        identifier SYM_LPARENTHESIS lparams SYM_RPARENTHESIS instr {
            let fargs = $3 in
            let instr = $5 in
            Node (Tfundef, [$1; Node (Tfunargs, fargs) ; instr ])
          }
    ;
      identifier:
        SYM_IDENTIFIER {  StringLeaf ($1) }
    ;

      integer : SYM_INTEGER { IntLeaf ($1) };

    lparams :
      identifier rest_params { Node (Targ, [$1]) :: $2 }
      | { [] };
    rest_params :
      SYM_COMMA identifier rest_params {
          Node (Targ, [$2]) :: $3
        }
      | { [] };
    instrs :
      | instr instrs { $1 :: $2 }
      | { [] };
    linstrs :
      SYM_LBRACE instrs SYM_RBRACE { Node (Tblock, $2) };
    instr :
      identifier SYM_ASSIGN expr SYM_SEMICOLON {
          Node (Tassign, [Node (Tassignvar,[$1; $3])])
        }
      | SYM_IF SYM_LPARENTHESIS expr SYM_RPARENTHESIS linstrs ntelse { Node (Tif, [$3; $5; $6]) }
      | SYM_WHILE SYM_LPARENTHESIS expr SYM_RPARENTHESIS instr { Node( Twhile, [$3; $5]) }
      | SYM_RETURN expr SYM_SEMICOLON { Node(Treturn, [$2]) }
      | SYM_PRINT expr SYM_SEMICOLON { Node(Tprint, [$2]) }
      | linstrs { $1 };
      ntelse :
        SYM_ELSE linstrs { $2 }
        | { Node(Tblock, []) };

      expr :
        | expr SYM_EQUALITY expr { Node (Tceq, [$1; $3]) }
        | expr SYM_NOTEQ expr { Node (Tne, [$1; $3]) }
        | expr SYM_PLUS expr { Node (Tadd, [$1; $3]) }
        | expr SYM_MINUS expr { Node (Tsub, [$1; $3]) }
        | expr SYM_ASTERISK expr { Node (Tmul, [$1; $3]) }
        | expr SYM_DIV expr { Node (Tdiv, [$1; $3]) }
        | expr SYM_MOD expr { Node (Tmod, [$1; $3]) }
        | expr SYM_LT expr { Node (Tclt, [$1; $3]) }
        | expr SYM_GT expr { Node (Tcgt, [$1; $3]) }
        | expr SYM_LEQ expr { Node (Tcle, [$1; $3]) }
        | expr SYM_GEQ expr { Node (Tcge, [$1; $3]) }
        | SYM_MINUS expr %prec UMINUS { Node (Tneg, [$2])}
        | integer { Node(Tint, [$1])}
        | identifier { $1 }
        | SYM_LPARENTHESIS expr SYM_RPARENTHESIS { $2 }
      ;