%token <string>IDENT
%token L_PAREN 
%token R_PAREN 
%token L_BRACKET R_BRACKET
%token COLUMN  
%token DOT 
%token <string>LITERAL
%token <string>INTEGER
%token COMA
%token EOF
%token CONCAT_OPERATOR

%token <ImportExpression.T.binary_operator>BINARY_OPERATOR
%token <ImportExpression.T.binary_operator>INEQUALITY_OPERATOR
%token <ImportExpression.T.binary_operator>EQUALITY_OPERATOR
%token <ImportExpression.T.binary_operator>BOOL_OPERATOR

%start <ImportDataTypes.Path.t ImportExpression.T.t> path_expr
%start <ImportDataTypes.Path.column ImportExpression.T.t> column_expr

%right BOOL_OPERATOR
%right INEQUALITY_OPERATOR EQUALITY_OPERATOR
%right CONCAT_OPERATOR BINARY_OPERATOR 

%%

path_expr: 
    | expr_(path_, EOF) EOF { $1 }
    | EOF { ImportExpression.T.Empty }
column_expr: 
    | expr_(column_, EOF) EOF { $1 }
    | EOF { ImportExpression.T.Empty }


path_: 
    | COLUMN
      column = IDENT    
        { ImportExpression.T.Path 
             ImportDataTypes.Path.{ alias = None
            ; column = ImportDataTypes.Path.column_of_string column
            }
        }

    | COLUMN
      table = IDENT
      DOT
      column = IDENT    
      { ImportExpression.T.Path
             ImportDataTypes.Path.{ alias = Some table 
            ; column = ImportDataTypes.Path.column_of_string column}
      }

column_: 
    | COLUMN
      column = IDENT
        { try ImportExpression.T.Path (ImportDataTypes.Path.column_of_string column)
        with _ -> ImportExpression.T.Literal column }

arguments(PATH):
    | L_PAREN
      expr = separated_list(COMA, expr_(PATH, COMA))
      R_PAREN
        { expr }

group(PATH):
    | L_BRACKET
      expr = separated_list(COMA, expr_(PATH, COMA))
      R_BRACKET
        { expr }

fixed(PATH):
    | d = INTEGER
        { ImportExpression.T.Integer d }
    | l = LITERAL 
        {
          if String.equal String.empty l then 
            ImportExpression.T.Empty 
          else
            ImportExpression.T.Literal l
        }

%inline boperator: 
        | e = BINARY_OPERATOR { e }
        | e = INEQUALITY_OPERATOR { e }
        | e = EQUALITY_OPERATOR { e }
        | e = BOOL_OPERATOR { e }

(* The expression evaluation receveive in parameters :
    1. the way to buidl a path, as we have two distinct ways to build them in
       the case of externals (the external_key does not allow a table name)
    2. a phantom type telling wich kind of element will end the expression.
       This can be EOF for the root expression, or COMA when inside a function.
       This prevent merlin to optimize thoses two path, and allow more precise
       error messages. *)
expr_(PATH, ENDING_PHANTOM):
    | L_PAREN
      e  = expr_(PATH, R_PAREN)
      R_PAREN
      { ImportExpression.T.Expr e
      }
    |
      p1 = expr_(PATH, ENDING_PHANTOM)
      CONCAT_OPERATOR
      p2 = expr_(PATH, COMA)
        { match p2 with 
           | ImportExpression.T.Concat args ->  ImportExpression.T.Concat (p1::args)
           | _ -> ImportExpression.T.Concat (p1::p2::[]) 
        }
    | p1 = expr_(PATH, ENDING_PHANTOM)

      op = boperator
      p2 = expr_(PATH, COMA)
      { ImportExpression.T.BOperator (op, p1, p2) }

    | p1 = expr_(PATH, ENDING_PHANTOM)
      op = EQUALITY_OPERATOR
      p2 = group(PATH)
      { ImportExpression.T.GEquality(op, p1, p2) }


        
    | p = PATH      
        { p }
    | f = fixed(PATH)
        { f }
    | s = IDENT
      args = arguments(PATH)
      { ImportExpression.T.function_of_name args s }
    | 
      s = IDENT
      L_PAREN
      opt_arg = opt_arg(PATH, COMA)?
      args1 = group(PATH)
      COMA
      args2 = group(PATH)
      R_PAREN
      { let window_name = ImportExpression.T.window_of_name s opt_arg in
        ImportExpression.T.Window (window_name, args1, args2) }
(*
    | (* This case is here to describe a window function which has 2 arguments
         level. 
         I’m not completely satisfied with it, as it prevent the ability to
         create a exprpression block with parens arround. *) 
      s = IDENT
      L_PAREN
      opt_arg = opt_arg(PATH, COMA)?
      args1 = arguments(PATH)
      COMA
      args2 = arguments(PATH)
      R_PAREN
      { let window_name = ImportExpression.T.window_of_name s opt_arg in
        let expr = ImportExpression.T.Window (window_name, args1, args2) in 

        let expr_repr = ImportExpression.Repr.repr ~top:true  (fun _ -> "")
        expr in 
        Printf.printf "Deprecated syntax in \"%s\" use [] instead of ()\n" expr_repr;


        expr 
      }
*)

opt_arg(PATH, SEP): 
    | expr = expr_(PATH, COMA)
      SEP
        { expr }