184 lines
5.6 KiB
OCaml
184 lines
5.6 KiB
OCaml
open! Base
|
|
open! Ppxlib
|
|
open Ast_builder.Default
|
|
open Helpers
|
|
|
|
module Reference = struct
|
|
type t =
|
|
{ types : type_declaration list
|
|
; binds : value_binding list list
|
|
; ident : longident_loc
|
|
; args : (arg_label * expression) list
|
|
}
|
|
|
|
let bind t binds = { t with binds = binds :: t.binds }
|
|
let bind_types t types = { t with types = types @ t.types }
|
|
|
|
let maybe_apply { types; binds; ident; args } ~loc maybe_arg =
|
|
let ident = pexp_ident ~loc ident in
|
|
let args =
|
|
match maybe_arg with
|
|
| None -> args
|
|
| Some arg -> args @ [ Nolabel, arg ]
|
|
in
|
|
let expr =
|
|
match args with
|
|
| [] -> ident
|
|
| _ -> pexp_apply ~loc ident args
|
|
in
|
|
with_types ~loc ~types (with_let ~loc ~binds expr)
|
|
;;
|
|
|
|
let apply t ~loc arg = maybe_apply t ~loc (Some arg)
|
|
let to_expression t ~loc = maybe_apply t ~loc None
|
|
|
|
let to_value_expression t ~loc ~rec_flag ~values_being_defined =
|
|
let may_refer_directly_to ident =
|
|
match rec_flag with
|
|
| Nonrecursive -> true
|
|
| Recursive -> not (Set.mem values_being_defined (Longident.name ident.txt))
|
|
in
|
|
match t with
|
|
| { types = []; binds = []; ident; args = [] } when may_refer_directly_to ident ->
|
|
pexp_ident ~loc ident
|
|
| _ -> fresh_lambda ~loc (fun ~arg -> apply t ~loc arg)
|
|
;;
|
|
end
|
|
|
|
module Lambda = struct
|
|
type t =
|
|
{ types : type_declaration list
|
|
; binds : value_binding list list
|
|
; cases : cases
|
|
}
|
|
|
|
let bind t binds = { t with binds = binds :: t.binds }
|
|
let bind_types t types = { t with types = types @ t.types }
|
|
|
|
(* generic case: use [function] or [match] *)
|
|
let maybe_apply_generic ~loc ~types ~binds maybe_arg cases =
|
|
let expr =
|
|
match maybe_arg with
|
|
| None -> pexp_function_cases ~loc cases
|
|
| Some arg -> pexp_match ~loc arg cases
|
|
in
|
|
with_types ~loc ~types (with_let ~loc ~binds expr)
|
|
;;
|
|
|
|
(* zero cases: synthesize an "impossible" case, i.e. [| _ -> .] *)
|
|
let maybe_apply_impossible ~loc ~types ~binds maybe_arg =
|
|
[ case ~lhs:(ppat_any ~loc) ~guard:None ~rhs:(pexp_unreachable ~loc) ]
|
|
|> maybe_apply_generic ~loc ~binds ~types maybe_arg
|
|
;;
|
|
|
|
(* one case without guard: use [fun] or [let] *)
|
|
let maybe_apply_simple ~loc ~types ~binds maybe_arg pat body =
|
|
let expr =
|
|
match maybe_arg with
|
|
| None -> pexp_fun ~loc Nolabel None pat body
|
|
| Some arg -> pexp_let ~loc Nonrecursive [ value_binding ~loc ~pat ~expr:arg ] body
|
|
in
|
|
with_types ~loc ~types (with_let ~loc ~binds expr)
|
|
;;
|
|
|
|
(* shared special-casing logic for [apply] and [to_expression] *)
|
|
let maybe_apply t ~loc maybe_arg =
|
|
match t with
|
|
| { types; binds; cases = [] } -> maybe_apply_impossible ~loc ~types ~binds maybe_arg
|
|
| { types; binds; cases = [ { pc_lhs; pc_guard = None; pc_rhs } ] } ->
|
|
maybe_apply_simple ~loc ~types ~binds maybe_arg pc_lhs pc_rhs
|
|
| { types; binds; cases } -> maybe_apply_generic ~loc ~types ~binds maybe_arg cases
|
|
;;
|
|
|
|
let apply t ~loc arg = maybe_apply t ~loc (Some arg)
|
|
let to_expression t ~loc = maybe_apply t ~loc None
|
|
|
|
let to_value_expression t ~loc =
|
|
match t with
|
|
| { types = []; binds = []; cases = _ } ->
|
|
(* lambdas without [let] are already values *)
|
|
let expr = to_expression t ~loc in
|
|
assert (is_value_expression expr);
|
|
expr
|
|
| _ -> fresh_lambda ~loc (fun ~arg -> apply t ~loc arg)
|
|
;;
|
|
end
|
|
|
|
type t =
|
|
| Reference of Reference.t
|
|
| Lambda of Lambda.t
|
|
|
|
let of_lambda cases = Lambda { types = []; binds = []; cases }
|
|
|
|
let of_reference_exn expr =
|
|
match expr.pexp_desc with
|
|
| Pexp_ident ident -> Reference { types = []; binds = []; ident; args = [] }
|
|
| Pexp_apply ({ pexp_desc = Pexp_ident ident; _ }, args) ->
|
|
Reference { types = []; binds = []; ident; args }
|
|
| _ ->
|
|
Location.raise_errorf
|
|
~loc:expr.pexp_loc
|
|
"ppx_sexp_conv: internal error.\n\
|
|
[Conversion.of_reference_exn] expected an identifier possibly applied to arguments.\n\
|
|
Instead, got:\n\
|
|
%s"
|
|
(Pprintast.string_of_expression expr)
|
|
;;
|
|
|
|
let to_expression t ~loc =
|
|
match t with
|
|
| Reference reference -> Reference.to_expression ~loc reference
|
|
| Lambda lambda -> Lambda.to_expression ~loc lambda
|
|
;;
|
|
|
|
let to_value_expression t ~loc ~rec_flag ~values_being_defined =
|
|
match t with
|
|
| Reference reference ->
|
|
Reference.to_value_expression ~loc ~rec_flag ~values_being_defined reference
|
|
| Lambda lambda -> Lambda.to_value_expression ~loc lambda
|
|
;;
|
|
|
|
let apply t ~loc e =
|
|
match t with
|
|
| Reference reference -> Reference.apply ~loc reference e
|
|
| Lambda lambda -> Lambda.apply ~loc lambda e
|
|
;;
|
|
|
|
let bind t binds =
|
|
match t with
|
|
| Reference reference -> Reference (Reference.bind reference binds)
|
|
| Lambda lambda -> Lambda (Lambda.bind lambda binds)
|
|
;;
|
|
|
|
let bind_types t types =
|
|
match t with
|
|
| Reference reference -> Reference (Reference.bind_types reference types)
|
|
| Lambda lambda -> Lambda (Lambda.bind_types lambda types)
|
|
;;
|
|
|
|
module Apply_all = struct
|
|
type t =
|
|
{ bindings : value_binding list
|
|
; arguments : pattern list
|
|
; converted : expression list
|
|
}
|
|
end
|
|
|
|
let gen_symbols list ~prefix =
|
|
List.mapi list ~f:(fun i _ -> gen_symbol ~prefix:(prefix ^ Int.to_string i) ())
|
|
;;
|
|
|
|
let apply_all ts ~loc =
|
|
let arguments_names = gen_symbols ts ~prefix:"arg" in
|
|
let converted_names = gen_symbols ts ~prefix:"res" in
|
|
let bindings =
|
|
List.map3_exn ts arguments_names converted_names ~f:(fun t arg conv ->
|
|
let expr = apply ~loc t (evar ~loc arg) in
|
|
value_binding ~loc ~pat:(pvar ~loc conv) ~expr)
|
|
in
|
|
({ bindings
|
|
; arguments = List.map arguments_names ~f:(pvar ~loc)
|
|
; converted = List.map converted_names ~f:(evar ~loc)
|
|
}
|
|
: Apply_all.t)
|
|
;;
|