forked from CakeML/candle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
compute.ml
790 lines (650 loc) · 27.7 KB
/
compute.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
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
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
(* ========================================================================== *)
(* Call-by-value reduction of terms similar to ML computation. *)
(* Translated from HOL4 computeLib *)
(* *)
(* (c) Copyright Alexey Solovyev 2018 *)
(* (c) Copyright Bruno Barras 2000 *)
(* *)
(* Distributed under the same license as HOL Light. *)
(* ========================================================================== *)
needs "pair.ml";;
module type Compute_sig = sig
type compset
val monitoring : (term -> bool) option ref
val stoppers : (term -> bool) option ref
val new_compset : thm list -> compset
val bool_compset : unit -> compset
val add_thms : thm list -> compset -> unit
val add_conv : term * int * conv -> compset -> unit
val set_skip : compset -> term -> int option -> unit
val scrub_const : compset -> term -> unit
val scrub_thms : thm list -> compset -> unit
val lazyfy_thm : thm -> thm
val strictify_thm : thm -> thm
val CBV_CONV : compset -> term -> thm
val WEAK_CBV_CONV : compset -> term -> thm
val the_compset : compset
val add_funs : thm list -> unit
val add_convs : (term * int * conv) list -> unit
val del_consts : term list -> unit
val del_funs : thm list -> unit
val EVAL_CONV : conv
val EVAL_RULE : thm -> thm
val EVAL_TAC : tactic
val RESTR_EVAL_CONV : term list -> conv
val RESTR_EVAL_RULE : term list -> thm -> thm
val RESTR_EVAL_TAC : term list -> tactic
val OR : term list -> term -> bool
type 'a fterm
type db
type transform = private Conversion of (term -> thm * db fterm) | Rrules of thm list
val listItems : compset -> (string * transform list) list
end;;
module Compute : Compute_sig = struct
let with_flag (flag, b) f x =
let fval = !flag in
let () = flag := b in
let res = try f x with e -> (flag := fval; raise e) in
flag := fval; res;;
let RIGHT_BETA th =
try TRANS th (BETA_CONV (rhs (concl th)))
with _ -> failwith "RIGHT_BETA";;
let RIGHT_ETA thm =
try TRANS thm (ETA_CONV (rhs (concl thm)))
with _ -> failwith "RIGHT_ETA";;
let MK_comb th =
let rator, rand = dest_comb (rhs (concl th)) in
let mka th1 th2 = TRANS th (MK_COMB (th1, th2)) in
(REFL rator, REFL rand, mka);;
let MK_abs th =
let bvar, body = dest_abs (rhs (concl th)) in
(bvar, REFL body, fun th1 -> TRANS th (ABS bvar th1));;
let rec BODY_CONJUNCTS th =
if is_forall (concl th) then
BODY_CONJUNCTS (SPEC_ALL th)
else if is_conj (concl th) then
BODY_CONJUNCTS (CONJUNCT1 th) @ BODY_CONJUNCTS (CONJUNCT2 th)
else [th];;
(*** compute_rules.sml ***)
type ('a, 'b, 'c) stack =
Ztop
| Zrator of 'a * ('a, 'b, 'c) stack
| Zrand of 'b * ('a, 'b, 'c) stack
| Zabs of 'c * ('a, 'b, 'c) stack;;
exception Dead_code of string;;
let rhs_concl = rand o concl;;
let refl_thm = REFL;;
let trans_thm = TRANS;;
let beta_thm = RIGHT_BETA;;
let evaluate th = th;;
let try_eta thm = try RIGHT_ETA thm with _ -> thm;;
(*---------------------------------------------------------------------------
* Precondition: f(arg) is a closure corresponding to b.
* Given (arg,(|- M = (a b), Stk)),
* returns (|- a = a, (<fun>,(|- b = b, f(arg)))::Stk)
* where <fun> = (|- a = a' , |- b = b') |-> |- M = (a' b')
*---------------------------------------------------------------------------*)
let push_in_stk f arg (th, stk) =
let (tha, thb, mka) = MK_comb th in
(tha, Zrator ((mka, (thb, f arg)), stk));;
let push_skip_stk f arg (th, stk) =
let (tha, thb, mka) = MK_comb th in
(tha, Zrand ((C mka, true, (thb, f arg)), stk));;
let push_lam_in_stk (th, stk) =
let (_, thb, mkl) = MK_abs th in
(thb, Zabs (try_eta o mkl, stk));;
(*---------------------------------------------------------------------------
Does conversions between
FUNIFY
|- (c t_1 .. t_n x) = M ---> |- (c t_1 .. t_n) = \x. M
<---
UNFUNIFY
In UNFUNIFY, we must avoid choosing an x that appears free in t_1..t_n.
---------------------------------------------------------------------------*)
let FUNIFY thm =
try
let x = rand (lhs (concl thm)) in
CONV_RULE (RATOR_CONV (RAND_CONV (CHANGED_CONV (PURE_REWRITE_CONV[ETA_AX])))) (ABS x thm)
with _ -> failwith "FUNIFY";;
let UNFUNIFY thm =
try
let lhs, rhs = dest_eq (concl thm) in
let x = variant (frees lhs) (fst (dest_abs rhs)) in
CONV_RULE (RAND_CONV BETA_CONV) (AP_THM thm x)
with _ -> failwith "UNFUNIFY";;
let repeat_on_conj f thm =
let rec iter th = try iter (f th) with _ -> th in
end_itlist CONJ (List.map iter (BODY_CONJUNCTS thm));;
let lazyfy_thm = repeat_on_conj FUNIFY;;
let strictify_thm = repeat_on_conj UNFUNIFY;;
(* Ensures a theorem is an equality. *)
let eq_intro thm =
if is_eq (concl thm) then thm else
if is_neg (concl thm) then EQF_INTRO thm
else EQT_INTRO thm;;
(*** clauses.sml ***)
(*---------------------------------------------------------------------------
* Checking that a given thm is a reduction rule we can handle:
* (c p1...pn) = rhs
* with p1...pn either a const applied to patterns or a free variable.
*---------------------------------------------------------------------------*)
type pattern =
Pvar of int
| Papp of term * pattern list;;
let check_arg_form trm =
let rec chk t stk free =
if is_comb t then
let rator, rand = dest_comb t in
let free', pat1 = chk rand [] free in
chk rator (pat1 :: stk) free'
else if is_var t then
if stk = [] then
let newi = List.length free in
try (free, Pvar (newi - index t free - 1))
with _ -> (t :: free, Pvar newi)
else
failwith ("check_arg_form: " ^ fst (dest_var t) ^ " occurs as a variable on lhs")
else if is_const t then
(free, Papp (t, List.rev stk))
else
failwith ("check_arg_form: lambda abstraction not allowed on lhs")
in
match chk trm [] [] with
| (fv, Papp (head, args)) -> (List.rev fv, head, args)
| _ -> failwith "check_arg_form: ill-formed lhs";;
(*---------------------------------------------------------------------------
* CLOS denotes a delayed substitution (closure).
* CST denotes an applied constant. Its first argument is the head constant;
* the second is the list of its arguments (we keep both the term and its
* abstract representation); the last one is the set of rewrites that
* still have a chance to be used, ie. those with a lhs wider than the
* number of applied args.
* NEUTR is a slight improvement of CST with an empty list of rewrites, so
* that arguments of a variable are immediatly strongly reduced.
*---------------------------------------------------------------------------*)
type 'a cst_rec = {
head: term;
args: (term * 'a fterm) list;
rws: 'a;
skip: int option
}
and 'a clos_rec = {
env: 'a fterm list;
term: 'a dterm
}
and 'a fterm =
Const of 'a cst_rec
| Neutr
| Clos of 'a clos_rec
(*---------------------------------------------------------------------------
* An alternative representation of terms, with all information needed:
* - they are real de Bruijn terms, so that abstraction destruction is in
* constant time.
* - application is n-ary (slight optimization)
* - we forget the names of variables
* - constants are tagged with the set of rewrites that apply to it.
* It's a reference since dterm is used to represent rhs of rewrites,
* and fixpoints equations add rewrites for a constant that appear in the
* rhs.
*---------------------------------------------------------------------------*)
and 'a dterm =
Bv of int
| Fv
| Cst of term * ('a * int option) ref
| App of 'a dterm * 'a dterm list (* order: outermost ahead *)
| Abs of 'a dterm;;
(* Invariant: the first arg of App never is an App. *)
let appl = function
| App (a, l1), arg -> App (a, arg :: l1)
| t, arg -> App (t, [arg]);;
(*---------------------------------------------------------------------------
* Type variable instantiation in dterm. Make it tail-recursive ?
*---------------------------------------------------------------------------*)
let inst_type_dterm = function
| [], v -> v
| tysub, v ->
let rec tyi_dt = function
| Cst (c, dbsk) -> Cst (inst tysub c, dbsk)
| App (h, l) -> App (tyi_dt h, map tyi_dt l)
| Abs v -> Abs (tyi_dt v)
| v -> v in
tyi_dt v;;
type action =
Rewrite of rewrite list
| Conv of (term -> thm * db fterm)
and try_rec = {
hcst: term;
rws: action;
tail: db
}
and db =
End_db
| Try of try_rec
| Need_arg of db
and rewrite_rec = {
cst: term; (* constant which the rule applies to *)
lhs: pattern list; (* patterns = constant args in lhs of thm *)
npv: int; (* number of distinct pat vars in lhs *)
rhs: db dterm;
thm: thm (* thm we use for rewriting *)
}
and rewrite =
Rw of rewrite_rec;;
let rec add_in_db = function
| (n, cst, act, End_db) ->
funpow n (fun a -> Need_arg a) (Try { hcst = cst; rws = act; tail = End_db })
| (0, cst, (Rewrite nrws as act), Try { hcst = hcst; rws = Rewrite rws as rw; tail = tail }) ->
if cst = hcst then Try { hcst = hcst; rws = Rewrite (nrws @ rws); tail = tail }
else Try { hcst = hcst; rws = rw; tail = add_in_db (0, cst, act, tail) }
| (n, cst, act, Try { hcst = hcst; rws = rws; tail = tail }) ->
Try { hcst = hcst; rws = rws; tail = add_in_db(n, cst, act, tail) }
| (0, cst, act, db) ->
Try { hcst = cst; rws = act; tail = db }
| (n, cst, act, Need_arg tail) ->
Need_arg (add_in_db(n - 1, cst, act, tail));;
let key_of (Rw {cst = cst; lhs = lhs}) =
let name, _ = dest_const cst in
(name, length lhs, cst);;
let is_skip = function
| (_, Const {skip = Some n; args = args}) -> n <= List.length args
| _ -> false;;
let partition_skip skip args =
match skip with
| Some n ->
let len = List.length args in
if n <= len then
chop_list (len - n) args
else
([], args)
| None -> ([], args);;
(*---------------------------------------------------------------------------
* equation database
* We should try to factorize the rules (cf discrimination nets)
* Rules are packed according to their head constant, and then sorted
* according to the width of their lhs.
*---------------------------------------------------------------------------*)
type compset =
Rws of (string, (db * int option) ref) Hashtbl.t;;
let empty_rws () = Rws (Hashtbl.create 100);;
let assoc_clause (Rws rws) cst =
try Hashtbl.find rws cst
with Not_found ->
let mt = ref (End_db, None) in
Hashtbl.add rws cst mt;
mt;;
let add_in_db_upd rws (name, arity, hcst) act =
let { contents = db, sk } as rl = assoc_clause rws name in
rl := add_in_db (arity, hcst, act, db), sk;;
let set_skip_name (Rws htbl as rws) p sk =
let { contents = db, _ } as rl = assoc_clause rws p in
rl := db, sk;;
let scrub_const (Rws htbl) c =
let name, _ = dest_const c in
Hashtbl.remove htbl name;;
let from_term (rws, env, t) =
let rec down (env, t, c) =
match t with
| Var _ -> up ((try Bv (index t env) with _ -> Fv), c)
| Const (name, _) -> up (Cst (t, assoc_clause rws name), c)
| Comb (rator, rand) -> down (env, rator, Zrator ((env, rand), c))
| Abs (bvar, body) -> down (bvar :: env, body, Zabs ((), c))
and up = function
| dt, Ztop -> dt
| dt, Zrator ((env, arg), c) -> down (env, arg, Zrand(dt, c))
| dt, Zrand (dr, c) -> up (appl (dr, dt), c)
| dt, Zabs (_, c) -> up (Abs dt, c)
in
down (env, t, Ztop);;
(*---------------------------------------------------------------------------
* Note: if the list of free variables of the lhs was empty, we could
* evaluate (weak reduction) the rhs now. This implies that the
* theorems must be added in dependencies order.
*---------------------------------------------------------------------------*)
let mk_rewrite rws eq_thm =
let lhs, rhs = dest_eq (concl eq_thm) in
let fv, cst, pats = check_arg_form lhs in
let gen_thm = itlist GEN fv eq_thm in
let rhsc = from_term (rws, rev fv, rhs) in
Rw {
cst = cst;
lhs = pats;
rhs = rhsc;
npv = length fv;
thm = gen_thm
};;
let unsuitable th =
let l, r = dest_eq (concl th) in
can (term_match [] l) r;;
let enter_thm rws thm0 =
let thm = eq_intro thm0 in
if unsuitable thm then ()
else
let rw = mk_rewrite rws thm in
add_in_db_upd rws (key_of rw) (Rewrite [rw]);;
let add_thms lthm rws =
List.iter (List.iter (enter_thm rws) o BODY_CONJUNCTS) lthm;;
let add_extern (cst, arity, fconv) rws =
let name, _ = dest_const cst in
add_in_db_upd rws (name, arity, cst) (Conv fconv);;
let new_rws () =
let rws = empty_rws () in
add_thms [REFL_CLAUSE] rws;
rws;;
let from_list lthm =
let rws = new_rws() in
add_thms lthm rws;
rws;;
let scrub_thms lthm rws =
let tmlist = map (concl o hd o BODY_CONJUNCTS) lthm in
let clist = map (fst o strip_comb o lhs o snd o strip_forall) tmlist in
List.iter (scrub_const rws) clist;;
(*---------------------------------------------------------------------------*)
(* Support for analysis of compsets *)
(*---------------------------------------------------------------------------*)
let rws_of (Rws htbl) =
let dblist = Hashtbl.fold (fun _ {contents = (db, _)} r -> db :: r) htbl [] in
let rec get_actions db =
match db with
| End_db -> []
| Need_arg db' -> get_actions db'
| Try {hcst = hcst; rws = rws; tail = tail} ->
(hcst, rws) :: get_actions tail in
let actionlist = List.concat (List.map get_actions dblist) in
let dest (Rw {thm = thm}) = thm in
let dest_action = function
| hcst, Rewrite rws -> (hcst, map dest rws)
| hcst, Conv _ -> (hcst, []) in
let rwlist = List.map dest_action actionlist in
rwlist;;
type transform =
Conversion of (term -> thm * db fterm)
| Rrules of thm list;;
(*---------------------------------------------------------------------------*)
(* Compute the "attachments" for each element of the compset. Fortunately, *)
(* it seems that the insertion of an attachment into a compset also makes *)
(* insertions for the constants mentioned in the rhs of the rewrite. *)
(* Otherwise, one would have to do a transitive closure sort of construction *)
(* to make all the dependencies explicit. *)
(*---------------------------------------------------------------------------*)
let deplist (Rws htbl) =
let dblist = Hashtbl.fold (fun s {contents = (db, _)} r -> (s, db) :: r) htbl [] in
let rec get_actions db =
match db with
| End_db -> []
| Need_arg db' -> get_actions db'
| Try {hcst = hcst; rws = rws; tail = tail} ->
rws :: get_actions tail in
let actionlist = List.map (fun (s, db) -> s, get_actions db) dblist in
let dest (Rw {thm = thm}) = thm in
let dest_action = function
| Rewrite rws -> Rrules (map dest rws)
| Conv ecnv -> Conversion ecnv in
let rwlist = List.map (fun (s, act) -> s, map dest_action act) actionlist in
rwlist;;
(*** equations.sml ***)
(*---------------------------------------------------------------------------
* The First order matching functions.
*
* We do not consider general non-linear patterns. We could try (requires a
* more elaborate conversion algorithm, and it makes matching and reduction
* mutually dependent).
*---------------------------------------------------------------------------*)
exception No_match;;
let match_const (bds, tbds) pc c =
let name, ty = dest_const pc in
let name', ty' = dest_const c in
if name = name' then
try bds, type_match ty ty' tbds with _ -> raise No_match
else raise No_match;;
(*---------------------------------------------------------------------------
* Match pattern variable number var with term arg.
* We do not need to match types, because pattern variables appear as argument
* of a constant which has already been matched succesfully.
*---------------------------------------------------------------------------*)
let match_var (bds, tbds) var arg =
let _ = match bds.(var) with
| Some (tm, _) -> if aconv tm (fst arg) then () else raise No_match
| None -> bds.(var) <- Some arg in
(bds, tbds);;
(*---------------------------------------------------------------------------
* Tries to match a list of pattern to a list of arguments.
* We assume that we already checked the lengths (probably a good short-cut)
*
* Returns a list of bindings that extend the ones given as
* argument (bds), or a No_match exception.
*---------------------------------------------------------------------------*)
let rec match_list bds pats args =
match (pats, args) with
| pat :: pats, arg :: args ->
match_list (match_solve bds pat arg) pats args
| [], [] -> bds
| _ -> raise (Dead_code "match_list")
and match_solve bds pat arg =
match (pat, arg) with
| Pvar var, arg -> match_var bds var arg
| Papp (phead, pargs), (_, Const {head = head; args = args}) ->
if length pargs = length args then
match_list (match_const bds phead head) pargs args
else
raise No_match
| _ -> raise No_match;;
(*---------------------------------------------------------------------------
* Try a sequence of rewrite rules. No attempt to factorize patterns!
*---------------------------------------------------------------------------*)
type 'a rule_inst = {
rule: rewrite;
inst: (term * 'a fterm) option array * (hol_type * hol_type) list
};;
let try_rwn ibds lt =
let rec try_rec = function
| [] -> raise No_match
| (Rw {lhs = lhs; npv = npv} as rw) :: rwn ->
let env = Array.make npv None in
try { rule = rw; inst = match_list (env, ibds) lhs lt }
with No_match -> try_rec rwn
in
try_rec;;
(*---------------------------------------------------------------------------
* Instantiating the rule according to the output of the matching.
*---------------------------------------------------------------------------*)
let comb_ct cst arg =
match cst with
| { head = head; args = args; rws = Need_arg tail; skip = skip } ->
Const { head = head; args = arg :: args; rws = tail; skip = skip }
| { head = head; args = args; rws = End_db; skip = skip } ->
Const { head = head; args = arg :: args; rws = End_db; skip = skip }
| { rws = Try _ } ->
raise (Dead_code "comb_ct: yet rules to try");;
let mk_clos (env, t) =
match t with
| Cst (hc, { contents = (db, b) }) -> Const {head = hc; args = []; rws = db; skip = b}
| Bv i -> List.nth env i
| Fv -> Neutr
| _ -> Clos {env = env; term = t};;
(*---------------------------------------------------------------------------
* It is probably this code that can be improved the most
*---------------------------------------------------------------------------*)
let inst_one_var (thm, lv) = function
| Some (tm, v) -> SPEC tm thm, v :: lv
| None -> raise (Dead_code "inst_rw");;
let inst_rw (th, monitoring, {rule = Rw {thm = thm; rhs = rhs}; inst = (bds, tysub)}) =
let tirhs = inst_type_dterm (tysub, rhs) in
let tithm = INST_TYPE tysub thm in
let spec_thm, venv = Array.fold_left inst_one_var (tithm, []) bds in
let () = if monitoring then print_term (concl spec_thm) in
trans_thm th spec_thm, mk_clos (venv, tirhs);;
let monitoring = ref (None: (term -> bool) option);;
let stoppers = ref (None: (term -> bool) option);;
(*---------------------------------------------------------------------------
* Reducing a constant
*---------------------------------------------------------------------------*)
let rec reduce_cst = function
| th, {head = head; args = args; rws = Try {hcst = hcst; rws = Rewrite rls; tail = tail}; skip = skip} ->
(try
let () = match !stoppers with None -> () | Some p -> if p head then raise No_match else () in
let _, _, tytheta = try term_match [] hcst head with _ -> raise No_match in
let rule_inst = try_rwn tytheta args rls in
let mon = match !monitoring with None -> false | Some f -> f head in
let insted = inst_rw (th, mon, rule_inst) in
(true, insted)
with No_match ->
reduce_cst (th, {head = head; args = args; rws = tail; skip = skip}))
| th, {head = head; args = args; rws = Try {hcst = hcst; rws = Conv fconv; tail = tail}; skip = skip} ->
(try
let thm, ft = fconv (rhs_concl th) in
(true, (trans_thm th thm, ft))
with _ ->
reduce_cst (th, {head = head; args = args; rws = tail; skip = skip}))
| th, cst -> (false, (th, Const cst));;
(*** computeLib.sml ***)
let auto_import_definitions = ref true;;
(* re-exporting types from clauses *)
let new_compset = from_list;;
let listItems = deplist;;
type cbv_stack =
((thm->thm->thm) * (thm * db fterm),
(thm->thm->thm) * bool * (thm * db fterm),
(thm->thm)) stack;;
let rec stack_out = function
| th, Ztop -> th
| th, Zrator ((mka, (thb, _)), ctx) -> stack_out (mka th thb, ctx)
| th, Zrand ((mka, _, (tha, _)), ctx) -> stack_out (mka tha th, ctx)
| th, Zabs (mkl, ctx) -> stack_out (mkl th, ctx);;
let initial_state rws t =
((refl_thm t, mk_clos ([], from_term (rws, [], t))), (Ztop: cbv_stack));;
(*---------------------------------------------------------------------------
* [cbv_wk (rws,(th,cl),stk)] puts the closure cl (useful information about
* the rhs of th) in head normal form (weak reduction). It returns either
* a closure which term is an abstraction, in a context other than Zappl,
* a variable applied to strongly reduced arguments, or a constant
* applied to weakly reduced arguments which does not match any rewriting
* rule.
*
* - substitution is propagated through applications.
* - if the rhs is an abstraction and there is one arg on the stack,
* this means we found a beta redex. mka rebuilds the application of
* the function to its argument, and Beta does the actual beta step.
* - for an applied constant, we look for a rewrite matching it.
* If we found one, then we apply the instantiated rule, and go on.
* Otherwise, we try to rebuild the thm.
* - for an already strongly normalized term or an unapplied abstraction,
* we try to rebuild the thm.
*---------------------------------------------------------------------------*)
let rec cbv_wk = function
| (th, Clos {env = env; term = App (a, args)}), stk ->
let tha, stka = rev_itlist (push_in_stk (curry mk_clos env)) args (th, stk) in
cbv_wk ((tha, mk_clos (env, a)), stka)
| (th, Clos {env = env; term = Abs body}), Zrator ((mka, (thb, cl)), s') ->
cbv_wk ((beta_thm (mka th thb), mk_clos (cl :: env, body)), s')
| (th, Const cargs), stk ->
let reduced, clos = reduce_cst (th, cargs) in
if reduced then cbv_wk (clos, stk) else cbv_up (clos, stk)
| clos, stk -> cbv_up (clos, stk)
(*---------------------------------------------------------------------------
* Tries to rebuild the thm, knowing that the closure has been weakly
* normalized, until it finds term still to reduce, or if a strong reduction
* may be required.
* - if we are done with a Rator, we start reducing the Rand
* - if we are done with the Rand of a const, we rebuild the application
* and look if it created a redex
* - an application to a NEUTR can be rebuilt only if the argument has been
* strongly reduced, which we now for sure only if itself is a NEUTR.
*---------------------------------------------------------------------------*)
and cbv_up = function
| hcl, Zrator ((mka, clos), ctx) ->
let new_state = clos, Zrand ((mka, false, hcl), ctx) in
if is_skip hcl then cbv_up new_state else cbv_wk new_state
| (thb, v), Zrand ((mka, false, (th, Const cargs)), stk) ->
cbv_wk ((mka th thb, comb_ct cargs (rhs_concl thb, v)), stk)
| (thb, Neutr), Zrand ((mka, false, (th, Neutr)), stk) ->
cbv_up ((mka th thb, Neutr), stk)
| clos, stk -> clos, stk;;
(*---------------------------------------------------------------------------
* [strong] continues the reduction of a term in head normal form under
* abstractions, and in the arguments of non reduced constant.
* precondition: the closure should be the output of cbv_wk
*---------------------------------------------------------------------------*)
let rec strong = function
| (th, Clos {env = env; term = Abs t}), stk ->
let thb, stk' = push_lam_in_stk (th, stk) in
strong (cbv_wk ((thb, mk_clos (Neutr :: env, t)), stk'))
| (_, Clos _), stk ->
raise (Dead_code "strong")
| (th, Const {skip = skip; args = args}), stk ->
let argssk, argsrd = partition_skip skip args in
let th', stk' = rev_itlist (push_skip_stk snd) argssk (th, stk) in
let th'', stk'' = rev_itlist (push_in_stk snd) argsrd (th', stk') in
strong_up (th'', stk'')
| (th, Neutr), stk -> strong_up (th, stk)
and strong_up = function
| th, Ztop -> th
| th, Zrand ((mka, false, (tha, Neutr)), ctx) ->
strong (cbv_wk ((mka tha th, Neutr), ctx))
| th, Zrand ((mka, false, clos), ctx) ->
raise (Dead_code "strong_up")
| th, Zrator ((mka, clos), ctx) ->
strong (cbv_wk (clos, Zrand ((mka, true, (th, Neutr)), ctx)))
| th, Zrand ((mka, true, (tha, _)), ctx) ->
strong_up (mka tha th, ctx)
| th, Zabs (mkl, ctx) ->
strong_up (mkl th, ctx);;
(*---------------------------------------------------------------------------
* [CBV_CONV rws t] is a conversion that does the full normalization of t,
* using rewrites rws.
*---------------------------------------------------------------------------*)
let CBV_CONV rws = evaluate o strong o cbv_wk o initial_state rws;;
(*---------------------------------------------------------------------------
* WEAK_CBV_CONV is the same as CBV_CONV except that it does not reduce
* under abstractions, and reduce weakly the arguments of constants.
* Reduction whenever we reach a state where a strong reduction is needed.
*---------------------------------------------------------------------------*)
let WEAK_CBV_CONV rws =
evaluate
o (fun ((th, _), stk) -> stack_out (th, stk))
o cbv_wk
o initial_state rws;;
(*---------------------------------------------------------------------------
* Adding an arbitrary conv
*---------------------------------------------------------------------------*)
let extern_of_conv rws conv tm =
let thm = conv tm in
thm, mk_clos ([], from_term (rws, [], rhs (concl thm)));;
let add_conv (cst, arity, conv) rws =
add_extern (cst, arity, extern_of_conv rws conv) rws;;
let set_skip compset c opt =
try
let name, _ = dest_const c in
set_skip_name compset name opt
with _ -> failwith "set_skip";;
(*---------------------------------------------------------------------------
Support for a global compset.
---------------------------------------------------------------------------*)
let bool_redns =
strictify_thm LET_DEF
:: List.map lazyfy_thm
[COND_CLAUSES; COND_ID; NOT_CLAUSES;
AND_CLAUSES; OR_CLAUSES; IMP_CLAUSES; EQ_CLAUSES];;
let bool_compset () =
let base = from_list bool_redns in
let () = set_skip base `COND: bool -> A -> A -> A` None in
(* change last parameter to SOME 1 to stop CBV_CONV looking at
conditionals' branches before the guard is fully true or false *)
base;;
let the_compset = bool_compset();;
let add_funs = C add_thms the_compset;;
let add_convs = List.iter (C add_conv the_compset);;
let del_consts = List.iter (scrub_const the_compset);;
let del_funs = C scrub_thms the_compset;;
let EVAL_CONV = CBV_CONV the_compset;;
let EVAL_RULE = CONV_RULE EVAL_CONV;;
let EVAL_TAC = CONV_TAC EVAL_CONV;;
let same_const c1 c2 = fst (dest_const c1) = fst (dest_const c2);;
let rec OR = function
| [] -> K false
| [x] -> same_const x
| h :: t -> fun x -> same_const h x || OR t x;;
let RESTR_EVAL_CONV clist =
with_flag (stoppers, Some (OR clist)) EVAL_CONV;;
let RESTR_EVAL_TAC = CONV_TAC o RESTR_EVAL_CONV;;
let RESTR_EVAL_RULE = CONV_RULE o RESTR_EVAL_CONV;;
end;;