| author | wenzelm | 
| Sat, 08 Sep 2018 22:43:25 +0200 | |
| changeset 68955 | 0851db8cde12 | 
| parent 67405 | e9ab4ad7bd15 | 
| child 69065 | 440f7a575760 | 
| permissions | -rw-r--r-- | 
| 52722 | 1 | (* Title: HOL/SMT_Examples/boogie.ML | 
| 2 | Author: Sascha Boehme, TU Muenchen | |
| 3 | ||
| 52734 | 4 | Proving Boogie-generated verification conditions. | 
| 52722 | 5 | *) | 
| 6 | ||
| 7 | signature BOOGIE = | |
| 8 | sig | |
| 55788 
67699e08e969
store blobs / inlined files as separate text lines: smaller values are more healthy for the Poly/ML RTS and allow implicit sharing;
 wenzelm parents: 
54447diff
changeset | 9 | val boogie_prove: theory -> string list -> unit | 
| 57232 | 10 | end; | 
| 52722 | 11 | |
| 12 | structure Boogie: BOOGIE = | |
| 13 | struct | |
| 14 | ||
| 15 | (* utility functions *) | |
| 16 | ||
| 17 | val as_int = fst o read_int o raw_explode | |
| 18 | ||
| 19 | val isabelle_name = | |
| 52732 | 20 | let | 
| 52722 | 21 | fun purge s = if Symbol.is_letter s orelse Symbol.is_digit s then s else | 
| 22 | (case s of | |
| 23 | "." => "_o_" | |
| 24 | | "_" => "_n_" | |
| 25 | | "$" => "_S_" | |
| 26 | | "@" => "_G_" | |
| 27 | | "#" => "_H_" | |
| 28 | | "^" => "_T_" | |
| 29 |       | _   => ("_" ^ string_of_int (ord s) ^ "_"))
 | |
| 30 | in prefix "b_" o translate_string purge end | |
| 31 | ||
| 32 | ||
| 33 | (* context *) | |
| 34 | ||
| 35 | type context = | |
| 36 | typ Symtab.table * (term * bool) Symtab.table * term list * term list | |
| 37 | ||
| 38 | val empty_context: context = (Symtab.empty, Symtab.empty, [], []) | |
| 39 | ||
| 40 | fun add_type name (tds, fds, axs, vcs) = | |
| 41 | let | |
| 42 |     val T = TFree (isabelle_name name, @{sort type})
 | |
