<commit_before>(** An expression. *)
open Typedtree
open SmartPrint
open Monad.Notations

module Header = struct
  type t = {
    name : Name.t;
    typ_vars : Name.t list;
    args : (Name.t * Type.t) list;
    structs : string list;
    typ : Type.t }

  let to_coq_structs (header : t) : SmartPrint.t =
    match header.structs with
    | [] -> empty
    | _ :: _ ->
      let structs = separate space (List.map (fun s -> !^ s) header.structs) in
      braces (nest (!^ "struct" ^^ structs))
end

module Definition = struct
  type 'a t = {
    is_rec : Recursivity.t;
    cases : (Header.t * 'a) list }
end

type match_existential_cast = {
  new_typ_vars : Name.t list;
  bound_vars : (Name.t * Type.t) list;
  return_typ : Type.t;
  use_axioms : bool;
  cast_result : bool }

(** The simplified OCaml AST we use. We do not use a mutualy recursive type to
    simplify the importation in Coq. *)
type t =
  | Constant of Constant.t
  | Variable of MixedPath.t * string list
  | Tuple of t list (** A tuple of expressions. *)
  | Constructor of PathName.t * string list * t list
    (** A constructor name, some implicits, and a list of arguments. *)
  | Apply of t * t list (** An application. *)
  | Return of string * t (** Application specialized for a return operation. *)
  | Function of Name.t * t (** An argument name and a body. *)
  | LetVar of string option * Name.t * Name.t list * t * t
    (** The let of a variable, with optionally a list of polymorphic variables.
        We optionally specify the symbol of the let operator as it may be
        non-standard for monadic binds. *)
  | LetFun of t option Definition.t * t
  | LetTyp of Name.t * Name.t list * Type.t * t
    (** The definition of a type. It is used to represent module values. *)
  | LetModuleUnpack of Name.t * PathName.t * t
    (** Open a first-class module. *)
  | Match of t * (Pattern.t * match_existential_cast option * t) list * bool
    (** Match an expression to a list of patterns. *)
  | Record of (PathName.t * t) list
    (** Construct a record giving an expression for each field. *)
  | Field of t * PathName.t (** Access to a field of a record. *)
  | IfThenElse of t * t * t (** The "else" part may be unit. *)
  | Module of (int * t option) Tree.t * (PathName.t * int * t) list
    (** The value of a first-class module. *)
  | ModuleNested of (string option * PathName.t * t) list
    (** The value of a first-class module inside another module
        (no existentials). There may be error messages.
        TODO: see if still useful. *)
  | ModuleCast of int Tree.t * MixedPath.t
    (** The cast of a module to another module type with potentially more
        existentials. *)
  | ModulePack of t (** Pack a module. *)
  | Functor of Name.t * Type.t * t
    (** A functor. *)
  | FunctorGenerative of t
    (** A generative functor. *)
  | TypeAnnotation of t * Type.t
    (** Annotate with a type. *)
  | Assert of Type.t * t (** The assert keyword. *)
  | Error of string (** An error message for unhandled expressions. *)
  | ErrorArray of t list (** An error produced by an array of elements. *)
  | ErrorTyp of Type.t (** An error composed of a type. *)
  | ErrorMessage of t * string
    (** An expression together with an error message. *)

(** Take a function expression and make explicit the list of arguments and
    the body. *)
let rec open_function (e : t) : Name.t list * t =
  match e with
  | Function (x, e) ->
    let (xs, e) = open_function e in
    (x :: xs, e)
  | _ -> ([], e)

let error_message (e : t) (category : Error.Category.t) (message : string) : t Monad.t =
  raise (ErrorMessage (e, message)) category message

let error_message_in_module
  (field : Name.t option)
  (e : t)
  (category : Error.Category.t)
  (message : string)
  : (string option * Name.t option * t) option Monad.t =
  raise (Some (Some message, field, e)) category message

module ModuleTypValues = struct
  type t =
    | Module of Name.t
    | ModuleFunctor of Name.t
    | Value of Name.t * int

  let get
    (typ_vars : Name.t Name.Map.t)
    (module_typ : Types.module_type)
    : t list Monad.t =
    get_env >>= fun env ->
    match Env.scrape_alias env module_typ with
    | Mty_signature signature ->
      signature |> Monad.List.filter_map (fun item ->
        match item with
        | Types.Sig_value (ident, { val_type; _ }, _) ->
          let* ident = Name.of_ident true ident in
          Type.of_typ_expr true typ_vars val_type >>= fun (_, _, new_typ_vars) ->
          return (Some (Value (
            ident,
            Name.Set.cardinal new_typ_vars
          )))
        | Sig_module (ident, _, { Types.md_type = Mty_functor _; _ }, _, _) ->
          let* name = Name.of_ident false ident in
          return (Some (ModuleFunctor name))
        | Sig_module (ident, _, _, _, _) ->
          let* name = Name.of_ident false ident in
          return (Some (Module name))
        | _ -> return None
      )
    | _ -> raise [] Unexpected "Module type signature not found"
end

let rec any_patterns_with_ith_true (is_guarded : bool) (i : int) (n : int)
  : Pattern.t list =
  if n = 0 then
    []
  else
    let head =
      if i = 0 && is_guarded then
        Pattern.Constructor (PathName.true_value, [])
      else
        Pattern.Any in
    head :: any_patterns_with_ith_true is_guarded (i - 1) (n - 1)

let rec get_include_name (module_expr : module_expr) : Name.t Monad.t =
  match module_expr.mod_desc with
  | Tmod_apply (applied_expr, _, _) ->
    begin match applied_expr.mod_desc with
    | Tmod_ident (path, _)
    | Tmod_constraint ({ mod_desc = Tmod_ident (path, _); _ }, _, _, _) ->
      let* path_name = PathName.of_path_with_convert false path in
      let* name = PathName.to_name false path_name in
      return (Name.suffix_by_include name)
    | _ -> get_include_name applied_expr
    end
  | Tmod_constraint (module_expr, _, _, _) -> get_include_name module_expr
  | _ ->
    raise
      (Name.of_string_raw "nameless_include")
      NotSupported
      (
        "Cannot find a name for this module expression.\n\n" ^
        "Try to first give a name to this module before doing the include."
      )

let build_module
  (typ_vars : Name.t Name.Map.t)
  (params_arity : int ModuleTypParams.t)
  (values : ModuleTypValues.t list)
  (signature_path : Path.t)
  (mixed_path_of_value_or_typ : Name.t -> MixedPath.t Monad.t)
  : t Monad.t =
  let* module_typ_params =
    params_arity |> Monad.List.map (function
      | Tree.Item (name, arity) ->
        let* mixed_path = mixed_path_of_value_or_typ name in
        return (Tree.Item (
          name,
          (arity, Some (Variable (mixed_path, [])))
        ))
      | Module (name, tree) ->
        return (Tree.Module (
          name,
          Tree.map (fun arity -> (arity, None)) tree
        ))
    ) in
  let* fields =
    values |> Monad.List.map (function
      | ModuleTypValues.Value (value, nb_free_vars) ->
        let* field_name =
          PathName.of_path_and_name_with_convert signature_path value in
        let* mixed_path = mixed_path_of_value_or_typ value in
        return (
          field_name,
          nb_free_vars,
          Variable (mixed_path, [])
        )
      | Module modul ->
        let* field_name =
          PathName.of_path_and_name_with_convert signature_path modul in
        return (
          field_name,
          0,
          Variable (
            MixedPath.Access (PathName.of_name [] modul, [], false),
            []
          )
        )
      | ModuleFunctor functo ->
        let* field_name =
          PathName.of_path_and_name_with_convert signature_path functo in
        return (
          field_name,
          0,
          Variable (
            MixedPath.PathName (PathName.of_name [] functo),
            []
          )
        )
    ) in
  return (Module (module_typ_params, fields))

(** Import an OCaml expression. *)
let rec of_expression (typ_vars : Name.t Name.Map.t) (e : expression)
  : t Monad.t =
  Attribute.of_attributes e.exp_attributes >>= fun attributes ->
  set_env e.exp_env (
  set_loc (Loc.of_location e.exp_loc) (
  match e.exp_desc with
  | Texp_ident (path, loc, _) ->
    let implicits = Attribute.get_implicits attributes in
    let* x = MixedPath.of_path true path (Some loc.txt) in
    return (Variable (x, implicits))
  | Texp_constant constant ->
    Constant.of_constant constant >>= fun constant ->
    return (Constant constant)
  | Texp_let (is_rec, cases, e2) ->
    of_expression typ_vars e2 >>= fun e2 ->
    of_let typ_vars is_rec cases e2
  | Texp_function {
      cases = [{
        c_lhs = {pat_desc = Tpat_var (x, _); _};
        c_rhs = e;
        _
      }];
      _
    }
  | Texp_function {
      cases = [{
        c_lhs = { pat_desc = Tpat_alias ({ pat_desc = Tpat_any; _ }, x, _); _ };
        c_rhs = e;
        _
      }];
      _
    } ->
    let* x = Name.of_ident true x in
    of_expression typ_vars e >>= fun e ->
    return (Function (x, e))
  | Texp_function { cases; _ } ->
    let is_gadt_match =
      Attribute.has_match_gadt attributes ||
      Attribute.has_match_gadt_with_result attributes in
    let do_cast_results = Attribute.has_match_gadt_with_result attributes in
    let is_with_default_case = Attribute.has_match_with_default attributes in
    open_cases typ_vars cases is_gadt_match do_cast_results is_with_default_case >>= fun (x, e) ->
    return (Function (x, e))
  | Texp_apply (e_f, e_xs) ->
    of_expression typ_vars e_f >>= fun e_f ->
    (e_xs |> Monad.List.filter_map (fun (_, e_x) ->
      match e_x with
      | Some e_x ->
        of_expression typ_vars e_x >>= fun e_x ->
        return (Some e_x)
      | None -> return None
    )) >>= fun e_xs ->
    (* We consider the OCaml's [@@] and [|>] operators as syntactic sugar. *)
    let (e_f, e_xs) =
      match (e_f, e_xs) with
      | (
          Variable (
            MixedPath.PathName {
              PathName.path = [Name.Make ("Pervasives" | "Stdlib")];
              base = Name.Make "op_atat"
            },
            []
          ),
          [f; x]
        ) ->
        (f, [x])
      | (
          Variable (
            MixedPath.PathName {
              PathName.path = [Name.Make ("Pervasives" | "Stdlib")];
              base = Name.Make "op_pipegt"
            },
            []
          ),
          [x; f]
        ) ->
        (f, [x])
      | _ -> (e_f, e_xs) in
    (* We introduce a monadic notation according to the configuration. *)
    let* configuration = get_configuration in
    let apply_with_let =
      match (e_f, e_xs) with
      | (
          Variable (MixedPath.PathName path_name, []),
          [e1; Function (x, e2)]
        ) ->
        let name = PathName.to_string path_name in
        begin match Configuration.is_monadic_let configuration name with
        | Some let_symbol -> Some (LetVar (Some let_symbol, x, [], e1, e2))
        | None -> None
        end
      | _ -> None in
    let apply_with_let_return =
      match (e_f, e_xs) with
      | (
          Variable (MixedPath.PathName path_name, []),
          [e1; Function (x, e2)]
        ) ->
        let name = PathName.to_string path_name in
        begin match Configuration.is_monadic_let_return configuration name with
        | Some (let_symbol, return_notation) ->
          Some (
            LetVar (Some let_symbol, x, [], e1, Return (return_notation, e2))
          )
        | None -> None
        end
      | _ -> None in
    let apply_with_return =
      match (e_f, e_xs) with
      | (
          Variable (MixedPath.PathName path_name, []),
          [e]
        ) ->
        let name = PathName.to_string path_name in
        begin match Configuration.is_monadic_return configuration name with
        | Some return_notation -> Some (Return (return_notation, e))
        | None -> None
        end
      | _ -> None in
    let apply_with_return_let =
      match (e_f, e_xs) with
      | (
          Variable (MixedPath.PathName path_name, []),
          [e1; Function (x, e2)]
        ) ->
        let name = PathName.to_string path_name in
        begin match Configuration.is_monadic_return_let configuration name with
        | Some (return_notation, let_symbol) ->
          Some (
            LetVar (Some let_symbol, x, [], Return (return_notation, e1), e2)
          )
        | None -> None
        end
      | _ -> None in
    let applies = [
      apply_with_let;
      apply_with_let_return;
      apply_with_return;
      apply_with_return_let;
    ] in
    begin match Util.List.find_map (fun x -> x) applies with
    | Some apply -> return apply<commit_msg>Push the return calls through pure binders<commit_after>(** An expression. *)
open Typedtree
open SmartPrint
open Monad.Notations

module Header = struct
  type t = {
    name : Name.t;
    typ_vars : Name.t list;
    args : (Name.t * Type.t) list;
    structs : string list;
    typ : Type.t }

  let to_coq_structs (header : t) : SmartPrint.t =
    match header.structs with
    | [] -> empty
    | _ :: _ ->
      let structs = separate space (List.map (fun s -> !^ s) header.structs) in
      braces (nest (!^ "struct" ^^ structs))
end

module Definition = struct
  type 'a t = {
    is_rec : Recursivity.t;
    cases : (Header.t * 'a) list }
end

type match_existential_cast = {
  new_typ_vars : Name.t list;
  bound_vars : (Name.t * Type.t) list;
  return_typ : Type.t;
  use_axioms : bool;
  cast_result : bool }

(** The simplified OCaml AST we use. We do not use a mutualy recursive type to
    simplify the importation in Coq. *)
type t =
  | Constant of Constant.t
  | Variable of MixedPath.t * string list
  | Tuple of t list (** A tuple of expressions. *)
  | Constructor of PathName.t * string list * t list
    (** A constructor name, some implicits, and a list of arguments. *)
  | Apply of t * t list (** An application. *)
  | Return of string * t (** Application specialized for a return operation. *)
  | Function of Name.t * t (** An argument name and a body. *)
  | LetVar of string option * Name.t * Name.t list * t * t
    (** The let of a variable, with optionally a list of polymorphic variables.
        We optionally specify the symbol of the let operator as it may be
        non-standard for monadic binds. *)
  | LetFun of t option Definition.t * t
  | LetTyp of Name.t * Name.t list * Type.t * t
    (** The definition of a type. It is used to represent module values. *)
  | LetModuleUnpack of Name.t * PathName.t * t
    (** Open a first-class module. *)
  | Match of t * (Pattern.t * match_existential_cast option * t) list * bool
    (** Match an expression to a list of patterns. *)
  | Record of (PathName.t * t) list
    (** Construct a record giving an expression for each field. *)
  | Field of t * PathName.t (** Access to a field of a record. *)
  | IfThenElse of t * t * t (** The "else" part may be unit. *)
  | Module of (int * t option) Tree.t * (PathName.t * int * t) list
    (** The value of a first-class module. *)
  | ModuleNested of (string option * PathName.t * t) list
    (** The value of a first-class module inside another module
        (no existentials). There may be error messages.
        TODO: see if still useful. *)
  | ModuleCast of int Tree.t * MixedPath.t
    (** The cast of a module to another module type with potentially more
        existentials. *)
  | ModulePack of t (** Pack a module. *)
  | Functor of Name.t * Type.t * t
    (** A functor. *)
  | FunctorGenerative of t
    (** A generative functor. *)
  | TypeAnnotation of t * Type.t
    (** Annotate with a type. *)
  | Assert of Type.t * t (** The assert keyword. *)
  | Error of string (** An error message for unhandled expressions. *)
  | ErrorArray of t list (** An error produced by an array of elements. *)
  | ErrorTyp of Type.t (** An error composed of a type. *)
  | ErrorMessage of t * string
    (** An expression together with an error message. *)

(** Take a function expression and make explicit the list of arguments and
    the body. *)
let rec open_function (e : t) : Name.t list * t =
  match e with
  | Function (x, e) ->
    let (xs, e) = open_function e in
    (x :: xs, e)
  | _ -> ([], e)

let error_message (e : t) (category : Error.Category.t) (message : string) : t Monad.t =
  raise (ErrorMessage (e, message)) category message

let error_message_in_module
  (field : Name.t option)
  (e : t)
  (category : Error.Category.t)
  (message : string)
  : (string option * Name.t option * t) option Monad.t =
  raise (Some (Some message, field, e)) category message

module ModuleTypValues = struct
  type t =
    | Module of Name.t
    | ModuleFunctor of Name.t
    | Value of Name.t * int

  let get
    (typ_vars : Name.t Name.Map.t)
    (module_typ : Types.module_type)
    : t list Monad.t =
    get_env >>= fun env ->
    match Env.scrape_alias env module_typ with
    | Mty_signature signature ->
      signature |> Monad.List.filter_map (fun item ->
        match item with
        | Types.Sig_value (ident, { val_type; _ }, _) ->
          let* ident = Name.of_ident true ident in
          Type.of_typ_expr true typ_vars val_type >>= fun (_, _, new_typ_vars) ->
          return (Some (Value (
            ident,
            Name.Set.cardinal new_typ_vars
          )))
        | Sig_module (ident, _, { Types.md_type = Mty_functor _; _ }, _, _) ->
          let* name = Name.of_ident false ident in
          return (Some (ModuleFunctor name))
        | Sig_module (ident, _, _, _, _) ->
          let* name = Name.of_ident false ident in
          return (Some (Module name))
        | _ -> return None
      )
    | _ -> raise [] Unexpected "Module type signature not found"
end

let rec any_patterns_with_ith_true (is_guarded : bool) (i : int) (n : int)
  : Pattern.t list =
  if n = 0 then
    []
  else
    let head =
      if i = 0 && is_guarded then
        Pattern.Constructor (PathName.true_value, [])
      else
        Pattern.Any in
    head :: any_patterns_with_ith_true is_guarded (i - 1) (n - 1)

let rec get_include_name (module_expr : module_expr) : Name.t Monad.t =
  match module_expr.mod_desc with
  | Tmod_apply (applied_expr, _, _) ->
    begin match applied_expr.mod_desc with
    | Tmod_ident (path, _)
    | Tmod_constraint ({ mod_desc = Tmod_ident (path, _); _ }, _, _, _) ->
      let* path_name = PathName.of_path_with_convert false path in
      let* name = PathName.to_name false path_name in
      return (Name.suffix_by_include name)
    | _ -> get_include_name applied_expr
    end
  | Tmod_constraint (module_expr, _, _, _) -> get_include_name module_expr
  | _ ->
    raise
      (Name.of_string_raw "nameless_include")
      NotSupported
      (
        "Cannot find a name for this module expression.\n\n" ^
        "Try to first give a name to this module before doing the include."
      )

let build_module
  (typ_vars : Name.t Name.Map.t)
  (params_arity : int ModuleTypParams.t)
  (values : ModuleTypValues.t list)
  (signature_path : Path.t)
  (mixed_path_of_value_or_typ : Name.t -> MixedPath.t Monad.t)
  : t Monad.t =
  let* module_typ_params =
    params_arity |> Monad.List.map (function
      | Tree.Item (name, arity) ->
        let* mixed_path = mixed_path_of_value_or_typ name in
        return (Tree.Item (
          name,
          (arity, Some (Variable (mixed_path, [])))
        ))
      | Module (name, tree) ->
        return (Tree.Module (
          name,
          Tree.map (fun arity -> (arity, None)) tree
        ))
    ) in
  let* fields =
    values |> Monad.List.map (function
      | ModuleTypValues.Value (value, nb_free_vars) ->
        let* field_name =
          PathName.of_path_and_name_with_convert signature_path value in
        let* mixed_path = mixed_path_of_value_or_typ value in
        return (
          field_name,
          nb_free_vars,
          Variable (mixed_path, [])
        )
      | Module modul ->
        let* field_name =
          PathName.of_path_and_name_with_convert signature_path modul in
        return (
          field_name,
          0,
          Variable (
            MixedPath.Access (PathName.of_name [] modul, [], false),
            []
          )
        )
      | ModuleFunctor functo ->
        let* field_name =
          PathName.of_path_and_name_with_convert signature_path functo in
        return (
          field_name,
          0,
          Variable (
            MixedPath.PathName (PathName.of_name [] functo),
            []
          )
        )
    ) in
  return (Module (module_typ_params, fields))

let rec smart_return (operator : string) (e : t) : t =
  match e with
  | LetVar (None, x, typ_params, e1, e2) ->
    LetVar (None, x, typ_params, e1, smart_return operator e2)
  | Match (e, cases, is_with_default_case) ->
    let cases =
      cases |> List.map (fun (p, existential_cast, e) ->
        (p, existential_cast, smart_return operator e)
      ) in
    Match (e, cases, is_with_default_case)
  | _ -> Return (operator, e)

(** Import an OCaml expression. *)
let rec of_expression (typ_vars : Name.t Name.Map.t) (e : expression)
  : t Monad.t =
  Attribute.of_attributes e.exp_attributes >>= fun attributes ->
  set_env e.exp_env (
  set_loc (Loc.of_location e.exp_loc) (
  match e.exp_desc with
  | Texp_ident (path, loc, _) ->
    let implicits = Attribute.get_implicits attributes in
    let* x = MixedPath.of_path true path (Some loc.txt) in
    return (Variable (x, implicits))
  | Texp_constant constant ->
    Constant.of_constant constant >>= fun constant ->
    return (Constant constant)
  | Texp_let (is_rec, cases, e2) ->
    of_expression typ_vars e2 >>= fun e2 ->
    of_let typ_vars is_rec cases e2
  | Texp_function {
      cases = [{
        c_lhs = {pat_desc = Tpat_var (x, _); _};
        c_rhs = e;
        _
      }];
      _
    }
  | Texp_function {
      cases = [{
        c_lhs = { pat_desc = Tpat_alias ({ pat_desc = Tpat_any; _ }, x, _); _ };
        c_rhs = e;
        _
      }];
      _
    } ->
    let* x = Name.of_ident true x in
    of_expression typ_vars e >>= fun e ->
    return (Function (x, e))
  | Texp_function { cases; _ } ->
    let is_gadt_match =
      Attribute.has_match_gadt attributes ||
      Attribute.has_match_gadt_with_result attributes in
    let do_cast_results = Attribute.has_match_gadt_with_result attributes in
    let is_with_default_case = Attribute.has_match_with_default attributes in
    open_cases typ_vars cases is_gadt_match do_cast_results is_with_default_case >>= fun (x, e) ->
    return (Function (x, e))
  | Texp_apply (e_f, e_xs) ->
    of_expression typ_vars e_f >>= fun e_f ->
    (e_xs |> Monad.List.filter_map (fun (_, e_x) ->
      match e_x with
      | Some e_x ->
        of_expression typ_vars e_x >>= fun e_x ->
        return (Some e_x)
      | None -> return None
    )) >>= fun e_xs ->
    (* We consider the OCaml's [@@] and [|>] operators as syntactic sugar. *)
    let (e_f, e_xs) =
      match (e_f, e_xs) with
      | (
          Variable (
            MixedPath.PathName {
              PathName.path = [Name.Make ("Pervasives" | "Stdlib")];
              base = Name.Make "op_atat"
            },
            []
          ),
          [f; x]
        ) ->
        (f, [x])
      | (
          Variable (
            MixedPath.PathName {
              PathName.path = [Name.Make ("Pervasives" | "Stdlib")];
              base = Name.Make "op_pipegt"
            },
            []
          ),
          [x; f]
        ) ->
        (f, [x])
      | _ -> (e_f, e_xs) in
    (* We introduce a monadic notation according to the configuration. *)
    let* configuration = get_configuration in
    let apply_with_let =
      match (e_f, e_xs) with
      | (
          Variable (MixedPath.PathName path_name, []),
          [e1; Function (x, e2)]
        ) ->
        let name = PathName.to_string path_name in
        begin match Configuration.is_monadic_let configuration name with
        | Some let_symbol -> Some (LetVar (Some let_symbol, x, [], e1, e2))
        | None -> None
        end
      | _ -> None in
    let apply_with_let_return =
      match (e_f, e_xs) with
      | (
          Variable (MixedPath.PathName path_name, []),
          [e1; Function (x, e2)]
        ) ->
        let name = PathName.to_string path_name in
        begin match Configuration.is_monadic_let_return configuration name with
        | Some (let_symbol, return_notation) ->
          Some (LetVar (
            Some let_symbol, x, [], e1, smart_return return_notation e2
          ))
        | None -> None
        end
      | _ -> None in
    let apply_with_return =
      match (e_f, e_xs) with
      | (
          Variable (MixedPath.PathName path_name, []),
          [e]
        ) ->
        let name = PathName.to_string path_name in
        begin match Configuration.is_monadic_return configuration name with
        | Some return_notation -> Some (smart_return return_notation e)
        | None -> None
        end
      | _ -> None in
    let apply_with_return_let =
      match (e_f, e_xs) with
      | (
          Variable (MixedPath.PathName path_name, []),
          [e1; Function (x, e2)]
        ) ->
        let name = PathName.to_string path_name in
        begin match Configuration.is_monadic_return_let configuration name with
        | Some (return_notation, let_symbol) ->
          Some (
            LetVar (Some let_symbol, x, [], smart_return return_notation e1, e2)
          )
        | None -> None
        end
      | _ -> None in
    let applies = [
      apply_with_let;
      apply_with_let_return;
      apply_with_return;
      apply_with_return_let;
    ] in
    begin match Util.List.find_map (fun x -> x) applies with
    | Some apply -> return apply
{'fraction_non_alphanumeric': 0.10736817509151803, 'fraction_numerical': 0.0018693044629644053, 'mean_word_length': 2.1875620655412114, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 186, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1110004', 'n_tokens_mistral': 8345, 'n_tokens_neox': 8242, 'n_words': 2481}