From 266e6dee6ed7452a9f801b0891173234fb0e1928 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr>
Date: Mon, 6 Feb 2023 17:34:02 +0100
Subject: [PATCH] [wp/server] proof navigation

---
 ivette/src/frama-c/plugins/wp/api/index.ts | 50 +++++++++++++++++
 src/plugins/wp/ProofEngine.ml              | 26 ++++-----
 src/plugins/wp/ProofEngine.mli             |  5 +-
 src/plugins/wp/gui/GuiNavigator.ml         |  2 +-
 src/plugins/wp/wpApi.ml                    | 65 +++++++++++++++++++---
 5 files changed, 124 insertions(+), 24 deletions(-)

diff --git a/ivette/src/frama-c/plugins/wp/api/index.ts b/ivette/src/frama-c/plugins/wp/api/index.ts
index bd48d1e056b..4a62dc61ded 100644
--- a/ivette/src/frama-c/plugins/wp/api/index.ts
+++ b/ivette/src/frama-c/plugins/wp/api/index.ts
@@ -325,4 +325,54 @@ export const getProofState: Server.GetRequest<
     parents: Json.index<'#node'>[], current: Json.index<'#node'> }
   >= getProofState_internal;
 
+const goToRoot_internal: Server.SetRequest<goal,null> = {
+  kind: Server.RqKind.SET,
+  name:   'plugins.wp.goToRoot',
+  input:  jGoal,
+  output: Json.jNull,
+  signals: [],
+};
+/** Go to root of proof tree */
+export const goToRoot: Server.SetRequest<goal,null>= goToRoot_internal;
+
+const goToPending_internal: Server.SetRequest<[ goal, number ],null> = {
+  kind: Server.RqKind.SET,
+  name:   'plugins.wp.goToPending',
+  input:  Json.jPair( jGoal, Json.jNumber,),
+  output: Json.jNull,
+  signals: [],
+};
+/** Go to k-th pending node of proof tree */
+export const goToPending: Server.SetRequest<[ goal, number ],null>= goToPending_internal;
+
+const goForward_internal: Server.SetRequest<goal,null> = {
+  kind: Server.RqKind.SET,
+  name:   'plugins.wp.goForward',
+  input:  jGoal,
+  output: Json.jNull,
+  signals: [],
+};
+/** Forward to next pending node */
+export const goForward: Server.SetRequest<goal,null>= goForward_internal;
+
+const goToNode_internal: Server.SetRequest<Json.index<'#node'>,null> = {
+  kind: Server.RqKind.SET,
+  name:   'plugins.wp.goToNode',
+  input:  Json.jIndex<'#node'>('#node'),
+  output: Json.jNull,
+  signals: [],
+};
+/** Set current node of associated proof tree */
+export const goToNode: Server.SetRequest<Json.index<'#node'>,null>= goToNode_internal;
+
+const removeNode_internal: Server.SetRequest<Json.index<'#node'>,null> = {
+  kind: Server.RqKind.SET,
+  name:   'plugins.wp.removeNode',
+  input:  Json.jIndex<'#node'>('#node'),
+  output: Json.jNull,
+  signals: [],
+};
+/** Forward to next pending node */
+export const removeNode: Server.SetRequest<Json.index<'#node'>,null>= removeNode_internal;
+
 /* ------------------------------------- */
diff --git a/src/plugins/wp/ProofEngine.ml b/src/plugins/wp/ProofEngine.ml
index 18afa7f14d7..95f598ec56e 100644
--- a/src/plugins/wp/ProofEngine.ml
+++ b/src/plugins/wp/ProofEngine.ml
@@ -109,7 +109,8 @@ let rec reset_node n =
   if Wpo.is_tactic n.goal then Wpo.remove n.goal ;
   match n.script with
   | Opened | Script _ -> ()
-  | Tactic(_,children) -> iter_all reset_node children
+  | Tactic(_,children) ->
+    n.script <- Opened ; iter_all reset_node children
 
 let reset_root = function None -> () | Some n -> reset_node n
 
@@ -123,7 +124,7 @@ let reset t =
     t.saved <- false ;
   end
 
-let remove w = if PROOFS.mem w then reset (PROOFS.get w)
+let clear w = if PROOFS.mem w then reset (PROOFS.get w)
 
 let saved t = t.saved
 let set_saved t s = t.saved <- s
