package mdx

  1. Overview
  2. Docs

Source file compat_top.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
let lookup_type typ env =
#if OCAML_VERSION >= (4, 10, 0)
  Env.find_type_by_name typ env |> fst
#else
  Env.lookup_type typ env
#endif

let lookup_value v env =
#if OCAML_VERSION >= (4, 10, 0)
  Env.find_value_by_name v env
#else
  Env.lookup_value v env
#endif

let find_value env loc id =
#if OCAML_VERSION >= (4, 10, 0)
  Env.lookup_value ~loc id env
#else
  Typetexp.find_value env loc id
#endif

let find_type env loc id =
#if OCAML_VERSION >= (4, 10, 0)
  Env.lookup_type ~loc id env
#else
  Typetexp.find_type env loc id
#endif

let find_constructor env loc id =
#if OCAML_VERSION >= (4, 10, 0)
  Env.lookup_constructor ~loc Env.Positive id env
#else
  Typetexp.find_constructor env loc id
#endif

let find_module env loc id =
#if OCAML_VERSION >= (4, 10, 0)
  Env.lookup_module ~loc id env
#else
  Typetexp.find_module env loc id
#endif

let find_modtype env loc id =
#if OCAML_VERSION >= (4, 10, 0)
  Env.lookup_modtype ~loc id env
#else
  Typetexp.find_modtype env loc id
#endif

let find_class env loc id =
#if OCAML_VERSION >= (4, 10, 0)
  Env.lookup_class ~loc id env
#else
  Typetexp.find_class env loc id
#endif

let find_class_type env loc id =
#if OCAML_VERSION >= (4, 10, 0)
  Env.lookup_cltype ~loc id env
#else
  Typetexp.find_class_type env loc id
#endif

let type_structure env str loc =
#if OCAML_VERSION >= (4, 14, 0)
  let tstr, _, _, _, env =
#else
  let tstr, _, _, env =
#endif
#if OCAML_VERSION >= (4, 12, 0)
    let _ = loc in
    Typemod.type_structure env str
#else
    Typemod.type_structure env str loc
#endif
  in
  tstr, env

let extension_constructor
    ~ext_type_path
    ~ext_type_params
    ~ext_args
    ~ext_ret_type
    ~ext_private
    ~ext_loc
    ~ext_attributes
  =
  let open Types in
  let ext_args =
    Cstr_tuple ext_args
  in
  { ext_type_path
  ; ext_type_params
  ; ext_args
  ; ext_ret_type
  ; ext_private
  ; ext_loc
  ; ext_attributes
#if OCAML_VERSION >= (5, 3, 0)
  ; ext_uid = Uid.mk ~current_unit:None
#elif OCAML_VERSION >= (4, 11, 0)
  ; ext_uid = Uid.mk ~current_unit:"mdx"
#endif
  }

let match_env
    ~value
    ~empty
    ~open_
    ~functor_arg
    ~constraints
    ~copy_types
    ~module_
    ~persistent
    ~type_
    ~modtype
    ~cltype
    ~class_
    ~extension
    ~value_unbound
    ~module_unbound
    env =
  ignore (constraints, persistent, copy_types, value_unbound, module_unbound);
  match env with
  | Env.Env_value (summary, id, _) ->
    value summary id
  | Env_empty -> empty ()
  | Env_open (summary, pid) ->
    open_ summary pid
  | Env_functor_arg (summary, id) -> functor_arg summary id
  | Env_module (summary, id, presence, _) ->
    let present = match presence with
      | Mp_present -> true
      | Mp_absent -> false
    in
    module_ summary id ~present
  | Env_type (summary, _, _) -> type_ summary
  | Env_modtype (summary, _, _) -> modtype summary
  | Env_cltype (summary, _, _) -> cltype summary
  | Env_class (summary, id, _) -> class_ summary id
  | Env_extension (summary, id, _) -> extension summary id
  | Env_constraints (summary, _) -> constraints summary
#if OCAML_VERSION >= (4, 10, 0)
  | Env_copy_types summary -> copy_types summary
  | Env_value_unbound (summary, _, _) -> value_unbound summary
  | Env_module_unbound (summary, _, _) -> module_unbound summary
#else
  | Env_copy_types (summary, _) -> copy_types summary
#endif
  | Env_persistent (summary, _) -> persistent summary

let ctype_is_equal =
#if OCAML_VERSION >= (4, 13, 0)
  Ctype.is_equal
#else
  Ctype.equal
#endif

let ctype_expand_head_and_get_desc env ty =
#if OCAML_VERSION >= (4, 14, 0)
  Types.get_desc (Ctype.expand_head env ty)
#else
  (Ctype.expand_head env ty).Types.desc
#endif

let ctype_get_desc ty =
#if OCAML_VERSION >= (4, 14, 0)
  Types.get_desc ty
#else
  (Ctype.repr ty).Types.desc
#endif

exception Exit_with_status of int


