Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port standard plugins to ppxlib registration and attributes #263

Merged
merged 23 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2e4db40
Register plugins directly with ppxlib
sim642 Jul 19, 2022
5cd7fa8
Add ppxlib-based with_path arg to show plugin
sim642 Jul 19, 2022
8e1e68f
Remove unused ~options and ~path arguments from plugins
sim642 Jul 19, 2022
1ff769b
Declare plugin attributes directly with ppxlib
sim642 Jul 19, 2022
2c08bed
Delegate to Ppxlib.Quoter
sim642 Jul 19, 2022
9a46e10
Fix typo org -> ord
sim642 Jul 19, 2022
4c9178c
Deprecate non-ppxlib derivers and attributes
sim642 Jul 19, 2022
9171d9a
Restore "derive"-prefixed extensions via custom extension
sim642 Jul 19, 2022
3359fea
Use input_name from ppxlib for show deriver path
sim642 Jul 20, 2022
f9a1e63
Adapt quoter to ppxlib 0.29.0
sim642 Mar 22, 2023
a69c7a2
Remove unused attrs argument in enum plugin
sim642 Mar 22, 2023
8ad6cc4
Replace failwith with raise_errorf
sim642 Mar 22, 2023
dc74b49
Revert "Deprecate non-ppxlib derivers and attributes"
sim642 Mar 23, 2023
e4a900e
Update test TODO about optional
sim642 Mar 23, 2023
ccfa830
Add PR #263 to CHANGELOG
sim642 Mar 23, 2023
12e4e41
Remove unnecessary unit argument from show plugin args
sim642 Mar 23, 2023
d5df400
Use Ppxlib.Ast_pattern.ebool from ppxlib 0.30.0
sim642 Mar 6, 2024
45ec862
Use Ppxlib.Attribute.declare_flag from ppxlib 0.32.0
sim642 Mar 6, 2024
34004c2
Use Ppxlib.Attribute.has_flag from ppxlib 0.32.0 for simple cases
sim642 Mar 6, 2024
0f41f8f
Use Ppxlib.Attribute.has_flag from ppxlib 0.32.0 for binary cases
sim642 Mar 6, 2024
5b6fedc
Inline has_flag variables
sim642 Mar 7, 2024
bb25c98
Remove duplicate extension for map plugin
sim642 Mar 7, 2024
5b7e6f5
Simplify label-ct attributes in create and make plugins
sim642 Mar 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ppx_deriving.opam
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ depends: [
"cppo" {build}
"ocamlfind"
"ppx_derivers"
"ppxlib" {>= "0.20.0"}
"ppxlib" {>= "0.27.0"}
sim642 marked this conversation as resolved.
Show resolved Hide resolved
"result"
"ounit2" {with-test}
]
Expand Down
29 changes: 7 additions & 22 deletions src/api/ppx_deriving.cppo.ml
Original file line number Diff line number Diff line change
Expand Up @@ -309,39 +309,24 @@ let attr_warning expr =
attr_loc = loc;
}

type quoter = {
mutable next_id : int;
mutable bindings : value_binding list;
}
type quoter = Quoter.t

let create_quoter () = { next_id = 0; bindings = [] }
let create_quoter () = Quoter.create ()
sim642 marked this conversation as resolved.
Show resolved Hide resolved

