From 6d8e39e99adc3cf3f5d377233e5765a5a3ea7379 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr>
Date: Mon, 6 Feb 2023 16:59:22 +0100
Subject: [PATCH] [wp/server] proof node & proof tree

---
 ivette/src/frama-c/plugins/wp/api/index.ts | 69 ++++++++++++++--
 src/plugins/wp/ProofEngine.ml              | 14 ++++
 src/plugins/wp/ProofEngine.mli             | 10 +++
 src/plugins/wp/wpApi.ml                    | 93 ++++++++++++++++++++--
 4 files changed, 173 insertions(+), 13 deletions(-)

diff --git a/ivette/src/frama-c/plugins/wp/api/index.ts b/ivette/src/frama-c/plugins/wp/api/index.ts
index 4e945ba0c77..bd48d1e056b 100644
--- a/ivette/src/frama-c/plugins/wp/api/index.ts
+++ b/ivette/src/frama-c/plugins/wp/api/index.ts
@@ -144,8 +144,6 @@ export interface goalsData {
   passed: boolean;
   /** Verdict Details */
   stats: stats;
-  /** Prover Results */
-  results: [ prover, result ][];
 }
 
 /** Decoder for `goalsData` */
@@ -160,7 +158,6 @@ export const jGoalsData: Json.Decoder<goalsData> =
     smoke: Json.jBoolean,
     passed: Json.jBoolean,
     stats: jStats,
-    results: Json.jArray(Json.jPair( jProver, jResult,)),
   });
 
 /** Natural order for `goalsData` */