@@ -195,6 +196,7 @@ let main t = t.main
 let head t = match t.head with
   | None -> t.main
   | Some n -> n.goal
+let tree n = proof ~main:n.tree
 let goal n = n.goal
 let stats n = n.stats
 let tree_context t = Wpo.get_context t.main
@@ -279,20 +281,18 @@ let rec forward t =
         forward t ;
       end
 
+let remove t node =
+  begin
+    Wpo.clear_results node.goal ;
+    t.head <- node.parent ;
+    if t.head = None then t.root <- None ;
+    reset_node node ;
+  end
+
 let cancel t =
   match t.head with
   | None -> ()
-  | Some node ->
-    begin
-      Wpo.clear_results node.goal ;
-      match node.script with
-      | Opened ->
-        t.head <- node.parent ;
-        if t.head = None then t.root <- None ;
-      | Tactic _ | Script _ ->
-        (*TODO: save the current script *)
-        node.script <- Opened ;
-    end
+  | Some node -> remove t node
 
 (* -------------------------------------------------------------------------- *)
 (* --- Sub-Goal                                                           --- *)
diff --git a/src/plugins/wp/ProofEngine.mli b/src/plugins/wp/ProofEngine.mli
index eb17cd87687..4e0cd3d9f62 100644
--- a/src/plugins/wp/ProofEngine.mli
+++ b/src/plugins/wp/ProofEngine.mli
@@ -42,7 +42,7 @@ end
 val get : Wpo.t -> [ `Script | `Proof | `Saved | `None ]
 val proof : main:Wpo.t -> tree
 val reset : tree -> unit
-val remove : Wpo.t -> unit
+val clear : Wpo.t -> unit
 
 (** Re-compute stats & set status of the entire script *)
 val validate : tree -> unit
@@ -70,10 +70,10 @@ val set_saved : tree -> bool -> unit
 val status : tree -> status
 val current : tree -> current
 val goto : tree -> position -> unit
-
 val root : tree -> node
 val main : tree -> Wpo.t
 val head : tree -> Wpo.t
+val tree : node -> tree
 val goal : node -> Wpo.t
 val tree_context : tree -> WpContext.t
 val node_context : node -> WpContext.t
@@ -89,6 +89,7 @@ val get_strategies : node -> int * Strategy.t array (* current index *)
 val set_strategies : node -> ?index:int -> Strategy.t array -> unit
 val forward : tree -> unit
 val cancel : tree -> unit
+val remove : tree -> node -> unit
 
 type fork
 val anchor : tree -> ?node:node -> unit -> node
diff --git a/src/plugins/wp/gui/GuiNavigator.ml b/src/plugins/wp/gui/GuiNavigator.ml
index 5deefdb1887..5f1ed5226ab 100644
--- a/src/plugins/wp/gui/GuiNavigator.ml
+++ b/src/plugins/wp/gui/GuiNavigator.ml
@@ -298,7 +298,7 @@ class behavior
 
     method private popup_delete_script () =
       match popup_target with
-      | Some(w,_) -> ProofEngine.remove w ; ProofSession.remove w
+      | Some(w,_) -> ProofEngine.clear w ; ProofSession.remove w
       | None -> ()
 
     method private popup_run mode () =
diff --git a/src/plugins/wp/wpApi.ml b/src/plugins/wp/wpApi.ml
index 1288c3437da..3ff25c452dd 100644
--- a/src/plugins/wp/wpApi.ml
+++ b/src/plugins/wp/wpApi.ml
@@ -52,7 +52,7 @@ module INDEX = State_builder.Ref
       let default () = Hashtbl.create 0
     end)
 
-module WPO : D.S with type t = Wpo.t =
+module Goal : D.S with type t = Wpo.t =
 struct
   type t = Wpo.t
   let jtype = D.declare ~package ~name:"goal"
@@ -146,7 +146,7 @@ let () = R.register ~package ~kind:`GET ~name:"getAvailableProvers"
        Why3Provers.provers ())
 
 (* -------------------------------------------------------------------------- *)
-(* --- WPO Array                                                          --- *)
+(* --- Goal Array                                                          --- *)
 (* -------------------------------------------------------------------------- *)
 
 let gmodel : Wpo.t S.model = S.model ()
@@ -261,10 +261,10 @@ let () = R.register ~package ~kind:`SET ~name:"cancelProofTasks"
 let proofStatus = R.signal ~package ~name:"proofStatus"
     ~descr:(Md.plain "Proof Status has changed")
 
