(**************************************************************************) (* *) (* OCaml *) (* *) (* Alain Frisch, LexiFi *) (* *) (* Copyright 2012 Institut National de Recherche en Informatique et *) (* en Automatique. *) (* *) (* All rights reserved. This file is distributed under the terms of *) (* the GNU Lesser General Public License version 2.1, with the *) (* special exception on linking described in the file LICENSE. *) (* *) (**************************************************************************) (* A generic Parsetree mapping class *) (* [@@@ocaml.warning "+9"] (* Ensure that record patterns don't miss any field. *) *) open Parsetree open Ast_helper open Location type mapper = { attribute: mapper -> attribute -> attribute; attributes: mapper -> attribute list -> attribute list; case: mapper -> case -> case; cases: mapper -> case list -> case list; class_declaration: mapper -> class_declaration -> class_declaration; class_description: mapper -> class_description -> class_description; class_expr: mapper -> class_expr -> class_expr; class_field: mapper -> class_field -> class_field; class_signature: mapper -> class_signature -> class_signature; class_structure: mapper -> class_structure -> class_structure; class_type: mapper -> class_type -> class_type; class_type_declaration: mapper -> class_type_declaration -> class_type_declaration; class_type_field: mapper -> class_type_field -> class_type_field; constructor_declaration: mapper -> constructor_declaration -> constructor_declaration; expr: mapper -> expression -> expression; extension: mapper -> extension -> extension; extension_constructor: mapper -> extension_constructor -> extension_constructor; include_declaration: mapper -> include_declaration -> include_declaration; include_description: mapper -> include_description -> include_description; label_declaration: mapper -> label_declaration -> label_declaration; location: mapper -> Location.t -> Location.t; module_binding: mapper -> module_binding -> module_binding; module_declaration: mapper -> module_declaration -> module_declaration; module_expr: mapper -> module_expr -> module_expr; module_type: mapper -> module_type -> module_type; module_type_declaration: mapper -> module_type_declaration -> module_type_declaration; open_description: mapper -> open_description -> open_description; pat: mapper -> pattern -> pattern; payload: mapper -> payload -> payload; signature: mapper -> signature -> signature; signature_item: mapper -> signature_item -> signature_item; structure: mapper -> structure -> structure; structure_item: mapper -> structure_item -> structure_item; typ: mapper -> core_type -> core_type; type_declaration: mapper -> type_declaration -> type_declaration; type_extension: mapper -> type_extension -> type_extension; type_kind: mapper -> type_kind -> type_kind; value_binding: mapper -> value_binding -> value_binding; value_description: mapper -> value_description -> value_description; with_constraint: mapper -> with_constraint -> with_constraint; } let map_fst f (x, y) = (f x, y) let map_snd f (x, y) = (x, f y) let map_tuple f1 f2 (x, y) = (f1 x, f2 y) let map_tuple3 f1 f2 f3 (x, y, z) = (f1 x, f2 y, f3 z) let map_opt f = function None -> None | Some x -> Some (f x) let map_loc sub {loc; txt} = {loc = sub.location sub loc; txt} module T = struct (* Type expressions for the core language *) let row_field sub = function | Rtag (l, attrs, b, tl) -> Rtag (l, sub.attributes sub attrs, b, List.map (sub.typ sub) tl) | Rinherit t -> Rinherit (sub.typ sub t) let map sub {ptyp_desc = desc; ptyp_loc = loc; ptyp_attributes = attrs} = let open Typ in let loc = sub.location sub loc in let attrs = sub.attributes sub attrs in match desc with | Ptyp_any -> any ~loc ~attrs () | Ptyp_var s -> var ~loc ~attrs s | Ptyp_arrow (lab, t1, t2) -> arrow ~loc ~attrs lab (sub.typ sub t1) (sub.typ sub t2) | Ptyp_tuple tyl -> tuple ~loc ~attrs (List.map (sub.typ sub) tyl) | Ptyp_constr (lid, tl) -> constr ~loc ~attrs (map_loc sub lid) (List.map (sub.typ sub) tl) | Ptyp_object (l, o) -> let f (s, a, t) = (s, sub.attributes sub a, sub.typ sub t) in object_ ~loc ~attrs (List.map f l) o | Ptyp_class (lid, tl) -> class_ ~loc ~attrs (map_loc sub lid) (List.map (sub.typ sub) tl) | Ptyp_alias (t, s) -> alias ~loc ~attrs (sub.typ sub t) s | Ptyp_variant (rl, b, ll) -> variant ~loc ~attrs (List.map (row_field sub) rl) b ll | Ptyp_poly (sl, t) -> poly ~loc ~attrs sl (sub.typ sub t) | Ptyp_package (lid, l) -> package ~loc ~attrs (map_loc sub lid) (List.map (map_tuple (map_loc sub) (sub.typ sub)) l) | Ptyp_extension x -> extension ~loc ~attrs (sub.extension sub x) let map_type_declaration sub {ptype_name; ptype_params; ptype_cstrs; ptype_kind; ptype_private; ptype_manifest; ptype_attributes; ptype_loc} = Type.mk (map_loc sub ptype_name) ~params:(List.map (map_fst (sub.typ sub)) ptype_params) ~priv:ptype_private ~cstrs:(List.map (map_tuple3 (sub.typ sub) (sub.typ sub) (sub.location sub)) ptype_cstrs) ~kind:(sub.type_kind sub ptype_kind) ?manifest:(map_opt (sub.typ sub) ptype_manifest) ~loc:(sub.location sub ptype_loc) ~attrs:(sub.attributes sub ptype_attributes) let map_type_kind sub = function | Ptype_abstract -> Ptype_abstract | Ptype_variant l -> Ptype_variant (List.map (sub.constructor_declaration sub) l) | Ptype_record l -> Ptype_record (List.map (sub.label_declaration sub) l) | Ptype_open -> Ptype_open let map_constructor_arguments sub = function | Pcstr_tuple l -> Pcstr_tuple (List.map (sub.typ sub) l) | Pcstr_record l -> Pcstr_record (List.map (sub.label_declaration sub) l) let map_type_extension sub {ptyext_path; ptyext_params; ptyext_constructors; ptyext_private; ptyext_attributes} = Te.mk (map_loc sub ptyext_path) (List.map (sub.extension_constructor sub) ptyext_constructors) ~params:(List.map (map_fst (sub.typ sub)) ptyext_params) ~priv:ptyext_private ~attrs:(sub.attributes sub ptyext_attributes) let map_extension_constructor_kind sub = function Pext_decl(ctl, cto) -> Pext_decl(map_constructor_arguments sub ctl, map_opt (sub.typ sub) cto) | Pext_rebind li -> Pext_rebind (map_loc sub li) let map_extension_constructor sub {pext_name; pext_kind; pext_loc; pext_attributes} = Te.constructor (map_loc sub pext_name) (map_extension_constructor_kind sub pext_kind) ~loc:(sub.location sub pext_loc) ~attrs:(sub.attributes sub pext_attributes) end module CT = struct (* Type expressions for the class language *) let map sub {pcty_loc = loc; pcty_desc = desc; pcty_attributes = attrs} = let open Cty in let loc = sub.location sub loc in let attrs = sub.attributes sub attrs in match desc with | Pcty_constr (lid, tys) -> constr ~loc ~attrs (map_loc sub lid) (List.map (sub.typ sub) tys) | Pcty_signature x -> signature ~loc ~attrs (sub.class_signature sub x) | Pcty_arrow (lab, t, ct) -> arrow ~loc ~attrs lab (sub.typ sub t) (sub.class_type sub ct) | Pcty_extension x -> extension ~loc ~attrs (sub.extension sub x) let map_field sub {pctf_desc = desc; pctf_loc = loc; pctf_attributes = attrs} = let open Ctf in let loc = sub.location sub loc in let attrs = sub.attributes sub attrs in match desc with | Pctf_inherit ct -> inherit_ ~loc ~attrs (sub.class_type sub ct) | Pctf_val (s, m, v, t) -> val_ ~loc ~attrs s m v (sub.typ sub t) | Pctf_method (s, p, v, t) -> method_ ~loc ~attrs s p v (sub.typ sub t) | Pctf_constraint (t1, t2) -> constraint_ ~loc ~attrs (sub.typ sub t1) (sub.typ sub t2) | Pctf_attribute x -> attribute ~loc (sub.attribute sub x) | Pctf_extension x -> extension ~loc ~attrs (sub.extension sub x) let map_signature sub {pcsig_self; pcsig_fields} = Csig.mk (sub.typ sub pcsig_self) (List.map (sub.class_type_field sub) pcsig_fields) end module MT = struct (* Type expressions for the module language *) let map sub {pmty_desc = desc; pmty_loc = loc; pmty_attributes = attrs} = let open Mty in let loc = sub.location sub loc in let attrs = sub.attributes sub attrs in match desc with | Pmty_ident s -> ident ~loc ~attrs (map_loc sub s) | Pmty_alias s -> alias ~loc ~attrs (map_loc sub s) | Pmty_signature sg -> signature ~loc ~attrs (sub.signature sub sg) | Pmty_functor (s, mt1, mt2) -> functor_ ~loc ~attrs (map_loc sub s) (Misc.may_map (sub.module_type sub) mt1) (sub.module_type sub mt2) | Pmty_with (mt, l) -> with_ ~loc ~attrs (sub.module_type sub mt) (List.map (sub.with_constraint sub) l) | Pmty_typeof me -> typeof_ ~loc ~attrs (sub.module_expr sub me) | Pmty_extension x -> extension ~loc ~attrs (sub.extension sub x) let map_with_constraint sub = function | Pwith_type (lid, d) -> Pwith_type (map_loc sub lid, sub.type_declaration sub d) | Pwith_module (lid, lid2) -> Pwith_module (map_loc sub lid, map_loc sub lid2) | Pwith_typesubst d -> Pwith_typesubst (sub.type_declaration sub d) | Pwith_modsubst (s, lid) -> Pwith_modsubst (map_loc sub s, map_loc sub lid) let map_signature_item sub {psig_desc = desc; psig_loc = loc} = let open Sig in let loc = sub.location sub loc in match desc with | Psig_value vd -> value ~loc (sub.value_description sub vd) | Psig_type (rf, l) -> type_ ~loc rf (List.map (sub.type_declaration sub) l) | Psig_typext te -> type_extension ~loc (sub.type_extension sub te) | Psig_exception ed -> exception_ ~loc (sub.extension_constructor sub ed) | Psig_module x -> module_ ~loc (sub.module_declaration sub x) | Psig_recmodule l -> rec_module ~loc (List.map (sub.module_declaration sub) l) | Psig_modtype x -> modtype ~loc (sub.module_type_declaration sub x) | Psig_open x -> open_ ~loc (sub.open_description sub x) | Psig_include x -> include_ ~loc (sub.include_description sub x) | Psig_class l -> class_ ~loc (List.map (sub.class_description sub) l) | Psig_class_type l -> class_type ~loc (List.map (sub.class_type_declaration sub) l) | Psig_extension (x, attrs) -> extension ~loc (sub.extension sub x) ~attrs:(sub.attributes sub attrs) | Psig_attribute x -> attribute ~loc (sub.attribute sub x) end module M = struct (* Value expressions for the module language *) let map sub {pmod_loc = loc; pmod_desc = desc; pmod_attributes = attrs} = let open Mod in let loc = sub.location sub loc in let attrs = sub.attributes sub attrs in match desc with | Pmod_ident x -> ident ~loc ~attrs (map_loc sub x) | Pmod_structure str -> structure ~loc ~attrs (sub.structure sub str) | Pmod_functor (arg, arg_ty, body) -> functor_ ~loc ~attrs (map_loc sub arg) (Misc.may_map (sub.module_type sub) arg_ty) (sub.module_expr sub body) | Pmod_apply (m1, m2) -> apply ~loc ~attrs (sub.module_expr sub m1) (sub.module_expr sub m2) | Pmod_constraint (m, mty) -> constraint_ ~loc ~attrs (sub.module_expr sub m) (sub.module_type sub mty) | Pmod_unpack e -> unpack ~loc ~attrs (sub.expr sub e) | Pmod_extension x -> extension ~loc ~attrs (sub.extension sub x) let map_structure_item sub {pstr_loc = loc; pstr_desc = desc} = let open Str in let loc = sub.location sub loc in match desc with | Pstr_eval (x, attrs) -> eval ~loc ~attrs:(sub.attributes sub attrs) (sub.expr sub x) | Pstr_value (r, vbs) -> value ~loc r (List.map (sub.value_binding sub) vbs) | Pstr_primitive vd -> primitive ~loc (sub.value_description sub vd) | Pstr_type (rf, l) -> type_ ~loc rf (List.map (sub.type_declaration sub) l) | Pstr_typext te -> type_extension ~loc (sub.type_extension sub te) | Pstr_exception ed -> exception_ ~loc (sub.extension_constructor sub ed) | Pstr_module x -> module_ ~loc (sub.module_binding sub x) | Pstr_recmodule l -> rec_module ~loc (List.map (sub.module_binding sub) l) | Pstr_modtype x -> modtype ~loc (sub.module_type_declaration sub x) | Pstr_open x -> open_ ~loc (sub.open_description sub x) | Pstr_class l -> class_ ~loc (List.map (sub.class_declaration sub) l) | Pstr_class_type l -> class_type ~loc (List.map (sub.class_type_declaration sub) l) | Pstr_include x -> include_ ~loc (sub.include_declaration sub x) | Pstr_extension (x, attrs) -> extension ~loc (sub.extension sub x) ~attrs:(sub.attributes sub attrs) | Pstr_attribute x -> attribute ~loc (sub.attribute sub x) end module E = struct (* Value expressions for the core language *) let map sub {pexp_loc = loc; pexp_desc = desc; pexp_attributes = attrs} = let open Exp in let loc = sub.location sub loc in let attrs = sub.attributes sub attrs in match desc with | Pexp_ident x -> ident ~loc ~attrs (map_loc sub x) | Pexp_constant x -> constant ~loc ~attrs x | Pexp_let (r, vbs, e) -> let_ ~loc ~attrs r (List.map (sub.value_binding sub) vbs) (sub.expr sub e) | Pexp_fun (lab, def, p, e) -> fun_ ~loc ~attrs lab (map_opt (sub.expr sub) def) (sub.pat sub p) (sub.expr sub e) | Pexp_function pel -> function_ ~loc ~attrs (sub.cases sub pel) | Pexp_apply (e, l) -> apply ~loc ~attrs (sub.expr sub e) (List.map (map_snd (sub.expr sub)) l) | Pexp_match (e, pel) -> match_ ~loc ~attrs (sub.expr sub e) (sub.cases sub pel) | Pexp_try (e, pel) -> try_ ~loc ~attrs (sub.expr sub e) (sub.cases sub pel) | Pexp_tuple el -> tuple ~loc ~attrs (List.map (sub.expr sub) el) | Pexp_construct (lid, arg) -> construct ~loc ~attrs (map_loc sub lid) (map_opt (sub.expr sub) arg) | Pexp_variant (lab, eo) -> variant ~loc ~attrs lab (map_opt (sub.expr sub) eo) | Pexp_record (l, eo) -> record ~loc ~attrs (List.map (map_tuple (map_loc sub) (sub.expr sub)) l) (map_opt (sub.expr sub) eo) | Pexp_field (e, lid) -> field ~loc ~attrs (sub.expr sub e) (map_loc sub lid) | Pexp_setfield (e1, lid, e2) -> setfield ~loc ~attrs (sub.expr sub e1) (map_loc sub lid) (sub.expr sub e2) | Pexp_array el -> array ~loc ~attrs (List.map (sub.expr sub) el) | Pexp_ifthenelse (e1, e2, e3) -> ifthenelse ~loc ~attrs (sub.expr sub e1) (sub.expr sub e2) (map_opt (sub.expr sub) e3) | Pexp_sequence (e1, e2) -> sequence ~loc ~attrs (sub.expr sub e1) (sub.expr sub e2) | Pexp_while (e1, e2) -> while_ ~loc ~attrs (sub.expr sub e1) (sub.expr sub e2) | Pexp_for (p, e1, e2, d, e3) -> for_ ~loc ~attrs (sub.pat sub p) (sub.expr sub e1) (sub.expr sub e2) d (sub.expr sub e3) | Pexp_coerce (e, t1, t2) -> coerce ~loc ~attrs (sub.expr sub e) (map_opt (sub.typ sub) t1) (sub.typ sub t2) | Pexp_constraint (e, t) -> constraint_ ~loc ~attrs (sub.expr sub e) (sub.typ sub t) | Pexp_send (e, s) -> send ~loc ~attrs (sub.expr sub e) s | Pexp_new lid -> new_ ~loc ~attrs (map_loc sub lid) | Pexp_setinstvar (s, e) -> setinstvar ~loc ~attrs (map_loc sub s) (sub.expr sub e) | Pexp_override sel -> override ~loc ~attrs (List.map (map_tuple (map_loc sub) (sub.expr sub)) sel) | Pexp_letmodule (s, me, e) -> letmodule ~loc ~attrs (map_loc sub s) (sub.module_expr sub me) (sub.expr sub e) | Pexp_assert e -> assert_ ~loc ~attrs (sub.expr sub e) | Pexp_lazy e -> lazy_ ~loc ~attrs (sub.expr sub e) | Pexp_poly (e, t) -> poly ~loc ~attrs (sub.expr sub e) (map_opt (sub.typ sub) t) | Pexp_object cls -> object_ ~loc ~attrs (sub.class_structure sub cls) | Pexp_newtype (s, e) -> newtype ~loc ~attrs s (sub.expr sub e) | Pexp_pack me -> pack ~loc ~attrs (sub.module_expr sub me) | Pexp_open (ovf, lid, e) -> open_ ~loc ~attrs ovf (map_loc sub lid) (sub.expr sub e) | Pexp_extension x -> extension ~loc ~attrs (sub.extension sub x) | Pexp_unreachable -> unreachable ~loc ~attrs () end module P = struct (* Patterns *) let map sub {ppat_desc = desc; ppat_loc = loc; ppat_attributes = attrs} = let open Pat in let loc = sub.location sub loc in let attrs = sub.attributes sub attrs in match desc with | Ppat_any -> any ~loc ~attrs () | Ppat_var s -> var ~loc ~attrs (map_loc sub s) | Ppat_alias (p, s) -> alias ~loc ~attrs (sub.pat sub p) (map_loc sub s) | Ppat_constant c -> constant ~loc ~attrs c | Ppat_interval (c1, c2) -> interval ~loc ~attrs c1 c2 | Ppat_tuple pl -> tuple ~loc ~attrs (List.map (sub.pat sub) pl) | Ppat_construct (l, p) -> construct ~loc ~attrs (map_loc sub l) (map_opt (sub.pat sub) p) | Ppat_variant (l, p) -> variant ~loc ~attrs l (map_opt (sub.pat sub) p) | Ppat_record (lpl, cf) -> record ~loc ~attrs (List.map (map_tuple (map_loc sub) (sub.pat sub)) lpl) cf | Ppat_array pl -> array ~loc ~attrs (List.map (sub.pat sub) pl) | Ppat_or (p1, p2) -> or_ ~loc ~attrs (sub.pat sub p1) (sub.pat sub p2) | Ppat_constraint (p, t) -> constraint_ ~loc ~attrs (sub.pat sub p) (sub.typ sub t) | Ppat_type s -> type_ ~loc ~attrs (map_loc sub s) | Ppat_lazy p -> lazy_ ~loc ~attrs (sub.pat sub p) | Ppat_unpack s -> unpack ~loc ~attrs (map_loc sub s) | Ppat_exception p -> exception_ ~loc ~attrs (sub.pat sub p) | Ppat_extension x -> extension ~loc ~attrs (sub.extension sub x) end module CE = struct (* Value expressions for the class language *) let map sub {pcl_loc = loc; pcl_desc = desc; pcl_attributes = attrs} = let open Cl in let loc = sub.location sub loc in let attrs = sub.attributes sub attrs in match desc with | Pcl_constr (lid, tys) -> constr ~loc ~attrs (map_loc sub lid) (List.map (sub.typ sub) tys) | Pcl_structure s -> structure ~loc ~attrs (sub.class_structure sub s) | Pcl_fun (lab, e, p, ce) -> fun_ ~loc ~attrs lab (map_opt (sub.expr sub) e) (sub.pat sub p) (sub.class_expr sub ce) | Pcl_apply (ce, l) -> apply ~loc ~attrs (sub.class_expr sub ce) (List.map (map_snd (sub.expr sub)) l) | Pcl_let (r, vbs, ce) -> let_ ~loc ~attrs r (List.map (sub.value_binding sub) vbs) (sub.class_expr sub ce) | Pcl_constraint (ce, ct) -> constraint_ ~loc ~attrs (sub.class_expr sub ce) (sub.class_type sub ct) | Pcl_extension x -> extension ~loc ~attrs (sub.extension sub x) let map_kind sub = function | Cfk_concrete (o, e) -> Cfk_concrete (o, sub.expr sub e) | Cfk_virtual t -> Cfk_virtual (sub.typ sub t) let map_field sub {pcf_desc = desc; pcf_loc = loc; pcf_attributes = attrs} = let open Cf in let loc = sub.location sub loc in let attrs = sub.attributes sub attrs in match desc with | Pcf_inherit (o, ce, s) -> inherit_ ~loc ~attrs o (sub.class_expr sub ce) s | Pcf_val (s, m, k) -> val_ ~loc ~attrs (map_loc sub s) m (map_kind sub k) | Pcf_method (s, p, k) -> method_ ~loc ~attrs (map_loc sub s) p (map_kind sub k) | Pcf_constraint (t1, t2) -> constraint_ ~loc ~attrs (sub.typ sub t1) (sub.typ sub t2) | Pcf_initializer e -> initializer_ ~loc ~attrs (sub.expr sub e) | Pcf_attribute x -> attribute ~loc (sub.attribute sub x) | Pcf_extension x -> extension ~loc ~attrs (sub.extension sub x) let map_structure sub {pcstr_self; pcstr_fields} = { pcstr_self = sub.pat sub pcstr_self; pcstr_fields = List.map (sub.class_field sub) pcstr_fields; } let class_infos sub f {pci_virt; pci_params = pl; pci_name; pci_expr; pci_loc; pci_attributes} = Ci.mk ~virt:pci_virt ~params:(List.map (map_fst (sub.typ sub)) pl) (map_loc sub pci_name) (f pci_expr) ~loc:(sub.location sub pci_loc) ~attrs:(sub.attributes sub pci_attributes) end (* Now, a generic AST mapper, to be extended to cover all kinds and cases of the OCaml grammar. The default behavior of the mapper is the identity. *) let default_mapper = { structure = (fun this l -> List.map (this.structure_item this) l); structure_item = M.map_structure_item; module_expr = M.map; signature = (fun this l -> List.map (this.signature_item this) l); signature_item = MT.map_signature_item; module_type = MT.map; with_constraint = MT.map_with_constraint; class_declaration = (fun this -> CE.class_infos this (this.class_expr this)); class_expr = CE.map; class_field = CE.map_field; class_structure = CE.map_structure; class_type = CT.map; class_type_field = CT.map_field; class_signature = CT.map_signature; class_type_declaration = (fun this -> CE.class_infos this (this.class_type this)); class_description = (fun this -> CE.class_infos this (this.class_type this)); type_declaration = T.map_type_declaration; type_kind = T.map_type_kind; typ = T.map; type_extension = T.map_type_extension; extension_constructor = T.map_extension_constructor; value_description = (fun this {pval_name; pval_type; pval_prim; pval_loc; pval_attributes} -> Val.mk (map_loc this pval_name) (this.typ this pval_type) ~attrs:(this.attributes this pval_attributes) ~loc:(this.location this pval_loc) ~prim:pval_prim ); pat = P.map; expr = E.map; module_declaration = (fun this {pmd_name; pmd_type; pmd_attributes; pmd_loc} -> Md.mk (map_loc this pmd_name) (this.module_type this pmd_type) ~attrs:(this.attributes this pmd_attributes) ~loc:(this.location this pmd_loc) ); module_type_declaration = (fun this {pmtd_name; pmtd_type; pmtd_attributes; pmtd_loc} -> Mtd.mk (map_loc this pmtd_name) ?typ:(map_opt (this.module_type this) pmtd_type) ~attrs:(this.attributes this pmtd_attributes) ~loc:(this.location this pmtd_loc) ); module_binding = (fun this {pmb_name; pmb_expr; pmb_attributes; pmb_loc} -> Mb.mk (map_loc this pmb_name) (this.module_expr this pmb_expr) ~attrs:(this.attributes this pmb_attributes) ~loc:(this.location this pmb_loc) ); open_description = (fun this {popen_lid; popen_override; popen_attributes; popen_loc} -> Opn.mk (map_loc this popen_lid) ~override:popen_override ~loc:(this.location this popen_loc) ~attrs:(this.attributes this popen_attributes) ); include_description = (fun this {pincl_mod; pincl_attributes; pincl_loc} -> Incl.mk (this.module_type this pincl_mod) ~loc:(this.location this pincl_loc) ~attrs:(this.attributes this pincl_attributes) ); include_declaration = (fun this {pincl_mod; pincl_attributes; pincl_loc} -> Incl.mk (this.module_expr this pincl_mod) ~loc:(this.location this pincl_loc) ~attrs:(this.attributes this pincl_attributes) ); value_binding = (fun this {pvb_pat; pvb_expr; pvb_attributes; pvb_loc} -> Vb.mk (this.pat this pvb_pat) (this.expr this pvb_expr) ~loc:(this.location this pvb_loc) ~attrs:(this.attributes this pvb_attributes) ); constructor_declaration = (fun this {pcd_name; pcd_args; pcd_res; pcd_loc; pcd_attributes} -> Type.constructor (map_loc this pcd_name) ~args:(T.map_constructor_arguments this pcd_args) ?res:(map_opt (this.typ this) pcd_res) ~loc:(this.location this pcd_loc) ~attrs:(this.attributes this pcd_attributes) ); label_declaration = (fun this {pld_name; pld_type; pld_loc; pld_mutable; pld_attributes} -> Type.field (map_loc this pld_name) (this.typ this pld_type) ~mut:pld_mutable ~loc:(this.location this pld_loc) ~attrs:(this.attributes this pld_attributes) ); cases = (fun this l -> List.map (this.case this) l); case = (fun this {pc_lhs; pc_guard; pc_rhs} -> { pc_lhs = this.pat this pc_lhs; pc_guard = map_opt (this.expr this) pc_guard; pc_rhs = this.expr this pc_rhs; } ); location = (fun this l -> l); extension = (fun this (s, e) -> (map_loc this s, this.payload this e)); attribute = (fun this (s, e) -> (map_loc this s, this.payload this e)); attributes = (fun this l -> List.map (this.attribute this) l); payload = (fun this -> function | PStr x -> PStr (this.structure this x) | PSig x -> PSig (this.signature this x) | PTyp x -> PTyp (this.typ this x) | PPat (x, g) -> PPat (this.pat this x, map_opt (this.expr this) g) ); } let rec extension_of_error {loc; msg; if_highlight; sub} = { loc; txt = "ocaml.error" }, PStr ([Str.eval (Exp.constant (Pconst_string (msg, None))); Str.eval (Exp.constant (Pconst_string (if_highlight, None)))] @ (List.map (fun ext -> Str.extension (extension_of_error ext)) sub)) let attribute_of_warning loc s = { loc; txt = "ocaml.ppwarning" }, PStr ([Str.eval ~loc (Exp.constant (Pconst_string (s, None)))]) module StringMap = Map.Make(struct type t = string let compare = compare end) let cookies = ref StringMap.empty let get_cookie k = try Some (StringMap.find k !cookies) with Not_found -> None let set_cookie k v = cookies := StringMap.add k v !cookies let tool_name_ref = ref "_none_" let tool_name () = !tool_name_ref module PpxContext = struct open Longident open Asttypes open Ast_helper let lid name = { txt = Lident name; loc = Location.none } let make_string x = Exp.constant (Pconst_string (x, None)) let make_bool x = if x then Exp.construct (lid "true") None else Exp.construct (lid "false") None let rec make_list f lst = match lst with | x :: rest -> Exp.construct (lid "::") (Some (Exp.tuple [f x; make_list f rest])) | [] -> Exp.construct (lid "[]") None let make_pair f1 f2 (x1, x2) = Exp.tuple [f1 x1; f2 x2] let make_option f opt = match opt with | Some x -> Exp.construct (lid "Some") (Some (f x)) | None -> Exp.construct (lid "None") None let get_cookies () = lid "cookies", make_list (make_pair make_string (fun x -> x)) (StringMap.bindings !cookies) let mk fields = { txt = "ocaml.ppx.context"; loc = Location.none }, Parsetree.PStr [Str.eval (Exp.record fields None)] let make ~tool_name () = let fields = [ lid "tool_name", make_string tool_name; lid "include_dirs", make_list make_string !Clflags.include_dirs; lid "load_path", make_list make_string !Config.load_path; lid "open_modules", make_list make_string !Clflags.open_modules; lid "for_package", make_option make_string !Clflags.for_package; lid "debug", make_bool !Clflags.debug; get_cookies () ] in mk fields let get_fields = function | PStr [{pstr_desc = Pstr_eval ({ pexp_desc = Pexp_record (fields, None) }, [])}] -> fields | _ -> raise_errorf "Internal error: invalid [@@@ocaml.ppx.context] syntax" let restore fields = let field name payload = let rec get_string = function | { pexp_desc = Pexp_constant (Pconst_string (str, None)) } -> str | _ -> raise_errorf "Internal error: invalid [@@@ocaml.ppx.context \ { %s }] string syntax" name and get_bool pexp = match pexp with | {pexp_desc = Pexp_construct ({txt = Longident.Lident "true"}, None)} -> true | {pexp_desc = Pexp_construct ({txt = Longident.Lident "false"}, None)} -> false | _ -> raise_errorf "Internal error: invalid [@@@ocaml.ppx.context \ { %s }] bool syntax" name and get_list elem = function | {pexp_desc = Pexp_construct ({txt = Longident.Lident "::"}, Some {pexp_desc = Pexp_tuple [exp; rest]}) } -> elem exp :: get_list elem rest | {pexp_desc = Pexp_construct ({txt = Longident.Lident "[]"}, None)} -> [] | _ -> raise_errorf "Internal error: invalid [@@@ocaml.ppx.context \ { %s }] list syntax" name and get_pair f1 f2 = function | {pexp_desc = Pexp_tuple [e1; e2]} -> (f1 e1, f2 e2) | _ -> raise_errorf "Internal error: invalid [@@@ocaml.ppx.context \ { %s }] pair syntax" name and get_option elem = function | { pexp_desc = Pexp_construct ({ txt = Longident.Lident "Some" }, Some exp) } -> Some (elem exp) | { pexp_desc = Pexp_construct ({ txt = Longident.Lident "None" }, None) } -> None | _ -> raise_errorf "Internal error: invalid [@@@ocaml.ppx.context \ { %s }] option syntax" name in match name with | "tool_name" -> tool_name_ref := get_string payload | "include_dirs" -> Clflags.include_dirs := get_list get_string payload | "load_path" -> Config.load_path := get_list get_string payload | "open_modules" -> Clflags.open_modules := get_list get_string payload | "for_package" -> Clflags.for_package := get_option get_string payload | "debug" -> Clflags.debug := get_bool payload | "cookies" -> let l = get_list (get_pair get_string (fun x -> x)) payload in cookies := List.fold_left (fun s (k, v) -> StringMap.add k v s) StringMap.empty l | _ -> () in List.iter (function ({txt=Lident name}, x) -> field name x | _ -> ()) fields let update_cookies fields = let fields = List.filter (function ({txt=Lident "cookies"}, _) -> false | _ -> true) fields in fields @ [get_cookies ()] end let ppx_context = PpxContext.make let ext_of_exn exn = match error_of_exn exn with | Some error -> extension_of_error error | None -> raise exn let apply_lazy ~source ~target mapper = let implem ast = let fields, ast = match ast with | {pstr_desc = Pstr_attribute ({txt = "ocaml.ppx.context"}, x)} :: l -> PpxContext.get_fields x, l | _ -> [], ast in PpxContext.restore fields; let ast = try let mapper = mapper () in mapper.structure mapper ast with exn -> [{pstr_desc = Pstr_extension (ext_of_exn exn, []); pstr_loc = Location.none}] in let fields = PpxContext.update_cookies fields in Str.attribute (PpxContext.mk fields) :: ast in let iface ast = let fields, ast = match ast with | {psig_desc = Psig_attribute ({txt = "ocaml.ppx.context"}, x)} :: l -> PpxContext.get_fields x, l | _ -> [], ast in PpxContext.restore fields; let ast = try let mapper = mapper () in mapper.signature mapper ast with exn -> [{psig_desc = Psig_extension (ext_of_exn exn, []); psig_loc = Location.none}] in let fields = PpxContext.update_cookies fields in Sig.attribute (PpxContext.mk fields) :: ast in let ic = open_in_bin source in let magic = really_input_string ic (String.length Config.ast_impl_magic_number) in let rewrite transform = Location.input_name := input_value ic; let ast = input_value ic in close_in ic; let ast = transform ast in let oc = open_out_bin target in output_string oc magic; output_value oc !Location.input_name; output_value oc ast; close_out oc and fail () = close_in ic; failwith "Ast_mapper: OCaml version mismatch or malformed input"; in if magic = Config.ast_impl_magic_number then rewrite (implem : structure -> structure) else if magic = Config.ast_intf_magic_number then rewrite (iface : signature -> signature) else fail () let drop_ppx_context_str ~restore = function | {pstr_desc = Pstr_attribute({Location.txt = "ocaml.ppx.context"}, a)} :: items -> if restore then PpxContext.restore (PpxContext.get_fields a); items | items -> items let drop_ppx_context_sig ~restore = function | {psig_desc = Psig_attribute({Location.txt = "ocaml.ppx.context"}, a)} :: items -> if restore then PpxContext.restore (PpxContext.get_fields a); items | items -> items let add_ppx_context_str ~tool_name ast = Ast_helper.Str.attribute (ppx_context ~tool_name ()) :: ast let add_ppx_context_sig ~tool_name ast = Ast_helper.Sig.attribute (ppx_context ~tool_name ()) :: ast let apply ~source ~target mapper = apply_lazy ~source ~target (fun () -> mapper) let run_main mapper = try let a = Sys.argv in let n = Array.length a in if n > 2 then let mapper () = try mapper (Array.to_list (Array.sub a 1 (n - 3))) with exn -> (* PR #6463 *) let f _ _ = raise exn in {default_mapper with structure = f; signature = f} in apply_lazy ~source:a.(n - 2) ~target:a.(n - 1) mapper else begin Printf.eprintf "Usage: %s [extra_args] \n%!" Sys.executable_name; exit 2 end with exn -> prerr_endline (Printexc.to_string exn); exit 2 let register_function = ref (fun _name f -> run_main f) let register name f = !register_function name f