%token IDENT %token L_PAREN %token R_PAREN %token L_BRACKET R_BRACKET %token COLUMN %token DOT %token LITERAL %token INTEGER %token COMA %token EOF %token CONCAT_OPERATOR %token BINARY_OPERATOR %token INEQUALITY_OPERATOR %token EQUALITY_OPERATOR %token BOOL_OPERATOR %start path_expr %start column_expr %right BOOL_OPERATOR %right INEQUALITY_OPERATOR EQUALITY_OPERATOR %right CONCAT_OPERATOR BINARY_OPERATOR %{ let function_of_name param f = match (String.lowercase_ascii f, param) with | "nvl", _ -> ImportExpression.T.Nvl param | "join", (ImportExpression.T.Literal sep:: tl) -> ImportExpression.T.Join (sep, tl) | "join", (ImportExpression.T.Empty:: tl) -> ImportExpression.T.Join ("", tl) | "upper", _ -> ImportExpression.T.Function' (ImportExpression.T.Upper, param) | "trim", _ -> ImportExpression.T.Function' (ImportExpression.T.Trim, param) | other, _ -> ImportExpression.T.Function (other, param) %} %% 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 Syntax.Path.{ alias = None ; column = ImportCSV.Csv.column_of_string column } } | COLUMN table = IDENT DOT column = IDENT { ImportExpression.T.Path Syntax.Path.{ alias = Some table ; column = ImportCSV.Csv.column_of_string column} } column_: | COLUMN column = IDENT { try ImportExpression.T.Path (ImportCSV.Csv.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) { 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 }