-module NODE = D.Index(Map.Make(ProofEngine.Node))(struct let name = "node" end)
+module Node = D.Index(Map.Make(ProofEngine.Node))(struct let name = "node" end)
 
 let () =
-  let snode = R.signature ~input:(module NODE) () in
+  let snode = R.signature ~input:(module Node) () in
   let set_title = R.result snode ~name:"result"
       ~descr:(Md.plain "Proof node title") (module D.Jstring) in
   let set_proved = R.result snode ~name:"proved"
@@ -292,11 +292,11 @@ let () =
 (* -------------------------------------------------------------------------- *)
 
 let () =
-  let state = R.signature ~input:(module WPO) () in
+  let state = R.signature ~input:(module Goal) () in
   let set_current = R.result state ~name:"current"
-      ~descr:(Md.plain "Current proof node.") (module NODE) in
+      ~descr:(Md.plain "Current proof node.") (module Node) in
   let set_parents = R.result state ~name:"parents"
-      ~descr:(Md.plain "Proof node parents.") (module D.Jlist(NODE)) in
+      ~descr:(Md.plain "Proof node parents.") (module D.Jlist(Node)) in
   let set_pending = R.result state ~name:"pending"
       ~descr:(Md.plain "Pending proof nodes.") (module D.Jint) in
   let set_index = R.result state ~name:"index"
@@ -310,7 +310,7 @@ let () =
       (module D.Jstring) in
   let set_children = R.result state ~name:"children"
       ~descr:(Md.plain "Proof node tactic children (id any)")
-      (module D.Jlist(D.Jpair(D.Jstring)(NODE))) in
+      (module D.Jlist(D.Jpair(D.Jstring)(Node))) in
   R.register_sig ~package
     ~kind:`GET ~name:"getProofState"
     ~descr:(Md.plain "Current Proof Status of a Goal") state
@@ -338,3 +338,52 @@ let () =
     end
 
 (* -------------------------------------------------------------------------- *)
+(* --- Proof Tree Management                                              --- *)
+(* -------------------------------------------------------------------------- *)
+
+let () = R.register ~package ~kind:`SET ~name:"goToRoot"
+    ~descr:(Md.plain "Go to root of proof tree")
+    ~input:(module Goal) ~output:(module D.Junit)
+    begin fun goal ->
+      let tree = ProofEngine.proof ~main:goal in
+      ProofEngine.goto tree `Main ;
+      R.emit proofStatus ;
+    end
+
+let () = R.register ~package ~kind:`SET ~name:"goToPending"
+    ~descr:(Md.plain "Go to k-th pending node of proof tree")
+    ~input:(module D.Jpair(Goal)(D.Jint)) ~output:(module D.Junit)
+    begin fun (goal,index) ->
+      let tree = ProofEngine.proof ~main:goal in
+      ProofEngine.goto tree (`Leaf index) ;
+      R.emit proofStatus ;
+    end
+
+let () = R.register ~package ~kind:`SET ~name:"goForward"
+    ~descr:(Md.plain "Forward to next pending node")
+    ~input:(module Goal) ~output:(module D.Junit)
+    begin fun goal ->
+      let tree = ProofEngine.proof ~main:goal in
+      ProofEngine.forward tree ;
+      R.emit proofStatus ;
+    end
+
+let () = R.register ~package ~kind:`SET ~name:"goToNode"
+    ~descr:(Md.plain "Set current node of associated proof tree")
+    ~input:(module Node) ~output:(module D.Junit)
+    begin fun node ->
+      let tree = ProofEngine.tree node in
+      ProofEngine.goto tree (`Node node) ;
+      R.emit proofStatus ;
+    end
+
+let () = R.register ~package ~kind:`SET ~name:"removeNode"
+    ~descr:(Md.plain "Forward to next pending node")
+    ~input:(module Node) ~output:(module D.Junit)
+    begin fun node ->
+      let tree = ProofEngine.tree node in
+      ProofEngine.remove tree node ;
+      R.emit proofStatus ;
+    end
+
+(* -------------------------------------------------------------------------- *)
-- 
GitLab