@@ -44,11 +44,12 @@ let annot_schema_ocaml : Atd.Annot.schema_section = {
4444 Type_def , " attr" ; (* <ocaml attr="..."> on a type def: append [@@...] *)
4545 Type_def , " module" ; (* <ocaml module="M"> on a type def: not supported, warns *)
4646 Type_def , " t" ; (* <ocaml t="..."> on a type def: not supported, warns *)
47- Type_expr , " repr" ; (* <ocaml repr="poly"> on a sum type *)
48- Type_expr , " module" ; (* <ocaml module="M"> on a wrap: use M.t/M.wrap/M.unwrap *)
49- Type_expr , " t" ; (* <ocaml t="..."> on a wrap: explicit OCaml type *)
50- Type_expr , " wrap" ; (* <ocaml wrap="..."> on a wrap: deserialize function *)
51- Type_expr , " unwrap" ; (* <ocaml unwrap="..."> on a wrap: serialize function *)
47+ Type_expr , " repr" ; (* <ocaml repr="poly"> on a sum type *)
48+ Type_expr , " field_prefix" ; (* <ocaml field_prefix="t_"> on a record type *)
49+ Type_expr , " module" ; (* <ocaml module="M"> on a wrap: use M.t/M.wrap/M.unwrap *)
50+ Type_expr , " t" ; (* <ocaml t="..."> on a wrap: explicit OCaml type *)
51+ Type_expr , " wrap" ; (* <ocaml wrap="..."> on a wrap: deserialize function *)
52+ Type_expr , " unwrap" ; (* <ocaml unwrap="..."> on a wrap: serialize function *)
5253 Import , " name" ; (* <ocaml name="..."> on an import: override OCaml alias *)
5354 Variant , " name" ; (* <ocaml name="..."> on a variant constructor *)
5455 Field , " default" ; (* <ocaml default="..."> on a with-default field *)
@@ -168,6 +169,26 @@ let make_local_env names =
168169 List. iter (fun n -> ignore (Atd.Unique_name. translate registry n)) names;
169170 Atd.Unique_name. translate registry
170171
172+ (*
173+ Build two name translators for a prefixed record:
174+ - label_tr: ATD field name → OCaml-safe label name (no prefix applied).
175+ E.g. "if" → "if_".
176+ - pftr: ATD field name → OCaml-safe record field name (prefix concatenated
177+ with the raw ATD name *before* checking for keyword conflicts).
178+ E.g. with prefix "mod": "ule" → "module_", "if" → "modif".
179+ When prefix = "" both translators are identical.
180+ *)
181+ let make_prefixed_trs prefix field_names =
182+ let label_tr = make_local_env field_names in
183+ let pftr =
184+ if prefix = " " then label_tr
185+ else
186+ let pnames = List. map (fun n -> prefix ^ n) field_names in
187+ let field_tr = make_local_env pnames in
188+ fun fname -> field_tr (prefix ^ fname)
189+ in
190+ (label_tr, pftr)
191+
171192(* ============ Annotation helpers ============ *)
172193
173194(* Get <ocaml default="..."> for with-default fields *)
@@ -281,6 +302,17 @@ let with_inline_doc decl_str loc an : B.t =
281302 | [one_line] -> [B. Line (sprintf " %s (** %s *)" decl_str one_line)]
282303 | lines -> B. Line decl_str :: ocamldoc_comment_block lines
283304
305+ (* Get <ocaml field_prefix="..."> from a record type expression's annotation.
306+ Returns the prefix string, or "" if the annotation is absent. *)
307+ let get_field_prefix rec_an =
308+ match Atd.Annot. get_opt_field
309+ ~parse: (fun s -> Some s)
310+ ~sections: [" ocaml" ] ~field: " field_prefix"
311+ rec_an
312+ with
313+ | None -> " "
314+ | Some prefix -> prefix
315+
284316(* Get <ocaml name="..."> to rename OCaml identifiers *)
285317let get_ocaml_name default_name an =
286318 Atd.Annot. get_field
@@ -657,23 +689,24 @@ let gen_type_def ~is_mli env ({A.name; param=params; annot=an; value=e; _} as de
657689 B. Line (sprintf " type %s%s =" params_str ocaml_name);
658690 B. Block (concat_map gen_case flat);
659691 ]
660- | Record (_ , fields , _ ) ->
692+ | Record (_ , fields , rec_an ) ->
693+ let prefix = get_field_prefix rec_an in
661694 let fields =
662695 List. filter_map (fun (f : field ) -> match f with
663696 | Field x -> Some x
664697 | Inherit _ -> assert false
665698 ) fields
666699 in
667- let ftr =
668- make_local_env (List. map (fun (_ , (fname , _ , _ ), _ ) -> fname) fields)
700+ let (_, pftr) =
701+ make_prefixed_trs prefix (List. map (fun (_ , (fname , _ , _ ), _ ) -> fname) fields)
669702 in
670703 [
671704 B. Line (sprintf " type %s%s = {" params_str ocaml_name);
672705 B. Block
673706 (concat_map
674707 (fun (loc , (fname , _ , an ), e ) ->
675708 with_inline_doc
676- (sprintf " %s: %s;" (ftr fname) (type_expr_str env e))
709+ (sprintf " %s: %s;" (pftr fname) (type_expr_str env e))
677710 loc an)
678711 fields);
679712 B. Line " }" ;
@@ -763,16 +796,16 @@ let gen_make_fun env ({A.name; param=params; value=e; _} : A.type_def) : B.t =
763796 let name = Atd.Type_name. basename name in
764797 let ocaml_name = env.tr name in
765798 match e with
766- | Record (_ , fields , _ ) ->
799+ | Record (_ , fields , rec_an ) ->
800+ let prefix = get_field_prefix rec_an in
767801 let fields =
768802 List. filter_map (fun (f : field ) -> match f with
769803 | Field x -> Some x
770804 | Inherit _ -> assert false
771805 ) fields
772806 in
773- let ftr =
774- make_local_env (List. map (fun (_ , (fname , _ , _ ), _ ) -> fname) fields)
775- in
807+ let fnames = List. map (fun (_ , (fname , _ , _ ), _ ) -> fname) fields in
808+ let (label_tr, pftr) = make_prefixed_trs prefix fnames in
776809 (* Try to resolve the default for a With_default field.
777810 Returns None if no default can be determined, in which case
778811 we skip generating make_* for this type. *)
@@ -792,17 +825,24 @@ let gen_make_fun env ({A.name; param=params; value=e; _} : A.type_def) : B.t =
792825 if List. exists (fun r -> r = None ) resolved then []
793826 else
794827 let gen_param (_ , (fname , kind , _ ), _ ) default_opt =
795- let ofname = ftr fname in
828+ let lname = label_tr fname in
796829 match kind with
797- | Required -> sprintf " ~%s" ofname
798- | Optional -> sprintf " ?%s" ofname
830+ | Required -> sprintf " ~%s" lname
831+ | Optional -> sprintf " ?%s" lname
799832 | With_default ->
800833 let default = Option. get (Option. get default_opt) in
801- sprintf " ?(%s = %s)" ofname default
834+ sprintf " ?(%s = %s)" lname default
802835 in
803836 let param_strs = List. map2 gen_param fields resolved in
804- let field_names =
805- List. map (fun (_ , (fname , _ , _ ), _ ) -> ftr fname) fields
837+ (* Labels are unprefixed; record field names carry the prefix.
838+ Emit 'pfname = lname' when they differ, else use shorthand. *)
839+ let field_assigns =
840+ List. map (fun (_ , (fname , _ , _ ), _ ) ->
841+ let lname = label_tr fname in
842+ let pfname = pftr fname in
843+ if pfname = lname then pfname
844+ else sprintf " %s = %s" pfname lname
845+ ) fields
806846 in
807847 [
808848 B. Line
@@ -811,7 +851,7 @@ let gen_make_fun env ({A.name; param=params; value=e; _} : A.type_def) : B.t =
811851 (String. concat " " param_strs)
812852 (full_type_name ocaml_name params));
813853 B. Block
814- [B. Line (sprintf " { %s }" (String. concat " ; " field_names ))];
854+ [B. Line (sprintf " { %s }" (String. concat " ; " field_assigns ))];
815855 ]
816856 | _ -> []
817857
@@ -944,17 +984,25 @@ let gen_of_yojson env ({A.name; param=params; annot=an; value=e; _} : A.type_def
944984 @ List. map gen_case flat
945985 @ [B. Line (sprintf " | _ -> Atdml_runtime.bad_sum \" %s\" x" name)])
946986 | Record (_ , fields , rec_an ) ->
987+ let prefix = get_field_prefix rec_an in
947988 let fields =
948989 List. filter_map (fun (f : field ) -> match f with
949990 | Field x -> Some x
950991 | Inherit _ -> assert false )
951992 fields
952993 in
953- let ftr =
954- make_local_env (List. map (fun (_ , (fname , _ , _ ), _ ) -> fname) fields)
955- in
956- let field_names =
957- List. map (fun (_ , (fname , _ , _ ), _ ) -> ftr fname) fields
994+ let fnames = List. map (fun (_ , (fname , _ , _ ), _ ) -> fname) fields in
995+ let (label_tr, pftr) = make_prefixed_trs prefix fnames in
996+ (* Local variable bindings use label_tr (unprefixed, keyword-safe).
997+ The record literal uses pftr (prefix applied before keyword check).
998+ Emit 'pfname = lname' when they differ, else use shorthand. *)
999+ let field_assigns =
1000+ List. map (fun (_ , (fname , _ , _ ), _ ) ->
1001+ let lname = label_tr fname in
1002+ let pfname = pftr fname in
1003+ if pfname = lname then pfname
1004+ else sprintf " %s = %s" pfname lname
1005+ ) fields
9581006 in
9591007 let (normalize, _) =
9601008 adapter_exprs (Atd.Json. get_json_record rec_an).json_record_adapter
@@ -984,8 +1032,8 @@ let gen_of_yojson env ({A.name; param=params; annot=an; value=e; _} : A.type_def
9841032 B. Line " (fun key -> Hashtbl.find_opt tbl key)" ];
9851033 B. Line " else (fun key -> List.assoc_opt key fields)" ];
9861034 B. Line " in" ]
987- @ List. map (gen_of_yojson_field env ftr name) fields
988- @ [B. Line (sprintf " { %s }" (String. concat " ; " field_names ))]);
1035+ @ List. map (gen_of_yojson_field env label_tr name) fields
1036+ @ [B. Line (sprintf " { %s }" (String. concat " ; " field_assigns ))]);
9891037 B. Line (sprintf " | _ -> Atdml_runtime.bad_type \" %s\" x" name) ])
9901038 in
9911039 match_block
@@ -1015,9 +1063,11 @@ let gen_of_yojson env ({A.name; param=params; annot=an; value=e; _} : A.type_def
10151063
10161064(* ============ Serialization function generation ============ *)
10171065
1018- let gen_yojson_of_field env ftr (_ , (fname , kind , an ), e ) : B.node =
1066+ (* [pftr] maps the ATD field name to the prefixed OCaml field name
1067+ used in the record type definition (e.g. "pre_" ^ ftr fname). *)
1068+ let gen_yojson_of_field env pftr (_ , (fname , kind , an ), e ) : B.node =
10191069 let json_name = Atd.Json. get_json_fname fname an in
1020- let ofname = ftr fname in
1070+ let ofname = pftr fname in
10211071 match kind with
10221072 | Required | With_default ->
10231073 B. Line (sprintf " [(\" %s\" , %s x.%s)];" json_name (writer_expr env e) ofname)
@@ -1088,23 +1138,25 @@ let gen_yojson_of env ({A.name; param=params; annot=an; value=e; _} : A.type_def
10881138 (B. Line " match x with"
10891139 :: List. map gen_case flat))
10901140 | Record (_ , fields , rec_an ) ->
1141+ let prefix = get_field_prefix rec_an in
10911142 let fields =
10921143 List. filter_map (fun (f : field ) -> match f with
10931144 | Field x -> Some x
10941145 | Inherit _ -> assert false )
10951146 fields
10961147 in
1097- let ftr =
1098- make_local_env (List. map (fun (_ , (fname , _ , _ ), _ ) -> fname) fields)
1099- in
1148+ let fnames = List. map (fun (_ , (fname , _ , _ ), _ ) -> fname) fields in
1149+ (* pftr maps ATD field names to the prefixed OCaml record field names,
1150+ with the prefix applied before keyword checking. *)
1151+ let (_, pftr) = make_prefixed_trs prefix fnames in
11001152 let (_, restore) =
11011153 adapter_exprs (Atd.Json. get_json_record rec_an).json_record_adapter
11021154 in
11031155 apply_restore restore
11041156 (B. Block
11051157 [
11061158 B. Line " `Assoc (List.concat [" ;
1107- B. Block (List. map (gen_yojson_of_field env ftr ) fields);
1159+ B. Block (List. map (gen_yojson_of_field env pftr ) fields);
11081160 B. Line " ])" ;
11091161 ])
11101162 | e ->
0 commit comments