From a384d0bb61c08c807cc19e329e38ba44e44c3d5c Mon Sep 17 00:00:00 2001 From: Allan Blanchard <allan.blanchard@cea.fr> Date: Mon, 31 Jan 2022 09:16:05 +0100 Subject: [PATCH] [wp] Multi select tactic - not available from GUI for now - TacClear and TacUnfold adapted --- src/plugins/wp/GuiSequent.ml | 2 +- src/plugins/wp/ProofScript.ml | 7 ++ src/plugins/wp/TacChoice.ml | 6 +- src/plugins/wp/TacClear.ml | 89 +++++++++++++++-------- src/plugins/wp/TacCompound.ml | 2 +- src/plugins/wp/TacNormalForm.ml | 2 +- src/plugins/wp/TacSplit.ml | 2 +- src/plugins/wp/TacUnfold.ml | 101 ++++++++++++++++++++++++-- src/plugins/wp/Tactical.ml | 15 +++- src/plugins/wp/Tactical.mli | 3 + src/plugins/wp/tests/wp_tip/TacNOP.ml | 1 + 11 files changed, 184 insertions(+), 46 deletions(-) diff --git a/src/plugins/wp/GuiSequent.ml b/src/plugins/wp/GuiSequent.ml index fdb29988b39..aaf112abc48 100644 --- a/src/plugins/wp/GuiSequent.ml +++ b/src/plugins/wp/GuiSequent.ml @@ -623,7 +623,7 @@ class focused (wtext : Wtext.text) = method set_target tgt = match tgt with - | Tactical.Empty | Tactical.Compose _ -> + | Tactical.Empty | Tactical.Compose _ | Tactical.Multi _ -> begin pcond#set_target Term ; plang#clear_target ; diff --git a/src/plugins/wp/ProofScript.ml b/src/plugins/wp/ProofScript.ml index 3e0bcc83cc2..531175ba1ee 100644 --- a/src/plugins/wp/ProofScript.ml +++ b/src/plugins/wp/ProofScript.ml @@ -97,6 +97,7 @@ let j_step = j_select "clause-step" let j_ingoal = j_select "inside-goal" let j_instep = j_select "inside-step" let j_compose = j_select "compose" +let j_multi = j_select "multi" let j_kint = j_select "kint" let j_range = j_select "range" let j_id a = "id" , `String a @@ -136,6 +137,9 @@ let rec json_of_selection = function `Assoc [ j_instep ; j_at s ; j_kind s ; j_occur n ; j_term e ; j_pattern m ] + | Multi es -> + `Assoc (j_multi :: j_args es) + and j_args = function | [] -> [] | es -> ["args" , `List (List.map json_of_selection es)] @@ -184,6 +188,9 @@ let rec selection_of_json ((hs,g) as s : sequent) js = let id = j_id js in let args = j_args js in Tactical.compose id (List.map (selection_of_json s) args) + | "multi" -> + let args = j_args js in + Tactical.multi @@ List.map (selection_of_json s) args | "kint" -> Tactical.cint (j_val js) | "range" -> Tactical.range (j_min js) (j_max js) | _ -> raise Not_found diff --git a/src/plugins/wp/TacChoice.ml b/src/plugins/wp/TacChoice.ml index 31f3ee05e0c..28ba26f16ae 100644 --- a/src/plugins/wp/TacChoice.ml +++ b/src/plugins/wp/TacChoice.ml @@ -45,7 +45,7 @@ class choice = Applicable (fun (hs,_) -> ["Choice",(hs,F.p_bool q)]) | _ -> Not_applicable end - | Empty | Compose _ | Clause _ | Inside(Step _,_) -> + | Empty | Compose _ | Clause _ | Inside(Step _,_) | Multi _ -> Not_applicable end @@ -59,7 +59,7 @@ class absurd = method select _feedback (s : Tactical.selection) = match s with - | Empty | Compose _ | Inside _ | Clause(Goal _) + | Empty | Compose _ | Inside _ | Clause(Goal _) | Multi _ -> Not_applicable | Clause(Step s) -> begin @@ -85,7 +85,7 @@ class contrapose = method select _feedback (s : Tactical.selection) = match s with - | Empty | Compose _ | Inside _ | Clause(Goal _) + | Empty | Compose _ | Inside _ | Clause(Goal _) | Multi _ -> Not_applicable | Clause(Step s) -> begin diff --git a/src/plugins/wp/TacClear.ml b/src/plugins/wp/TacClear.ml index a16fa3aa2f7..fb177fc292b 100644 --- a/src/plugins/wp/TacClear.ml +++ b/src/plugins/wp/TacClear.ml @@ -23,6 +23,58 @@ open Tactical open Conditions +let condition original p = (* keep original kind of simple condition *) + match original.condition with + | Type _ -> Type p + | Have _ -> Have p + | When _ -> When p + | Core _ -> Core p + | Init _ -> Init p + | _ -> assert false + +let tactical_step step = + Tactical.replace_single ~at:step.id ("Removed", Conditions.Have Lang.F.p_true) + +let tactical_inside step remove = + let rec collect p = + match Lang.F.p_expr p with + | And ps -> collect_list ps + | _ -> [ p ] + and collect_list = function + | [] -> [] + | p :: l -> List.rev_append (List.rev @@ collect p) (collect_list l) + in + begin match step.condition with + | Type p | Have p | When p | Core p | Init p -> + let ps = Lang.F.e_props @@ collect p in + let ps = List.filter (fun x -> not @@ List.mem x remove) ps in + let cond = condition step @@ Lang.F.p_bool @@ Lang.F.e_and ps in + Tactical.replace_single ~at:step.id ("Filtered", cond) + + | _ -> raise Not_found + end + +module Smap = Qed.Idxmap.Make + (struct + type t = step + let id s = s.id + end) + +let collect_remove m = function + | Inside(Step step, remove) -> + let l = + try Smap.find step m + with Not_found -> [] + in + Smap.add step (remove :: l) m + | _ -> raise Not_found + +let fold_selection s seq = + let m = List.fold_left collect_remove Smap.empty s in + "Filtered", Smap.fold (fun s l seq -> snd @@ tactical_inside s l seq) m seq + +let process (f: sequent -> string * sequent) s = [ f s ] + class clear = object(_) inherit Tactical.make ~id:"Wp.clear" @@ -33,39 +85,14 @@ class clear = method select _feedback sel = match sel with | Clause(Step step) -> - let removed = [ "Cleared hypothesis", Conditions.Have Lang.F.p_true] in - Applicable (Tactical.replace ~at:step.id removed) + Applicable(process @@ tactical_step step) | Inside(Step step, remove) -> - let cond p = (* keep original kind of step *) - match step.condition with - | Type _ -> Type p - | Have _ -> Have p - | When _ -> When p - | Core _ -> Core p - | Init _ -> Init p - | _ -> assert false (* see above pattern matching *) - in - let rec collect p = - match Lang.F.p_expr p with - | And ps -> collect_list ps - | _ -> [ p ] - and collect_list = function - | [] -> [] - | p :: l -> List.rev_append (List.rev @@ collect p) (collect_list l) - in - begin match step.condition with - | Type p | Have p | When p | Core p | Init p -> - let ps = Lang.F.e_props @@ collect p in - if List.mem remove ps then - let ps = List.filter (fun x -> x <> remove) ps in - let cond = cond @@ Lang.F.p_bool @@ Lang.F.e_and ps in - let filtered = [ "Filtered", cond ] in - Applicable (Tactical.replace ~at:step.id filtered) - else - Not_applicable - - | _ -> Not_applicable + begin + try Applicable(process @@ tactical_inside step [remove]) + with Not_found -> Not_applicable end + | Multi es -> + Applicable (process @@ fold_selection es) | _ -> Not_applicable end diff --git a/src/plugins/wp/TacCompound.ml b/src/plugins/wp/TacCompound.ml index e4fe08b7833..53c984d681c 100644 --- a/src/plugins/wp/TacCompound.ml +++ b/src/plugins/wp/TacCompound.ml @@ -209,7 +209,7 @@ class compound = | Clause (Step s) -> process_have feedback s | Clause (Goal p) -> process_goal feedback p | Inside(_,e) -> process_expand feedback ?at:(Tactical.at s) e - | Empty | Compose _ -> raise Not_found + | Empty | Compose _ | Multi _ -> raise Not_found in Applicable process end diff --git a/src/plugins/wp/TacNormalForm.ml b/src/plugins/wp/TacNormalForm.ml index f7e78a43906..a3ca166d4d3 100644 --- a/src/plugins/wp/TacNormalForm.ml +++ b/src/plugins/wp/TacNormalForm.ml @@ -73,7 +73,7 @@ let match_selection = function else Some (false, e, f_nf_hyp s e) | _ -> None end - | Inside(_,_) | Compose _ | Empty -> None + | Inside(_,_) | Compose _ | Empty | Multi _ -> None class normal_form = object diff --git a/src/plugins/wp/TacSplit.ml b/src/plugins/wp/TacSplit.ml index 1e20d4073b3..20857de0be0 100644 --- a/src/plugins/wp/TacSplit.ml +++ b/src/plugins/wp/TacSplit.ml @@ -187,7 +187,7 @@ class split = split_cmp at "Split (<)" x y in match s with - | Empty | Compose _ -> Not_applicable + | Empty | Compose _ | Multi _ -> Not_applicable | Inside(_,e) -> begin let at = Tactical.at s in diff --git a/src/plugins/wp/TacUnfold.ml b/src/plugins/wp/TacUnfold.ml index fd4f9e3a3b9..e64bfec45f9 100644 --- a/src/plugins/wp/TacUnfold.ml +++ b/src/plugins/wp/TacUnfold.ml @@ -22,6 +22,7 @@ open Lang open Tactical +open Conditions (* -------------------------------------------------------------------------- *) (* --- Unfold Definition Tactical --- *) @@ -48,6 +49,8 @@ let range f es = (F.p_leq e (F.e_zint b)) in F.e_prop (F.p_all range es) +(* Used only for non Multi selection *) + let rec applicable ?at e f es = function | phi::others -> begin @@ -61,6 +64,82 @@ let rec applicable ?at e f es = function | [] -> Not_applicable +(* Used only for Multi selection *) + +module Smap = Qed.Idxmap.Make + (struct + type t = step + let id s = s.id + end) + +let condition original p = (* keep original kind of simple condition *) + match original.condition with + | Type _ -> Type p + | Have _ -> Have p + | When _ -> When p + | Core _ -> Core p + | Init _ -> Init p + | _ -> assert false + +let collect_term_to_unfold (g, m) = function + | Inside(Step step, unfold) -> + let l = + try Smap.find step m + with Not_found -> [] + in + g, Smap.add step (unfold :: l) m + | Inside (Goal _, unfold) -> + begin match g with + | None -> Some [ unfold ], m + | Some g -> Some (unfold :: g), m + end + | _ -> raise Not_found + +let rec collect_unfold phis m e = + match phis with + | phi :: others -> + begin + try + match F.repr e with + | Qed.Logic.Fun(f,es) -> Lang.F.Tmap.add e (phi f es) m + | _ -> raise Not_found + with Not_found | Invalid_argument _ -> collect_unfold others m e + end + | [] -> m + +let unfolds_from_list phis es = + List.fold_left (collect_unfold phis) Lang.F.Tmap.empty es + +let unfolds_from_smap phis m = + Smap.map (fun _s es -> unfolds_from_list phis es) m + +let tactical_inside step unfolds sequent = + if Lang.F.Tmap.is_empty unfolds + then raise Not_found + else match step.condition with + | Type p | Have p | When p | Core p | Init p -> + let subst t = Lang.F.Tmap.find t unfolds in + let p = condition step @@ Lang.p_subst subst p in + snd @@ Tactical.replace_single ~at:step.id ("Unfolded", p) sequent + | _ -> raise Not_found + +let tactical_goal unfolds (seq, g) = + if Lang.F.Tmap.is_empty unfolds + then raise Not_found + else + let subst t = Lang.F.Tmap.find t unfolds in + seq, Lang.p_subst subst g + +let fold_selection goal_unfolds step_unfolds sequent = + "Unfolded multiple selection", + let add_goal = match goal_unfolds with + | None -> Extlib.id + | Some goal_unfolds -> tactical_goal goal_unfolds + in + add_goal @@ Smap.fold tactical_inside step_unfolds sequent + +let process (f: sequent -> string * sequent) s = [ f s ] + class unfold = object inherit Tactical.make ~id:"Wp.unfold" @@ -69,13 +148,21 @@ class unfold = ~params:[] method select _feedback (s : Tactical.selection) = - let at = Tactical.at s in - let e = Tactical.selected s in - match F.repr e with - | Qed.Logic.Fun(f,es) -> - applicable ?at e f es [ definition ; range ] - | _ -> Not_applicable - + let unfoldings = [ definition ; range ] in + match s with + | Multi es -> + let goal, steps = + List.fold_left collect_term_to_unfold (None, Smap.empty) es in + let goal = Option.map (unfolds_from_list unfoldings) goal in + let steps = unfolds_from_smap unfoldings steps in + Applicable (process @@ fold_selection goal steps) + | s -> + let at = Tactical.at s in + let e = Tactical.selected s in + match F.repr e with + | Qed.Logic.Fun(f,es) -> + applicable ?at e f es unfoldings + | _ -> Not_applicable end let tactical = Tactical.export (new unfold) diff --git a/src/plugins/wp/Tactical.ml b/src/plugins/wp/Tactical.ml index 5fcf6a7bc0a..1da597fa8d3 100644 --- a/src/plugins/wp/Tactical.ml +++ b/src/plugins/wp/Tactical.ml @@ -79,6 +79,7 @@ type selection = | Clause of clause | Inside of clause * term | Compose of compose + | Multi of selection list and compose = | Cint of Integer.t @@ -105,6 +106,7 @@ let selected = function | Inside(_,t) -> t | Clause c -> e_prop (head c) | Compose code -> composed code + | Multi _s -> e_true (* For now, do not provide this *) let get_int_z z = try Some (Integer.to_int_exn z) with Z.Overflow -> None @@ -148,6 +150,10 @@ let rec pp_selection fmt = function Format.fprintf fmt "@[<hov 2>Compose '%s'" id ; List.iter (fun e -> Format.fprintf fmt "(%a)" pp_selection e) es ; Format.fprintf fmt "@]" + | Multi es -> + Format.fprintf fmt "@[<hov 2>Multi-selection" ; + List.iter (fun e -> Format.fprintf fmt "(%a)" pp_selection e) es ; + Format.fprintf fmt "@]" let int a = Compose(Cint (Integer.of_int a)) let cint a = Compose(Cint a) @@ -161,6 +167,8 @@ let compose id es = | _ -> Compose(Code(e,id,es)) with Not_found -> Empty +let multi es = Multi es + let findhead (s:selection) e = match s with | Empty -> None @@ -172,6 +180,7 @@ let findhead (s:selection) e = else None | Compose(Code(v,_,_)) as s -> if v == e then Some s else None + | Multi _ -> None let rec lookup (s:selection) e q = match findhead s e with @@ -334,7 +343,7 @@ class type feedback = (* -------------------------------------------------------------------------- *) let at = function - | Empty | Clause (Goal _) | Inside(Goal _,_) | Compose _ -> None + | Empty | Clause (Goal _) | Inside(Goal _,_) | Compose _ | Multi _ -> None | Clause (Step s) | Inside(Step s,_) -> Some s.id let mapi f cases = @@ -357,6 +366,10 @@ let replace ~at cases sequent = descr , Conditions.replace ~at step sequent) cases +let replace_single ~at (descr,cond) sequent = + let step = Conditions.(step ~descr cond) in + descr , Conditions.replace ~at step sequent + let replace_step ~at conditions sequent = let step = let s = Conditions.step_at (fst sequent) at in diff --git a/src/plugins/wp/Tactical.mli b/src/plugins/wp/Tactical.mli index 78159e588ce..f7ea9130bf0 100644 --- a/src/plugins/wp/Tactical.mli +++ b/src/plugins/wp/Tactical.mli @@ -41,6 +41,7 @@ type selection = | Clause of clause | Inside of clause * term | Compose of compose + | Multi of selection list and compose = private | Cint of Integer.t @@ -51,6 +52,7 @@ val int : int -> selection val cint : Integer.t -> selection val range : int -> int -> selection val compose : string -> selection list -> selection +val multi : selection list -> selection val get_int : selection -> int option val destruct : selection -> selection list @@ -179,6 +181,7 @@ val at : selection -> int option val mapi : (int -> int -> 'a -> 'b) -> 'a list -> 'b list val insert : ?at:int -> (string * pred) list -> process val replace : at:int -> (string * condition) list -> process +val replace_single : at:int -> string * condition -> sequent -> string * sequent val replace_step : at:int -> condition list -> process val split : (string * pred) list -> process val rewrite : ?at:int -> (string * pred * term * term) list -> process diff --git a/src/plugins/wp/tests/wp_tip/TacNOP.ml b/src/plugins/wp/tests/wp_tip/TacNOP.ml index 7b2413cd0a6..a398efe3ac5 100644 --- a/src/plugins/wp/tests/wp_tip/TacNOP.ml +++ b/src/plugins/wp/tests/wp_tip/TacNOP.ml @@ -16,6 +16,7 @@ class nop = match s with | Empty -> Not_applicable | Compose _ + | Multi _ | Inside _ | Clause _ -> feedback#set_title "NOP" ; -- GitLab