| 43 | val tds' = Symtab.update (name, T) tds | |
| 44 | in (tds', fds, axs, vcs) end | |
| 45 | ||
| 46 | fun add_func name Ts T unique (tds, fds, axs, vcs) = | |
| 47 | let | |
| 48 | val t = Free (isabelle_name name, Ts ---> T) | |
| 49 | val fds' = Symtab.update (name, (t, unique)) fds | |
| 50 | in (tds, fds', axs, vcs) end | |
| 51 | ||
| 52 | fun add_axiom t (tds, fds, axs, vcs) = (tds, fds, t :: axs, vcs) | |
| 53 | ||
| 54 | fun add_vc t (tds, fds, axs, vcs) = (tds, fds, axs, t :: vcs) | |
| 55 | ||
| 56 | fun lookup_type (tds, _, _, _) name = | |
| 57 | (case Symtab.lookup tds name of | |
| 58 | SOME T => T | |
| 59 | | NONE => error "Undeclared type") | |
| 60 | ||
| 61 | fun lookup_func (_, fds, _, _) name = | |
| 62 | (case Symtab.lookup fds name of | |
| 63 | SOME t_unique => t_unique | |
| 64 | | NONE => error "Undeclared function") | |
| 65 | ||
| 66 | ||
| 67 | (* constructors *) | |
| 68 | ||
| 69 | fun mk_var name T = Free ("V_" ^ isabelle_name name, T)
 | |
| 70 | ||
| 71 | fun mk_arrayT (Ts, T) = Type (@{type_name "fun"}, [HOLogic.mk_tupleT Ts, T])
 | |
| 72 | ||
| 73 | fun mk_binary t (t1, t2) = t $ t1 $ t2 | |
| 74 | ||
| 75 | fun mk_nary _ t [] = t | |
| 76 | | mk_nary f _ ts = uncurry (fold_rev f) (split_last ts) | |
| 77 | ||
| 78 | fun mk_distinct [] = @{const HOL.True}
 | |
| 79 |   | mk_distinct [_] = @{const HOL.True}
 | |
| 80 | | mk_distinct (t :: ts) = | |
| 81 | let | |
| 82 | fun mk_noteq u u' = | |
| 83 | HOLogic.mk_conj (HOLogic.mk_not (HOLogic.mk_eq (t, u)), u') | |
| 84 | in fold_rev mk_noteq ts (mk_distinct ts) end | |
| 85 | ||
| 86 | fun mk_store m k v = | |
| 87 | let | |
| 88 | val mT = Term.fastype_of m and kT = Term.fastype_of k | |
| 89 | val vT = Term.fastype_of v | |
| 90 |   in Const (@{const_name fun_upd}, mT --> kT --> vT --> mT) $ m $ k $ v end
 | |
| 91 | ||
| 92 | fun mk_quant q (Free (x, T)) t = q T $ absfree (x, T) t | |
| 93 |   | mk_quant _ t _ = raise TERM ("bad variable", [t])
 | |
| 94 | ||
| 58061 | 95 | val patternT = @{typ "SMT.pattern"}
 | 
| 52722 | 96 | |
| 97 | fun mk_pat t = | |
| 58061 | 98 |   Const (@{const_name "SMT.pat"}, Term.fastype_of t --> patternT) $ t
 | 
| 52722 | 99 | |
| 100 | fun mk_pattern [] = raise TERM ("mk_pattern", [])
 | |
| 58061 | 101 | | mk_pattern ts = SMT_Util.mk_symb_list patternT (map mk_pat ts) | 
| 52722 | 102 | |
| 103 | fun mk_trigger [] t = t | |
| 104 | | mk_trigger pss t = | |
| 58061 | 105 |       @{term "SMT.trigger"} $
 | 
| 106 |         SMT_Util.mk_symb_list @{typ "SMT.pattern SMT.symb_list"} (map mk_pattern pss) $ t
 | |
| 52722 | 107 | |
| 108 | ||
| 109 | (* parser *) | |
| 110 | ||
| 111 | fun repeat f n ls = | |
| 112 | let fun apply (xs, ls) = f ls |>> (fn x => x :: xs) | |
| 113 | in funpow (as_int n) apply ([], ls) |>> rev end | |
| 114 | ||
| 115 | fun parse_type _ (["bool"] :: ls) = (@{typ bool}, ls)
 | |
| 116 |   | parse_type _ (["int"] :: ls) = (@{typ int}, ls)
 | |
| 117 | | parse_type cx (["array", arity] :: ls) = | |
| 118 | repeat (parse_type cx) arity ls |>> mk_arrayT o split_last | |
| 119 |   | parse_type cx (("type-con" :: name :: _) :: ls) = (lookup_type cx name, ls)
 | |
| 120 | | parse_type _ _ = error "Bad type" | |
| 121 | ||
| 122 | fun parse_expr _ (["true"] :: ls) = (@{term True}, ls)
 | |
| 123 |   | parse_expr _ (["false"] :: ls) = (@{term False}, ls)
 | |
| 124 | | parse_expr cx (["not"] :: ls) = parse_expr cx ls |>> HOLogic.mk_not | |
| 57232 | 125 |   | parse_expr cx (["and", n] :: ls) = parse_nary_expr cx n HOLogic.mk_conj @{term True} ls
 | 
| 126 |   | parse_expr cx (["or", n] :: ls) = parse_nary_expr cx n HOLogic.mk_disj @{term False} ls
 | |
| 127 |   | parse_expr cx (["implies"] :: ls) = parse_bin_expr cx (mk_binary @{term HOL.implies}) ls
 | |
| 52722 | 128 | | parse_expr cx (["="] :: ls) = parse_bin_expr cx HOLogic.mk_eq ls | 
| 129 | | parse_expr cx (["var", name] :: ls) = parse_type cx ls |>> mk_var name | |
| 130 | | parse_expr cx (["fun", name, n] :: ls) = | |
| 131 | let val (t, _) = lookup_func cx name | |
| 132 | in repeat (parse_expr cx) n ls |>> curry Term.list_comb t end | |
| 133 |   | parse_expr cx (("label" :: _) :: ls) = parse_expr cx ls
 | |
| 57232 | 134 |   | parse_expr _ (["int-num", n] :: ls) = (HOLogic.mk_number @{typ int} (as_int n), ls)
 | 
| 67399 | 135 |   | parse_expr cx (["<"] :: ls) = parse_bin_expr cx (mk_binary @{term "(<) :: int => _"}) ls
 | 
| 136 |   | parse_expr cx (["<="] :: ls) = parse_bin_expr cx (mk_binary @{term "(<=) :: int => _"}) ls
 | |
| 137 |   | parse_expr cx ([">"] :: ls) = parse_bin_expr cx (mk_binary @{term "(<) :: int => _"}o swap) ls
 | |
| 52722 | 138 | | parse_expr cx ([">="] :: ls) = | 
| 67399 | 139 |       parse_bin_expr cx (mk_binary @{term "(<=) :: int => _"} o swap) ls
 | 
| 140 |   | parse_expr cx (["+"] :: ls) = parse_bin_expr cx (mk_binary @{term "(+) :: int => _"}) ls
 | |
| 141 |   | parse_expr cx (["-"] :: ls) = parse_bin_expr cx (mk_binary @{term "(-) :: int => _"}) ls
 | |
| 142 |   | parse_expr cx (["*"] :: ls) = parse_bin_expr cx (mk_binary @{term "( * ) :: int => _"}) ls
 | |
| 57232 | 143 |   | parse_expr cx (["/"] :: ls) = parse_bin_expr cx (mk_binary @{term boogie_div}) ls
 | 
| 144 |   | parse_expr cx (["%"] :: ls) = parse_bin_expr cx (mk_binary @{term boogie_mod}) ls
 | |
| 52722 | 145 | | parse_expr cx (["select", n] :: ls) = | 
| 146 | repeat (parse_expr cx) n ls | |
| 147 | |>> (fn ts => hd ts $ HOLogic.mk_tuple (tl ts)) | |
| 148 | | parse_expr cx (["store", n] :: ls) = | |
| 149 | repeat (parse_expr cx) n ls | |
| 150 | |>> split_last | |
| 151 | |>> (fn (ts, t) => mk_store (hd ts) (HOLogic.mk_tuple (tl ts)) t) | |
| 152 | | parse_expr cx (["forall", vars, pats, atts] :: ls) = | |
| 153 | parse_quant cx HOLogic.all_const vars pats atts ls | |
| 154 | | parse_expr cx (["exists", vars, pats, atts] :: ls) = | |
| 155 | parse_quant cx HOLogic.exists_const vars pats atts ls | |
| 156 | | parse_expr _ _ = error "Bad expression" | |
| 157 | ||
| 158 | and parse_bin_expr cx f ls = ls |> parse_expr cx ||>> parse_expr cx |>> f | |
| 159 | ||
| 160 | and parse_nary_expr cx n f c ls = | |
| 161 | repeat (parse_expr cx) n ls |>> mk_nary (curry f) c | |
| 162 | ||
| 163 | and parse_quant cx q vars pats atts ls = | |
| 164 | let | |
| 165 | val ((((vs, pss), _), t), ls') = | |
| 166 | ls | |
| 167 | |> repeat (parse_var cx) vars | |
| 168 | ||>> repeat (parse_pat cx) pats | |
| 169 | ||>> repeat (parse_attr cx) atts | |
| 170 | ||>> parse_expr cx | |
| 171 | in (fold_rev (mk_quant q) vs (mk_trigger pss t), ls') end | |
| 172 | ||
| 173 | and parse_var cx (["var", name] :: ls) = parse_type cx ls |>> mk_var name | |
| 174 | | parse_var _ _ = error "Bad variable" | |
| 175 | ||
| 176 | and parse_pat cx (["pat", n] :: ls) = repeat (parse_expr cx) n ls | |
| 177 | | parse_pat _ _ = error "Bad pattern" | |
| 178 | ||
| 179 | and parse_attr cx (["attribute", name, n] :: ls) = | |
| 180 | let | |
| 181 | fun attr (["expr-attr"] :: ls) = parse_expr cx ls |>> K () | |
| 182 |           | attr (("string-attr" :: _) :: ls) = ((), ls)
 | |
| 183 | | attr _ = error "Bad attribute value" | |
| 184 | in repeat attr n ls |>> K name end | |
| 185 | | parse_attr _ _ = error "Bad attribute" | |
| 186 | ||
| 187 | fun parse_func cx arity n ls = | |
| 188 | let | |
| 189 | val ((Ts, atts), ls') = | |
| 190 | ls |> repeat (parse_type cx) arity ||>> repeat (parse_attr cx) n | |
| 67405 
e9ab4ad7bd15
uniform use of Standard ML op-infix -- eliminated warnings;
 wenzelm parents: 
67399diff
changeset | 191 | val unique = member (op =) atts "unique" | 
| 52722 | 192 | in ((split_last Ts, unique), ls') end | 
| 193 | ||
| 194 | fun parse_decl (("type-decl" :: name :: _) :: ls) cx = (ls, add_type name cx)
 | |
| 195 | | parse_decl (["fun-decl", name, arity, n] :: ls) cx = | |
| 196 | let val (((Ts, T), unique), ls') = parse_func cx arity n ls | |
| 197 | in (ls', add_func name Ts T unique cx) end | |
| 198 |   | parse_decl (("axiom" :: _) :: ls) cx =
 | |
| 199 | let val (t, ls') = parse_expr cx ls | |
| 200 | in (ls', add_axiom t cx) end | |
| 201 |   | parse_decl (("var-decl" :: _) :: ls) cx =
 | |
| 202 | parse_type cx ls |> snd |> rpair cx | |
| 203 |   | parse_decl (("vc" :: _) :: ls) cx =
 | |
| 204 | let val (t, ls') = parse_expr cx ls | |
| 205 | in (ls', add_vc t cx) end | |
| 206 | | parse_decl _ _ = error "Bad declaration" | |
| 207 | ||
| 208 | fun parse_lines [] cx = cx | |
| 209 | | parse_lines ls cx = parse_decl ls cx |-> parse_lines | |
| 210 | ||
| 211 | ||
| 55788 
67699e08e969
store blobs / inlined files as separate text lines: smaller values are more healthy for the Poly/ML RTS and allow implicit sharing;
 wenzelm parents: 
54447diff
changeset | 212 | (* splitting of text lines into a lists of tokens *) | 
| 52722 | 213 | |
| 214 | fun is_blank c = (c = " " orelse c = "\t" orelse c = "\r" orelse c = "\n") | |
| 215 | ||
| 55788 
67699e08e969
store blobs / inlined files as separate text lines: smaller values are more healthy for the Poly/ML RTS and allow implicit sharing;
 wenzelm parents: 
54447diff
changeset | 216 | val token_lines = | 
| 
67699e08e969
store blobs / inlined files as separate text lines: smaller values are more healthy for the Poly/ML RTS and allow implicit sharing;
 wenzelm parents: 
54447diff
changeset | 217 | map (String.tokens (is_blank o str)) | 
| 
67699e08e969
store blobs / inlined files as separate text lines: smaller values are more healthy for the Poly/ML RTS and allow implicit sharing;
 wenzelm parents: 
54447diff
changeset | 218 | #> filter (fn [] => false | _ => true) | 
| 52722 | 219 | |
| 220 | ||
| 221 | (* proving verification conditions *) | |
| 222 | ||
| 223 | fun add_unique_axioms (tds, fds, axs, vcs) = | |
| 224 | Symtab.fold (fn (_, (t, true)) => cons t | _ => I) fds [] | |
| 225 | |> map (swap o Term.dest_Free) | |
| 226 | |> AList.group (op =) | |
| 227 | |> map (fn (T, ns) => mk_distinct (map (Free o rpair T) ns)) | |
| 228 | |> (fn axs' => (tds, fds, axs' @ axs, vcs)) | |
| 229 | ||
| 230 | fun build_proof_context thy (tds, fds, axs, vcs) = | |
| 231 | let | |
| 232 | val vc = | |
| 233 | (case vcs of | |
| 234 | [vc] => vc | |
| 235 | | _ => error "Bad number of verification conditions") | |
| 236 | in | |
| 237 | Proof_Context.init_global thy | |
| 238 | |> Symtab.fold (fn (_, T) => Variable.declare_typ T) tds | |
| 239 | |> Symtab.fold (fn (_, (t, _)) => Variable.declare_term t) fds | |
| 240 | |> fold Variable.declare_term axs | |
| 241 | |> fold Variable.declare_term vcs | |
| 242 | |> pair (map HOLogic.mk_Trueprop axs, HOLogic.mk_Trueprop vc) | |
| 243 | end | |
| 244 | ||
| 245 | val boogie_rules = | |
| 61424 
c3658c18b7bc
prod_case as canonical name for product type eliminator
 haftmann parents: 
59936diff
changeset | 246 |   [@{thm fst_conv}, @{thm snd_conv}, @{thm prod.collapse}] @
 | 
| 52722 | 247 |   [@{thm fun_upd_same}, @{thm fun_upd_apply}]
 | 
| 248 | ||
| 249 | fun boogie_tac ctxt axioms = | |
| 58061 | 250 | ALLGOALS (SMT_Solver.smt_tac ctxt (boogie_rules @ axioms)) | 
| 52722 | 251 | |
| 55788 
67699e08e969
store blobs / inlined files as separate text lines: smaller values are more healthy for the Poly/ML RTS and allow implicit sharing;
 wenzelm parents: 
54447diff
changeset | 252 | fun boogie_prove thy lines = | 
| 52722 | 253 | let | 
| 254 | val ((axioms, vc), ctxt) = | |
| 255 | empty_context | |
| 55788 
67699e08e969
store blobs / inlined files as separate text lines: smaller values are more healthy for the Poly/ML RTS and allow implicit sharing;
 wenzelm parents: 
54447diff
changeset | 256 | |> parse_lines (token_lines lines) | 
| 52722 | 257 | |> add_unique_axioms | 
| 54447 
019394de2b41
proper thy_load command 'boogie_file' -- avoid direct access to file-system;
 wenzelm parents: 
52734diff
changeset | 258 | |> build_proof_context thy | 
| 52722 | 259 | |
| 57232 | 260 |     val _ = Goal.prove ctxt [] axioms vc (fn {prems, context} => boogie_tac context prems)
 | 
| 52732 | 261 | val _ = writeln "Verification condition proved successfully" | 
| 52722 | 262 | |
| 54447 
019394de2b41
proper thy_load command 'boogie_file' -- avoid direct access to file-system;
 wenzelm parents: 
52734diff
changeset | 263 | in () end | 
| 
019394de2b41
proper thy_load command 'boogie_file' -- avoid direct access to file-system;
 wenzelm parents: 
52734diff
changeset | 264 | |
| 
019394de2b41
proper thy_load command 'boogie_file' -- avoid direct access to file-system;
 wenzelm parents: 
52734diff
changeset | 265 | |
| 
019394de2b41
proper thy_load command 'boogie_file' -- avoid direct access to file-system;
 wenzelm parents: 
52734diff
changeset | 266 | (* Isar command *) | 
| 
019394de2b41
proper thy_load command 'boogie_file' -- avoid direct access to file-system;
 wenzelm parents: 
52734diff
changeset | 267 | |
| 
019394de2b41
proper thy_load command 'boogie_file' -- avoid direct access to file-system;
 wenzelm parents: 
52734diff
changeset | 268 | val _ = | 
| 59936 
b8ffc3dc9e24
@{command_spec} is superseded by @{command_keyword};
 wenzelm parents: 
58061diff
changeset | 269 |   Outer_Syntax.command @{command_keyword boogie_file}
 | 
| 54447 
019394de2b41
proper thy_load command 'boogie_file' -- avoid direct access to file-system;
 wenzelm parents: 
52734diff
changeset | 270 | "prove verification condition from .b2i file" | 
| 56208 | 271 | (Resources.provide_parse_files "boogie_file" >> (fn files => | 
| 54447 
019394de2b41
proper thy_load command 'boogie_file' -- avoid direct access to file-system;
 wenzelm parents: 
52734diff
changeset | 272 | Toplevel.theory (fn thy => | 
| 
019394de2b41
proper thy_load command 'boogie_file' -- avoid direct access to file-system;
 wenzelm parents: 
52734diff
changeset | 273 | let | 
| 55788 
67699e08e969
store blobs / inlined files as separate text lines: smaller values are more healthy for the Poly/ML RTS and allow implicit sharing;
 wenzelm parents: 
54447diff
changeset | 274 |           val ([{lines, ...}], thy') = files thy;
 | 
| 
67699e08e969
store blobs / inlined files as separate text lines: smaller values are more healthy for the Poly/ML RTS and allow implicit sharing;
 wenzelm parents: 
54447diff
changeset | 275 | val _ = boogie_prove thy' lines; | 
| 54447 
019394de2b41
proper thy_load command 'boogie_file' -- avoid direct access to file-system;
 wenzelm parents: 
52734diff
changeset | 276 | in thy' end))) | 
| 52722 | 277 | |
| 57232 | 278 | end; |