let quote ~quoter expr =
let loc = !Ast_helper.default_loc in
let name = "__" ^ string_of_int quoter.next_id in
let (binding_body, quoted_expr) = match expr with
(* Optimize identifier quoting by avoiding closure.
See https://github.com/ocaml-ppx/ppx_deriving/pull/252. *)
| { pexp_desc = Pexp_ident _; _ } ->
(expr, evar name)
| _ ->
([%expr fun () -> [%e expr]], [%expr [%e evar name] ()])
in
quoter.bindings <- (Vb.mk (pvar name) binding_body) :: quoter.bindings;
quoter.next_id <- quoter.next_id + 1;
quoted_expr
Quoter.quote quoter expr

let sanitize ?(module_=Lident "Ppx_deriving_runtime") ?(quoter=create_quoter ()) expr =
let loc = !Ast_helper.default_loc in
let body =
let loc = !Ast_helper.default_loc in
let attrs = [attr_warning [%expr "-A"]] in
let modname = { txt = module_; loc } in
Exp.open_ ~loc ~attrs
(Opn.mk ~loc ~attrs ~override:Override (Mod.ident ~loc ~attrs modname))
expr in
match quoter.bindings with
| [] -> body
| bindings -> Exp.let_ Nonrecursive bindings body
let sanitized = Quoter.sanitize quoter body in
(* ppxlib quoter uses Recursive, ppx_deriving's used Nonrecursive - silence warning *)
{ sanitized with pexp_attributes = attr_warning [%expr "-39"] :: sanitized.pexp_attributes}
sim642 marked this conversation as resolved.
Show resolved Hide resolved

let with_quoter fn a =
let quoter = create_quoter () in
Expand Down
6 changes: 6 additions & 0 deletions src/api/ppx_deriving.cppo.mli
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type deriver = {

(** [register deriver] registers [deriver] according to its [name] field. *)
val register : deriver -> unit
[@@deprecated]
sim642 marked this conversation as resolved.
Show resolved Hide resolved

(** [add_register_hook hook] adds [hook] to be executed whenever a new deriver
is registered. *)
Expand Down Expand Up @@ -71,6 +72,7 @@ val create :
path:string list ->
module_type_declaration -> signature) ->
unit -> deriver
[@@deprecated]

(** [lookup name] looks up a deriver called [name]. *)
val lookup : string -> deriver option
Expand Down Expand Up @@ -169,6 +171,7 @@ let deriver = "index"
in error messages. *)
val get_expr : deriver:string -> 'a conv -> expression -> 'a
end
[@@deprecated]
sim642 marked this conversation as resolved.
Show resolved Hide resolved

(** {2 Hygiene} *)

Expand Down Expand Up @@ -227,6 +230,7 @@ val mangle_lid : ?fixpoint:string ->
or [\[\@deriver.attr\]] if any attribute with name starting with [\@deriver] exists,
or [\[\@attr\]] otherwise. *)
val attr : deriver:string -> string -> attributes -> attribute option
[@@deprecated]
sim642 marked this conversation as resolved.
Show resolved Hide resolved

(** [attr_warning expr] builds the attribute [\@ocaml.warning expr] *)
val attr_warning: expression -> attribute
Expand Down Expand Up @@ -386,3 +390,5 @@ module Ast_convenience : sig
val optional : string -> arg_label
end
end

val module_from_input_name: unit -> label list
sim642 marked this conversation as resolved.
Show resolved Hide resolved
83 changes: 51 additions & 32 deletions src_plugins/create/ppx_deriving_create.cppo.ml
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,41 @@ open Ppx_deriving.Ast_convenience
let deriver = "create"
let raise_errorf = Ppx_deriving.raise_errorf

let parse_options options =
options |> List.iter (fun (name, expr) ->
match name with
| _ -> raise_errorf ~loc:expr.pexp_loc "%s does not support option %s" deriver name)
let attr_default context = Attribute.declare "deriving.create.default" context
Ast_pattern.(single_expr_payload __) (fun e -> e)
let ct_attr_default = attr_default Attribute.Context.core_type
let label_attr_default = attr_default Attribute.Context.label_declaration

let attr_default attrs =
Ppx_deriving.(attrs |> attr ~deriver "default" |> Arg.(get_attr ~deriver expr))
let attr_split context = Attribute.declare "deriving.create.split" context
Ast_pattern.(pstr nil) ()
let ct_attr_split = attr_split Attribute.Context.core_type
let label_attr_split = attr_split Attribute.Context.label_declaration

let attr_split attrs =
Ppx_deriving.(attrs |> attr ~deriver "split" |> Arg.get_flag ~deriver)
let attr_main context = Attribute.declare "deriving.create.main" context
Ast_pattern.(pstr nil) ()
let ct_attr_main = attr_main Attribute.Context.core_type
let label_attr_main = attr_main Attribute.Context.label_declaration

let attribute_get2 attr1 x1 attr2 x2 =
sim642 marked this conversation as resolved.
Show resolved Hide resolved
match Attribute.get attr1 x1, Attribute.get attr2 x2 with
| Some _ as y, _ -> y
| None, y -> y
sim642 marked this conversation as resolved.
Show resolved Hide resolved

let find_main labels =
List.fold_left (fun (main, labels) ({ pld_type; pld_loc; pld_attributes } as label) ->
if Ppx_deriving.(pld_type.ptyp_attributes @ pld_attributes |>
attr ~deriver "main" |> Arg.get_flag ~deriver) then
let is_main = match attribute_get2 ct_attr_main pld_type label_attr_main label with
| Some () -> true
| None -> false
in
if is_main then
sim642 marked this conversation as resolved.
Show resolved Hide resolved
match main with
| Some _ -> raise_errorf ~loc:pld_loc "Duplicate [@deriving.%s.main] annotation" deriver
| None -> Some label, labels
else
main, label :: labels)
(None, []) labels

