1 (* Title: Provers/blast.ML
3 Author: Lawrence C Paulson, Cambridge University Computer Laboratory
4 Copyright 1997 University of Cambridge
6 Generic tableau prover with proof reconstruction
8 SKOLEMIZES ReplaceI WRONGLY: allow new vars in prems, or forbid such rules??
9 Needs explicit instantiation of assumptions?
12 Blast_tac is often more powerful than fast_tac, but has some limitations.
14 * ignores wrappers (addss, addbefore, addafter, addWrapper, ...);
15 this restriction is intrinsic
16 * ignores elimination rules that don't have the correct format
17 (conclusion must be a formula variable)
18 * ignores types, which can make HOL proofs fail
19 * rules must not require higher-order unification, e.g. apply_type in ZF
20 + message "Function Var's argument not a bound variable" relates to this
21 * its proof strategy is more general but can actually be slower
24 "Recursive" chains of rules can sometimes exclude other unsafe formulae
25 from expansion. This happens because newly-created formulae always
26 have priority over existing ones. But obviously recursive rules
27 such as transitivity are treated specially to prevent this. Sometimes
28 the formulae get into the wrong order (see WRONG below).
31 Calls to Blast.overloaded (see HOL/Set.ML for examples) are needed
32 to tell Blast_tac when to retain some type information. Make sure
33 you know the constant's internal name, which might be "op <=" or
36 With substition for equalities (hyp_subst_tac):
37 When substitution affects a haz formula or literal, it is moved
38 back to the list of safe formulae.
39 But there's no way of putting it in the right place. A "moved" or
40 "no DETERM" flag would prevent proofs failing here.
43 (*Should be a type abbreviation?*)
44 type netpair = (int*(bool*thm)) Net.net * (int*(bool*thm)) Net.net;
47 (*Assumptions about constants:
48 --The negation symbol is "Not"
49 --The equality symbol is "op ="
50 --The is-true judgement symbol is "Trueprop"
51 --There are no constants named "*Goal* or "*False*"
53 signature BLAST_DATA =
56 val notE : thm (* [| ~P; P |] ==> R *)
58 val contr_tac : int -> tactic
59 val dup_intr : thm -> thm
60 val hyp_subst_tac : bool ref -> int -> tactic
61 val claset : unit -> claset
62 val rep_cs : (* dependent on classical.ML *)
63 claset -> {safeIs: thm list, safeEs: thm list,
64 hazIs: thm list, hazEs: thm list,
65 swrappers: (string * wrapper) list,
66 uwrappers: (string * wrapper) list,
67 safe0_netpair: netpair, safep_netpair: netpair,
68 haz_netpair: netpair, dup_netpair: netpair, xtra_netpair: ContextRules.netpair}
69 val cla_modifiers: (Args.T list -> (Method.modifier * Args.T list)) list
70 val cla_meth': (claset -> int -> tactic) -> thm list -> Proof.context -> Proof.method
77 exception TRANS of string (*reports translation errors*)
80 | TConst of string * term
81 | Skolem of string * term option ref list
83 | Var of term option ref
88 val depth_tac : claset -> int -> int -> tactic
89 val blast_tac : claset -> int -> tactic
90 val Blast_tac : int -> tactic
91 val overloaded : string * (Term.typ -> Term.typ) -> unit
92 val get_overloads : unit -> (string * (Term.typ -> Term.typ)) list
93 val setup : (theory -> theory) list
97 val fullTrace : branch list list ref
98 val fromType : (indexname * term) list ref -> Term.typ -> term
99 val fromTerm : Term.term -> term
100 val fromSubgoal : Term.term -> term
101 val instVars : term -> (unit -> unit)
102 val toTerm : int -> term -> Term.term
103 val readGoal : Sign.sg -> string -> term
104 val tryInThy : theory -> int -> string ->
105 (int->tactic) list * branch list list * (int*int*exn) list
106 val trygl : claset -> int -> int ->
107 (int->tactic) list * branch list list * (int*int*exn) list
108 val Trygl : int -> int ->
109 (int->tactic) list * branch list list * (int*int*exn) list
110 val normBr : branch -> branch
114 functor BlastFun(Data: BLAST_DATA) : BLAST =
117 type claset = Data.claset;
119 val trace = ref false
120 and stats = ref false; (*for runtime and search statistics*)
124 | TConst of string * term (*type of overloaded constant--as a term!*)
125 | Skolem of string * term option ref list
127 | Var of term option ref
133 (** Basic syntactic operations **)
135 fun is_Var (Var _) = true
138 fun dest_Var (Var x) = x;
143 (* maps (f, [t1,...,tn]) to f(t1,...,tn) *)
144 val list_comb : term * term list -> term = foldl (op $);
147 (* maps f(t1,...,tn) to (f, [t1,...,tn]) ; naturally tail-recursive*)
148 fun strip_comb u : term * term list =
149 let fun stripc (f$t, ts) = stripc (f, t::ts)
154 (* maps f(t1,...,tn) to f , which is never a combination *)
155 fun head_of (f$t) = head_of f
159 (** Particular constants **)
161 fun negate P = Const"Not" $ P;
163 fun mkGoal P = Const"*Goal*" $ P;
165 fun isGoal (Const"*Goal*" $ _) = true
168 val Trueprop = Term.Const("Trueprop", Type("o",[])-->propT);
169 fun mk_tprop P = Term.$ (Trueprop, P);
171 fun isTrueprop (Term.Const("Trueprop",_)) = true
172 | isTrueprop _ = false;
175 (*** Dealing with overloaded constants ***)
177 (*alist is a map from TVar names to Vars. We need to unify the TVars
178 faithfully in order to track overloading*)
179 fun fromType alist (Term.Type(a,Ts)) = list_comb (Const a,
180 map (fromType alist) Ts)
181 | fromType alist (Term.TFree(a,_)) = Free a
182 | fromType alist (Term.TVar (ixn,_)) =
183 (case (assoc_string_int(!alist,ixn)) of
184 None => let val t' = Var(ref None)
185 in alist := (ixn, t') :: !alist; t'
190 val overloads = ref ([]: (string * (Term.typ -> Term.typ)) list)
193 fun overloaded arg = (overloads := arg :: (!overloads));
195 fun get_overloads() = !overloads;
197 (*Convert a possibly overloaded Term.Const to a Blast.Const or Blast.TConst,
198 converting its type to a Blast.term in the latter case.*)
199 fun fromConst alist (a,T) =
200 case assoc_string (!overloads, a) of
201 None => Const a (*not overloaded*)
204 handle Match => error
205 ("Blast_tac: unexpected type for overloaded constant " ^ a)
206 in TConst(a, fromType alist T') end;
211 (*Tests whether 2 terms are alpha-convertible; chases instantiations*)
212 fun (Const a) aconv (Const b) = a=b
213 | (TConst (a,ta)) aconv (TConst (b,tb)) = a=b andalso ta aconv tb
214 | (Skolem (a,_)) aconv (Skolem (b,_)) = a=b (*arglists must then be equal*)
215 | (Free a) aconv (Free b) = a=b
216 | (Var(ref(Some t))) aconv u = t aconv u
217 | t aconv (Var(ref(Some u))) = t aconv u
218 | (Var v) aconv (Var w) = v=w (*both Vars are un-assigned*)
219 | (Bound i) aconv (Bound j) = i=j
220 | (Abs(_,t)) aconv (Abs(_,u)) = t aconv u
221 | (f$t) aconv (g$u) = (f aconv g) andalso (t aconv u)
225 fun mem_term (_, []) = false
226 | mem_term (t, t'::ts) = t aconv t' orelse mem_term(t,ts);
228 fun ins_term(t,ts) = if mem_term(t,ts) then ts else t :: ts;
230 fun mem_var (v: term option ref, []) = false
231 | mem_var (v, v'::vs) = v=v' orelse mem_var(v,vs);
233 fun ins_var(v,vs) = if mem_var(v,vs) then vs else v :: vs;
238 (*Accumulates the Vars in the term, suppressing duplicates*)
239 fun add_term_vars (Skolem(a,args), vars) = add_vars_vars(args,vars)
240 | add_term_vars (Var (v as ref None), vars) = ins_var (v, vars)
241 | add_term_vars (Var (ref (Some u)), vars) = add_term_vars(u,vars)
242 | add_term_vars (TConst (_,t), vars) = add_term_vars(t,vars)
243 | add_term_vars (Abs (_,body), vars) = add_term_vars(body,vars)
244 | add_term_vars (f$t, vars) = add_term_vars (f, add_term_vars(t, vars))
245 | add_term_vars (_, vars) = vars
246 (*Term list version. [The fold functionals are slow]*)
247 and add_terms_vars ([], vars) = vars
248 | add_terms_vars (t::ts, vars) = add_terms_vars (ts, add_term_vars(t,vars))
249 (*Var list version.*)
250 and add_vars_vars ([], vars) = vars
251 | add_vars_vars (ref (Some u) :: vs, vars) =
252 add_vars_vars (vs, add_term_vars(u,vars))
253 | add_vars_vars (v::vs, vars) = (*v must be a ref None*)
254 add_vars_vars (vs, ins_var (v, vars));
257 (*Chase assignments in "vars"; return a list of unassigned variables*)
258 fun vars_in_vars vars = add_vars_vars(vars,[]);
262 (*increment a term's non-local bound variables
263 inc is increment for bound variables
264 lev is level at which a bound variable is considered 'loose'*)
265 fun incr_bv (inc, lev, u as Bound i) = if i>=lev then Bound(i+inc) else u
266 | incr_bv (inc, lev, Abs(a,body)) = Abs(a, incr_bv(inc,lev+1,body))
267 | incr_bv (inc, lev, f$t) = incr_bv(inc,lev,f) $ incr_bv(inc,lev,t)
268 | incr_bv (inc, lev, u) = u;
270 fun incr_boundvars 0 t = t
271 | incr_boundvars inc t = incr_bv(inc,0,t);
274 (*Accumulate all 'loose' bound vars referring to level 'lev' or beyond.
275 (Bound 0) is loose at level 0 *)
276 fun add_loose_bnos (Bound i, lev, js) = if i<lev then js
277 else (i-lev) ins_int js
278 | add_loose_bnos (Abs (_,t), lev, js) = add_loose_bnos (t, lev+1, js)
279 | add_loose_bnos (f$t, lev, js) =
280 add_loose_bnos (f, lev, add_loose_bnos (t, lev, js))
281 | add_loose_bnos (_, _, js) = js;
283 fun loose_bnos t = add_loose_bnos (t, 0, []);
285 fun subst_bound (arg, t) : term =
286 let fun subst (t as Bound i, lev) =
287 if i<lev then t (*var is locally bound*)
288 else if i=lev then incr_boundvars lev arg
289 else Bound(i-1) (*loose: change it*)
290 | subst (Abs(a,body), lev) = Abs(a, subst(body,lev+1))
291 | subst (f$t, lev) = subst(f,lev) $ subst(t,lev)
296 (*Normalize...but not the bodies of ABSTRACTIONS*)
297 fun norm t = case t of
298 Skolem (a,args) => Skolem(a, vars_in_vars args)
299 | TConst(a,aT) => TConst(a, norm aT)
300 | (Var (ref None)) => t
301 | (Var (ref (Some u))) => norm u
302 | (f $ u) => (case norm f of
303 Abs(_,body) => norm (subst_bound (u, body))
308 (*Weak (one-level) normalize for use in unification*)
309 fun wkNormAux t = case t of
310 (Var v) => (case !v of
313 | (f $ u) => (case wkNormAux f of
314 Abs(_,body) => wkNorm (subst_bound (u, body))
316 | Abs (a,body) => (*eta-contract if possible*)
317 (case wkNormAux body of
319 if (0 mem_int loose_bnos f) orelse wkNorm t <> Bound 0
321 else wkNorm (incr_boundvars ~1 f)
324 and wkNorm t = case head_of t of
327 | Skolem(a,args) => t
332 (*Does variable v occur in u? For unification.
333 Dangling bound vars are also forbidden.*)
335 let fun occL lev [] = false (*same as (exists occ), but faster*)
336 | occL lev (u::us) = occ lev u orelse occL lev us
337 and occ lev (Var w) =
339 (case !w of None => false
340 | Some u => occ lev u)
341 | occ lev (Skolem(_,args)) = occL lev (map Var args)
342 (*ignore TConst, since term variables can't occur in types (?) *)
343 | occ lev (Bound i) = lev <= i
344 | occ lev (Abs(_,u)) = occ (lev+1) u
345 | occ lev (f$u) = occ lev u orelse occ lev f
351 val trail = ref [] : term option ref list ref;
355 (*Restore the trail to some previous state: for backtracking*)
359 trail := tl (!trail);
360 ntrail := !ntrail - 1);
363 (*First-order unification with bound variables.
364 "vars" is a list of variables local to the rule and NOT to be put
365 on the trail (no point in doing so)
367 fun unify(vars,t,u) =
369 fun update (t as Var v, u) =
371 else if varOccur v u then raise UNIFY
372 else if mem_var(v, vars) then v := Some u
373 else (*avoid updating Vars in the branch if possible!*)
374 if is_Var u andalso mem_var(dest_Var u, vars)
375 then dest_Var u := Some t
377 trail := v :: !trail; ntrail := !ntrail + 1)
379 case (wkNorm t, wkNorm u) of
380 (nt as Var v, nu) => update(nt,nu)
381 | (nu, nt as Var v) => update(nt,nu)
382 | (TConst(a,at), TConst(b,bt)) => if a=b then unifyAux(at,bt)
384 | (Abs(_,t'), Abs(_,u')) => unifyAux(t',u')
385 (*NB: can yield unifiers having dangling Bound vars!*)
386 | (f$t', g$u') => (unifyAux(f,g); unifyAux(t',u'))
387 | (nt, nu) => if nt aconv nu then () else raise UNIFY
388 in (unifyAux(t,u); true) handle UNIFY => (clearTo n; false)
392 (*Convert from "real" terms to prototerms; eta-contract
393 Code is duplicated with fromSubgoal. Correct this?*)
395 let val alistVar = ref []
396 and alistTVar = ref []
397 fun from (Term.Const aT) = fromConst alistTVar aT
398 | from (Term.Free (a,_)) = Free a
399 | from (Term.Bound i) = Bound i
400 | from (Term.Var (ixn,T)) =
401 (case (assoc_string_int(!alistVar,ixn)) of
402 None => let val t' = Var(ref None)
403 in alistVar := (ixn, t') :: !alistVar; t'
406 | from (Term.Abs (a,_,u)) =
408 u' as (f $ Bound 0) =>
409 if (0 mem_int loose_bnos f) then Abs(a,u')
410 else incr_boundvars ~1 f
412 | from (Term.$ (f,u)) = from f $ from u
415 (*A debugging function: replaces all Vars by dummy Frees for visual inspection
416 of whether they are distinct. Function revert undoes the assignments.*)
418 let val name = ref "a"
420 fun inst (TConst(a,t)) = inst t
421 | inst (Var(v as ref None)) = (updated := v :: (!updated);
422 v := Some (Free ("?" ^ !name));
423 name := Symbol.bump_string (!name))
424 | inst (Abs(a,t)) = inst t
425 | inst (f $ u) = (inst f; inst u)
427 fun revert() = seq (fn v => v:=None) (!updated)
428 in inst t; revert end;
431 (* A1==>...An==>B goes to [A1,...,An], where B is not an implication *)
432 fun strip_imp_prems (Const"==>" $ (Const"Trueprop" $ A) $ B) =
433 A :: strip_imp_prems B
434 | strip_imp_prems (Const"==>" $ A $ B) = A :: strip_imp_prems B
435 | strip_imp_prems _ = [];
437 (* A1==>...An==>B goes to B, where B is not an implication *)
438 fun strip_imp_concl (Const"==>" $ A $ B) = strip_imp_concl B
439 | strip_imp_concl (Const"Trueprop" $ A) = A
440 | strip_imp_concl A = A : term;
443 (*** Conversion of Elimination Rules to Tableau Operations ***)
445 exception ElimBadConcl and ElimBadPrem;
447 (*The conclusion becomes the goal/negated assumption *False*: delete it!
448 If we don't find it then the premise is ill-formed and could cause
450 fun delete_concl [] = raise ElimBadPrem
451 | delete_concl (Const "*Goal*" $ (Var (ref (Some (Const"*False*")))) :: Ps) =
453 | delete_concl (Const "Not" $ (Var (ref (Some (Const"*False*")))) :: Ps) =
455 | delete_concl (P::Ps) = P :: delete_concl Ps;
457 fun skoPrem vars (Const "all" $ Abs (_, P)) =
458 skoPrem vars (subst_bound (Skolem (gensym "S_", vars), P))
459 | skoPrem vars P = P;
462 delete_concl (mkGoal (strip_imp_concl t) :: strip_imp_prems t);
464 (*Expects elimination rules to have a formula variable as conclusion*)
465 fun convertRule vars t =
466 let val (P::Ps) = strip_imp_prems t
467 val Var v = strip_imp_concl t
468 in v := Some (Const"*False*");
469 (P, map (convertPrem o skoPrem vars) Ps)
471 handle Bind => raise ElimBadConcl;
474 (*Like dup_elim, but puts the duplicated major premise FIRST*)
475 fun rev_dup_elim th = th RSN (2, revcut_rl) |> assumption 2 |> Seq.hd;
478 (*Rotate the assumptions in all new subgoals for the LIFO discipline*)
480 (*Count new hyps so that they can be rotated*)
482 | nNewHyps (Const "*Goal*" $ _ :: Ps) = nNewHyps Ps
483 | nNewHyps (P::Ps) = 1 + nNewHyps Ps;
485 fun rot_tac [] i st = Seq.single st
486 | rot_tac (0::ks) i st = rot_tac ks (i+1) st
487 | rot_tac (k::ks) i st = rot_tac ks (i+1) (rotate_rule (~k) i st);
489 fun rot_subgoals_tac (rot, rl) =
490 rot_tac (if rot then map nNewHyps rl else [])
494 fun TRACE rl tac st i = if !trace then (prth rl; tac st i) else tac st i;
496 (*Resolution/matching tactics: if upd then the proof state may be updated.
497 Matching makes the tactics more deterministic in the presence of Vars.*)
498 fun emtac upd rl = TRACE rl (if upd then etac rl else ematch_tac [rl]);
499 fun rmtac upd rl = TRACE rl (if upd then rtac rl else match_tac [rl]);
501 (*Tableau rule from elimination rule.
502 Flag "upd" says that the inference updated the branch.
503 Flag "dup" requests duplication of the affected formula.*)
504 fun fromRule vars rl =
505 let val trl = rl |> rep_thm |> #prop |> fromTerm |> convertRule vars
506 fun tac (upd, dup,rot) i =
507 emtac upd (if dup then rev_dup_elim rl else rl) i
509 rot_subgoals_tac (rot, #2 trl) i
510 in Option.SOME (trl, tac) end
511 handle ElimBadPrem => (*reject: prems don't preserve conclusion*)
512 (warning("Ignoring weak elimination rule\n" ^ string_of_thm rl);
514 | ElimBadConcl => (*ignore: conclusion is not just a variable*)
515 (if !trace then (warning("Ignoring ill-formed elimination rule:\n" ^
516 "conclusion should be a variable\n" ^ string_of_thm rl))
521 (*** Conversion of Introduction Rules ***)
523 fun convertIntrPrem t = mkGoal (strip_imp_concl t) :: strip_imp_prems t;
525 fun convertIntrRule vars t =
526 let val Ps = strip_imp_prems t
527 val P = strip_imp_concl t
528 in (mkGoal P, map (convertIntrPrem o skoPrem vars) Ps)
531 (*Tableau rule from introduction rule.
532 Flag "upd" says that the inference updated the branch.
533 Flag "dup" requests duplication of the affected formula.
534 Since haz rules are now delayed, "dup" is always FALSE for
535 introduction rules.*)
536 fun fromIntrRule vars rl =
537 let val trl = rl |> rep_thm |> #prop |> fromTerm |> convertIntrRule vars
538 fun tac (upd,dup,rot) i =
539 rmtac upd (if dup then Data.dup_intr rl else rl) i
541 rot_subgoals_tac (rot, #2 trl) i
545 val dummyVar = Term.Var (("etc",0), dummyT);
547 (*Convert from prototerms to ordinary terms with dummy types
548 Ignore abstractions; identify all Vars; STOP at given depth*)
549 fun toTerm 0 _ = dummyVar
550 | toTerm d (Const a) = Term.Const (a,dummyT)
551 | toTerm d (TConst(a,_)) = Term.Const (a,dummyT) (*no need to convert type*)
552 | toTerm d (Skolem(a,_)) = Term.Const (a,dummyT)
553 | toTerm d (Free a) = Term.Free (a,dummyT)
554 | toTerm d (Bound i) = Term.Bound i
555 | toTerm d (Var _) = dummyVar
556 | toTerm d (Abs(a,_)) = dummyVar
557 | toTerm d (f $ u) = Term.$ (toTerm d f, toTerm (d-1) u);
560 fun netMkRules P vars (nps: netpair list) =
562 (Const "*Goal*" $ G) =>
563 let val pG = mk_tprop (toTerm 2 G)
564 val intrs = List.concat
565 (map (fn (inet,_) => Net.unify_term inet pG)
567 in map (fromIntrRule vars o #2) (Tactic.orderlist intrs) end
569 let val pP = mk_tprop (toTerm 3 P)
570 val elims = List.concat
571 (map (fn (_,enet) => Net.unify_term enet pP)
573 in List.mapPartial (fromRule vars o #2) (Tactic.orderlist elims) end;
576 (*Pending formulae carry md (may duplicate) flags*)
578 {pairs: ((term*bool) list * (*safe formulae on this level*)
579 (term*bool) list) list, (*haz formulae on this level*)
580 lits: term list, (*literals: irreducible formulae*)
581 vars: term option ref list, (*variables occurring in branch*)
582 lim: int}; (*resource limit*)
584 val fullTrace = ref[] : branch list list ref;
586 (*Normalize a branch--for tracing*)
587 fun norm2 (G,md) = (norm G, md);
589 fun normLev (Gs,Hs) = (map norm2 Gs, map norm2 Hs);
591 fun normBr {pairs, lits, vars, lim} =
592 {pairs = map normLev pairs,
593 lits = map norm lits,
598 val dummyTVar = Term.TVar(("a",0), []);
599 val dummyVar2 = Term.Var(("var",0), dummyT);
601 (*convert Blast_tac's type representation to real types for tracing*)
602 fun showType (Free a) = Term.TFree (a,[])
603 | showType (Var _) = dummyTVar
605 (case strip_comb t of
606 (Const a, us) => Term.Type(a, map showType us)
609 (*Display top-level overloading if any*)
610 fun topType (TConst(a,t)) = Some (showType t)
611 | topType (Abs(a,t)) = topType t
612 | topType (f $ u) = (case topType f of
618 (*Convert from prototerms to ordinary terms with dummy types for tracing*)
619 fun showTerm d (Const a) = Term.Const (a,dummyT)
620 | showTerm d (TConst(a,_)) = Term.Const (a,dummyT)
621 | showTerm d (Skolem(a,_)) = Term.Const (a,dummyT)
622 | showTerm d (Free a) = Term.Free (a,dummyT)
623 | showTerm d (Bound i) = Term.Bound i
624 | showTerm d (Var(ref(Some u))) = showTerm d u
625 | showTerm d (Var(ref None)) = dummyVar2
626 | showTerm d (Abs(a,t)) = if d=0 then dummyVar
627 else Term.Abs(a, dummyT, showTerm (d-1) t)
628 | showTerm d (f $ u) = if d=0 then dummyVar
629 else Term.$ (showTerm d f, showTerm (d-1) u);
631 fun string_of sign d t = Sign.string_of_term sign (showTerm d t);
633 fun traceTerm sign t =
635 val stm = string_of sign 8 t'
638 None => stm (*no type to attach*)
639 | Some T => stm ^ "\t:: " ^ Sign.string_of_typ sign T
643 val prs = std_output;
645 (*Print tracing information at each iteration of prover*)
646 fun tracing sign brs =
647 let fun printPairs (((G,_)::_,_)::_) = prs(traceTerm sign G)
648 | printPairs (([],(H,_)::_)::_) = prs(traceTerm sign H ^ "\t (Unsafe)")
650 fun printBrs (brs0 as {pairs, lits, lim, ...} :: brs) =
651 (fullTrace := brs0 :: !fullTrace;
652 seq (fn _ => prs "+") brs;
653 prs (" [" ^ Int.toString lim ^ "] ");
656 in if !trace then printBrs (map normBr brs) else ()
659 fun traceMsg s = if !trace then writeln s else ();
661 (*Tracing: variables updated in the last branch operation?*)
662 fun traceVars sign ntrl =
664 (case !ntrail-ntrl of
666 | 1 => prs"\t1 variable UPDATED:"
667 | n => prs("\t" ^ Int.toString n ^ " variables UPDATED:");
668 (*display the instantiations themselves, though no variable names*)
669 seq (fn v => prs(" " ^ string_of sign 4 (the (!v))))
670 (List.take(!trail, !ntrail-ntrl));
674 (*Tracing: how many new branches are created?*)
678 0 => prs"branch closed by rule"
679 | 1 => prs"branch extended (1 new subgoal)"
680 | n => prs("branch split: "^ Int.toString n ^ " new subgoals")
685 (*** Code for handling equality: naive substitution, like hyp_subst_tac ***)
687 (*Replace the ATOMIC term "old" by "new" in t*)
688 fun subst_atomic (old,new) t =
689 let fun subst (Var(ref(Some u))) = subst u
690 | subst (Abs(a,body)) = Abs(a, subst body)
691 | subst (f$t) = subst f $ subst t
692 | subst t = if t aconv old then new else t
695 (*Eta-contract a term from outside: just enough to reduce it to an atom*)
696 fun eta_contract_atom (t0 as Abs(a, body)) =
697 (case eta_contract2 body of
698 f $ Bound 0 => if (0 mem_int loose_bnos f) then t0
699 else eta_contract_atom (incr_boundvars ~1 f)
701 | eta_contract_atom t = t
702 and eta_contract2 (f$t) = f $ eta_contract_atom t
703 | eta_contract2 t = eta_contract_atom t;
706 (*When can we safely delete the equality?
707 Not if it equates two constants; consider 0=1.
708 Not if it resembles x=t[x], since substitution does not eliminate x.
709 Not if it resembles ?x=0; another goal could instantiate ?x to Suc(i)
710 Prefer to eliminate Bound variables if possible.
711 Result: true = use as is, false = reorient first *)
713 (*Can t occur in u? For substitution.
714 Does NOT examine the args of Skolem terms: substitution does not affect them.
715 REFLEXIVE because hyp_subst_tac fails on x=x.*)
717 let (*NO vars are permitted in u except the arguments of t, if it is
718 a Skolem term. This ensures that no equations are deleted that could
719 be instantiated to a cycle. For example, x=?a is rejected because ?a
720 could be instantiated to Suc(x).*)
722 Skolem(_,vars) => vars
724 fun occEq u = (t aconv u) orelse occ u
725 and occ (Var(ref(Some u))) = occEq u
726 | occ (Var v) = not (mem_var (v, vars))
727 | occ (Abs(_,u)) = occEq u
728 | occ (f$u) = occEq u orelse occEq f
734 (*Take apart an equality (plain or overloaded). NO constant Trueprop*)
735 fun dest_eq (Const "op =" $ t $ u) =
736 (eta_contract_atom t, eta_contract_atom u)
737 | dest_eq (TConst("op =",_) $ t $ u) =
738 (eta_contract_atom t, eta_contract_atom u)
739 | dest_eq _ = raise DEST_EQ;
741 (*Reject the equality if u occurs in (or equals!) t*)
742 fun check (t,u,v) = if substOccur t u then raise DEST_EQ else v;
744 (*IF the goal is an equality with a substitutable variable
745 THEN orient that equality ELSE raise exception DEST_EQ*)
746 fun orientGoal (t,u) = case (t,u) of
747 (Skolem _, _) => check(t,u,(t,u)) (*eliminates t*)
748 | (_, Skolem _) => check(u,t,(u,t)) (*eliminates u*)
749 | (Free _, _) => check(t,u,(t,u)) (*eliminates t*)
750 | (_, Free _) => check(u,t,(u,t)) (*eliminates u*)
751 | _ => raise DEST_EQ;
753 (*Substitute through the branch if an equality goal (else raise DEST_EQ).
754 Moves affected literals back into the branch, but it is not clear where
755 they should go: this could make proofs fail.*)
756 fun equalSubst sign (G, {pairs, lits, vars, lim}) =
757 let val (t,u) = orientGoal(dest_eq G)
758 val subst = subst_atomic (t,u)
759 fun subst2(G,md) = (subst G, md)
760 (*substitute throughout list; extract affected formulae*)
761 fun subForm ((G,md), (changed, pairs)) =
763 in if nG aconv G then (changed, (G,md)::pairs)
764 else ((nG,md)::changed, pairs)
766 (*substitute throughout "stack frame"; extract affected formulae*)
767 fun subFrame ((Gs,Hs), (changed, frames)) =
768 let val (changed', Gs') = foldr subForm (Gs, (changed, []))
769 val (changed'', Hs') = foldr subForm (Hs, (changed', []))
770 in (changed'', (Gs',Hs')::frames) end
771 (*substitute throughout literals; extract affected ones*)
772 fun subLit (lit, (changed, nlits)) =
773 let val nlit = subst lit
774 in if nlit aconv lit then (changed, nlit::nlits)
775 else ((nlit,true)::changed, nlits)
777 val (changed, lits') = foldr subLit (lits, ([], []))
778 val (changed', pairs') = foldr subFrame (pairs, (changed, []))
779 in if !trace then writeln ("Substituting " ^ traceTerm sign u ^
780 " for " ^ traceTerm sign t ^ " in branch" )
782 {pairs = (changed',[])::pairs', (*affected formulas, and others*)
783 lits = lits', (*unaffected literals*)
789 exception NEWBRANCHES and CLOSEF;
793 (*Trying eq_contr_tac first INCREASES the effort, slowing reconstruction*)
794 val contr_tac = ematch_tac [Data.notE] THEN'
795 (eq_assume_tac ORELSE' assume_tac);
797 val eContr_tac = TRACE Data.notE contr_tac;
798 val eAssume_tac = TRACE asm_rl (eq_assume_tac ORELSE' assume_tac);
800 (*Try to unify complementary literals and return the corresponding tactic. *)
801 fun tryClose (Const"*Goal*" $ G, L) =
802 if unify([],G,L) then Some eAssume_tac else None
803 | tryClose (G, Const"*Goal*" $ L) =
804 if unify([],G,L) then Some eAssume_tac else None
805 | tryClose (Const"Not" $ G, L) =
806 if unify([],G,L) then Some eContr_tac else None
807 | tryClose (G, Const"Not" $ L) =
808 if unify([],G,L) then Some eContr_tac else None
812 (*Were there Skolem terms in the premise? Must NOT chase Vars*)
813 fun hasSkolem (Skolem _) = true
814 | hasSkolem (Abs (_,body)) = hasSkolem body
815 | hasSkolem (f$t) = hasSkolem f orelse hasSkolem t
816 | hasSkolem _ = false;
818 (*Attach the right "may duplicate" flag to new formulae: if they contain
819 Skolem terms then allow duplication.*)
820 fun joinMd md [] = []
821 | joinMd md (G::Gs) = (G, hasSkolem G orelse md) :: joinMd md Gs;
823 (*Convert a Goal to an ordinary Not. Used also in dup_intr, where a goal like
824 Ex(P) is duplicated as the assumption ~Ex(P). *)
825 fun negOfGoal (Const"*Goal*" $ G) = negate G
828 fun negOfGoal2 (G,md) = (negOfGoal G, md);
830 (*Converts all Goals to Nots in the safe parts of a branch. They could
831 have been moved there from the literals list after substitution (equalSubst).
832 There can be at most one--this function could be made more efficient.*)
833 fun negOfGoals pairs = map (fn (Gs,haz) => (map negOfGoal2 Gs, haz)) pairs;
835 (*Tactic. Convert *Goal* to negated assumption in FIRST position*)
836 fun negOfGoal_tac i = TRACE Data.ccontr (rtac Data.ccontr) i THEN
840 (** Backtracking and Pruning **)
842 (*clashVar vars (n,trail) determines whether any of the last n elements
843 of "trail" occur in "vars" OR in their instantiations*)
844 fun clashVar [] = (fn _ => false)
846 let fun clash (0, _) = false
847 | clash (_, []) = false
848 | clash (n, v::vs) = exists (varOccur v) vars orelse clash(n-1,vs)
852 (*nbrs = # of branches just prior to closing this one. Delete choice points
853 for goals proved by the latest inference, provided NO variables in the
854 next branch have been updated.*)
855 fun prune (1, nxtVars, choices) = choices (*DON'T prune at very end: allow
856 backtracking over bad proofs*)
857 | prune (nbrs, nxtVars, choices) =
858 let fun traceIt last =
859 let val ll = length last
860 and lc = length choices
861 in if !trace andalso ll<lc then
862 (writeln("Pruning " ^ Int.toString(lc-ll) ^ " levels");
866 fun pruneAux (last, _, _, []) = last
867 | pruneAux (last, ntrl, trl, (ntrl',nbrs',exn) :: choices) =
869 then last (*don't backtrack beyond first solution of goal*)
870 else if nbrs' > nbrs then pruneAux (last, ntrl, trl, choices)
871 else (* nbrs'=nbrs *)
872 if clashVar nxtVars (ntrl-ntrl', trl) then last
873 else (*no clashes: can go back at least this far!*)
874 pruneAux(choices, ntrl', List.drop(trl, ntrl-ntrl'),
876 in traceIt (pruneAux (choices, !ntrail, !trail, choices)) end;
878 fun nextVars ({pairs, lits, vars, lim} :: _) = map Var vars
881 fun backtrack (choices as (ntrl, nbrs, exn)::_) =
882 (if !trace then (writeln ("Backtracking; now there are " ^
883 Int.toString nbrs ^ " branches"))
886 | backtrack _ = raise PROVE;
888 (*Add the literal G, handling *Goal* and detecting duplicates.*)
889 fun addLit (Const "*Goal*" $ G, lits) =
890 (*New literal is a *Goal*, so change all other Goals to Nots*)
891 let fun bad (Const"*Goal*" $ _) = true
892 | bad (Const"Not" $ G') = G aconv G'
895 | change (Const"*Goal*" $ G' :: lits) =
896 if G aconv G' then change lits
897 else Const"Not" $ G' :: change lits
898 | change (Const"Not" $ G' :: lits) =
899 if G aconv G' then change lits
900 else Const"Not" $ G' :: change lits
901 | change (lit::lits) = lit :: change lits
903 Const "*Goal*" $ G :: (if exists bad lits then change lits else lits)
905 | addLit (G,lits) = ins_term(G, lits)
908 (*For calculating the "penalty" to assess on a branching factor of n
909 log2 seems a little too severe*)
910 fun log n = if n<4 then 0 else 1 + log(n div 4);
913 (*match(t,u) says whether the term u might be an instance of the pattern t
914 Used to detect "recursive" rules such as transitivity*)
915 fun match (Var _) u = true
916 | match (Const"*Goal*") (Const"Not") = true
917 | match (Const"Not") (Const"*Goal*") = true
918 | match (Const a) (Const b) = (a=b)
919 | match (TConst (a,ta)) (TConst (b,tb)) = a=b andalso match ta tb
920 | match (Free a) (Free b) = (a=b)
921 | match (Bound i) (Bound j) = (i=j)
922 | match (Abs(_,t)) (Abs(_,u)) = match t u
923 | match (f$t) (g$u) = match f g andalso match t u
927 (*Branches closed: number of branches closed during the search
928 Branches tried: number of branches created by splitting (counting from 1)*)
932 fun printStats (b, start, tacs) =
934 writeln (endTiming start ^ " for search. Closed: "
935 ^ Int.toString (!nclosed) ^
936 " tried: " ^ Int.toString (!ntried) ^
937 " tactics: " ^ Int.toString (length tacs))
941 (*Tableau prover based on leanTaP. Argument is a list of branches. Each
942 branch contains a list of unexpanded formulae, a list of literals, and a
943 bound on unsafe expansions.
944 "start" is CPU time at start, for printing search time
946 fun prove (sign, start, cs, brs, cont) =
947 let val {safe0_netpair, safep_netpair, haz_netpair, ...} = Data.rep_cs cs
948 val safeList = [safe0_netpair, safep_netpair]
949 and hazList = [haz_netpair]
950 fun prv (tacs, trs, choices, []) =
951 (printStats (!trace orelse !stats, start, tacs);
952 cont (tacs, trs, choices)) (*all branches closed!*)
953 | prv (tacs, trs, choices,
954 brs0 as {pairs = ((G,md)::br, haz)::pairs,
955 lits, vars, lim} :: brs) =
956 (*apply a safe rule only (possibly allowing instantiation);
957 defer any haz formulae*)
958 let exception PRV (*backtrack to precisely this recursion!*)
960 val nbrs = length brs0
961 val nxtVars = nextVars brs
963 val rules = netMkRules G vars safeList
964 (*Make a new branch, decrementing "lim" if instantiations occur*)
965 fun newBr (vars',lim') prems =
967 if (exists isGoal prem)
968 then {pairs = ((joinMd md prem, []) ::
969 negOfGoals ((br, haz)::pairs)),
970 lits = map negOfGoal lits,
973 else {pairs = ((joinMd md prem, []) ::
980 (*Seek a matching rule. If unifiable then add new premises
982 fun deeper [] = raise NEWBRANCHES
983 | deeper (((P,prems),tac)::grls) =
984 if unify(add_term_vars(P,[]), P, G)
985 then (*P comes from the rule; G comes from the branch.*)
986 let val updated = ntrl < !ntrail (*branch updated*)
987 val lim' = if updated
988 then lim - (1+log(length rules))
989 else lim (*discourage branching updates*)
990 val vars = vars_in_vars vars
991 val vars' = foldr add_terms_vars (prems, vars)
992 val choices' = (ntrl, nbrs, PRV) :: choices
993 val tacs' = (tac(updated,false,true))
994 :: tacs (*no duplication; rotate*)
996 traceNew prems; traceVars sign ntrl;
997 (if null prems then (*closed the branch: prune!*)
998 (nclosed := !nclosed + 1;
999 prv(tacs', brs0::trs,
1000 prune (nbrs, nxtVars, choices'),
1002 else (*prems non-null*)
1003 if lim'<0 (*faster to kill ALL the alternatives*)
1004 then (traceMsg"Excessive branching: KILLED";
1005 clearTo ntrl; raise NEWBRANCHES)
1007 (ntried := !ntried + length prems - 1;
1008 prv(tacs', brs0::trs, choices',
1009 newBr (vars',lim') prems)))
1012 (*Backtrack at this level.
1013 Reset Vars and try another rule*)
1014 (clearTo ntrl; deeper grls)
1015 else (*backtrack to previous level*)
1019 (*Try to close branch by unifying with head goal*)
1020 fun closeF [] = raise CLOSEF
1022 case tryClose(G,L) of
1026 (if !trace then (prs"branch closed";
1027 traceVars sign ntrl)
1029 prune (nbrs, nxtVars,
1030 (ntrl, nbrs, PRV) :: choices))
1031 in nclosed := !nclosed + 1;
1032 prv (tac::tacs, brs0::trs, choices', brs)
1034 (*reset Vars and try another literal
1035 [this handler is pruned if possible!]*)
1036 (clearTo ntrl; closeF Ls)
1038 (*Try to unify a queued formula (safe or haz) with head goal*)
1039 fun closeFl [] = raise CLOSEF
1040 | closeFl ((br, haz)::pairs) =
1042 handle CLOSEF => closeF (map fst haz)
1043 handle CLOSEF => closeFl pairs
1044 in tracing sign brs0;
1045 if lim<0 then (traceMsg "Limit reached. "; backtrack choices)
1047 prv (Data.hyp_subst_tac trace :: tacs,
1050 (G, {pairs = (br,haz)::pairs,
1051 lits = lits, vars = vars, lim = lim})
1053 handle DEST_EQ => closeF lits
1054 handle CLOSEF => closeFl ((br,haz)::pairs)
1055 handle CLOSEF => deeper rules
1056 handle NEWBRANCHES =>
1057 (case netMkRules G vars hazList of
1058 [] => (*there are no plausible haz rules*)
1059 (traceMsg "moving formula to literals";
1060 prv (tacs, brs0::trs, choices,
1061 {pairs = (br,haz)::pairs,
1062 lits = addLit(G,lits),
1065 | _ => (*G admits some haz rules: try later*)
1066 (traceMsg "moving formula to haz list";
1067 prv (if isGoal G then negOfGoal_tac :: tacs
1071 {pairs = (br, haz@[(negOfGoal G, md)])::pairs,
1074 lim = lim} :: brs)))
1076 | prv (tacs, trs, choices,
1077 {pairs = ([],haz)::(Gs,haz')::pairs, lits, vars, lim} :: brs) =
1078 (*no more "safe" formulae: transfer haz down a level*)
1079 prv (tacs, trs, choices,
1080 {pairs = (Gs,haz@haz')::pairs,
1084 | prv (tacs, trs, choices,
1085 brs0 as {pairs = [([], (H,md)::Hs)],
1086 lits, vars, lim} :: brs) =
1087 (*no safe steps possible at any level: apply a haz rule*)
1088 let exception PRV (*backtrack to precisely this recursion!*)
1091 val rules = netMkRules H vars hazList
1092 (*new premises of haz rules may NOT be duplicated*)
1093 fun newPrem (vars,P,dup,lim') prem =
1094 let val Gs' = map (fn Q => (Q,false)) prem
1095 and Hs' = if dup then Hs @ [(negOfGoal H, md)] else Hs
1096 and lits' = if (exists isGoal prem)
1097 then map negOfGoal lits
1099 in {pairs = if exists (match P) prem then [(Gs',Hs')]
1100 (*Recursive in this premise. Don't make new
1101 "stack frame". New haz premises will end up
1102 at the BACK of the queue, preventing
1103 exclusion of others*)
1104 else [(Gs',[]), ([],Hs')],
1109 fun newBr x prems = map (newPrem x) prems @ brs
1110 (*Seek a matching rule. If unifiable then add new premises
1112 fun deeper [] = raise NEWBRANCHES
1113 | deeper (((P,prems),tac)::grls) =
1114 if unify(add_term_vars(P,[]), P, H)
1116 let val updated = ntrl < !ntrail (*branch updated*)
1117 val vars = vars_in_vars vars
1118 val vars' = foldr add_terms_vars (prems, vars)
1119 (*duplicate H if md permits*)
1120 val dup = md (*earlier had "andalso vars' <> vars":
1121 duplicate only if the subgoal has new vars*)
1122 (*any instances of P in the subgoals?
1123 NB: boolean "recur" affects tracing only!*)
1124 and recur = exists (exists (match P)) prems
1125 val lim' = (*Decrement "lim" extra if updates occur*)
1126 if updated then lim - (1+log(length rules))
1128 (*It is tempting to leave "lim" UNCHANGED if
1129 both dup and recur are false. Proofs are
1130 found at shallower depths, but looping
1131 occurs too often...*)
1133 (*Allowing backtracking from a rule application
1134 if other matching rules exist, if the rule
1135 updated variables, or if the rule did not
1136 introduce new variables. This latter condition
1137 means it is not a standard "gamma-rule" but
1138 some other form of unsafe rule. Aim is to
1139 emulate Fast_tac, which allows all unsafe steps
1141 not(null grls) (*other rules to try?*)
1143 orelse vars=vars' (*no new Vars?*)
1144 val tac' = tac(updated, dup, true)
1145 (*if recur then perhaps shouldn't call rotate_tac: new
1146 formulae should be last, but that's WRONG if the new
1147 formulae are Goals, since they remain in the first
1151 if lim'<0 andalso not (null prems)
1152 then (*it's faster to kill ALL the alternatives*)
1153 (traceMsg"Excessive branching: KILLED";
1154 clearTo ntrl; raise NEWBRANCHES)
1157 if !trace andalso dup then prs" (duplicating)"
1159 if !trace andalso recur then prs" (recursive)"
1161 traceVars sign ntrl;
1162 if null prems then nclosed := !nclosed + 1
1163 else ntried := !ntried + length prems - 1;
1166 (ntrl, length brs0, PRV) :: choices,
1167 newBr (vars', P, dup, lim') prems)
1170 then (*reset Vars and try another rule*)
1171 (clearTo ntrl; deeper grls)
1172 else (*backtrack to previous level*)
1176 in tracing sign brs0;
1177 if lim<1 then (traceMsg "Limit reached. "; backtrack choices)
1179 handle NEWBRANCHES =>
1180 (*cannot close branch: move H to literals*)
1181 prv (tacs, brs0::trs, choices,
1182 {pairs = [([], Hs)],
1187 | prv (tacs, trs, choices, _ :: brs) = backtrack choices
1188 in prv ([], [], [(!ntrail, length brs, PROVE)], brs) end;
1191 (*Construct an initial branch.*)
1192 fun initBranch (ts,lim) =
1193 {pairs = [(map (fn t => (t,true)) ts, [])],
1195 vars = add_terms_vars (ts,[]),
1199 (*** Conversion & Skolemization of the Isabelle proof state ***)
1201 (*Make a list of all the parameters in a subgoal, even if nested*)
1204 fun discard_foralls (Const("all",_)$Abs(a,T,t)) = discard_foralls t
1205 | discard_foralls t = t;
1209 (*List of variables not appearing as arguments to the given parameter*)
1210 fun getVars [] i = []
1211 | getVars ((_,(v,is))::alist) i =
1212 if i mem is then getVars alist i
1213 else v :: getVars alist i;
1215 exception TRANS of string;
1217 (*Translation of a subgoal: Skolemize all parameters*)
1219 let val alistVar = ref []
1220 and alistTVar = ref []
1221 fun hdvar ((ix,(v,is))::_) = v
1223 let val (ht,ts) = Term.strip_comb t
1224 fun apply u = list_comb (u, map (from lev) ts)
1226 | bounds (Term.Bound i::ts) =
1227 if i<lev then raise TRANS
1228 "Function unknown's argument not a parameter"
1229 else i-lev :: bounds ts
1230 | bounds ts = raise TRANS
1231 "Function unknown's argument not a bound variable"
1234 Term.Const aT => apply (fromConst alistTVar aT)
1235 | Term.Free (a,_) => apply (Free a)
1236 | Term.Bound i => apply (Bound i)
1237 | Term.Var (ix,_) =>
1238 (case (assoc_string_int(!alistVar,ix)) of
1239 None => (alistVar := (ix, (ref None, bounds ts))
1241 Var (hdvar(!alistVar)))
1242 | Some(v,is) => if is=bounds ts then Var v
1244 ("Discrepancy among occurrences of "
1245 ^ Syntax.string_of_vname ix))
1246 | Term.Abs (a,_,body) =>
1247 if null ts then Abs(a, from (lev+1) body)
1248 else raise TRANS "argument not in normal form"
1251 val npars = length (Logic.strip_params t)
1253 (*Skolemize a subgoal from a proof state*)
1254 fun skoSubgoal i t =
1257 (subst_bound (Skolem (gensym "T_", getVars (!alistVar) i),
1261 in skoSubgoal 0 (from 0 (discard_foralls t)) end;
1265 (fullTrace:=[]; trail := []; ntrail := 0;
1266 nclosed := 0; ntried := 1);
1269 (*Tactic using tableau engine and proof reconstruction.
1270 "start" is CPU time at start, for printing SEARCH time
1271 (also prints reconstruction time)
1272 "lim" is depth limit.*)
1273 fun timing_depth_tac start cs lim i st0 =
1275 let val st = ObjectLogic.atomize_goal i st0;
1276 val {sign,...} = rep_thm st
1277 val skoprem = fromSubgoal (List.nth(prems_of st, i-1))
1278 val hyps = strip_imp_prems skoprem
1279 and concl = strip_imp_concl skoprem
1280 fun cont (tacs,_,choices) =
1281 let val start = startTiming()
1283 case Seq.pull(EVERY' (rev tacs) i st) of
1284 None => (writeln ("PROOF FAILED for depth " ^
1286 if !trace then writeln "************************\n"
1289 | cell => (if (!trace orelse !stats)
1290 then writeln (endTiming start ^ " for reconstruction")
1292 Seq.make(fn()=> cell))
1294 in prove (sign, start, cs, [initBranch (mkGoal concl :: hyps, lim)], cont)
1296 handle PROVE => Seq.empty);
1298 (*Public version with fixed depth*)
1299 fun depth_tac cs lim i st = timing_depth_tac (startTiming()) cs lim i st;
1301 fun blast_tac cs i st =
1302 ((DEEPEN (1,20) (timing_depth_tac (startTiming()) cs) 0) i
1303 THEN flexflex_tac) st
1304 handle TRANS s => (warning ("Blast_tac: " ^ s); Seq.empty);
1306 fun Blast_tac i = blast_tac (Data.claset()) i;
1309 (*** For debugging: these apply the prover to a subgoal and return
1310 the resulting tactics, trace, etc. ***)
1312 (*Translate subgoal i from a proof state*)
1313 fun trygl cs lim i =
1315 let val st = topthm()
1316 val {sign,...} = rep_thm st
1317 val skoprem = fromSubgoal (List.nth(prems_of st, i-1))
1318 val hyps = strip_imp_prems skoprem
1319 and concl = strip_imp_concl skoprem
1320 in timeap prove (sign, startTiming(), cs,
1321 [initBranch (mkGoal concl :: hyps, lim)], I)
1323 handle Subscript => error("There is no subgoal " ^ Int.toString i));
1325 fun Trygl lim i = trygl (Data.claset()) lim i;
1327 (*Read a string to make an initial, singleton branch*)
1328 fun readGoal sign s = read_cterm sign (s,propT) |>
1329 term_of |> fromTerm |> rand |> mkGoal;
1331 fun tryInThy thy lim s =
1333 timeap prove (Theory.sign_of thy,
1336 [initBranch ([readGoal (Theory.sign_of thy) s], lim)],
1340 (** method setup **)
1343 Method.bang_sectioned_args'
1344 Data.cla_modifiers (Scan.lift (Scan.option Args.nat)) m;
1346 fun blast_meth None = Data.cla_meth' blast_tac
1347 | blast_meth (Some lim) = Data.cla_meth' (fn cs => depth_tac cs lim);
1349 val setup = [Method.add_methods [("blast", blast_args blast_meth,
1350 "tableau prover")]];