(* Copyright (C) 2002, HELM Team. * * This file is part of HELM, an Hypertextual, Electronic * Library of Mathematics, developed at the Computer Science * Department, University of Bologna, Italy. * * HELM is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * HELM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HELM; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. * * For details, see the HELM World-Wide-Web page, * http://cs.unibo.it/helm/. *) open CicReduction open ProofEngineTypes open UriManager (** DEBUGGING *) (** perform debugging output? *) let debug = false let debug_print = fun _ -> () (** debugging print *) let warn s = if debug then debug_print ("TACTICALS WARNING: " ^ s) let id_tac = let id_tac (proof,goal) = let _, metasenv, _, _ = proof in let _, _, _ = CicUtil.lookup_meta goal metasenv in (proof,[goal]) in mk_tactic id_tac let fail_tac = let fail_tac (proof,goal) = let _, metasenv, _, _ = proof in let _, _, _ = CicUtil.lookup_meta goal metasenv in raise (Fail "fail tactical") in mk_tactic fail_tac module type Status = sig type input_status type output_status type tactic val id_tac : tactic val mk_tactic : (input_status -> output_status) -> tactic val apply_tactic : tactic -> input_status -> output_status val goals : output_status -> ProofEngineTypes.goal list val set_goals: output_status -> ProofEngineTypes.goal list -> output_status val focus : output_status -> ProofEngineTypes.goal -> input_status end module type T = sig type tactic val first: tactics: (string * tactic) list -> tactic val thens: start: tactic -> continuations: tactic list -> tactic val then_: start: tactic -> continuation: tactic -> tactic (** "folding" of then_ *) val seq: tactics: tactic list -> tactic val repeat_tactic: tactic: tactic -> tactic val do_tactic: n: int -> tactic: tactic -> tactic val try_tactic: tactic: tactic -> tactic val solve_tactics: tactics: (string * tactic) list -> tactic end module Make (S:Status) : T with type tactic = S.tactic = struct type tactic = S.tactic (** naive implementation of ORELSE tactical, try a sequence of tactics in turn: if one fails pass to the next one and so on, eventually raises (failure "no tactics left") *) let first ~tactics = let rec first ~(tactics: (string * tactic) list) status = warn "in Tacticals.first"; match tactics with | (descr, tac)::tactics -> warn ("Tacticals.first IS TRYING " ^ descr); (try let res = S.apply_tactic tac status in warn ("Tacticals.first: " ^ descr ^ " succedeed!!!"); res with e -> match e with (Fail _) | (CicTypeChecker.TypeCheckerFailure _) | (CicUnification.UnificationFailure _) -> warn ( "Tacticals.first failed with exn: " ^ Printexc.to_string e); first ~tactics status | _ -> raise e (* [e] must not be caught ; let's re-raise it *) ) | [] -> raise (Fail "first: no tactics left") in S.mk_tactic (first ~tactics) let thens ~start ~continuations = let thens ~start ~continuations status = let output_status = S.apply_tactic start status in let new_goals = S.goals output_status in try let output_status,goals = List.fold_left2 (fun (output_status,goals) goal tactic -> let status = S.focus output_status goal in let output_status' = S.apply_tactic tactic status in let new_goals' = S.goals output_status' in (output_status',goals@new_goals') ) (output_status,[]) new_goals continuations in S.set_goals output_status goals with Invalid_argument _ -> let debug = Printf.sprintf "thens: expected %i new goals, found %i" (List.length continuations) (List.length new_goals) in raise (Fail debug) in S.mk_tactic (thens ~start ~continuations ) let then_ ~start ~continuation = let then_ ~start ~continuation status = let output_status = S.apply_tactic start status in let new_goals = S.goals output_status in let output_status,goals = List.fold_left (fun (output_status,goals) goal -> let status = S.focus output_status goal in let output_status' = S.apply_tactic continuation status in let new_goals' = S.goals output_status' in (output_status',goals@new_goals') ) (output_status,[]) new_goals in S.set_goals output_status goals in S.mk_tactic (then_ ~start ~continuation) let rec seq ~tactics = match tactics with | [] -> assert false | [tac] -> tac | hd :: tl -> then_ ~start:hd ~continuation:(seq ~tactics:tl) (* TODO: x debug: i due tatticali seguenti non contano quante volte hanno applicato la tattica *) (* This keep on appling tactic until it fails *) (* When generates more than one goal, you have a tree of application on the tactic, repeat_tactic works in depth on this tree *) let repeat_tactic ~tactic = let rec repeat_tactic ~tactic status = warn "in repeat_tactic"; try let output_status = S.apply_tactic tactic status in let goallist = S.goals output_status in let rec step output_status goallist = match goallist with [] -> output_status,[] | head::tail -> let status = S.focus output_status head in let output_status' = repeat_tactic ~tactic status in let goallist' = S.goals output_status' in let output_status'',goallist'' = step output_status' tail in output_status'',goallist'@goallist'' in let output_status,goallist = step output_status goallist in S.set_goals output_status goallist with (Fail _) as e -> warn ("Tacticals.repeat_tactic failed after nth time with exception: " ^ Printexc.to_string e) ; S.apply_tactic S.id_tac status in S.mk_tactic (repeat_tactic ~tactic) (* This tries to apply tactic n times *) let do_tactic ~n ~tactic = let rec do_tactic ~n ~tactic status = warn "in do_tactic"; try let output_status = if (n>0) then S.apply_tactic tactic status else S.apply_tactic S.id_tac status in let goallist = S.goals output_status in (* else (proof, []) in *) (* perche' non va bene questo? stessa questione di ##### ? *) let rec step output_status goallist = match goallist with [] -> output_status, [] | head::tail -> let status = S.focus output_status head in let output_status' = do_tactic ~n:(n-1) ~tactic status in let goallist' = S.goals output_status' in let (output_status'', goallist'') = step output_status' tail in output_status'', goallist'@goallist'' in let output_status,goals = step output_status goallist in S.set_goals output_status goals with (Fail _) as e -> warn ("Tacticals.do_tactic failed after nth time with exception: " ^ Printexc.to_string e) ; S.apply_tactic S.id_tac status in S.mk_tactic (do_tactic ~n ~tactic) (* This applies tactic and catches its possible failure *) let try_tactic ~tactic = let rec try_tactic ~tactic status = warn "in Tacticals.try_tactic"; try S.apply_tactic tactic status with (Fail _) as e -> warn ( "Tacticals.try_tactic failed with exn: " ^ Printexc.to_string e); S.apply_tactic S.id_tac status in S.mk_tactic (try_tactic ~tactic) (* This tries tactics until one of them doesn't _solve_ the goal *) (* TODO: si puo' unificare le 2(due) chiamate ricorsive? *) let solve_tactics ~tactics = let rec solve_tactics ~(tactics: (string * tactic) list) status = warn "in Tacticals.solve_tactics"; match tactics with | (descr, currenttactic)::moretactics -> warn ("Tacticals.solve_tactics is trying " ^ descr); (try let output_status = S.apply_tactic currenttactic status in let goallist = S.goals output_status in match goallist with [] -> warn ("Tacticals.solve_tactics: " ^ descr ^ " solved the goal!!!"); (* questo significa che non ci sono piu' goal, o che current_tactic non ne ha aperti di nuovi? (la 2a!) ##### nel secondo caso basta per dire che solve_tactics has solved the goal? (si!) *) output_status | _ -> warn ("Tacticals.solve_tactics: try the next tactic"); solve_tactics ~tactics:(moretactics) status with (Fail _) as e -> warn ("Tacticals.solve_tactics: current tactic failed with exn: " ^ Printexc.to_string e); solve_tactics ~tactics status ) | [] -> raise (Fail "solve_tactics cannot solve the goal"); S.apply_tactic S.id_tac status in S.mk_tactic (solve_tactics ~tactics) end module ProofEngineStatus = struct type input_status = ProofEngineTypes.status type output_status = ProofEngineTypes.proof * ProofEngineTypes.goal list type tactic = ProofEngineTypes.tactic let id_tac = id_tac let mk_tactic = ProofEngineTypes.mk_tactic let apply_tactic = ProofEngineTypes.apply_tactic let goals (_,goals) = goals let set_goals (proof,_) goals = proof,goals let focus (proof,_) goal = proof,goal end module ProofEngineTacticals = Make(ProofEngineStatus) include ProofEngineTacticals