(* 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/. *) (* $Id$ *) (* open CicReduction open ProofEngineTypes open UriManager *) (** DEBUGGING *) (** perform debugging output? *) let debug = false let debug_print = fun _ -> () (** debugging print *) let info s = debug_print (lazy ("TACTICALS INFO: " ^ (Lazy.force s))) module PET = ProofEngineTypes let id_tac = let id_tac (proof,goal) = let _, metasenv, _subst, _, _, _ = proof in let _, _, _ = CicUtil.lookup_meta goal metasenv in (proof,[goal]) in PET.mk_tactic id_tac let fail_tac = let fail_tac (proof,goal) = let _, metasenv, _subst, _ , _, _ = proof in let _, _, _ = CicUtil.lookup_meta goal metasenv in raise (PET.Fail (lazy "fail tactical")) in PET.mk_tactic fail_tac type goal = PET.goal (** TODO needed until tactics start returning both opened and closed goals * First part of the function performs a diff among goals ~before tactic * application and ~after it. Second part will add as both opened and closed * the goals which are returned as opened by the tactic *) let goals_diff ~before ~after ~opened = let sort_opened opened add = opened @ (List.filter (fun g -> not (List.mem g opened)) add) in let remove = List.fold_left (fun remove e -> if List.mem e after then remove else e :: remove) [] before in let add = List.fold_left (fun add e -> if List.mem e before then add else e :: add) [] after in let add, remove = (* adds goals which have been both opened _and_ closed *) List.fold_left (fun (add, remove) opened_goal -> if List.mem opened_goal before then opened_goal :: add, opened_goal :: remove else add, remove) (add, remove) opened in sort_opened opened add, remove module ProofEngineStatus = struct module Stack = Continuationals.Stack (* The stack is used and saved only at the very end of the eval function; it is read only at the beginning of the eval; we need it just to apply several times in a row this machine to an initial stack, i.e. to chain several applications of the machine together, i.e. to dump and restore the state of the machine. The stack is never used by the tactics: each tactic of type PET.tactic ignore the stack. To make a tactic from the eval function of a machine we apply the eval on a fresh stack (via the mk_tactic). *) type input_status = PET.status (* (proof, goal) *) * Stack.t type output_status = (PET.proof * goal list * goal list) * Stack.t type tactic = PET.tactic (* f is an eval function of a machine; the machine is applied to a fresh stack and the final stack is read back to obtain the result of the tactic *) let mk_tactic f = PET.mk_tactic (fun ((proof, goal) as pstatus) -> let stack = [ [ 1, Stack.Open goal ], [], [], `NoTag ] in let istatus = pstatus, stack in let (proof, _, _), stack = f istatus in let opened = Continuationals.Stack.open_goals stack in proof, opened) (* it applies a tactic ignoring (and preserving) the stack *) let apply_tactic tac ((proof, _) as pstatus, stack) = let proof', opened = PET.apply_tactic tac pstatus in let before = PET.goals_of_proof proof in let after = PET.goals_of_proof proof' in let opened_goals, closed_goals = goals_diff ~before ~after ~opened in (proof', opened_goals, closed_goals), stack let goals ((_, opened, closed), _) = opened, closed (* Done only at the beginning of the eval of the machine *) let get_stack = snd (* Done only at the end of the eval of the machine *) let set_stack stack (opstatus, _) = opstatus, stack let inject ((proof, _), stack) = ((proof, [], []), stack) let focus goal ((proof, _, _), stack) = (proof, goal), stack end module S = ProofEngineStatus module C = Continuationals.Make (S) type tactic = S.tactic let fold_eval status ts = let istatus = List.fold_left (fun istatus t -> S.focus ~-1 (C.eval t istatus)) status ts in S.inject istatus (* Tacticals implemented on top of tynycals *) let thens ~start ~continuations = S.mk_tactic (fun istatus -> fold_eval istatus ([ C.Tactical (C.Tactic start); C.Branch ] @ (HExtlib.list_concat ~sep:[ C.Shift ] (List.map (fun t -> [ C.Tactical (C.Tactic t) ]) continuations)) @ [ C.Merge ])) let then_ ~start ~continuation = S.mk_tactic (fun istatus -> let ostatus = C.eval (C.Tactical (C.Tactic start)) istatus in let opened,closed = S.goals ostatus in match opened with [] -> ostatus | _ -> fold_eval (S.focus ~-1 ostatus) [ C.Semicolon; C.Tactical (C.Tactic continuation) ]) let seq ~tactics = S.mk_tactic (fun istatus -> fold_eval istatus (HExtlib.list_concat ~sep:[ C.Semicolon ] (List.map (fun t -> [ C.Tactical (C.Tactic t) ]) tactics))) (* Tacticals that cannot be implemented on top of tynycals *) let const_tac res = PET.mk_tactic (fun _ -> res) let if_ ~start ~continuation ~fail = let if_ status = let xoutput = try let result = PET.apply_tactic start status in info (lazy ("Tacticals.if_: succedeed!!!")); Some result with PET.Fail _ -> None in let tactic = match xoutput with | Some res -> then_ ~start:(const_tac res) ~continuation | None -> fail in PET.apply_tactic tactic status in PET.mk_tactic if_ let ifs ~start ~continuations ~fail = let ifs status = let xoutput = try let result = PET.apply_tactic start status in info (lazy ("Tacticals.ifs: succedeed!!!")); Some result with PET.Fail _ -> None in let tactic = match xoutput with | Some res -> thens ~start:(const_tac res) ~continuations | None -> fail in PET.apply_tactic tactic status in PET.mk_tactic ifs let first ~tactics = let rec first ~(tactics: tactic list) status = info (lazy "in Tacticals.first"); match tactics with [] -> raise (PET.Fail (lazy "first: no tactics left")) | tac::tactics -> try let res = PET.apply_tactic tac status in info (lazy ("Tacticals.first: succedeed!!!")); res with PET.Fail _ -> first ~tactics status in PET.mk_tactic (first ~tactics) let rec do_tactic ~n ~tactic = PET.mk_tactic (function status -> if n = 0 then PET.apply_tactic id_tac status else PET.apply_tactic (then_ ~start:tactic ~continuation:(do_tactic ~n:(n-1) ~tactic)) status) (* This applies tactic and catches its possible failure *) let try_tactic ~tactic = let try_tactic status = try PET.apply_tactic tactic status with (PET.Fail _) as e -> info (lazy ( "Tacticals.try_tactic failed with exn: " ^ Printexc.to_string e)); PET.apply_tactic id_tac status in PET.mk_tactic try_tactic let rec repeat_tactic ~tactic = ProofEngineTypes.mk_tactic (fun status -> ProofEngineTypes.apply_tactic (then_ ~start:tactic ~continuation:(try_tactic (repeat_tactic ~tactic))) status) (* This tries tactics until one of them solves the goal *) let solve_tactics ~tactics = let rec solve_tactics ~(tactics: tactic list) status = info (lazy "in Tacticals.solve_tactics"); match tactics with | currenttactic::moretactics -> (try let (proof, opened) as output_status = PET.apply_tactic currenttactic status in match opened with | [] -> info (lazy ("Tacticals.solve_tactics: solved the goal!!!")); output_status | _ -> info (lazy ("Tacticals.solve_tactics: try the next tactic")); raise (PET.Fail (lazy "Goal not solved")) with (PET.Fail _) as e -> info (lazy ( "Tacticals.solve_tactics: current tactic failed with exn: " ^ Printexc.to_string e)); solve_tactics ~tactics:moretactics status) | [] -> raise (PET.Fail (lazy "solve_tactics cannot solve the goal")) in PET.mk_tactic (solve_tactics ~tactics) let progress_tactic ~tactic = let msg = lazy "Failed to progress" in let progress_tactic (((_,metasenv,_subst,_,_,_),g) as istatus) = let ((_,metasenv',_subst,_,_,_),opened) as ostatus = PET.apply_tactic tactic istatus in match opened with | [g1] -> let _,oc,oldty = CicUtil.lookup_meta g metasenv in let _,nc,newty = CicUtil.lookup_meta g1 metasenv' in if oldty = newty && oc = nc then raise (PET.Fail msg) else ostatus | _ -> ostatus in PET.mk_tactic progress_tactic