1 (* Title: HOL/Boogie/Tools/boogie_commands.ML
2 Author: Sascha Boehme, TU Muenchen
4 Isar commands to create a Boogie environment simulation.
7 signature BOOGIE_COMMANDS =
9 val setup: theory -> theory
12 structure Boogie_Commands: BOOGIE_COMMANDS =
17 fun boogie_open ((quiet, files), offsets) thy =
19 val ([{src_path = path, text, ...}: Token.file], thy') = files thy
22 val _ = snd (Path.split_ext path) = ext orelse
23 error ("Bad file ending of file " ^ Path.print path ^ ", " ^
24 "expected file ending " ^ quote ext)
26 val _ = Boogie_VCs.is_closed thy orelse
27 error ("Found the beginning of a new Boogie environment, " ^
28 "but another Boogie environment is still open.")
31 |> Boogie_Loader.parse_b2i (not quiet) offsets text
37 VC_Take of int list * (bool * string list) option |
38 VC_Only of string list |
39 VC_Without of string list |
40 VC_Examine of string list |
43 fun get_vc thy vc_name =
44 (case Boogie_VCs.lookup thy vc_name of
47 (case AList.lookup (op =) (Boogie_VCs.state_of thy) vc_name of
48 SOME Boogie_VCs.Proved => error ("The verification condition " ^
49 quote vc_name ^ " has already been proved.")
50 | _ => error ("There is no verification condition " ^
51 quote vc_name ^ ".")))
55 (case Boogie_Tactics.split t of
57 | _ => error "Multiple goals.")
61 val (us, u) = split_goal t
62 val assms = [((@{binding vc_trace}, []), map (rpair []) us)]
64 pair [u] o snd o Proof_Context.add_assms_i Assumption.assume_export assms
67 fun single_prove goal ctxt thm =
68 Goal.prove ctxt [] [] goal (fn {context, ...} => HEADGOAL (
69 Boogie_Tactics.split_tac
70 THEN' Boogie_Tactics.drop_assert_at_tac
71 THEN' SUBPROOF (fn _ => Tactic.rtac thm 1) context))
73 fun boogie_vc (vc_name, vc_opts) thy =
75 val vc = get_vc thy vc_name
78 (case Boogie_VCs.extract vc l of
80 | NONE => error ("There is no assertion to be proved with label " ^
86 | VC_Take (ps, NONE) => [Boogie_VCs.paths_and ps [] vc]
87 | VC_Take (ps, SOME (true, ls)) => [Boogie_VCs.paths_and ps ls vc]
88 | VC_Take (ps, SOME (false, ls)) => [Boogie_VCs.paths_without ps ls vc]
89 | VC_Only ls => [Boogie_VCs.only ls vc]
90 | VC_Without ls => [Boogie_VCs.without ls vc]
91 | VC_Examine ls => map (extract vc) ls
92 | VC_Single l => [extract vc l])
93 val ts = map Boogie_VCs.prop_of_vc vcs
95 val (prepare, finish) =
97 VC_Single _ => (single_prep (hd ts), single_prove (hd ts))
98 | _ => (pair ts, K I))
100 val discharge = fold (Boogie_VCs.discharge o pair vc_name)
101 fun after_qed [thms] = Proof_Context.background_theory (discharge (vcs ~~ thms))
104 Proof_Context.init_global thy
105 |> fold Variable.auto_fixes ts
106 |> (fn ctxt1 => ctxt1
108 |-> (fn us => fn ctxt2 => ctxt2
109 |> Proof.theorem NONE (fn thmss => fn ctxt =>
110 let val export = map (finish ctxt1) o Proof_Context.export ctxt ctxt2
111 in after_qed (map export thmss) ctxt end) [map (rpair []) us]))
115 fun write_list head =
116 map Pretty.str o sort (dict_ord string_ord o pairself raw_explode) #>
117 Pretty.writeln o Pretty.big_list head
119 fun parens s = "(" ^ s ^ ")"
121 fun boogie_status thy =
123 fun string_of_state Boogie_VCs.Proved = "proved"
124 | string_of_state Boogie_VCs.NotProved = "not proved"
125 | string_of_state Boogie_VCs.PartiallyProved = "partially proved"
127 Boogie_VCs.state_of thy
128 |> map (fn (name, proved) => name ^ " " ^ parens (string_of_state proved))
129 |> write_list "Boogie verification conditions:"
132 fun boogie_status_vc full vc_name thy =
134 fun pretty tag s = s ^ " " ^ parens tag
136 val (not_proved, proved) = Boogie_VCs.state_of_vc thy vc_name
139 then write_list ("Assertions of Boogie verification condition " ^
141 (map (pretty "proved") proved @ map (pretty "not proved") not_proved)
142 else write_list ("Unproved assertions of Boogie verification condition " ^
143 quote vc_name ^ ":") not_proved
146 fun boogie_status_vc_paths full vc_name thy =
148 fun labels ls = Pretty.blk (0, Pretty.breaks (map Pretty.str ls))
153 [Pretty.big_list ("Path " ^ string_of_int (i+1) ^ ":")
154 [labels (map (fn (n, true) => n | (n, _) => parens n) ns)]]
156 let val ns' = map_filter (fn (n, true) => SOME n | _ => NONE) ns
160 [Pretty.big_list ("Unproved assertions of path " ^
161 string_of_int (i+1) ^ ":") [labels ns']]
166 ("Paths of Boogie verification condition " ^ quote vc_name ^ ":")
167 (flat (map_index pp (Boogie_VCs.path_names_of (get_vc thy vc_name)))))
172 fun trying s = tracing ("Trying " ^ s ^ " ...")
173 fun success_on s = tracing ("Succeeded on " ^ s ^ ".")
174 fun failure_on s c = tracing ("Failed on " ^ s ^ c)
176 fun string_of_asserts vc = space_implode ", " (fst (Boogie_VCs.names_of vc))
178 fun string_of_path (i, n) =
179 "path " ^ string_of_int i ^ " of " ^ string_of_int n
181 fun itemize_paths ps =
182 let val n = length ps
183 in fst (fold_map (fn p => fn i => (((i, n), p), i+1)) ps 1) end
185 fun par_map f = flat o Par_List.map f
188 let val n = Boogie_VCs.size_of vc
190 if n <= 1 then fst (Boogie_VCs.names_of vc)
192 let val (vc1, vc2) = the (Boogie_VCs.split_path (n div 2) vc)
193 in par_map f [vc1, vc2] end
196 fun prove thy meth vc =
197 Proof_Context.init_global thy
198 |> Proof.theorem NONE (K I) [[(Boogie_VCs.prop_of_vc vc, [])]]
201 |> Proof.global_done_proof
203 fun boogie_narrow_vc (quick, timeout) vc_name meth thy =
205 fun tp t = TimeLimit.timeLimit (Time.fromSeconds t) (prove thy meth)
207 fun try_vc t (tag, split_tag) split vc = (trying tag;
208 (case try (tp t) vc of
209 SOME _ => (success_on tag; [])
210 | NONE => (failure_on tag split_tag; split vc)))
212 fun some_asserts vc =
214 val (t, sep) = if Boogie_VCs.size_of vc = 1 then (timeout, ".")
215 else (quick, ", further splitting ...")
216 in try_vc t (string_of_asserts vc, sep) (divide some_asserts) vc end
219 try_vc quick (string_of_path p, ", splitting into assertions ...")
220 (divide some_asserts)
223 try_vc quick ("full goal", ", splitting into paths ...")
224 (par_map (uncurry single_path) o itemize_paths o Boogie_VCs.paths_of)
226 val unsolved = complete_vc (get_vc thy vc_name)
229 then writeln ("Completely solved Boogie verification condition " ^
231 else write_list ("Unsolved assertions of Boogie verification condition " ^
232 quote vc_name ^ ":") unsolved
235 fun boogie_scan_vc timeout vc_name meth thy =
237 val tp = TimeLimit.timeLimit (Time.fromSeconds timeout) (prove thy meth)
239 val vc = get_vc thy vc_name
240 fun prove_assert name =
241 (trying name; tp (the (Boogie_VCs.extract vc name)))
242 val find_first_failure = find_first (is_none o try prove_assert)
244 (case find_first_failure (fst (Boogie_VCs.names_of vc)) of
245 SOME name => writeln ("failed on " ^ quote name)
246 | NONE => writeln "succeeded")
254 fun not_proved (_, Boogie_VCs.Proved) = NONE
255 | not_proved (name, _) = SOME name
257 val unproved = map_filter not_proved (Boogie_VCs.state_of thy)
259 if null unproved then Boogie_VCs.close thy
260 else error (Pretty.string_of (Pretty.big_list
261 "The following verification conditions have not been proved:"
262 (map Pretty.str unproved)))
267 (* syntax and setup *)
269 fun scan_val n f = Args.$$$ n -- Args.colon |-- f
270 fun scan_arg f = Args.parens f
271 fun scan_opt n = Scan.optional (scan_arg (Args.$$$ n >> K true)) false
273 val vc_offsets = Scan.optional (Args.bracks (Parse.list1
274 (Parse.string --| Args.colon -- Parse.nat))) []
277 Outer_Syntax.command @{command_spec "boogie_open"}
278 "open a new Boogie environment and load a Boogie-generated .b2i file"
279 (scan_opt "quiet" -- Thy_Load.provide_parse_files "boogie_open" -- vc_offsets >>
280 (Toplevel.theory o boogie_open))
283 val vc_name = Parse.name
285 val vc_label = Parse.name
286 val vc_labels = Scan.repeat1 vc_label
289 Parse.nat -- (Args.$$$ "-" |-- Parse.nat) >> (op upto) ||
294 (scan_val "assertion" vc_label >> VC_Single ||
295 scan_val "examine" vc_labels >> VC_Examine ||
296 scan_val "take" ((Parse.list vc_paths >> flat) -- Scan.option (
297 scan_val "without" vc_labels >> pair false ||
298 scan_val "and_also" vc_labels >> pair true) >> VC_Take) ||
299 scan_val "only" vc_labels >> VC_Only ||
300 scan_val "without" vc_labels >> VC_Without) ||
301 Scan.succeed VC_Complete
304 Outer_Syntax.command @{command_spec "boogie_vc"}
305 "enter into proof mode for a specific verification condition"
306 (vc_name -- vc_opts >> (fn args =>
307 (Toplevel.print o Toplevel.theory_to_proof (boogie_vc args))))
310 val quick_timeout = 5
311 val default_timeout = 20
313 fun timeout name = Scan.optional (scan_val name Parse.nat)
317 Args.$$$ "scan" |-- timeout "timeout" quick_timeout >> boogie_scan_vc ||
318 Args.$$$ "narrow" |-- timeout "step_timeout" quick_timeout --
319 timeout "final_timeout" default_timeout >> boogie_narrow_vc) --
320 vc_name -- Method.parse >>
321 (fn ((f, vc_name), (meth, _)) => f vc_name meth)
326 (Args.$$$ "paths" >> K (boogie_status_vc_paths true) ||
327 Scan.succeed (boogie_status_vc true)) ||
328 Args.$$$ "paths" >> K (boogie_status_vc_paths false)) ||
329 Scan.succeed (boogie_status_vc false)) --
330 vc_name >> (fn (f, vc_name) => f vc_name)
332 fun status_cmd f = Toplevel.no_timing o Toplevel.keep (fn state =>
333 f (Toplevel.theory_of state))
336 Outer_Syntax.improper_command @{command_spec "boogie_status"}
337 "show the name and state of all loaded verification conditions"
338 (status_test >> status_cmd ||
339 status_vc >> status_cmd ||
340 Scan.succeed (status_cmd boogie_status))
344 Outer_Syntax.command @{command_spec "boogie_end"}
345 "close the current Boogie environment"
346 (Scan.succeed (Toplevel.theory boogie_end))
349 val setup = Theory.at_end (fn thy =>
351 val _ = Boogie_VCs.is_closed thy
352 orelse error ("Found the end of the theory, " ^
353 "but the last Boogie environment is still open.")