package coq-waterproof

  1. Overview
  2. Docs

Source file wp_bullets.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
(******************************************************************************)
(*                  This file is part of Waterproof-lib.                      *)
(*                                                                            *)
(*   Waterproof-lib 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 3 of the License, or      *)
(*                    (at your option) any later version.                     *)
(*                                                                            *)
(*     Waterproof-lib 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 Waterproof-lib. If not, see <https://www.gnu.org/licenses/>.  *)
(*                                                                            *)
(******************************************************************************)

open Proof
open Proof_bullet

(** The majority of the code below is copied and adapted directly from
the source code of the Rocq theorem prover itself, namely from Proof_bullet.ml. *)

(** Preparatory definitions that are useful for both defined modes. *)

let bullet_eq b1 b2 = match b1, b2 with
| Dash n1, Dash n2 -> n1 = n2
| Star n1, Star n2 -> n1 = n2
| Plus n1, Plus n2 -> n1 = n2
| _ -> false

let pr_bullet b =
  match b with
  | Dash n -> Pp.(str (String.make n '-'))
  | Star n -> Pp.(str (String.make n '*'))
  | Plus n -> Pp.(str (String.make n '+'))

type suggestion =
  | Suggest of t (* this bullet is mandatory here *)
  | Unfinished of t (* no mandatory bullet here, but this bullet is unfinished *)
  | NoBulletInUse (* No mandatory bullet (or brace) here, no bullet pending,
               some focused goals exists. *)
  | NeedClosingBrace (* Some unfocussed goal exists "{" needed to focus them *)
  | ProofFinished (* No more goal anywhere *)

let suggest_on_solved_goal sugg =
  match sugg with
  | NeedClosingBrace -> Pp.(str"Try closing this subproof by typing a space and curly brace \" }\".")
  | NoBulletInUse -> Pp.mt ()
  | ProofFinished -> Pp.mt ()
  | Suggest b -> Pp.(str"Write a bullet " ++ pr_bullet b ++ str" to embark on the next subproof.")
  | Unfinished b -> Pp.(str"The current subproof with bullet " ++ pr_bullet b ++ str" is unfinished.")

(* give always a message. *)
let suggest_on_error sugg =
  match sugg with
  | NeedClosingBrace -> Pp.(str"Try closing this subproof by typing a space and curly brace \" }\".")
  | NoBulletInUse -> assert false (* This should never raise an error. *)
  | ProofFinished -> Pp.(str"No more goals.")
  | Suggest b -> Pp.(str"Write a bullet " ++ pr_bullet b ++ str" to embark on the next subproof.")
  | Unfinished b -> Pp.(str"Current bullet " ++ pr_bullet b ++ str" is not finished.")

let bullet_kind = (new_focus_kind "wp_bullet_kind" : t list focus_kind)
let bullet_cond = done_cond ~loose_end:true bullet_kind

let get_bullets pr =
  if is_last_focus bullet_kind pr then
    get_at_focus bullet_kind pr
  else
    []

let has_bullet bul pr =
  let rec has_bullet = function
    | b'::_ when bullet_eq bul b' -> true
    | _::l -> has_bullet l
    | [] -> false
  in
  has_bullet (get_bullets pr)

(* pop a bullet from proof [pr]. There should be at least one
    bullet in use. If pop impossible (pending proofs on this level
    of bullet or higher) then raise [Proof.CannotUnfocusThisWay]. *)
let pop pr =
  match get_bullets pr with
  | b::_ -> Some (unfocus bullet_kind pr (), b)
  | _ -> None

let push (b:t) pr =
  focus bullet_cond (b::get_bullets pr) 1 pr

let suggest_bullet (prf : Proof.t): suggestion =
  if is_done prf then ProofFinished
  else if not (no_focused_goal prf)
  then (* No suggestion if a bullet is not mandatory, look for an unfinished bullet *)
    match get_bullets prf with
    | b::_ -> Unfinished b
    | _ -> NoBulletInUse
  else (* There is no goal under focus but some are unfocussed,
          let us look at the bullet needed. *)
    let rec loop prf =
      match pop prf with
      | Some (prf, b) ->
        (* pop went well, this means that there are no more goals
          *under this* bullet b, see if a new b can be pushed. *)
        begin
          try ignore (push b prf); Suggest b
          with e when CErrors.noncritical e ->
            (* b could not be pushed, so we must look for a outer bullet *)
            loop prf
        end
      | None ->
        (* No pop was possible, but there are still
            subgoals somewhere: there must be a "}" to use. *)
        NeedClosingBrace
    in
    loop prf

(** Definitions for the bullet behavior "Waterproof Strict Subproofs".
This is the same as the ordinary "Strict Subproofs" bullet behavior,
except that the suggestions and error messages are slightly different. *)
module Strict = struct
  exception FailedBullet of t * suggestion

  let _ =
    CErrors.register_handler
      (function
      | FailedBullet (b,sugg) ->
        let prefix = Pp.(str"Wrong bullet " ++ pr_bullet b ++ str": ") in
        Some Pp.(str "[Focus]" ++ spc () ++ prefix ++ suggest_on_error sugg)
      | _ -> None)

  let rec pop_until (prf : Proof.t) bul : Proof.t =
    let prf', b = Option.get (pop prf) in
    if bullet_eq bul b then prf'
    else pop_until prf' bul

  let put p bul =
    try
      if not (has_bullet bul p) then
        (* bullet is not in use, so pushing it is always ok unless
           no goal under focus. *)
        push bul p
      else
        match suggest_bullet p with
        | Suggest suggested_bullet when bullet_eq bul suggested_bullet
            -> (* suggested_bullet is mandatory and you gave the right one *)
          let p' = pop_until p bul in
          push bul p'
      (* the bullet you gave is in use but not the right one *)
        | sugg -> raise (FailedBullet (bul,sugg))
    with NoSuchGoals _ -> (* push went bad *)
      raise (FailedBullet (bul,suggest_bullet p))

  let strict = {
    name = "Waterproof Strict Subproofs";
    put = put;
    suggest = (fun prf -> suggest_on_solved_goal (suggest_bullet prf))

  }
end

let _ = register_behavior Strict.strict

(** Definitions for the bullet behavior "Waterproof Relaxed Subproofs."
A bullet point will unfocus all finished subproofs, and will focus a new subproof. *)
module Relaxed = struct
  (** Unfocus as many proofs as possible: this should correspond to unfocusing
  all subproofs that are finished. *)
  let rec pop_until (prf : Proof.t) : Proof.t =
    try
      let prf', b = Option.get (pop prf) in
      pop_until prf'
    with e when CErrors.noncritical e ->
      prf

  (** The intention here is to unfocus all finished subproofs, and then
    start a new bullet. *)
  let put p bul =
    let prf = pop_until p in
    push bul prf

  let wp_relaxed_bullets = {
    name = "Waterproof Relaxed Subproofs";
    put = put;
    suggest = (fun prf -> suggest_on_solved_goal (suggest_bullet prf))

  }
end

let _ = register_behavior Relaxed.wp_relaxed_bullets