%{

open TinyML
open FSharp.Common.Parsing.LexYacc
open TinyML.Ast

let parse_error_rich = Some (fun ctx -> raise (ParseErrorContextException ctx))

%}
 

%token <System.Int32> INT
%token <System.Double> FLOAT
%token <System.Char> CHAR
%token <System.String> STRING ID

%token IF THEN ELSE FUN ARROW LET REC IN
       TRUE FALSE 
       BRA KET 
       PLUS MINUS STAR SLASH PERCENT
       PLUS_DOT MINUS_DOT STAR_DOT SLASH_DOT PERCENT_DOT
       LT GT LEQ GEQ EQ NEQ
       LT_DOT GT_DOT LEQ_DOT GEQ_DOT EQ_DOT NEQ_DOT
       AND OR NOT
       COLON SEMICOLON2 COMMA
%token EOF

%nonassoc THEN ELSE
%left COMMA
%right ARROW
%left OR
%left AND 
%left EQ LT GT LEQ GEQ NEQ
%left PLUS PLUS_DOT MINUS MINUS_DOT
%left STAR STAR_DOT SLASH SLASH_DOT MOD MOD_DOT
%left APP
%nonassoc NOT
%nonassoc UMINUS

%type < TinyML.Ast.expr > program
%type < TinyML.Ast.interactive > interactive

%start program
%start interactive

%%

program:
    expr EOF                           { $1 }

interactive:
    expr SEMICOLON2                     { IExpr $1 }
  | binding SEMICOLON2                  { IBinding $1 }

expr:
    expr_tuple_atom                         { $1 }
  | BRA expr_tuple_atoms KET                { Tuple $2 }


binding:
  | LET ID EQ expr                       { (false, $2, None, $4) }
  | LET ID parms EQ expr                 { (false, $2, None, fold_params $3 $5) }
  
  | LET ID COLON ty EQ expr              { (false, $2, Some $4, $6) }
  | LET ID parms COLON ty EQ expr        { (false, $2, Some $5, fold_params $3 $7) }

  | LET REC ID EQ expr                   { (true, $3, None, $5) }
  | LET REC ID parms EQ expr             { (true, $3, None, fold_params $4 $6 )}

  | LET REC ID parms COLON ty EQ expr    { (true, $3, Some $6, fold_params $4 $8) }
  | LET REC ID COLON ty EQ expr          { (true, $3, Some $5, $7) }

parms:
    ID                            { [$1, None] }
  | BRA ID COLON ty KET           { [$2, Some $4]}
  | ID parms                      { ($1, None) :: $2 }
  | BRA ID COLON ty KET parms     { ($2, Some $4) :: $6}

expr_app_atom:
    INT                                 { Lit (LInt $1) }
  | FLOAT                               { Lit (LFloat $1) }
  | STRING                              { Lit (LString $1) }
  | CHAR                                { Lit (LChar $1) }
  | TRUE                                { Lit (LBool true) }
  | FALSE                               { Lit (LBool false) }
  | BRA KET                             { Lit LUnit }

  | ID                                  { Var $1 }
  
  | BRA expr KET                        { $2 }

expr_tuple_atom:
    expr_app_atom                               { $1 }
  | expr_tuple_atom expr_app_atom %prec APP     { App ($1, $2) }

  | FUN ID ARROW expr                           { Lambda ($2, None, $4) }
  | FUN BRA ID COLON ty KET ARROW expr          { Lambda ($3, Some $5, $8) }

  | binding IN expr                             { LetIn ($1, $3) }
  
  | NOT expr_tuple_atom							            { UnOp ("not", $2) }
  | MINUS expr_tuple_atom %prec UMINUS  		            { UnOp ("-", $2) }
  | MINUS_DOT expr_tuple_atom %prec UMINUS  		        { UnOp ("-.", $2) }

  | expr_tuple_atom PLUS expr_tuple_atom					{ BinOp ($1, "+", $3) }
  | expr_tuple_atom PLUS_DOT expr_tuple_atom			    { BinOp ($1, "+.", $3) }
  | expr_tuple_atom MINUS expr_tuple_atom					{ BinOp ($1, "-", $3) }
  | expr_tuple_atom MINUS_DOT expr_tuple_atom				{ BinOp ($1, "-.", $3) }
  | expr_tuple_atom STAR expr_tuple_atom					{ BinOp ($1, "*", $3) }
  | expr_tuple_atom STAR_DOT expr_tuple_atom				{ BinOp ($1, "*.", $3) }
  | expr_tuple_atom SLASH expr_tuple_atom					{ BinOp ($1, "/", $3) }
  | expr_tuple_atom SLASH_DOT expr_tuple_atom				{ BinOp ($1, "/.", $3) }
  | expr_tuple_atom PERCENT expr_tuple_atom				    { BinOp ($1, "%", $3) }
  | expr_tuple_atom PERCENT_DOT expr_tuple_atom			    { BinOp ($1, "%.", $3) }

  | expr_tuple_atom EQ expr_tuple_atom						    { BinOp ($1, "=", $3) }
  | expr_tuple_atom EQ_DOT expr_tuple_atom						{ BinOp ($1, "=.", $3) }
  | expr_tuple_atom NEQ expr_tuple_atom					    	{ BinOp ($1, "<>", $3) }
  | expr_tuple_atom NEQ_DOT expr_tuple_atom						{ BinOp ($1, "<>.", $3) }
  | expr_tuple_atom LT expr_tuple_atom					    	{ BinOp ($1, "<", $3) }
  | expr_tuple_atom LT_DOT expr_tuple_atom						{ BinOp ($1, "<.", $3) }
  | expr_tuple_atom GT expr_tuple_atom						    { BinOp ($1, ">", $3) }
  | expr_tuple_atom GT_DOT expr_tuple_atom						{ BinOp ($1, ">.", $3) }
  | expr_tuple_atom LEQ expr_tuple_atom					    	{ BinOp ($1, "<=", $3) }
  | expr_tuple_atom LEQ_DOT expr_tuple_atom						{ BinOp ($1, "<=.", $3) }
  | expr_tuple_atom GEQ expr_tuple_atom						    { BinOp ($1, ">=", $3) }
  | expr_tuple_atom GEQ_DOT expr_tuple_atom						{ BinOp ($1, ">=.", $3) }
  | expr_tuple_atom AND expr_tuple_atom               { BinOp ($1, "and", $3) }
  | expr_tuple_atom OR expr_tuple_atom				        { BinOp ($1, "or", $3) }

  | IF expr THEN expr                                       { IfThenElse ($2, $4, None) }
  | IF expr THEN expr ELSE expr                             { IfThenElse ($2, $4, Some $6) }

expr_tuple_atoms:
    expr_tuple_atom                             { [$1] }
  | expr_tuple_atom COMMA expr_tuple_atoms      { $1 :: $3 }


ty:
    ID                                  { TyName $1 }
  | ty ARROW ty                         { TyArrow ($1, $3) }
  | ty_tuple                            { TyTuple $1 }
  | BRA ty KET                          { $2 }

ty_tuple:
    ty                                  { [$1] }
  | ty STAR ty_tuple                    { $1 :: $3 }