let str_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
parse_options options;
let str_of_type ({ ptype_loc = loc } as type_decl) =
let quoter = Ppx_deriving.create_quoter () in
let creator =
match type_decl.ptype_kind with
Expand All @@ -46,14 +57,17 @@ let str_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
| None ->
Exp.fun_ Label.nolabel None (punit ()) (record fields)
in
List.fold_left (fun accum { pld_name = { txt = name }; pld_type; pld_attributes } ->
let attrs = pld_attributes @ pld_type.ptyp_attributes in
let pld_type = Ppx_deriving.remove_pervasives ~deriver pld_type in
match attr_default attrs with
List.fold_left (fun accum ({ pld_name = { txt = name }; pld_type; pld_attributes } as label) ->
match attribute_get2 label_attr_default label ct_attr_default pld_type with
| Some default -> Exp.fun_ (Label.optional name) (Some (Ppx_deriving.quote ~quoter default))
(pvar name) accum
| None ->
if attr_split attrs then
let split = match attribute_get2 label_attr_split label ct_attr_split pld_type with
| Some () -> true
| None -> false
in
let pld_type = Ppx_deriving.remove_pervasives ~deriver pld_type in
if split then
match pld_type with
| [%type: [%t? lhs] * [%t? rhs] list] when name.[String.length name - 1] = 's' ->
let name' = String.sub name 0 (String.length name - 1) in
Expand All @@ -78,8 +92,7 @@ let str_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
let wrap_predef_option typ =
typ

let sig_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
parse_options options;
let sig_of_type ({ ptype_loc = loc } as type_decl) =
let typ = Ppx_deriving.core_type_of_type_decl type_decl in
let typ =
match type_decl.ptype_kind with
Expand All @@ -92,13 +105,16 @@ let sig_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
| None ->
Typ.arrow Label.nolabel (tconstr "unit" []) typ
in
List.fold_left (fun accum { pld_name = { txt = name; loc }; pld_type; pld_attributes } ->
let attrs = pld_type.ptyp_attributes @ pld_attributes in
let pld_type = Ppx_deriving.remove_pervasives ~deriver pld_type in
match attr_default attrs with
List.fold_left (fun accum ({ pld_name = { txt = name; loc }; pld_type; pld_attributes } as label) ->
match attribute_get2 ct_attr_default pld_type label_attr_default label with
| Some _ -> Typ.arrow (Label.optional name) (wrap_predef_option pld_type) accum
| None ->
if attr_split attrs then
let split = match attribute_get2 ct_attr_split pld_type label_attr_split label with
| Some () -> true
| None -> false
in
let pld_type = Ppx_deriving.remove_pervasives ~deriver pld_type in
if split then
match pld_type with
| [%type: [%t? lhs] * [%t? rhs] list] when name.[String.length name - 1] = 's' ->
let name' = String.sub name 0 (String.length name - 1) in
Expand All @@ -118,11 +134,14 @@ let sig_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
in
[Sig.value (Val.mk (mknoloc (Ppx_deriving.mangle_type_decl (`Prefix deriver) type_decl)) typ)]

let () =
Ppx_deriving.(register (create deriver
~type_decl_str: (fun ~options ~path type_decls ->
[Str.value Nonrecursive (List.concat (List.map (str_of_type ~options ~path) type_decls))])
~type_decl_sig: (fun ~options ~path type_decls ->
List.concat (List.map (sig_of_type ~options ~path) type_decls))
()
))
let impl_generator = Deriving.Generator.V2.make_noarg (fun ~ctxt:_ (_, type_decls) ->
[Str.value Nonrecursive (List.concat (List.map str_of_type type_decls))])

let intf_generator = Deriving.Generator.V2.make_noarg (fun ~ctxt:_ (_, type_decls) ->
List.concat (List.map sig_of_type type_decls))

let deriving: Deriving.t =
Deriving.add
deriver
~str_type_decl:impl_generator
~sig_type_decl:intf_generator
46 changes: 22 additions & 24 deletions src_plugins/enum/ppx_deriving_enum.cppo.ml
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,15 @@ module Stdlib = Pervasives
let deriver = "enum"
let raise_errorf = Ppx_deriving.raise_errorf

let parse_options options =
options |> List.iter (fun (name, expr) ->
match name with
| _ -> raise_errorf ~loc:expr.pexp_loc "%s does not support option %s" deriver name)

let attr_value attrs =
Ppx_deriving.(attrs |> attr ~deriver "value" |> Arg.(get_attr ~deriver int))
let attr_value context = Attribute.declare "deriving.enum.value" context
Ast_pattern.(single_expr_payload (eint __)) (fun i -> i)
let constr_attr_value = attr_value Attribute.Context.constructor_declaration
let rtag_attr_value = attr_value Attribute.Context.rtag

let mappings_of_type type_decl =
let map acc mappings attrs constr_name =
let map acc mappings attr_value x attrs constr_name =
sim642 marked this conversation as resolved.
Show resolved Hide resolved
let value =
match attr_value attrs with
match Attribute.get attr_value x with
| Some idx -> idx | None -> acc
in
(value + 1, (value, constr_name) :: mappings)
Expand All @@ -31,11 +28,11 @@ let mappings_of_type type_decl =
match type_decl.ptype_kind, type_decl.ptype_manifest with
| Ptype_variant constrs, _ ->
`Regular,
List.fold_left (fun (acc, mappings) { pcd_name; pcd_args; pcd_attributes; pcd_loc } ->
List.fold_left (fun (acc, mappings) ({ pcd_name; pcd_args; pcd_attributes; pcd_loc } as constr) ->
if pcd_args <> Pcstr_tuple([]) then
raise_errorf ~loc:pcd_loc
"%s can be derived only for argumentless constructors" deriver;
map acc mappings pcd_attributes pcd_name)
map acc mappings constr_attr_value constr pcd_attributes pcd_name)
(0, []) constrs
| Ptype_abstract, Some { ptyp_desc = Ptyp_variant (constrs, Closed, None); ptyp_loc } ->
`Polymorphic,
Expand All @@ -55,7 +52,7 @@ let mappings_of_type type_decl =
match row_field.prf_desc with
| Rinherit _ -> error_inherit loc
| Rtag (name, true, []) ->
map acc mappings attrs name
map acc mappings rtag_attr_value row_field attrs name
| Rtag _ -> error_arguments loc
)
(0, []) constrs
Expand All @@ -77,8 +74,7 @@ let mappings_of_type type_decl =
mappings |> List.stable_sort (fun (a,_) (b,_) -> Stdlib.compare a b) |> check_dup;
kind, mappings

let str_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
parse_options options;
let str_of_type ({ ptype_loc = loc } as type_decl) =
let kind, mappings = mappings_of_type type_decl in
let patt name =
match kind with
Expand Down Expand Up @@ -106,9 +102,8 @@ let str_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
Vb.mk (pvar (Ppx_deriving.mangle_type_decl (`Suffix "of_enum") type_decl))
(Exp.function_ from_enum_cases)]

let sig_of_type ~options ~path type_decl =
let sig_of_type type_decl =
let loc = type_decl.ptype_loc in
parse_options options;
let typ = Ppx_deriving.core_type_of_type_decl type_decl in
[Sig.value (Val.mk (mknoloc (Ppx_deriving.mangle_type_decl (`Prefix "min") type_decl))
[%type: Ppx_deriving_runtime.int]);
Expand All @@ -119,11 +114,14 @@ let sig_of_type ~options ~path type_decl =
Sig.value (Val.mk (mknoloc (Ppx_deriving.mangle_type_decl (`Suffix "of_enum") type_decl))
[%type: Ppx_deriving_runtime.int -> [%t typ] Ppx_deriving_runtime.option])]

let () =
Ppx_deriving.(register (create deriver
~type_decl_str: (fun ~options ~path type_decls ->
[Str.value Nonrecursive (List.concat (List.map (str_of_type ~options ~path) type_decls))])
~type_decl_sig: (fun ~options ~path type_decls ->
List.concat (List.map (sig_of_type ~options ~path) type_decls))
()
))
let impl_generator = Deriving.Generator.V2.make_noarg (fun ~ctxt:_ (_, type_decls) ->
[Str.value Nonrecursive (List.concat (List.map str_of_type type_decls))])

let intf_generator = Deriving.Generator.V2.make_noarg (fun ~ctxt:_ (_, type_decls) ->
List.concat (List.map sig_of_type type_decls))

let deriving: Deriving.t =
Deriving.add
deriver
~str_type_decl:impl_generator
~sig_type_decl:intf_generator
Loading