-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathfrontend.ml
More file actions
332 lines (293 loc) · 12.9 KB
/
Copy pathfrontend.ml
File metadata and controls
332 lines (293 loc) · 12.9 KB
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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
open Utility
open Transform
let _show s program =
Debug.print (s ^ ": " ^ Sugartypes.show_program program);
program
let _show_sentence s sentence =
Debug.print (s ^ ": " ^ Sugartypes.show_sentence sentence);
sentence
(* Shall we re-run the frontend type-checker after each TransformSugar transformation? *)
let check_frontend_transformations =
Settings.(flag "recheck_frontend_transformations"
|> synopsis "Toggles whether to re-run the type checker after each (front end) transformation pass"
|> convert parse_bool
|> sync)
let check_frontend_transformations_dump =
Settings.(flag "recheck_frontend_transformations_dump"
|> synopsis "Toggles whether to dump the AST after each transformation pass"
|> convert parse_bool
|> sync)
let check_frontend_transformations_filter =
Settings.(option ~default:(Some "all") "recheck_frontend_transformations_filter"
|> convert Utility.some
|> sync)
(* Print Sugar AST before frontend processing? *)
let show_pre_frontend_ast
= Settings.(flag "show_pre_frontend_ast"
|> synopsis "Dumps the undecorated front-end AST"
|> convert parse_bool
|> sync)
(* Print Sugar AST after frontend processing? *)
let show_post_frontend_ast
= Settings.(flag "show_post_frontend_ast"
|> synopsis "Dumps the decorated front-end AST"
|> convert parse_bool
|> sync)
let verify_transformation transformer =
Settings.get check_frontend_transformations &&
match Settings.get check_frontend_transformations_filter with
| None | Some "" | Some "none" -> false
| Some "all" -> true
| Some filter -> String.split_on_char '\n' filter |> List.mem transformer
let trace_type_error transform_name print program program' stacktrace exn =
Debug.f "error: transformation pass '%s' produced an ill-typed program.\n%!" transform_name;
let print x =
let buffer = Buffer.create 1024 in
let formatter = Format.formatter_of_buffer buffer in
Format.pp_set_margin formatter 120;
print formatter x;
Format.pp_print_flush formatter ();
Buffer.contents buffer
in
Debug.if_set check_frontend_transformations_dump
(fun () ->
Printf.sprintf "Program before transformation:\n%s\n\nProgram after transformation:\n%s\n\n"
(print program) (print program'));
Printexc.raise_with_backtrace exn stacktrace
(* Pipeline infrastructure. *)
type 'a result =
{ program: 'a;
datatype: Types.datatype;
context: Context.t }
(* The [Untyped] module contains the infrastructure for untyped
transform passes. *)
module Untyped = struct
type transformer = (module Untyped.S)
(* This functor constructs a conditional transformer from a given
transformer and a (side-effecting) condition. *)
module Conditional(T : sig
include Untyped.S
val condition : unit -> bool
end) = struct
module Untyped = struct
let name = Printf.sprintf "Conditional(%s)" T.Untyped.name
let program state program =
if T.condition ()
then T.Untyped.program state program
else Identity.Untyped.program state program
let sentence state sentence =
if T.condition ()
then T.Untyped.sentence state sentence
else Identity.Untyped.sentence state sentence
end
end
let only_if : bool Settings.setting -> (module Untyped.S) -> transformer
= fun setting (module T) ->
(module Conditional(struct include T let condition () = Settings.get setting end))
module Collect_FFI_Files : Untyped.S = struct
open Untyped
module Untyped = struct
let name = "collect_ffi_files"
let program state program =
let ffi_files' = ModuleUtils.get_ffi_files program in
let context' = context state in
let context'' = Context.({ context' with ffi_files = ffi_files' }) in
return context'' program
let sentence state sentence =
return state sentence (* TODO FIXME bug. A sentence can contain an alien declaration. *)
end
end
(* Collection of transformers. *)
let transformers : transformer array
= [| (module CheckXmlQuasiquotes)
; (module DesugarSwitchFuns)
; (module DesugarModules)
; (module Shunting)
; (module Collect_FFI_Files)
; only_if Basicsettings.Sessions.exceptions_enabled (module DesugarSessionExceptions)
; (module DesugarLAttributes)
; (module LiftRecursive)
; (module DesugarTypeVariables)
; (module DesugarEffects)
; (module DesugarDatatypes)
|]
let run : Context.t -> ((module Untyped.S) -> Context.t -> 'a -> 'a Transform.Untyped.result) -> 'a -> 'a result
= fun context' select program ->
let module TU = Transform.Untyped in
let apply : 'a TU.result -> transformer -> 'a TU.result
= fun (TU.Result { program; state }) (module T) ->
Debug.if_set Basicsettings.show_stages (fun () -> T.Untyped.name ^"...");
select (module T) state program
in
let (TU.Result { state; program }) =
Array.fold_left apply TU.(return context' program) transformers
in
{ context = state; program;
datatype = Types.Not_typed (* Slight abuse! *) }
let run_sentence : Context.t ->
Sugartypes.sentence ->
Sugartypes.sentence result
= fun context sentence ->
run context (fun (module T) -> T.Untyped.sentence) sentence
let run_program : Context.t ->
Sugartypes.program ->
Sugartypes.program result
= fun context program ->
run context (fun (module T) -> T.Untyped.program) program
end
(* The [Typeability_preserving] module runs transformation passes
which _must_ preserve typeability, i.e. the preimage and image of
the transform must be typeable. *)
module Typeability_preserving = struct
type transformer = (module Typeable.S)
(* This functor constructs a conditional transformer from a given
transformer and a (side-effecting) condition. *)
module Conditional(T : sig
include Typeable.S
val condition : unit -> bool
end) = struct
module Typeable = struct
let name = Printf.sprintf "Conditional(%s)" T.Typeable.name
let program state program =
if T.condition ()
then T.Typeable.program state program
else Identity.Typeable.program state program
let sentence state sentence =
if T.condition ()
then T.Typeable.sentence state sentence
else Identity.Typeable.sentence state sentence
end
end
let only_if : bool Settings.setting -> (module Typeable.S) -> transformer
= fun setting (module T) ->
(module Conditional(struct
include T
let condition () = Settings.get setting end))
(* Collection of transformers. *)
let transformers : transformer array
= [| (module DesugarInners) (* this must always be done first after type inference *)
; (module DesugarCP)
; only_if
Basicsettings.Sessions.exceptions_enabled
(module DesugarSessionExceptions)
; (module DesugarProcesses)
; (module DesugarFors)
; (module DesugarRegexes)
; (module DesugarFormlets)
; (module DesugarPages)
; (module DesugarFuns) |]
(* Run program transformers. *)
let run_program : Context.t ->
Types.datatype ->
Sugartypes.program ->
Sugartypes.program result
= fun context' datatype program ->
let apply : Sugartypes.program Transform.Typeable.result -> transformer -> Sugartypes.program Transform.Typeable.result
= fun (Transform.Typeable.Result { state; program }) (module T) ->
let (Transform.Typeable.Result payload) as result =
Debug.if_set Basicsettings.show_stages (fun () -> T.Typeable.name ^"...");
T.Typeable.program state program
in
if verify_transformation T.Typeable.name then
let tyenv =
Context.typing_environment payload.state.Transform.Typeable.context
in
(* TODO(dhil): Ultimately we may want to move from
typeability preserving transformations to type-preserving
transformations in which case the type checker should
check *against* the datatype in transformation state
[payload]. *)
try
let (program, datatype, tyenv') =
TypeSugar.Check.program
{ tyenv with Types.desugared = true }
payload.program in
(* TODO(dhil): Verify post-transformation invariants. *)
let context = { payload.state.Transform.Typeable.context with
Context.typing_environment = Types.extend_typing_environment tyenv tyenv' } in
let state = { Typeable.datatype = datatype
; Typeable.context = context }
in (Transform.Typeable.Result { state; program })
with exn ->
let stacktrace = Printexc.get_raw_backtrace () in
trace_type_error T.Typeable.name Sugartypes.pp_program program payload.program stacktrace exn
else result
in
let (Transform.Typeable.Result { state; program }) =
let initial_state = Transform.Typeable.{ datatype; context = context' } in
Array.fold_left apply Transform.Typeable.(return initial_state program) transformers
in
{ datatype = state.Transform.Typeable.datatype;
context = state.Transform.Typeable.context;
program }
(* Run sentence transformers. *)
let run_sentence : Context.t ->
Types.datatype ->
Sugartypes.sentence ->
Sugartypes.sentence result
= fun context' datatype program ->
let apply : Sugartypes.sentence Transform.Typeable.result -> transformer -> Sugartypes.sentence Transform.Typeable.result
= fun (Transform.Typeable.Result { state; program }) (module T) ->
let (Transform.Typeable.Result payload) as result =
T.Typeable.sentence state program
in
if verify_transformation T.Typeable.name then
let tyenv =
Context.typing_environment payload.state.Transform.Typeable.context
in
(* TODO(dhil): Ultimately we may want to move from
typeability preserving transformations to type-preserving
transformations in which case the type checker should
check *against* the datatype in transformation state
[payload]. *)
try
let (program, datatype, tyenv') =
TypeSugar.Check.sentence
{ tyenv with Types.desugared = true }
payload.program in
(* TODO(dhil): Verify post-transformation invariants. *)
let context = { payload.state.Transform.Typeable.context with
Context.typing_environment = Types.extend_typing_environment tyenv tyenv' } in
let state = { Typeable.datatype = datatype
; Typeable.context = context }
in (Transform.Typeable.Result { state; program })
with exn ->
let stacktrace = Printexc.get_raw_backtrace () in
trace_type_error T.Typeable.name Sugartypes.pp_sentence program payload.program stacktrace exn
else result
in
let (Transform.Typeable.Result { state; program }) =
let initial_state = Transform.Typeable.{ datatype; context = context' } in
Array.fold_left apply Transform.Typeable.(return initial_state program) transformers
in
{ datatype = state.Transform.Typeable.datatype;
context = state.Transform.Typeable.context;
program }
end
(* Parametric transformation runner. *)
let transform show untyped_run typeable_run typechecker_run context program =
(* Dump the undecorated AST. *)
Debug.if_set show_pre_frontend_ast
(fun () -> Printf.sprintf "AST before frontend transformations:\n%s\n\n" (show program));
(* Untyped transformations. *)
let { program; context; _ } =
untyped_run context program
in
(* Typechecking. *)
let (program, datatype, tenv) =
typechecker_run Context.(typing_environment context) program
in
(* Typeability preserving transformations. *)
let result =
let result = typeable_run context datatype program in
let tenv' = Context.(typing_environment result.context) in
{ result with context = Context.{ context with typing_environment = Types.extend_typing_environment tenv' tenv } }
in
(* Dump the decorated AST. *)
Debug.if_set show_post_frontend_ast
(fun () -> Printf.sprintf "AST after frontend transformations:\n%s\n\n" (show result.program)) ;
result
let program =
transform Sugartypes.show_program Untyped.run_program Typeability_preserving.run_program TypeSugar.Check.program
let interactive =
transform Sugartypes.show_sentence Untyped.run_sentence Typeability_preserving.run_sentence TypeSugar.Check.sentence