@@ -168,7 +165,7 @@ export const byGoalsData: Compare.Order<goalsData> =
   Compare.byFields
     <{ wpo: Json.key<'#wpo'>, property: marker, name: string,
        fct?: Json.key<'#fct'>, bhv?: string, thy?: string, smoke: boolean,
-       passed: boolean, stats: stats, results: [ prover, result ][] }>({
+       passed: boolean, stats: stats }>({
     wpo: Compare.string,
     property: byMarker,
     name: Compare.string,
@@ -178,7 +175,6 @@ export const byGoalsData: Compare.Order<goalsData> =
     smoke: Compare.boolean,
     passed: Compare.boolean,
     stats: byStats,
-    results: Compare.array(Compare.pair(byProver,byResult,)),
   });
 
 /** Signal for array [`goals`](#goals)  */
@@ -266,4 +262,67 @@ const cancelProofTasks_internal: Server.SetRequest<null,null> = {
 /** Cancel all scheduled proof tasks. */
 export const cancelProofTasks: Server.SetRequest<null,null>= cancelProofTasks_internal;
 
+/** Proof Status has changed */
+export const proofStatus: Server.Signal = {
+  name: 'plugins.wp.proofStatus',
+};
+
+const getNodeInfos_internal: Server.GetRequest<
+  Json.index<'#node'>,
+  { stats: string, size: number, pending: number, proved: boolean,
+    result: string }
+  > = {
+  kind: Server.RqKind.GET,
+  name:   'plugins.wp.getNodeInfos',
+  input:  Json.jIndex<'#node'>('#node'),
+  output: Json.jObject({
+            stats: Json.jString,
+            size: Json.jNumber,
+            pending: Json.jNumber,
+            proved: Json.jBoolean,
+            result: Json.jString,
+          }),
+  signals: [ { name: 'plugins.wp.proofStatus' },
+             { name: 'plugins.wp.signalGoals' } ],
+};
+/** Proof node information */
+export const getNodeInfos: Server.GetRequest<
+  Json.index<'#node'>,
+  { stats: string, size: number, pending: number, proved: boolean,
+    result: string }
+  >= getNodeInfos_internal;
+
+const getProofState_internal: Server.GetRequest<
+  goal,
+  { children: [ string, Json.index<'#node'> ][], tactic: string,
+    results: [ prover, result ][], index: number, pending: number,
+    parents: Json.index<'#node'>[], current: Json.index<'#node'> }
+  > = {
+  kind: Server.RqKind.GET,
+  name:   'plugins.wp.getProofState',
+  input:  jGoal,
+  output: Json.jObject({
+            children: Json.jArray(
+                        Json.jPair(
+                          Json.jString,
+                          Json.jIndex<'#node'>('#node'),
+                        )),
+            tactic: Json.jString,
+            results: Json.jArray(Json.jPair( jProver, jResult,)),
+            index: Json.jNumber,
+            pending: Json.jNumber,
+            parents: Json.jArray(Json.jIndex<'#node'>('#node')),
+            current: Json.jIndex<'#node'>('#node'),
+          }),
+  signals: [ { name: 'plugins.wp.proofStatus' },
+             { name: 'plugins.wp.signalGoals' } ],
+};
+/** Current Proof Status of a Goal */
+export const getProofState: Server.GetRequest<
+  goal,
+  { children: [ string, Json.index<'#node'> ][], tactic: string,
+    results: [ prover, result ][], index: number, pending: number,
+    parents: Json.index<'#node'>[], current: Json.index<'#node'> }
+  >= getProofState_internal;
+
 /* ------------------------------------- */
diff --git a/src/plugins/wp/ProofEngine.ml b/src/plugins/wp/ProofEngine.ml
index bb9af717ec9..18afa7f14d7 100644
--- a/src/plugins/wp/ProofEngine.ml
+++ b/src/plugins/wp/ProofEngine.ml
@@ -39,6 +39,15 @@ and script =
   | Script of ProofScript.jscript (* to replay *)
   | Tactic of ProofScript.jtactic * (string * node) list (* played *)
 
+module Node =
+struct
+  type t = node
+  let hash t = Wpo.S.hash t.goal
+  let equal a b = Wpo.S.equal a.goal b.goal
+  let compare a b = Wpo.S.compare a.goal b.goal
+  let pretty fmt a = Wpo.S.pretty fmt a.goal
+end
+
 type tree = {
   main : Wpo.t ; (* Main goal to be proved. *)
   mutable pool : Lang.F.pool option ; (* Global pool variable *)
@@ -344,6 +353,11 @@ let mk_root ~tree =
   tree.head <- root ;
   node
 
+let root tree =
+  match tree.root with
+  | Some node -> node
+  | None -> mk_root ~tree
+
 (* -------------------------------------------------------------------------- *)
 (* --- Forking                                                            --- *)
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/wp/ProofEngine.mli b/src/plugins/wp/ProofEngine.mli
index f009fd7c28e..eb17cd87687 100644
--- a/src/plugins/wp/ProofEngine.mli
+++ b/src/plugins/wp/ProofEngine.mli
@@ -30,6 +30,15 @@ type tree
 (** A proof node *)
 type node
 
+module Node :
+sig
+  type t = node
+  val hash : t -> int
+  val equal : t -> t -> bool
+  val compare : t -> t -> int
+  val pretty : Format.formatter -> t -> unit
+end
+
 val get : Wpo.t -> [ `Script | `Proof | `Saved | `None ]
 val proof : main:Wpo.t -> tree
 val reset : tree -> unit
@@ -62,6 +71,7 @@ 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 goal : node -> Wpo.t
diff --git a/src/plugins/wp/wpApi.ml b/src/plugins/wp/wpApi.ml
index 879866c429b..1288c3437da 100644
--- a/src/plugins/wp/wpApi.ml
+++ b/src/plugins/wp/wpApi.ml
@@ -47,7 +47,7 @@ module INDEX = State_builder.Ref
          let mem_project = Datatype.never_any_project
        end))
     (struct
-      let name = "WpAPpi.INDEX"
+      let name = "WpApi.INDEX"
       let dependencies = [ Ast.self ]
       let default () = Hashtbl.create 0
     end)
@@ -198,13 +198,7 @@ let () = S.column gmodel ~name:"stats"
     ~descr:(Md.plain "Verdict Details")
     ~data:(module STATS) ~get:get_stats
 
-(*TODO: remove this field! *)
-let () = S.column gmodel ~name:"results"
-    ~descr:(Md.plain "Prover Results")
-    ~data:(module D.Jlist(D.Jpair(PROVER)(RESULT)))
-    ~get:Wpo.get_results
-
-let _garray = S.register_array ~package ~name:"goals"
+let garray = S.register_array ~package ~name:"goals"
     ~descr:(Md.plain "Generated Goals")
     ~key:(fun g -> g.Wpo.po_gid)
     ~keyName:"wpo"
@@ -261,3 +255,86 @@ let () = R.register ~package ~kind:`SET ~name:"cancelProofTasks"
     (fun () -> let server = ProverTask.server () in Task.cancel_all server)
 
 (* -------------------------------------------------------------------------- *)
+(* --- Proof Node                                                         --- *)
+(* -------------------------------------------------------------------------- *)
+
+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)
+
+let () =
+  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"
+      ~descr:(Md.plain "Proof node complete") (module D.Jbool) in
+  let set_pending = R.result snode ~name:"pending"
+      ~descr:(Md.plain "Pending children") (module D.Jint) in
+  let set_size = R.result snode ~name:"size"
+      ~descr:(Md.plain "Proof size") (module D.Jint) in
+  let set_stats = R.result snode ~name:"stats"
+      ~descr:(Md.plain "Node statistics") (module D.Jstring) in
+  R.register_sig ~package ~kind:`GET ~name:"getNodeInfos"
+    ~descr:(Md.plain "Proof node information") snode
+    ~signals:[proofStatus;S.signal garray]
+    begin fun rq node ->
+      set_title rq (ProofEngine.title node) ;
+      set_proved rq (ProofEngine.proved node) ;
+      set_pending rq (ProofEngine.pending node) ;
+      let s = ProofEngine.stats node in
+      set_size rq (Stats.proofs s) ;
+      set_stats rq (Pretty_utils.to_string Stats.pretty s) ;
+    end
+
+(* -------------------------------------------------------------------------- *)
+(* --- Proof Tree                                                         --- *)
+(* -------------------------------------------------------------------------- *)
+
+let () =
+  let state = R.signature ~input:(module WPO) () in
+  let set_current = R.result state ~name:"current"
+      ~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
+  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"
+      ~descr:(Md.plain "Current node index among pending nodes (else -1)")
+      (module D.Jint) in
+  let set_results = R.result state ~name:"results"
+      ~descr:(Md.plain "Prover results for current node")
+      (module D.Jlist(D.Jpair(PROVER)(RESULT))) in
+  let set_tactic = R.result state ~name:"tactic"
+      ~descr:(Md.plain "Proof node tactic header (if any)")
+      (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
+  R.register_sig ~package
+    ~kind:`GET ~name:"getProofState"
+    ~descr:(Md.plain "Current Proof Status of a Goal") state
+    ~signals:[proofStatus;S.signal garray]
+    begin fun rq wpo ->
+      let tree = ProofEngine.proof ~main:wpo in
+      let current,index =
+        match ProofEngine.current tree with
+        | `Main -> ProofEngine.root tree,-1
+        | `Internal node -> node,-1
+        | `Leaf(idx,node) -> node,idx in
+      let rec parents node = match ProofEngine.parent node with
+        | None -> []
+        | Some p -> p::parents p in
+      let tactic = match ProofEngine.tactical current with
+        | None -> ""
+        | Some { header } -> header in
+      set_current rq current ;
+      set_parents rq (parents current) ;
+      set_index rq index ;
+      set_pending rq (ProofEngine.pending current) ;
+      set_results rq (Wpo.get_results (ProofEngine.goal current)) ;
+      set_tactic rq tactic ;
+      set_children rq (ProofEngine.children current) ;
+    end
+
+(* -------------------------------------------------------------------------- *)
-- 
GitLab