Source file coq.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
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
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
(** Translate the parser-level AST to Coq.
There are two modes:
- raw_coq mode (option -o raw_coq): translation of the AST as it is
(lambdapi-calculus is a subset system of coq if we ignore rules)
- stt_coq mode (option -o stt_coq): translation of the AST as an encoding in
simple type theory.
The encoding can be specified through a lambdapi file (option --encoding).
In both modes, a renaming map can be provided to rename some identifiers.
The renaming map can be specified through a lambdapi file (option --renaming).
*)
open Lplib open Extra
open Common open Pos open Error
open Parsing open Syntax
open Core
let log = Logger.make 'x' "xprt" "export"
let log = log.pp
(** Symbols necessary to encode STT. *)
type builtin =
Set | Prop | Arr | El | Imp | All | Prf | Eq | Or | And | Ex | Not
let index_of_builtin = function
| Set -> 0 | Prop -> 1 | Arr -> 2 | El -> 3 | Imp -> 4 | All -> 5
| Prf -> 6 | Eq -> 7 | Or -> 8 | And -> 9 | Ex -> 10 | Not -> 11
let nb_builtins = 12
let builtin_of_index = function
| 0 -> Set | 1 -> Prop | 2 -> Arr | 3 -> El | 4 -> Imp | 5 -> All
| 6 -> Prf | 7 -> Eq | 8 -> Or | 9 -> And | 10 -> Ex | 11 -> Not
| _ -> assert false
let _ =
for i = 0 to nb_builtins - 1 do
assert (index_of_builtin (builtin_of_index i) = i)
done
let index_of_name = function
| "Set" -> Some 0 | "prop" -> Some 1 | "arr" -> Some 2 | "El" -> Some 3
| "imp" -> Some 4 | "all" -> Some 5 | "Prf" -> Some 6 | "eq" -> Some 7
| "or" -> Some 8 | "and" -> Some 9 | "ex" -> Some 10 | "not" -> Some 11
| _ -> None
let name_of_index = function
| 0 -> "Set" | 1 -> "prop" | 2 -> "arr" | 3 -> "El" | 4 -> "imp"| 5 -> "all"
| 6 -> "Prf" | 7 -> "eq" | 8 -> "or" | 9 -> "and" | 10 -> "ex" | 11 -> "not"
| _ -> assert false
let _ =
for i = 0 to nb_builtins - 1 do
assert (index_of_name (name_of_index i) = Some i)
done
let builtin : Term.qident array =
let path = ["STTfa"] in
Array.init nb_builtins (fun i -> path, name_of_index i)
let sym b = builtin.(index_of_builtin b)
(** Set renaming map from file. *)
let rmap = ref StrMap.empty
let set_renaming : string -> unit = fun f ->
let consume = function
| {elt=P_builtin(coq_id,{elt=([],lp_id);_});_} ->
if Logger.log_enabled() then log "rename %s into %s" lp_id coq_id;
rmap := StrMap.add lp_id coq_id !rmap
| {pos;_} -> fatal pos "Invalid command."
in
Stream.iter consume (Parser.parse_file f)
(** Set symbols whose declarations have to be erased. *)
let erase = ref StrSet.empty
module Qid = struct type t = Term.qident let compare = Stdlib.compare end
module QidMap = Map.Make(Qid)
let map_erased_qid_coq = ref QidMap.empty
let set_mapping : string -> unit = fun f ->
let consume = function
| {elt=P_builtin(coq_id,lp_qid);_} ->
if Logger.log_enabled() then
log "rename %a into %s" Pretty.qident lp_qid coq_id;
let id = snd lp_qid.elt in
if Logger.log_enabled() then log "erase %s" id;
erase := StrSet.add id !erase;
map_erased_qid_coq :=
QidMap.add lp_qid.elt coq_id !map_erased_qid_coq;
if fst lp_qid.elt = [] && id <> coq_id then
rmap := StrMap.add id coq_id !rmap
| {pos;_} -> fatal pos "Invalid command."
in
Stream.iter consume (Parser.parse_file f)
(** Set encoding. *)
let map_qid_builtin = ref QidMap.empty
let set_encoding : string -> unit = fun f ->
let found = Array.make nb_builtins false in
let consume = function
| {elt=P_builtin(n,lp_qid);pos} ->
begin match index_of_name n with
| Some i ->
if Logger.log_enabled() then
log "builtin \"%s\" = %a" n Pretty.qident lp_qid;
builtin.(i) <- lp_qid.elt;
found.(i) <- true;
let b = builtin_of_index i in
map_qid_builtin := QidMap.add lp_qid.elt b !map_qid_builtin;
if b = El || b = Prf then
(if Logger.log_enabled() then log "erase %s" (snd lp_qid.elt);
erase := StrSet.add (snd lp_qid.elt) !erase)
| None -> fatal pos "Unknown builtin."
end
| {pos;_} -> fatal pos "Invalid command."
in
Stream.iter consume (Parser.parse_file f);
Array.iteri
(fun i b ->
if not b then
let pos =
Some {fname=Some f;start_line=0;start_col=0;end_line=0;end_col=0}
in fatal pos "Builtin %s undefined." (name_of_index i))
found
(** Basic printing functions. We use Printf for efficiency reasons. *)
let out = Printf.printf
let char = output_char
let string = output_string
let prefix pre elt oc x = string oc pre; elt oc x
let suffix elt suf oc x = elt oc x; string oc suf
let list elt sep oc xs =
match xs with
| [] -> ()
| x::xs -> elt oc x; List.iter (prefix sep elt oc) xs
(** Translation of identifiers. *)
let translate_ident : string -> string = fun s ->
try StrMap.find s !rmap with Not_found -> s
let raw_ident oc s = string oc (translate_ident s)
let ident oc {elt;_} = raw_ident oc elt
let param_id oc idopt =
match idopt with
| Some id -> ident oc id
| None -> char oc '_'
let param_ids = list param_id " "
let raw_path = list string "."
let path oc {elt;_} = raw_path oc elt
let qident oc {elt=(mp,s);_} =
match mp with
| [] -> raw_ident oc s
| _::_ -> raw_path oc mp; char oc '.'; raw_ident oc s
(** Translation of terms. *)
let stt = Stdlib.ref false
let use_implicits = Stdlib.ref false
let use_notations = Stdlib.ref false
let p_get_args : p_term -> p_term * p_term list = fun t ->
let rec p_get_args t acc =
match t.elt with
| P_Appl(t, u) -> p_get_args t (u::acc)
| P_Wrap t -> p_get_args t acc
| _ -> t, acc
in p_get_args t []
let app t default cases =
let h, ts = p_get_args t in
if !stt then
match h.elt with
| P_Iden({elt;_},expl) ->
begin match QidMap.find_opt elt !map_qid_builtin with
| None -> default h ts
| Some builtin -> cases h ts expl builtin
end
| _ -> default h ts
else default h ts
let rec term oc t =
match t.elt with
| P_Meta _ -> wrn t.pos "TODO"; assert false
| P_Patt _ -> wrn t.pos "TODO"; assert false
| P_Expl _ -> wrn t.pos "TODO"; assert false
| P_Type -> string oc "Type"
| P_Wild -> char oc '_'
| P_NLit i ->
if !stt then
match QidMap.find_opt ([],i) !map_erased_qid_coq with
| Some s -> string oc s
| None -> raw_ident oc i
else raw_ident oc i
| P_Iden(qid,b) ->
if b then char oc '@';
if !stt then
match QidMap.find_opt qid.elt !map_erased_qid_coq with
| Some s -> string oc s
| None -> qident oc qid
else qident oc qid
| P_Arro(u,v) -> arrow oc u v
| P_Abst(xs,u) -> abst oc xs u
| P_Prod(xs,u) -> prod oc xs u
| P_LLet(x,xs,a,u,v) ->
string oc "let "; ident oc x; params_list oc xs; typopt oc a;
string oc " := "; term oc u; string oc " in "; term oc v
| P_Wrap u -> term oc u
| P_Appl _ ->
let default h ts = paren oc h; char oc ' '; list paren " " oc ts in
app t default
(fun h ts expl builtin ->
match !use_notations, !use_implicits && not expl, builtin, ts with
| _, _, (El|Prf), [u] -> term oc u
| _, _, (Arr|Imp), [u;v] -> arrow oc u v
| _, _, All, [_;{elt=P_Wrap({elt=P_Abst([_] as xs,u);_});_}]
| _, true, All, [{elt=P_Wrap({elt=P_Abst([_] as xs,u);_});_}]
-> prod oc xs u
| _, _, Ex, [_;{elt=P_Wrap({elt=P_Abst([x],u);_});_}]
| _, true, Ex, [{elt=P_Wrap({elt=P_Abst([x],u);_});_}] ->
string oc "exists "; raw_params oc x; string oc ", "; term oc u
| true, _, Eq, [_;u;v]
| true, true, Eq, [u;v] -> paren oc u; string oc " = "; paren oc v
| true, _, Or, [u;v] -> paren oc u; string oc " \\/ "; paren oc v
| true, _, And, [u;v] -> paren oc u; string oc " /\\ "; paren oc v
| true, _, Not, [u] -> string oc "~ "; paren oc u
| _ -> default h ts)
and arrow oc u v = paren oc u; string oc " -> "; term oc v
and abst oc xs u =
string oc "fun"; params_list_in_abs oc xs; string oc " => "; term oc u
and prod oc xs u =
string oc "forall"; params_list_in_abs oc xs; string oc ", "; term oc u
and paren oc t =
let default() = char oc '('; term oc t; char oc ')' in
match t.elt with
| P_Arro _ | P_Abst _ | P_Prod _ | P_LLet _ | P_Wrap _ -> default()
| P_Appl _ ->
app t (fun _ _ -> default())
(fun _ ts _ builtin ->
match builtin, ts with
| (El|Prf), [u] -> paren oc u
| _ -> default())
| _ -> term oc t
and raw_params oc (ids,t,_) = param_ids oc ids; typopt oc t
and params oc ((ids,t,b) as x) =
match b, t with
| true, _ -> char oc '{'; raw_params oc x; char oc '}'
| false, Some _ -> char oc '('; raw_params oc x; char oc ')'
| false, None -> param_ids oc ids
and params_list oc = List.iter (prefix " " params oc)
and params_list_in_abs oc l =
match l with
| [ids,t,false] -> char oc ' '; param_ids oc ids; typopt oc t
| _ -> params_list oc l
and typopt oc t = Option.iter (prefix " : " term oc) t
(** Translation of commands. *)
let is_lem x = is_opaq x || is_priv x
let command oc {elt; pos} =
begin match elt with
| P_open ps -> string oc "Import "; list path " " oc ps; string oc ".\n"
| P_require (true, ps) ->
string oc "Require Import "; list path " " oc ps; string oc ".\n"
| P_require (false, ps) ->
string oc "Require "; list path " " oc ps; string oc ".\n"
| P_require_as (p,i) ->
string oc "Module "; ident oc i; string oc " := "; path oc p;
string oc ".\n"
| P_symbol
{ p_sym_mod; p_sym_nam; p_sym_arg; p_sym_typ;
p_sym_trm; p_sym_prf=_; p_sym_def } ->
if not (StrSet.mem p_sym_nam.elt !erase) then
let p_sym_arg =
if !stt then
let pos = None in
let _Set = {elt=P_Iden({elt=sym Set;pos},false);pos} in
List.map (function ids, None, b -> ids, Some _Set, b | x -> x)
p_sym_arg
else p_sym_arg
in
begin match p_sym_def, p_sym_trm, p_sym_arg, p_sym_typ with
| true, Some t, _, Some a when List.exists is_lem p_sym_mod ->
string oc "Lemma "; ident oc p_sym_nam; params_list oc p_sym_arg;
string oc " : "; term oc a; string oc ".\nProof. exact (";
term oc t; string oc "). Qed.\n"
| true, Some t, _, _ ->
string oc "Definition "; ident oc p_sym_nam;
params_list oc p_sym_arg; typopt oc p_sym_typ;
string oc " := "; term oc t;
if List.exists is_opaq p_sym_mod then
(string oc ".\nOpaque "; ident oc p_sym_nam);
string oc ".\n"
| false, _, [], Some t ->
string oc "Axiom "; ident oc p_sym_nam; string oc " : ";
term oc t; string oc ".\n"
| false, _, _, Some t ->
string oc "Axiom "; ident oc p_sym_nam; string oc " : forall";
params_list oc p_sym_arg; string oc ", "; term oc t;
string oc ".\n"
| _ -> wrn pos "Command not translated."
end
| _ -> wrn pos "Command not translated."
end
let ast oc = Stream.iter (command oc)
(** Set Coq required file. *)
let require = ref None
let set_requiring : string -> unit = fun f -> require := Some f
let print : ast -> unit = fun s ->
let oc = stdout in
begin match !require with
| Some f -> string oc ("Require Import "^f^".\n")
| None -> ()
end;
ast oc s