5
module Rewrite_tds = Pa_type_conv.Rewrite_tds
8
include Pa_type_conv.Gen
9
let idp loc id = <:patt< $lid:id$ >>
10
let ide loc id = <:expr< $lid:id$ >>
11
let let_in loc list_lid_expr body =
12
List.fold_right list_lid_expr ~init:body ~f:(fun (lid, expr) body ->
13
<:expr< let $lid:lid$ = $expr$ in $body$ >>)
19
let rec aux acc index =
20
if index < 0 then acc else
21
let acc = f index :: acc in
26
let fold_righti list ~f ~init =
27
let length = length list in
30
~f:(fun el (acc, index) -> let acc = f index el acc in (acc, pred index))
31
~init:(init, pred length)
39
~f:(fun (acc, index) el -> f index el :: acc, succ index)
46
(* camlp4 is very confusing with its tuple representation *)
48
val expr : Ast.loc -> Ast.expr list -> Ast.expr
49
val patt : Ast.loc -> Ast.patt list -> Ast.patt
50
val ctyp : Ast.loc -> Ast.ctyp list -> Ast.ctyp
52
let make fct = function
55
| (_ :: _) as list -> fct list
56
let expr loc = make (fun list -> <:expr< ($tup:Ast.exCom_of_list list$) >>)
57
let patt loc = make (fun list -> <:patt< ($tup:Ast.paCom_of_list list$) >>)
58
let ctyp loc = make (fun list -> <:ctyp< ($tup:Ast.tyCom_of_list list$) >>)
61
module Field_case = struct
69
module Variant_case = struct
72
ctyp : Ast.ctyp option;
80
let label = t.label in
81
if t.poly then <:patt< `$label$ >> else <:patt< $uid:label$ >>
84
let label = t.label in
85
if t.poly then <:expr< `$label$ >> else <:expr< $uid:label$ >>
87
let ocaml_repr ~loc { label ; poly ; arity_index ; _ } =
89
then <:expr< Typerep_lib.Std.Typerep_obj.repr_of_poly_variant `$label$ >>
90
else <:expr< $`int:arity_index$ >>
93
module Branches = struct
95
let fields = Ast.list_of_ctyp fields [] in
96
let mapi index = function
97
| <:ctyp< $lid:label$ : mutable $ctyp$ >>
98
| <:ctyp< $lid:label$ : $ctyp$ >>
100
{ Field_case.label ; ctyp ; index }
101
| ctyp -> Gen.unknown_type ctyp "Util.branches(record)"
103
List.mapi fields ~f:mapi
106
(* duplicates like [ `A | `B | `A ] cause warnings in the generated code (duplicated
107
patterns), so we don't have to deal with them. *)
108
let rec extract = function
109
| <:ctyp< [ $row_fields$ ] >>
110
| <:ctyp< [< $row_fields$ ] >>
111
| <:ctyp< [> $row_fields$ ] >>
112
| <:ctyp< [= $row_fields$ ] >> ->
114
| <:ctyp< $tp1$ | $tp2$ >> -> extract tp1 @ extract tp2
117
let cases = extract alts in
118
let no_arg = let r = ref (-1) in fun () -> incr r; !r in
119
let with_arg = let r = ref (-1) in fun () -> incr r; !r in
120
let mapi index = function
121
| <:ctyp< `$label$ >> ->
128
arity_index = no_arg ();
130
| <:ctyp< `$label$ of $ctyp$ >> ->
137
arity_index = with_arg ();
139
| <:ctyp< $uid:label$ >> ->
146
arity_index = no_arg ();
148
| <:ctyp@loc< $uid:label$ of $ctyp$ >> ->
149
let args = Ast.list_of_ctyp ctyp [] in
150
let arity = List.length args in
151
let ctyp = Tuple.ctyp loc args in
158
arity_index = with_arg ();
160
| ctyp -> Gen.unknown_type ctyp "Util.branches(variant)"
162
List.mapi cases ~f:mapi
165
module Typerep_signature = struct
166
let sig_of_type_definitions ~sig_of_one_def ~ctyp =
167
let rec aux = function
168
| Ast.TyDcl (loc, type_name, params, rhs, cl) ->
169
sig_of_one_def ~loc ~type_name ~params ~rhs ~cl
170
| Ast.TyAnd (loc, tp1, tp2) ->
179
let sig_of_of_t make_ty ~loc ~type_name ~params =
182
<:ctyp< $acc$ $param$ >>
184
List.fold_left ~f:fold ~init:<:ctyp< $lid:type_name$ >> params
186
let returned = <:ctyp< $make_ty t_with_params$ >> in
188
let param = Gen.drop_variance_annotations param in
189
let loc = Ast.loc_of_ctyp param in
190
<:ctyp< $make_ty param$ -> $acc$ >>
192
List.fold_right ~f:fold ~init:returned params
194
let sig_of_typerep_of_t ~loc =
195
let make_ty params = <:ctyp< Typerep_lib.Std.Typerep.t $params$ >> in
196
sig_of_of_t make_ty ~loc
198
let sig_of_typename_of_t ~loc =
199
let make_ty params = <:ctyp< Typerep_lib.Std.Typename.t $params$ >> in
200
sig_of_of_t make_ty ~loc
202
let sig_of_one_def ~loc ~type_name ~params ~rhs:_ ~cl:_ =
203
let typerep_of = sig_of_typerep_of_t ~loc ~type_name ~params in
204
let typename_of = sig_of_typename_of_t ~loc ~type_name ~params in
206
value $lid: "typerep_of_" ^ type_name$ : $typerep_of$;
207
value $lid: "typename_of_" ^ type_name$ : $typename_of$;
210
let sig_generator _rec ctyp =
211
sig_of_type_definitions ~sig_of_one_def ~ctyp
214
Pa_type_conv.add_sig_generator "typerep" sig_generator
217
module Typerep_implementation = struct
221
val typename_field : loc:Ast.loc -> type_name:string option -> Ast.expr
223
val arg_of_param : string -> string
225
val params_names : params:Ast.ctyp list -> string list
226
val params_patts : loc:Ast.loc -> params_names:string list -> Ast.patt list
228
val type_name_module_definition :
229
loc:Ast.loc -> type_name:string -> params_names:string list -> Ast.str_item
232
loc:Ast.loc -> type_name:string -> params_names:string list -> Ast.expr -> Ast.expr
234
val typerep_of_t_coerce :
235
loc:Ast.loc -> type_name:string -> params_names:string list -> Ast.ctyp option
237
val typerep_abstract :
238
loc:Ast.loc -> type_name:string -> params_names:string list -> Ast.str_item
242
val field_n_ident : fields:(Field_case.t list) -> int -> string
246
-> typerep_of_type:(Ast.ctyp -> Ast.expr)
247
-> fields:Field_case.t list
248
-> (int * string * Ast.expr) list
250
val create : loc:Ast.loc -> fields:Field_case.t list -> Ast.expr
252
val has_double_array_tag : loc:Ast.loc -> fields:Field_case.t list -> Ast.expr
257
val tag_n_ident : variants:(Variant_case.t list) -> int -> string
261
-> typerep_of_type:(Ast.ctyp -> Ast.expr)
262
-> variants:Variant_case.t list
263
-> (int * Ast.expr) list
265
val value : loc:Ast.loc -> variants:Variant_case.t list -> Ast.expr
267
val polymorphic : loc:Ast.loc -> variants:Variant_case.t list -> Ast.expr
272
let str_item_type_and_name loc ~params_names ~type_name =
274
List.map params_names
275
~f:(fun name -> <:ctyp< '$lid:name$ >>)
278
let fold acc name = <:ctyp< $acc$ '$lid:name$ >> in
279
let init = <:ctyp< $lid:type_name$ >> in
280
List.fold_left ~f:fold ~init params_names
282
let tds = Ast.TyDcl (loc, "t", params, prototype, []) in
283
let type_t = Rewrite_tds.str_ loc false tds in
285
let full_type_name = Printf.sprintf "%s.%s"
286
(Pa_type_conv.get_conv_path ()) type_name
288
<:str_item< value name = $str:full_type_name$ >>
290
<:module_expr< struct $type_t$; $name_def$; end >>
292
let arg_of_param name = "_of_" ^ name
293
let name_of_t ~type_name = "name_of_" ^ type_name
295
let typename_field ~loc ~type_name =
298
<:expr< Typerep_lib.Std.Typename.create () >>
300
<:expr< Typerep_lib.Std.Typerep.Named.typename_of_t
301
$lid:name_of_t ~type_name$ >>
303
let params_names ~params =
304
List.map params ~f:(fun ty -> Gen.get_tparam_id ty)
306
let params_patts ~loc ~params_names =
307
List.map params_names ~f:(fun s -> Gen.idp loc (arg_of_param s))
309
let type_name_module_name ~type_name = "Typename_of_" ^ type_name
311
let with_named ~loc ~type_name ~params_names expr =
313
let init = <:expr< $uid:type_name_module_name ~type_name$.named >> in
314
List.fold_left params_names ~init ~f:(fun acc name ->
315
let arg = arg_of_param name in
316
<:expr< $acc$ $lid:arg$>>
319
let name_of_t = name_of_t ~type_name in
320
let args = <:expr< ( $lid:name_of_t$, Some (lazy $expr$) ) >> in
321
<:expr< let $lid:name_of_t$ = $name_t$ in
322
Typerep_lib.Std.Typerep.Named $args$ >>
324
let typerep_of_t_coerce ~loc ~type_name ~params_names =
325
match params_names with
329
let fold acc name = <:ctyp< $acc$ '$lid:name$ >> in
330
let init = <:ctyp< $lid:type_name$ >> in
331
let t = List.fold_left ~f:fold ~init params_names in
332
<:ctyp< Typerep_lib.Std.Typerep.t $t$ >>
336
let arg = <:ctyp< Typerep_lib.Std.Typerep.t '$lid:name$ >> in
337
<:ctyp< $arg$ -> $acc$ >>
339
List.fold_right ~init:returned ~f:fold params_names
341
let f name = <:ctyp< '$name$ >> in
343
List.fold_left ~f:(fun a b -> <:ctyp< $a$ $f b$>>) ~init:(f hd) tl
345
Some <:ctyp< ! $typevars$ . $coerce$ >> (* forall *)
347
let type_name_module_definition ~loc ~type_name ~params_names =
348
let name = type_name_module_name ~type_name in
349
let type_arity = List.length params_names in
350
let make = <:module_expr< Typerep_lib.Std.Make_typename.$uid:"Make"
351
^ (string_of_int type_arity)$ >>
353
let type_name_struct =
354
str_item_type_and_name loc ~params_names ~type_name
356
let type_name_module = <:module_expr< $make$ $type_name_struct$ >> in
358
<:str_item< module $uid:name$ = $type_name_module$ >>
361
let lid = "typename_of_" ^ type_name in
362
<:str_item< value $lid:lid$ = $uid:name$.typename_of_t >>
369
let typerep_abstract ~loc ~type_name ~params_names =
370
let type_name_struct =
371
str_item_type_and_name loc ~params_names ~type_name
373
let type_arity = List.length params_names in
375
<:module_expr< Typerep_lib.Std.Type_abstract.$uid:"Make"
376
^ (string_of_int type_arity)$ >>
378
<:str_item< include $make$ $type_name_struct$ >>
380
let field_or_tag_n_ident prefix ~list n =
381
if n < 0 || n > List.length list then assert false;
382
prefix ^ string_of_int n
384
module Record = struct
385
let field_n_ident ~fields:list = field_or_tag_n_ident "field" ~list
387
let fields ~loc ~typerep_of_type ~fields =
388
let map { Field_case.ctyp ; label ; index } =
389
let rep = typerep_of_type ctyp in
390
index, label, <:expr<
391
Typerep_lib.Std.Typerep.Field.internal_use_only
392
{ Typerep_lib.Std.Typerep.Field_internal.
394
index = $`int:index$;
396
tyid = Typerep_lib.Std.Typename.create ();
397
get = (fun t -> t.$lid:label$);
401
List.map ~f:map fields
403
let has_double_array_tag ~loc ~fields =
405
let map { Field_case.label ; _ } =
406
(* The value must be a float else this segfaults. This is tested by the
407
unit tests in case this property changes. *)
408
<:rec_binding< $lid:label$ =
409
Typerep_lib.Std.Typerep_obj.double_array_value >>
411
List.map ~f:map fields
413
<:expr< Typerep_lib.Std.Typerep_obj.has_double_array_tag
414
{ $list:fields_binding$ } >>
416
let create ~loc ~fields =
418
(* Calling [get] on the fields from left to right matters, so that iteration
419
goes left to right too. *)
421
let map { Field_case.label ; _ } = <:rec_binding< $lid:label$ >> in
422
List.map ~f:map fields
424
let record = <:expr< { $list:fields_binding$ } >> in
425
let foldi index' { Field_case.label ; index; _ } acc =
426
if index <> index' then assert false;
427
let rhs = <:expr< get $lid:field_n_ident ~fields index$ >> in
428
<:expr< let $lid:label$ = $rhs$ in $acc$ >>
430
List.fold_righti fields ~f:foldi ~init:record
432
<:expr< fun { Typerep_lib.Std.Typerep.Record_internal.get = get }
436
module Variant = struct
437
(* tag_0, tag_1, etc. *)
438
let tag_n_ident ~variants:list = field_or_tag_n_ident "tag" ~list
440
let polymorphic ~loc ~variants =
444
| hd :: _ -> hd.Variant_case.poly
446
<:expr< $`bool:polymorphic$ >>
448
let tags ~loc ~typerep_of_type ~variants =
449
let create ({ Variant_case.arity ; _ } as variant) =
450
let constructor = Variant_case.expr ~loc variant in
453
<:expr< Typerep_lib.Std.Typerep.Tag_internal.Const $constructor$ >>
455
let arg_tuple i = "v" ^ string_of_int i in
458
let f i = <:patt< $lid:arg_tuple i$ >> in
459
Tuple.patt loc (List.init arity ~f)
462
let f i = <:expr< $lid:arg_tuple i$ >> in
463
let args = Tuple.expr loc (List.init arity ~f) in
464
<:expr< $constructor$ $args$ >>
468
<:expr< Typerep_lib.Std.Typerep.Tag_internal.Args
469
(fun $patt$ -> $expr$) >>
471
let mapi index' ({ Variant_case.ctyp ; label ; arity ; index ; _ } as variant) =
472
if index <> index' then assert false;
476
typerep_of_type ctyp, <:expr< Typerep_lib.Std.Typename.create () >>
478
<:expr< typerep_of_tuple0 >>, <:expr< typename_of_tuple0 >>
480
let label_string = Pa_type_conv.Gen.regular_constr_of_revised_constr label in
482
Typerep_lib.Std.Typerep.Tag.internal_use_only
483
{ Typerep_lib.Std.Typerep.Tag_internal.
484
label = $str:label_string$;
486
arity = $`int:arity$;
487
index = $`int:index$;
488
ocaml_repr = $Variant_case.ocaml_repr ~loc variant$;
490
create = $create variant$;
494
List.mapi ~f:mapi variants
496
let value ~loc ~variants =
498
let arg_tuple i = "v" ^ string_of_int i in
499
let mapi index' ({ Variant_case.arity ; index ; _ } as variant) =
500
if index <> index' then assert false;
501
let constructor = Variant_case.patt ~loc variant in
503
if arity = 0 then constructor, <:expr< value_tuple0 >>
506
let f i = <:patt< $lid:arg_tuple i$ >> in
507
let args = Tuple.patt loc (List.init arity ~f) in
508
<:patt< $constructor$ $args$ >>
511
let f i = <:expr< $lid:arg_tuple i$ >> in
512
Tuple.expr loc (List.init arity ~f)
516
let tag = <:expr< $lid:tag_n_ident ~variants index$ >> in
517
let prod = <:expr< Typerep_lib.Std.Typerep.Variant_internal.Value
520
<:match_case< $patt$ -> $prod$ >>
522
List.mapi ~f:mapi variants
524
<:expr< fun [ $list:match_cases$ ] >>
528
let mk_abst_call loc tn rev_path =
529
<:expr< $id:Gen.ident_of_rev_path loc (("typerep_of_" ^ tn) :: rev_path)$ >>
531
(* Conversion of type paths *)
532
let typerep_of_path_fun loc id =
533
match Gen.get_rev_id_path id [] with
534
| tn :: rev_path -> mk_abst_call loc tn rev_path
537
let rec typerep_of_type = function
538
| <:ctyp@loc< $ty$ $param$ >> ->
539
typerep_of_type_app loc ~ty ~param
540
| <:ctyp@loc< '$parm$ >> -> Gen.ide loc (Util.arg_of_param parm)
541
| <:ctyp@loc< $id:id$ >> -> typerep_of_path_fun loc id
542
| <:ctyp< [< $row_fields$ ] >>
543
| <:ctyp< [> $row_fields$ ] >>
544
| <:ctyp< [= $row_fields$ ] >> -> typerep_of_variant ~type_name:None row_fields
545
| <:ctyp< ( $tup:tuple$ ) >> -> typerep_of_tuple tuple
546
| ctyp -> Gen.unknown_type ctyp "typerep_of_type"
548
and typerep_of_type_app loc ~ty ~param =
549
let typerep_of_ty = typerep_of_type ty in
550
let typerep_of_param = typerep_of_type param in
551
<:expr< $typerep_of_ty$ $typerep_of_param$ >>
553
and typerep_of_tuple tuple =
554
let loc = Ast.loc_of_ctyp tuple in
555
let typereps = List.map (Ast.list_of_ctyp tuple []) ~f:typerep_of_type in
556
let typerep_of_tuple =
557
let len = List.length typereps in
558
if len < 2 || len > 5
560
Gen.error tuple ~fn:"typerep impl_gen"
561
~msg:(Printf.sprintf "unsupported tuple arity %d. must be in {2,3,4,5}" len)
563
Gen.ide loc ("typerep_of_tuple" ^ string_of_int len)
565
Gen.apply loc typerep_of_tuple typereps
568
and typerep_of_record ~type_name ctyp =
569
let loc = Ast.loc_of_ctyp ctyp in
570
let fields = Branches.fields ctyp in
571
let field_ident i = Util.Record.field_n_ident ~fields i in
572
let indexed_fields = Util.Record.fields ~loc ~typerep_of_type ~fields in
575
List.map ~f:(fun (index,_,_) ->
576
<:expr< Typerep_lib.Std.Typerep.Record_internal.Field
577
$lid:field_ident index$ >>
580
<:expr< [| $list:fields$ |] >>
583
"typename", Util.typename_field ~loc ~type_name:(Some type_name);
584
"has_double_array_tag", Util.Record.has_double_array_tag ~loc ~fields;
585
"fields", fields_array;
586
"create", Util.Record.create ~loc ~fields;
590
<:rec_binding< Typerep_lib.Std.Typerep.Record_internal.$lid:name$ >>
592
List.map ~f:map bindings
596
<:expr< Typerep_lib.Std.Typerep.Record.internal_use_only
597
{ $list:fields_binding$ } >>
599
<:expr< Typerep_lib.Std.Typerep.Record $fields$ >>
601
let record = Gen.let_in loc bindings record in
602
let record = List.fold_right indexed_fields ~f:(fun (index, _, expr) acc ->
603
<:expr< let $lid:field_ident index$ = $expr$ in $acc$ >>
608
and typerep_of_variant ~type_name ctyp =
609
let loc = Ast.loc_of_ctyp ctyp in
610
let variants = Branches.variants ctyp in
611
let tags = Util.Variant.tags ~loc ~typerep_of_type ~variants in
612
let tag_ident i = Util.Variant.tag_n_ident ~variants i in
615
List.map ~f:(fun (index,_) ->
616
<:expr< Typerep_lib.Std.Typerep.Variant_internal.Tag $lid:tag_ident index$ >>
619
<:expr< [| $list:tags$ |] >>
622
"typename", Util.typename_field ~loc ~type_name;
624
"polymorphic", Util.Variant.polymorphic ~loc ~variants;
625
"value", Util.Variant.value ~loc ~variants;
629
<:rec_binding< Typerep_lib.Std.Typerep.Variant_internal.
630
$lid:name$ = $lid:name$ >>
632
List.map ~f:map bindings
635
let tags = <:expr< Typerep_lib.Std.Typerep.Variant.internal_use_only
636
{ $list:tags_binding$ } >> in
637
<:expr< Typerep_lib.Std.Typerep.Variant $tags$ >>
639
let variant = Gen.let_in loc bindings variant in
640
let variant = List.fold_right tags ~f:(fun (index, expr) acc ->
641
<:expr< let $lid:tag_ident index$ = $expr$ in $acc$ >>
646
let impl_of_one_def ~loc ~type_name ~params ~rhs:ctyp =
648
Gen.switch_tp_def ctyp
649
~alias:(fun (_:Loc.t) ctyp -> typerep_of_type ctyp)
650
~sum:(fun (_:Loc.t) -> typerep_of_variant ~type_name:(Some type_name))
651
~record:(fun (_:Loc.t) -> typerep_of_record ~type_name)
652
~variants:(fun (_:Loc.t) -> typerep_of_variant ~type_name:(Some type_name))
653
~mani:(fun (_:Loc.t) _tp1 ctyp -> body ctyp)
655
Loc.raise loc (Failure "typerep cannot be applied on abstract types, except \
656
like 'type t with typerep(abstract)'")
659
let body = body ctyp in
660
let params_names = Util.params_names ~params in
661
let params_patts = Util.params_patts ~loc ~params_names in
662
let body = Util.with_named ~loc ~type_name ~params_names body in
663
let arguments = List.map2 params_names params_patts ~f:(fun name patt ->
664
(* Add type annotations to parameters, at least to avoid the unused type warning. *)
665
let loc = Ast.loc_of_patt patt in
666
<:patt< ($patt$ : Typerep_lib.Std.Typerep.t $lid:name$) >>)
668
let body = Gen.abstract loc arguments body in
669
let body = List.fold_right params_names ~init:body ~f:(fun name acc ->
670
<:expr< fun (type $name$) -> $acc$ >> )
673
match Util.typerep_of_t_coerce ~loc ~type_name ~params_names with
675
<:expr< ($body$ : $coerce$) >>
678
let bnd = Gen.idp loc ("typerep_of_" ^ type_name) in
679
let binding = <:binding< $bnd$ = $body$ >> in
680
Util.type_name_module_definition ~loc ~type_name ~params_names, binding
682
let rec with_typerep_aux = function
683
| Ast.TyDcl (loc, type_name, params, rhs, _cl) ->
684
[impl_of_one_def ~loc ~type_name ~params ~rhs]
685
| <:ctyp< $ctyp1$ and $ctyp2$ >> ->
686
with_typerep_aux ctyp1 @ with_typerep_aux ctyp2
689
let with_typerep rec_ ctyp =
692
| Ast.TyDcl (loc, type_name, _, rhs, _) ->
693
loc, rec_ && Gen.type_is_recursive type_name rhs
694
| <:ctyp@loc< $_$ and $_$ >> ->
700
| true -> <:rec_flag< rec >>
701
| false -> <:rec_flag< >>
703
let prelude, bindings = List.split (with_typerep_aux ctyp) in
706
value $rec:rec_flag$ $list:bindings$;
709
let rec with_typerep_abstract rec_ ctyp =
711
| Ast.TyDcl (loc, type_name, params, _ctyp, _cl) ->
713
let params_names = Util.params_names ~params in
714
Util.typerep_abstract ~loc ~type_name ~params_names
715
| <:ctyp@loc< $ctyp1$ and $ctyp2$ >> ->
717
$with_typerep_abstract rec_ ctyp1$;
718
$with_typerep_abstract rec_ ctyp2$;
721
Gen.error ctyp ~fn:"typerep impl_gen" ~msg:"unsupported type def"
723
module Config = struct
726
warn_23_field : unit;
734
let gram_entry : t Gram.Entry.t = Gram.Entry.mk "typerep_arguments"
739
LIDENT "abstract" -> (fun acc -> { acc with abstract = true })
741
Loc.raise loc (Failure (Printf.sprintf "Unknown typerep argument %S" id));
744
v = LIST0 typerep_arg SEP "," ; `EOI ->
745
ignore loc; (* don't know how to ignore it otherwise *)
747
~f:(fun acc f -> f acc)
754
let () = Pa_type_conv.add_generator_with_arg "typerep" Config.gram_entry
755
(fun conf rec_ ctyp ->
756
let config = match conf with None -> Config.default | Some conf -> conf in
757
if config.Config.abstract
758
then with_typerep_abstract rec_ ctyp
759
else with_typerep rec_ ctyp
762
let typerep_of_quote (loc : Ast.loc) (_loc_name_opt : string option) (cnt_str : string) =
763
Pa_type_conv.set_conv_path_if_not_set loc;
764
let ctyp = Gram.parse_string Syntax.ctyp_quot loc cnt_str in
768
Quotation.add "typerep_of" Quotation.DynAst.expr_tag typerep_of_quote