Source file util_tptp.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
(** {1 Utils related to TPTP parsing} *)
open Logtk
module T = TypedSTerm
module PT = STerm
module F = T.Form
module A = Ast_tptp
module Loc = ParseLocation
module Err = CCResult
type 'a or_error = ('a, string) CCResult.t
type typed = T.t
type untyped = PT.t
exception Error of string
let error msg = raise (Error msg)
let errorf msg = CCFormat.ksprintf msg ~f:error
let section = Util.Section.root
let stat_def_as_rw = Util.mk_stat "tptp.def_as_rewrite"
(** {2 Printing/Parsing} *)
type parse_cache = (string, unit) Hashtbl.t
let create_parse_cache () = Hashtbl.create 16
let find_file name dir =
let rec search dir =
let cur_name = Filename.concat dir name in
Util.debugf 2 ~section "search `%s`@ as `%s`" (fun k->k name cur_name);
if Sys.file_exists cur_name
then Some cur_name
else
let dir' = Filename.dirname dir in
if dir = dir'
then None
else search dir'
in
let search_env () =
try
let dir' = Sys.getenv "TPTP" in
search dir'
with Not_found -> None
in
if Filename.is_relative name
then match search dir with
| None -> search_env ()
| Some _ as res -> res
else if Sys.file_exists name
then Some name
else None
let parse_lexbuf ?names buf =
try
let decls = Parse_tptp.parse_declarations Lex_tptp.token buf in
let q = Queue.create () in
List.iter
(fun decl -> match decl, names with
| (A.CNF _ | A.FOF _ | A.TFF _ | A.THF _ | A.TypeDecl _ | A.NewType _), None ->
Queue.push decl q
| (A.CNF _ | A.FOF _ | A.TFF _ | A.THF _ | A.TypeDecl _ | A.NewType _), Some names ->
if List.mem (A.get_name decl) names
then Queue.push decl q
else ()
| (A.Include _ | A.IncludeOnly _), _ ->
Queue.push decl q)
decls;
Err.return (Iter.of_queue q)
with
| Error msg | Sys_error msg ->
Err.fail msg
| e ->
Err.fail (Printexc.to_string e)
let _find_and_open filename dir =
match filename with
| "stdin" -> stdin
| _ ->
match find_file filename dir with
| Some filename ->
begin try open_in filename
with Sys_error msg ->
errorf "error when opening file `%s`: %s" filename msg
end
| None -> errorf "could not find file `%s`" filename
let parse_file ?cache ~recursive f =
let parse_cache =
lazy (if recursive then CCOpt.get_lazy create_parse_cache cache
else assert false)
in
let dir = Filename.dirname f in
let result_decls = Queue.create () in
let rec parse_this_file ?names filename =
let input = _find_and_open filename dir in
try
let buf = Lexing.from_channel input in
Loc.set_file buf filename;
let decls = Parse_tptp.parse_declarations Lex_tptp.token buf in
List.iter
(fun decl -> match decl, names with
| (A.CNF _ | A.FOF _ | A.TFF _ | A.THF _ | A.TypeDecl _ | A.NewType _), None ->
Queue.push decl result_decls
| (A.CNF _ | A.FOF _ | A.TFF _ | A.THF _ | A.TypeDecl _ | A.NewType _), Some names ->
if List.mem (A.get_name decl) names
then Queue.push decl result_decls
else ()
| A.Include f, _ when recursive ->
if Hashtbl.mem (Lazy.force parse_cache) f
then Util.debugf 2 "@[<2>ignore include of `%s`, already parsed@]" (fun k->k f)
else (
Hashtbl.add (Lazy.force parse_cache) f ();
parse_this_file ?names:None f
)
| A.IncludeOnly (f, names'), _ when recursive ->
if Hashtbl.mem (Lazy.force parse_cache) f
then Util.debugf 2 "@[<2>ignore include of `%s`, already parsed@]" (fun k->k f)
else (
Util.debugf 2
"@[<2>caution: partial include of `%s` will not be cached"
(fun k->k f);
parse_this_file ~names:names' f
)
| (A.Include _ | A.IncludeOnly _), _ ->
Queue.push decl result_decls)
decls;
close_in input
with e ->
close_in input;
raise e
in
try
parse_this_file ?names:None f;
Err.return (Iter.of_queue result_decls)
with
| Error msg | Sys_error msg ->
Err.fail (Util.err_spf "in parse_tptp: %s" msg)
| e -> Err.fail (Printexc.to_string e)
let fpf = Format.fprintf
let print_into ppt oc decls =
fpf oc "@[<v>%a@]@?"
(Util.pp_iter ~sep:"" (A.pp ppt))
decls
let print_into_file ppt file decls =
CCIO.with_out file
(fun oc ->
let out = Format.formatter_of_out_channel oc in
print_into ppt out decls;
Format.pp_print_flush out ())
let has_includes decls =
Iter.exists
(function
| A.Include _
| A.IncludeOnly _ -> true
| A.FOF _
| A.CNF _
| A.TFF _
| A.THF _
| A.NewType _
| A.TypeDecl _ -> false)
decls
(** {2 Bridge to UntypedAST} *)
module UA = UntypedAST
let enable_def_as_rewrite = ref false
let attr_of_info = function
| A.GString "ac"
| A.GString "AC"
| A.GNode ("ac", [])
| A.GNode ("AC", []) -> [UA.attr_ac]
| A.GNode ("infix", [A.GString n]) -> [UA.attr_infix n]
| A.GNode ("prefix", [A.GString n]) -> [UA.attr_prefix n]
| g ->
Util.warnf "unknown attribute `%a`, ignore" A.pp_general g;
[]
let rec looks_like_def f = match PT.view f with
| PT.Bind (Binder.Forall, _, f') -> looks_like_def f'
| PT.AppBuiltin ((Builtin.Equiv | Builtin.Eq), ([_;lhs;rhs] | [lhs;rhs])) ->
let as_atom t = match PT.view t with
| PT.Const id | PT.App ({PT.term=PT.Const id; _}, _) -> Some id
| _ -> None
in
begin match as_atom lhs with
| None -> false
| Some id ->
PT.Seq.symbols rhs |> Iter.for_all (fun id' -> id<>id')
end
| _ -> false
let to_ast st =
let conv_form name role f =
let name = A.string_of_name name in
let attrs = [UA.attr_name name] in
match role with
| A.R_question
| A.R_conjecture -> UA.goal ~attrs f
| A.R_negated_conjecture -> UA.goal ~attrs (PT.not_ f)
| A.R_lemma -> UA.lemma ~attrs f
| A.R_definition
when !enable_def_as_rewrite &&
looks_like_def f ->
Util.debugf ~section 3 "(@[tptp.def_as_rewrite@ %a@])"
(fun k->k PT.pp f);
Util.incr_stat stat_def_as_rw;
UA.rewrite ~attrs f
| A.R_definition
| A.R_axiom
| A.R_hypothesis
| A.R_assumption
| A.R_theorem
| A.R_plain
| A.R_finite _
| A.R_type
| A.R_unknown -> UA.assert_ ~attrs f
and conv_decl name s ty info =
let name = A.string_of_name name in
let attr_name = UA.attr_name name in
let attrs' =
CCList.flat_map
(function
| A.GNode ("attrs", l) -> CCList.flat_map attr_of_info l
| _ -> [])
info
in
UA.decl s ty ~attrs:(attr_name :: attrs')
in
begin match st with
| A.Include _
| A.IncludeOnly _ -> error "cannot convert `Include` to UntypedAST"
| A.TypeDecl (name,s,ty,info)
| A.NewType (name,s,ty,info) ->
conv_decl name s ty info
| A.TFF (name,role,f,_)
| A.THF (name,role,f,_)
| A.FOF (name,role,f,_) ->
conv_form name role f
| A.CNF (name,role,f,_) ->
assert (not (CCList.is_empty f));
let f =
if List.length f == 1 then List.hd f else PT.or_ f
in
conv_form name role f
end
let name_sym_ sy =
let str = CCFormat.sprintf "'ty_decl_%s'" sy in
A.NameString str
let of_ast st =
let name = match UA.name st with
| None -> A.NameString "no_name"
| Some s -> A.NameString s
in
match st.UA.stmt with
| UA.Include s -> A.Include s
| UA.Decl (s,ty) ->
let name = name_sym_ s in
A.TypeDecl (name, s, ty, [])
| UA.Def _ -> error "cannot convert `def` statement into TPTP"
| UA.Rewrite _ -> error "cannot convert `rewrite` statement into TPTP"
| UA.Data _ -> error "cannot convert `data` statement into TPTP"
| UA.Goal f -> A.TFF (name, A.R_conjecture, f, [])
| UA.Assert f -> A.TFF (name, A.R_axiom, f, [])
| UA.Lemma f -> A.TFF (name, A.R_lemma, f, [])
let () =
Options.add_opts
[ "--tptp-def-as-rewrite", Arg.Set enable_def_as_rewrite, " in TPTP, definitions as rewrite rules";
"--no-tptp-def-as-rewrite", Arg.Clear enable_def_as_rewrite, " disable definition->rewrite in TPTP";
]