mte/unikernel/duniverse/ppx_sexp_conv/expander/conversion.ml
2025-11-11 02:07:51 +01:00

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)
;;