let execute_phrase print_outcome ppf phr =
#if OCAML_VERSION >= (4, 12, 0)
  match Toploop.execute_phrase print_outcome ppf phr with
  | v -> v
  | exception Compenv.Exit_with_status status ->
      raise (Exit_with_status status)
#else
  Toploop.execute_phrase print_outcome ppf phr
#endif

#if OCAML_VERSION < (4, 14, 0)
let std_err = Format.err_formatter

let patch_directive name directive =
  let patched_name = Format.asprintf "mdx_%s" name in
  let directive_info = Toploop.{ section = "MDX PATCHED"; doc = "Patched by MDX" } in
  Toploop.add_directive patched_name directive directive_info;
  patched_name

(* port of Topdirs.action_on_suberror *)
let action_on_suberror b =
  if not b && not !Sys.interactive then
    raise (Exit_with_status 125)

let dir_use ppf name =
  action_on_suberror (Toploop.use_file ppf name)

let mdx_use = patch_directive "use" (Directive_string (dir_use std_err))

let mdx_install_printer = patch_directive "install_printer" (Directive_ident (Topdirs.dir_install_printer std_err))
let mdx_remove_printer = patch_directive "remove_printer" (Directive_ident (Topdirs.dir_remove_printer std_err))
#endif

#if OCAML_VERSION >= (4, 11, 0) && OCAML_VERSION < (4, 14, 0)

let dir_use_output ppf name =
  action_on_suberror (Toploop.use_output ppf name)

let mdx_use_output = patch_directive "use_output" (Directive_string (dir_use_output std_err))
#endif

#if OCAML_VERSION < (4, 13, 0)
let mdx_trace = patch_directive "trace" (Directive_ident (Topdirs.dir_trace std_err))
let mdx_untrace = patch_directive "untrace" (Directive_ident (Topdirs.dir_untrace std_err))
let mdx_untrace_all = patch_directive "untrace_all" (Directive_none (Topdirs.dir_untrace_all std_err))
#endif

#if OCAML_VERSION < (4, 13, 0)
(* [load] cannot be patched to return errors because the underlying code is not exposed:
   It would require [Topdirs.load_file] with the first argument to be [false] but the exposed
   version hardcodes it to [true].
  *)
let mdx_load = patch_directive "load" (Directive_string (Topdirs.dir_load std_err))

(* On the other hand, [load_rec] can be patched because the curried [true] is the only
   difference between these directives *)
let dir_load_rec ppf name =
  action_on_suberror (Topdirs.load_file ppf name)

let mdx_load_rec = patch_directive "load_rec" (Directive_string (dir_load_rec std_err))

#elif OCAML_VERSION >= (4, 13, 0) && OCAML_VERSION < (4, 14, 0)
(* OCaml 4.13 exposes [Topeval.load_file] which allows us to patch [#load] too *)
let dir_load ppf name =
  action_on_suberror (Topeval.load_file false ppf name)

let mdx_load = patch_directive "load" (Directive_string (dir_load std_err))

(* This uses [Topeval.load_file] because [Topdirs.load_file] is deprecated on 4.13 *)
let dir_load_rec ppf name =
  action_on_suberror (Topeval.load_file true ppf name)

let mdx_load_rec = patch_directive "load_rec" (Directive_string (dir_load_rec std_err))
#endif

let redirect_directive directive =
  match directive with
#if OCAML_VERSION < (4, 14, 0)
  | "load" -> mdx_load
  | "load_rec" -> mdx_load_rec
  | "use" -> mdx_use
  | "install_printer" -> mdx_install_printer
  | "remove_printer" -> mdx_remove_printer
#endif
#if OCAML_VERSION >= (4, 11, 0) && OCAML_VERSION < (4, 14, 0)
  | "use_output" -> mdx_use_output
#endif
#if OCAML_VERSION < (4, 13, 0)
  | "trace" -> mdx_trace
  | "untrace" -> mdx_untrace
  | "untrace_all" -> mdx_untrace_all
#endif
  | v -> v

let rec get_id_in_path = function
  | Path.Pident id -> id
  | Path.Pdot (p, _) -> get_id_in_path p
  | Path.Papply (_, p) -> get_id_in_path p
#if OCAML_VERSION >= (5, 1, 0)
  | Path.Pextra_ty (p, _) -> get_id_in_path p
#endif

let get_id_opt = function
  | Path.Pident id -> Some id
  | Path.Pdot _ -> None
  | Path.Papply _ -> None
#if OCAML_VERSION >= (5, 1, 0)
  | Path.Pextra_ty _ -> None
#endif

let mk_fun loc exp =
  let punit =
    Ast_helper.Pat.construct (Location.mkloc (Longident.Lident "()") loc) None
  in
  let label = Asttypes.Nolabel in
  let default = None in
#if OCAML_VERSION >= (5, 2, 0)
  let param =
    { Parsetree.pparam_loc= loc
    ; pparam_desc= Pparam_val (label, default, punit) }
  in
  Ast_helper.Exp.function_ [param] None (Pfunction_body exp)
#else
  Ast_helper.Exp.fun_ label default punit exp
#endif
OCaml

Innovation. Community. Security.