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
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
module Loc = Why3.Loc
module Term = Why3.Term
module Decl = Why3.Decl
module Thy = Why3.Theory
module Tsk = Why3.Task
type kind = Goal | When | WhenNot
type shape =
| Empty
| Cat of shape * shape
| Range of kind * Loc.position
let (<+>) (a : shape) (b : shape) : shape =
match a,b with
| Empty,c | c,Empty -> c
| _ -> Cat(a,b)
let (<*>) (a : shape) (b : shape) : shape =
match a,b with
| Empty,_ | _,Empty -> Empty
| _ -> Cat(a,b)
let default kind l s =
match s, l with
| Empty, Some p -> Range(kind,p)
| shape, _ -> shape
let rec collect kind (t : Term.term) =
match t.t_loc with
| Some p -> Range(kind,p)
| None -> Term.t_fold (fun rgs t -> rgs <+> collect kind t) Empty t
let rec hyp (t : Term.term) =
default When t.t_loc @@
match t.t_node with
| Tbinop(Tand,a,b) -> hyp a <*> hyp b
| Term.Tlet (_,fb) ->
let _,t = Term.t_open_bound fb in hyp t
| Tnot p -> collect WhenNot p
| _ -> collect When t
let rec goal (t : Term.term) =
default Goal t.t_loc @@
match t.t_node with
| Tbinop(Timplies,h,p) -> hyp h <*> goal p
| Tquant(Tforall,bnd) ->
let _,_,p = Term.t_open_quant bnd in goal p
| Term.Tlet (_,fb) ->
let _,t = Term.t_open_bound fb in goal t
| _ -> collect Goal t
let decl (d: Decl.decl) : shape =
match d.d_node with
| Dprop(Pgoal,pr,f) -> default Goal pr.pr_name.id_loc @@ goal f
| Dprop(Paxiom,pr,f) -> default When pr.pr_name.id_loc @@ hyp f
| _ -> Empty
let tdecl (td : Thy.tdecl) : shape =
match td.td_node with Decl d -> decl d | _ -> Empty
let rec task (t : Tsk.task) : shape =
match t with
| None -> Empty
| Some { task_decl ; task_prev } ->
let p = task task_prev in
let d = tdecl task_decl in
p <+> d
type chunk = { kind : kind ; range : Range.range }
type env = {
file : string ;
mutable goal : chunk list ;
mutable hyps : chunk list ;
}
let make ~file = { file ; goal = [] ; hyps = [] }
let rec collect env = function
| Empty -> ()
| Cat(a,b) -> collect env a ; collect env b
| Range(kind,p) ->
let f,a,b,c,d = Loc.get p in
if f = env.file then
let chunk = { kind ; range = ((a,b),(c,d)) } in
match kind with
| Goal ->
env.goal <- chunk :: env.goal
| When | WhenNot ->
env.hyps <- chunk :: env.hyps
let by_range a b = Stdlib.compare a.range b.range
let disjoint a b = Range.disjoint a.range b.range
let chunks ~file (t : Tsk.task) =
let env = make ~file in
collect env (task t) ;
let goal = env.goal in
let hyps = List.filter (fun h -> List.for_all (disjoint h) goal) env.hyps in
List.sort by_range (goal @ hyps)
let decls ~file (thy : Why3.Theory.theory) =
let lines = ref [] in
List.iter
(fun (td : Why3.Theory.tdecl) ->
match td.td_node with
| Meta _ | Clone _ | Use _ -> ()
| Decl d ->
Why3.Ident.Sid.iter
(fun id ->
match id.id_loc with
| None -> ()
| Some loc ->
let f,l,_,_,_ = Loc.get loc in
if f = file then lines := l :: !lines
) d.d_news
) thy.th_decls ;
Array.of_list @@ List.sort_uniq Int.compare !lines
let locate l lines =
let n = Array.length lines in
if n = 0 then
0, Sys.max_string_length
else
let li = lines.(0) in
if l < li then (0,li) else
if n = 1 then li, Sys.max_string_length else
let lj = lines.(n-1) in
if l >= lj then lj, Sys.max_string_length else
let rec lookup i li j lj =
if j - i <= 1 then li,lj else
let k = i + (j-i) / 2 in
let lk = lines.(k) in
if l < lk
then lookup i li k lk
else lookup k lk j lj
in lookup 0 li (n-1) lj
let window ~file thy chunks =
let p = ref max_int in
let q = ref min_int in
List.iter
(fun chunk ->
match chunk.kind with
| Goal ->
p := min !p @@ Range.first_line chunk.range ;
q := max !q @@ Range.last_line chunk.range ;
| When | WhenNot -> ()
) chunks ;
let lines = decls ~file thy in
if !p > !q then 0,0,[] else
let u,v =
if !p < !q then
let p1,p2 = locate !p lines in
let q1,q2 = locate !q lines in
min p1 q1, max p2 q2
else locate !p lines
in
!p, !q,
List.filter
(fun chunk ->
u <= Range.first_line chunk.range && Range.last_line chunk.range <= v
) chunks
type style = | Kind of kind | Text
type cursor = {
text : string ;
context : int ;
mutable edge : bool ;
mutable style : style ;
mutable comment : int ;
mutable offset : int ;
mutable pos : Range.pos ;
}
let keywords = lazy
begin
let m = ref Dict.empty in
List.iter
(fun k -> m := Dict.add k k !m)
Why3.Keywords.keywords ; !m
end
let pp_open_style = function
| Text -> ()
| Comment -> Format.printf "@{<gray>"
| Kind Goal -> Format.printf "@{<bold>@{<bright red>"
| Kind When -> Format.printf "@{<green>"
| Kind WhenNot -> Format.printf "@{<magenta>"
let pp_close_style = function
| Text -> ()
| Comment | Kind (When | WhenNot) -> Format.printf "@}"
| Kind Goal -> Format.printf "@}@}"
let is_text cursor = (cursor.style = Text)
let open_style cursor sty =
match cursor.style with
| Text -> cursor.style <- sty ; pp_open_style sty
| Comment | Kind _ -> ()
let close_style cursor sty =
if sty = cursor.style then
begin
pp_close_style cursor.style ;
cursor.style <- Text ;
end
let lino cursor =
begin
pp_close_style cursor.style ;
Format.printf "@{<orange>%4d:@} " @@ fst cursor.pos ;
pp_open_style cursor.style ;
end
let edge = function
| 'a'..'z' | 'A'..'Z' | '0'..'9' | '\'' | '_' -> false
| _ -> true
let move cursor d =
begin
let l,c = cursor.pos in
cursor.offset <- cursor.offset + d ;
cursor.pos <- l, c+d ;
end
let text k =
let n = String.length text in
k + 2 < n && text.[k+1] = '*' && text.[k+2] = ')'
let text k =
let n = String.length text in
k + 1 < n && text.[k+1] = '*'
let text k =
let n = String.length text in
k + 1 < n && text.[k+1] = ')'
let skip cursor pos =
if cursor.comment > 0 then close_style cursor Comment ;
while Range.(<<) cursor.pos pos do
let k = cursor.offset in
let c = cursor.text.[k] in
match c with
| '\n' ->
cursor.pos <- Range.after cursor.pos c ;
cursor.offset <- succ k ;
| '(' when comment_mult cursor.text k ->
move cursor 3 ;
| '(' when comment_start cursor.text k ->
cursor.comment <- succ cursor.comment ;
move cursor 2 ;
| '*' when comment_end cursor.text k ->
cursor.comment <- pred cursor.comment ;
move cursor 2 ;
| _ ->
move cursor 1 ;
cursor.edge <- edge c
done
let print cursor ?(endline=true) pos =
if cursor.comment > 0 then open_style cursor Comment ;
while Range.(<<) cursor.pos pos do
let k = cursor.offset in
let c = cursor.text.[k] in
begin
match
if cursor.edge && is_text cursor then
Dict.find cursor.text k (Lazy.force keywords)
else None
with
| None ->
begin
match c with
| '\n' when (endline || fst cursor.pos + 1 < fst pos) ->
cursor.pos <- Range.after cursor.pos c ;
cursor.offset <- succ k ;
cursor.edge <- true ;
Format.print_char c ;
lino cursor ;
| '(' when comment_mult cursor.text k ->
move cursor 3 ;
cursor.edge <- true ;
Format.print_string "(*)" ;
| '(' when comment_start cursor.text k ->
move cursor 2 ;
cursor.edge <- true ;
if cursor.comment = 0 then open_style cursor Comment ;
cursor.comment <- succ cursor.comment ;
Format.print_string "(*"
| '*' when comment_end cursor.text k ->
move cursor 2 ;
cursor.edge <- true ;
Format.print_string "*)" ;
cursor.comment <- pred cursor.comment ;
if cursor.comment = 0 then close_style cursor Comment ;
| _ ->
cursor.pos <- Range.after cursor.pos c ;
cursor.offset <- succ k ;
cursor.edge <- edge c ;
Format.print_char c ;
end
| Some kwd ->
let n = String.length kwd in
let s = String.length cursor.text in
if k+n >= s || edge cursor.text.[k+n] then
Format.printf "@{<bold>%s@}" kwd
else
Format.print_string kwd ;
let l,p = cursor.pos in
cursor.pos <- l, p+n;
cursor.offset <- k+n;
cursor.edge <- false;
end
done
let line n = (n,0)
let rec decorate cursor head tail =
print cursor @@ fst head.range ;
let style = Kind head.kind in
open_style cursor style ;
print cursor @@ snd head.range ;
close_style cursor style ;
match tail with
| [] ->
let q = Range.last_line head.range + cursor.context + 1 in
print cursor ~endline:false @@ line q
| next :: tail ->
let q = Range.last_line head.range in
let p' = Range.first_line next.range in
let ctxt = cursor.context in
if q + 2 * ctxt + 2 < p' then
begin
print cursor ~endline:false @@ line (q + ctxt + 1) ;
pp_close_style cursor.style ;
Format.printf " @{<orange>•••@}@." ;
pp_open_style cursor.style ;
skip cursor @@ line (p' - ctxt) ;
lino cursor ;
end ;
decorate cursor next tail
let pp_range fmt (p,q) =
if p < q then Format.fprintf fmt "%d:%d" p q else Format.pp_print_int fmt p
let pp_expl fmt t =
let expl = Session.task_expl t in
if expl <> "" then Format.fprintf fmt ":%s" expl
let dump ~file ~context thy task =
ignore thy ;
let p,q,rs = chunks ~file task |> window ~file thy in
match rs with [] -> () | head::tail ->
Format.printf "@{<orange>[%s:%a%a]@}@\n"
file pp_range (p,q) pp_expl task ;
let text = Utils.readfile ~file in
let cursor = {
text ; context ; offset = 0 ; pos = Range.start ;
edge = false ; comment = 0 ; style = Text ;
} in
try
let p = Range.first_line head.range in
skip cursor @@ line (p - cursor.context) ;
lino cursor ;
decorate cursor head tail ;
if context > 1 then
begin
lino cursor ;
Format.print_newline () ;
end ;
with Invalid_argument _ | Exit ->
if snd cursor.pos > 0 then
Format.print_newline ()