|
1 (* Title: HOL/Boogie/Tools/boogie_loader.ML |
|
2 Author: Sascha Boehme, TU Muenchen |
|
3 |
|
4 Loading and interpreting Boogie-generated files. |
|
5 *) |
|
6 |
|
7 signature BOOGIE_LOADER = |
|
8 sig |
|
9 val load_b2i: bool -> Path.T -> theory -> theory |
|
10 end |
|
11 |
|
12 structure Boogie_Loader: BOOGIE_LOADER = |
|
13 struct |
|
14 |
|
15 fun log verbose text args thy = |
|
16 if verbose |
|
17 then (Pretty.writeln (Pretty.big_list text (map Pretty.str args)); thy) |
|
18 else thy |
|
19 |
|
20 val isabelle_name = |
|
21 let |
|
22 fun purge s = if Symbol.is_letdig s then s else |
|
23 (case s of |
|
24 "." => "_o_" |
|
25 | "_" => "_n_" |
|
26 | "$" => "_S_" |
|
27 | "@" => "_G_" |
|
28 | "#" => "_H_" |
|
29 | "^" => "_T_" |
|
30 | _ => ("_" ^ string_of_int (ord s) ^ "_")) |
|
31 in prefix "b_" o translate_string purge end |
|
32 |
|
33 datatype attribute_value = StringValue of string | TermValue of Term.term |
|
34 |
|
35 |
|
36 |
|
37 local |
|
38 fun lookup_type_name thy name arity = |
|
39 let val intern = Sign.intern_type thy name |
|
40 in |
|
41 if Sign.declared_tyname thy intern |
|
42 then |
|
43 if Sign.arity_number thy intern = arity then SOME intern |
|
44 else error ("Boogie: type already declared with different arity: " ^ |
|
45 quote name) |
|
46 else NONE |
|
47 end |
|
48 |
|
49 fun declare (name, arity) thy = |
|
50 let val isa_name = isabelle_name name |
|
51 in |
|
52 (case lookup_type_name thy isa_name arity of |
|
53 SOME type_name => ((type_name, false), thy) |
|
54 | NONE => |
|
55 let |
|
56 val bname = Binding.name isa_name |
|
57 val args = Name.variant_list [] (replicate arity "'") |
|
58 val (T, thy') = |
|
59 ObjectLogic.typedecl (Binding.name isa_name, args, NoSyn) thy |
|
60 val type_name = fst (Term.dest_Type T) |
|
61 in ((type_name, true), thy') end) |
|
62 end |
|
63 |
|
64 fun type_names ((name, _), (new_name, new)) = |
|
65 if new then SOME (new_name ^ " (was " ^ name ^ ")") else NONE |
|
66 in |
|
67 fun declare_types verbose tys = |
|
68 fold_map declare tys #-> (fn tds => |
|
69 log verbose "Declared types:" (map_filter type_names (tys ~~ tds)) #> |
|
70 rpair (Symtab.make (map fst tys ~~ map fst tds))) |
|
71 end |
|
72 |
|
73 |
|
74 |
|
75 local |
|
76 val quote_mixfix = |
|
77 Symbol.explode #> map |
|
78 (fn "_" => "'_" |
|
79 | "(" => "'(" |
|
80 | ")" => "')" |
|
81 | "/" => "'/" |
|
82 | s => s) #> |
|
83 implode |
|
84 |
|
85 fun mk_syntax name i = |
|
86 let |
|
87 val syn = quote_mixfix name |
|
88 val args = concat (separate ",/ " (replicate i "_")) |
|
89 in |
|
90 if i = 0 then Mixfix (syn, [], 1000) |
|
91 else Mixfix (syn ^ "()'(/" ^ args ^ "')", replicate i 0, 1000) |
|
92 end |
|
93 |
|
94 fun process_attributes T = |
|
95 let |
|
96 fun const name = SOME (Const (name, T)) |
|
97 |
|
98 fun choose builtin = |
|
99 (case builtin of |
|
100 "bvnot" => const @{const_name z3_bvnot} |
|
101 | "bvand" => const @{const_name z3_bvand} |
|
102 | "bvor" => const @{const_name z3_bvor} |
|
103 | "bvxor" => const @{const_name z3_bvxor} |
|
104 | "bvadd" => const @{const_name z3_bvadd} |
|
105 | "bvneg" => const @{const_name z3_bvneg} |
|
106 | "bvsub" => const @{const_name z3_bvsub} |
|
107 | "bvmul" => const @{const_name z3_bvmul} |
|
108 | "bvudiv" => const @{const_name z3_bvudiv} |
|
109 | "bvurem" => const @{const_name z3_bvurem} |
|
110 | "bvsdiv" => const @{const_name z3_bvsdiv} |
|
111 | "bvsrem" => const @{const_name z3_bvsrem} |
|
112 | "bvshl" => const @{const_name z3_bvshl} |
|
113 | "bvlshr" => const @{const_name z3_bvlshr} |
|
114 | "bvashr" => const @{const_name z3_bvashr} |
|
115 | "bvult" => const @{const_name z3_bvult} |
|
116 | "bvule" => const @{const_name z3_bvule} |
|
117 | "bvugt" => const @{const_name z3_bvugt} |
|
118 | "bvuge" => const @{const_name z3_bvuge} |
|
119 | "bvslt" => const @{const_name z3_bvslt} |
|
120 | "bvsle" => const @{const_name z3_bvsle} |
|
121 | "bvsgt" => const @{const_name z3_bvsgt} |
|
122 | "bvsge" => const @{const_name z3_bvsge} |
|
123 | "sign_extend" => const @{const_name z3_sign_extend} |
|
124 | _ => NONE) |
|
125 |
|
126 fun is_builtin att = |
|
127 (case att of |
|
128 ("bvbuiltin", [StringValue builtin]) => choose builtin |
|
129 | ("bvint", [StringValue "ITE"]) => const @{const_name If} |
|
130 | _ => NONE) |
|
131 in get_first is_builtin end |
|
132 |
|
133 fun lookup_const thy name T = |
|
134 let |
|
135 val intern = Sign.intern_const thy name |
|
136 val is_type_instance = Type.typ_instance o Sign.tsig_of |
|
137 in |
|
138 if Sign.declared_const thy intern |
|
139 then |
|
140 if is_type_instance thy (T, Sign.the_const_type thy intern) |
|
141 then SOME (Const (intern, T)) |
|
142 else error ("Boogie: function already declared with different type: " ^ |
|
143 quote name) |
|
144 else NONE |
|
145 end |
|
146 |
|
147 fun declare (name, ((Ts, T), atts)) thy = |
|
148 let val isa_name = isabelle_name name and U = Ts ---> T |
|
149 in |
|
150 (case lookup_const thy isa_name U of |
|
151 SOME t => (((name, t), false), thy) |
|
152 | NONE => |
|
153 (case process_attributes U atts of |
|
154 SOME t => (((name, t), false), thy) |
|
155 | NONE => |
|
156 thy |
|
157 |> Sign.declare_const ((Binding.name isa_name, U), |
|
158 mk_syntax name (length Ts)) |
|
159 |> apfst (rpair true o pair name))) |
|
160 end |
|
161 |
|
162 fun const_names ((name, _), ((_, t), new)) = |
|
163 if new then SOME (fst (Term.dest_Const t) ^ " (as " ^ name ^ ")") else NONE |
|
164 in |
|
165 fun declare_functions verbose fns = |
|
166 fold_map declare fns #-> (fn fds => |
|
167 log verbose "Declared constants:" (map_filter const_names (fns ~~ fds)) #> |
|
168 rpair (Symtab.make (map fst fds))) |
|
169 end |
|
170 |
|
171 |
|
172 |
|
173 local |
|
174 fun name_axioms axs = |
|
175 let fun mk_name idx = "axiom_" ^ string_of_int (idx + 1) |
|
176 in map_index (fn (idx, t) => (mk_name idx, HOLogic.mk_Trueprop t)) axs end |
|
177 |
|
178 fun only_new_boogie_axioms thy = |
|
179 let val baxs = map Thm.prop_of (Boogie_Axioms.get (ProofContext.init thy)) |
|
180 in filter_out (member (op aconv) baxs o snd) end |
|
181 in |
|
182 fun add_axioms verbose axs thy = |
|
183 let val axs' = only_new_boogie_axioms thy (name_axioms axs) |
|
184 in |
|
185 thy |
|
186 |> PureThy.add_axioms (map (rpair [] o apfst Binding.name) axs') |
|
187 |-> Context.theory_map o fold Boogie_Axioms.add_thm |
|
188 |> log verbose "The following axioms were added:" (map fst axs') |
|
189 |> Context.theory_map (fn context => fold Split_VC_SMT_Rules.add_thm |
|
190 (Boogie_Axioms.get (Context.proof_of context)) context) |
|
191 end |
|
192 end |
|
193 |
|
194 |
|
195 |
|
196 fun add_vcs verbose vcs thy = |
|
197 let |
|
198 val reused = duplicates (op =) (map (fst o fst) vcs) |
|
199 fun rename (n, i) = isabelle_name n ^ |
|
200 (if member (op =) reused n then "_" ^ string_of_int i else "") |
|
201 |
|
202 fun decorate (name, t) = (rename name, HOLogic.mk_Trueprop t) |
|
203 val vcs' = map decorate vcs |
|
204 in |
|
205 thy |
|
206 |> Boogie_VCs.set vcs' |
|
207 |> log verbose "The following verification conditions were loaded:" |
|
208 (map fst vcs') |
|
209 end |
|
210 |
|
211 |
|
212 |
|
213 local |
|
214 fun mk_bitT i T = |
|
215 if i = 0 |
|
216 then Type (@{type_name "Numeral_Type.bit0"}, [T]) |
|
217 else Type (@{type_name "Numeral_Type.bit1"}, [T]) |
|
218 |
|
219 fun mk_binT size = |
|
220 if size = 0 then @{typ "Numeral_Type.num0"} |
|
221 else if size = 1 then @{typ "Numeral_Type.num1"} |
|
222 else let val (q, r) = Integer.div_mod size 2 in mk_bitT r (mk_binT q) end |
|
223 in |
|
224 fun mk_wordT size = |
|
225 if size >= 0 then Type (@{type_name "word"}, [mk_binT size]) |
|
226 else raise TYPE ("mk_wordT: " ^ quote (string_of_int size), [], []) |
|
227 end |
|
228 |
|
229 local |
|
230 fun dest_binT T = |
|
231 (case T of |
|
232 Type (@{type_name "Numeral_Type.num0"}, _) => 0 |
|
233 | Type (@{type_name "Numeral_Type.num1"}, _) => 1 |
|
234 | Type (@{type_name "Numeral_Type.bit0"}, [T]) => 2 * dest_binT T |
|
235 | Type (@{type_name "Numeral_Type.bit1"}, [T]) => 1 + 2 * dest_binT T |
|
236 | _ => raise TYPE ("dest_binT", [T], [])) |
|
237 in |
|
238 val dest_wordT = (fn |
|
239 Type (@{type_name "word"}, [T]) => dest_binT T |
|
240 | T => raise TYPE ("dest_wordT", [T], [])) |
|
241 end |
|
242 |
|
243 fun mk_arrayT (Ts, T) = Type (@{type_name "fun"}, [HOLogic.mk_tupleT Ts, T]) |
|
244 |
|
245 |
|
246 |
|
247 datatype token = Token of string | Newline | EOF |
|
248 |
|
249 fun tokenize path = |
|
250 let |
|
251 fun blank c = (c = #" " orelse c = #"\t" orelse c = #"\n" orelse c = #"\r") |
|
252 fun split line (i, tss) = (i + 1, |
|
253 map (pair i) (map Token (String.tokens blank line) @ [Newline]) :: tss) |
|
254 in apsnd (flat o rev) (File.fold_lines split path (1, [])) end |
|
255 |
|
256 fun stopper i = Scan.stopper (K (i, EOF)) (fn (_, EOF) => true | _ => false) |
|
257 |
|
258 fun scan_err msg [] = "Boogie (at end of input): " ^ msg |
|
259 | scan_err msg ((i, _) :: _) = "Boogie (at line " ^ string_of_int i ^ "): " ^ |
|
260 msg |
|
261 |
|
262 fun scan_fail msg = Scan.fail_with (scan_err msg) |
|
263 |
|
264 fun finite scan path = |
|
265 let val (i, ts) = tokenize path |
|
266 in |
|
267 (case Scan.error (Scan.finite (stopper i) scan) ts of |
|
268 (x, []) => x |
|
269 | (_, ts) => error (scan_err "unparsed input" ts)) |
|
270 end |
|
271 |
|
272 fun read_int' s = (case read_int (explode s) of (i, []) => SOME i | _ => NONE) |
|
273 |
|
274 fun $$$ s = Scan.one (fn (_, Token s') => s = s' | _ => false) |
|
275 val str = Scan.some (fn (_, Token s) => SOME s | _ => NONE) |
|
276 val num = Scan.some (fn (_, Token s) => read_int' s | _ => NONE) |
|
277 |
|
278 fun scan_line key scan = |
|
279 $$$ key |-- scan --| Scan.one (fn (_, Newline) => true | _ => false) |
|
280 fun scan_line' key = scan_line key (Scan.succeed ()) |
|
281 |
|
282 fun scan_count scan i = |
|
283 if i > 0 then scan ::: scan_count scan (i - 1) else Scan.succeed [] |
|
284 |
|
285 fun scan_lookup kind tab key = |
|
286 (case Symtab.lookup tab key of |
|
287 SOME value => Scan.succeed value |
|
288 | NONE => scan_fail ("undefined " ^ kind ^ ": " ^ quote key)) |
|
289 |
|
290 fun typ tds = |
|
291 let |
|
292 fun tp st = |
|
293 (scan_line' "bool" >> K @{typ bool} || |
|
294 scan_line' "int" >> K @{typ int} || |
|
295 scan_line "bv" num >> mk_wordT || |
|
296 scan_line "type-con" (str -- num) :|-- (fn (name, arity) => |
|
297 scan_lookup "type constructor" tds name -- scan_count tp arity >> |
|
298 Type) || |
|
299 scan_line "array" num :|-- (fn arity => |
|
300 scan_count tp (arity - 1) -- tp >> mk_arrayT) || |
|
301 scan_fail "illegal type") st |
|
302 in tp end |
|
303 |
|
304 local |
|
305 fun mk_nary _ t [] = t |
|
306 | mk_nary f _ (t :: ts) = fold f ts t |
|
307 |
|
308 fun mk_distinct T ts = |
|
309 Const (@{const_name distinct}, HOLogic.listT T --> @{typ bool}) $ |
|
310 HOLogic.mk_list T ts |
|
311 |
|
312 fun quant name f = scan_line name (num -- num -- num) >> pair f |
|
313 val quants = |
|
314 quant "forall" HOLogic.all_const || |
|
315 quant "exists" HOLogic.exists_const || |
|
316 scan_fail "illegal quantifier kind" |
|
317 fun mk_quant q (n, T) t = q T $ Term.absfree (isabelle_name n, T, t) |
|
318 |
|
319 val patternT = @{typ pattern} |
|
320 fun mk_pattern _ [] = raise TERM ("mk_pattern", []) |
|
321 | mk_pattern n [t] = Const (n, Term.fastype_of t --> patternT) $ t |
|
322 | mk_pattern n (t :: ts) = |
|
323 let val T = patternT --> Term.fastype_of t --> patternT |
|
324 in Const (@{const_name andpat}, T) $ mk_pattern n ts $ t end |
|
325 fun patt n c scan = |
|
326 scan_line n num :|-- scan_count scan >> (mk_pattern c) |
|
327 fun pattern scan = |
|
328 patt "pat" @{const_name pat} scan || |
|
329 patt "nopat" @{const_name nopat} scan || |
|
330 scan_fail "illegal pattern kind" |
|
331 fun mk_trigger [] t = t |
|
332 | mk_trigger ps t = @{term trigger} $ HOLogic.mk_list patternT ps $ t |
|
333 |
|
334 val label_kind = |
|
335 $$$ "pos" >> K @{term block_at} || |
|
336 $$$ "neg" >> K @{term assert_at} || |
|
337 scan_fail "illegal label kind" |
|
338 |
|
339 fun mk_select (m, k) = |
|
340 let |
|
341 val mT = Term.fastype_of m and kT = Term.fastype_of k |
|
342 val vT = Term.range_type mT |
|
343 in Const (@{const_name boogie_select}, mT --> kT --> vT) $ m $ k end |
|
344 |
|
345 fun mk_store ((m, k), v) = |
|
346 let |
|
347 val mT = Term.fastype_of m and kT = Term.fastype_of k |
|
348 val vT = Term.fastype_of v |
|
349 in |
|
350 Const (@{const_name boogie_store}, mT --> kT --> vT --> mT) $ m $ k $ v |
|
351 end |
|
352 |
|
353 fun mk_extract ((msb, lsb), t) = |
|
354 let |
|
355 val dT = Term.fastype_of t and rT = mk_wordT (msb - lsb) |
|
356 val nT = @{typ nat} |
|
357 val mk_nat_num = HOLogic.mk_number @{typ nat} |
|
358 val m = HOLogic.mk_number @{typ nat} |
|
359 in |
|
360 Const (@{const_name boogie_bv_extract}, [nT, nT, dT] ---> rT) $ |
|
361 mk_nat_num msb $ mk_nat_num lsb $ t |
|
362 end |
|
363 |
|
364 fun mk_concat (t1, t2) = |
|
365 let |
|
366 val T1 = Term.fastype_of t1 and T2 = Term.fastype_of t2 |
|
367 val U = mk_wordT (dest_wordT T1 + dest_wordT T2) |
|
368 in Const (@{const_name boogie_bv_concat}, [T1, T2] ---> U) $ t1 $ t2 end |
|
369 in |
|
370 fun expr tds fds = |
|
371 let |
|
372 fun binop t (u1, u2) = t $ u1 $ u2 |
|
373 fun binexp s f = scan_line' s |-- exp -- exp >> f |
|
374 |
|
375 and exp st = |
|
376 (scan_line' "true" >> K @{term True} || |
|
377 scan_line' "false" >> K @{term False} || |
|
378 scan_line' "not" |-- exp >> HOLogic.mk_not || |
|
379 scan_line "and" num :|-- scan_count exp >> |
|
380 mk_nary (curry HOLogic.mk_conj) @{term True} || |
|
381 scan_line "or" num :|-- scan_count exp >> |
|
382 mk_nary (curry HOLogic.mk_disj) @{term False} || |
|
383 binexp "implies" (binop @{term "op -->"}) || |
|
384 scan_line "distinct" num :|-- scan_count exp >> |
|
385 (fn [] => @{term True} |
|
386 | ts as (t :: _) => mk_distinct (Term.fastype_of t) ts) || |
|
387 binexp "=" HOLogic.mk_eq || |
|
388 scan_line "var" str -- typ tds >> (Free o apfst isabelle_name) || |
|
389 scan_line "fun" (str -- num) :|-- (fn (name, arity) => |
|
390 scan_lookup "constant" fds name -- scan_count exp arity >> |
|
391 Term.list_comb) || |
|
392 quants :|-- (fn (q, ((n, k), i)) => |
|
393 scan_count (scan_line "var" str -- typ tds) n -- |
|
394 scan_count (pattern exp) k -- |
|
395 scan_count (attribute tds fds) i -- |
|
396 exp >> (fn (((vs, ps), _), t) => |
|
397 fold_rev (mk_quant q) vs (mk_trigger ps t))) || |
|
398 scan_line "label" (label_kind -- num -- num) -- exp >> |
|
399 (fn (((l, line), col), t) => |
|
400 if line = 0 orelse col = 0 then t |
|
401 else l $ Free ("Line_" ^ string_of_int line ^ "_Column_" ^ |
|
402 string_of_int col, @{typ bool}) $ t) || |
|
403 scan_line "int-num" num >> HOLogic.mk_number @{typ int} || |
|
404 binexp "<" (binop @{term "op < :: int => _"}) || |
|
405 binexp "<=" (binop @{term "op <= :: int => _"}) || |
|
406 binexp ">" (binop @{term "op < :: int => _"} o swap) || |
|
407 binexp ">=" (binop @{term "op <= :: int => _"} o swap) || |
|
408 binexp "+" (binop @{term "op + :: int => _"}) || |
|
409 binexp "-" (binop @{term "op + :: int => _"}) || |
|
410 binexp "*" (binop @{term "op + :: int => _"}) || |
|
411 binexp "/" (binop @{term boogie_div}) || |
|
412 binexp "%" (binop @{term boogie_mod}) || |
|
413 scan_line "select" num :|-- (fn arity => |
|
414 exp -- (scan_count exp (arity - 1) >> HOLogic.mk_tuple) >> |
|
415 mk_select) || |
|
416 scan_line "store" num :|-- (fn arity => |
|
417 exp -- (scan_count exp (arity - 2) >> HOLogic.mk_tuple) -- exp >> |
|
418 mk_store) || |
|
419 scan_line "bv-num" (num -- num) >> (fn (size, i) => |
|
420 HOLogic.mk_number (mk_wordT size) i) || |
|
421 scan_line "bv-extract" (num -- num) -- exp >> mk_extract || |
|
422 binexp "bv-concat" mk_concat || |
|
423 scan_fail "illegal expression") st |
|
424 in exp end |
|
425 |
|
426 and attribute tds fds = |
|
427 let |
|
428 val attr_val = |
|
429 scan_line' "expr-attr" |-- expr tds fds >> TermValue || |
|
430 scan_line "string-attr" (Scan.repeat1 str) >> |
|
431 (StringValue o space_implode " ") || |
|
432 scan_fail "illegal attribute value" |
|
433 in |
|
434 scan_line "attribute" (str -- num) :|-- (fn (name, i) => |
|
435 scan_count attr_val i >> pair name) || |
|
436 scan_fail "illegal attribute" |
|
437 end |
|
438 end |
|
439 |
|
440 fun type_decls verbose = Scan.depend (fn thy => |
|
441 Scan.repeat (scan_line "type-decl" (str -- num -- num) :|-- (fn (ty, i) => |
|
442 scan_count (attribute Symtab.empty Symtab.empty) i >> K ty)) >> |
|
443 (fn tys => declare_types verbose tys thy)) |
|
444 |
|
445 fun fun_decls verbose tds = Scan.depend (fn thy => |
|
446 Scan.repeat (scan_line "fun-decl" (str -- num -- num) :|-- |
|
447 (fn ((name, arity), i) => |
|
448 scan_count (typ tds) (arity - 1) -- typ tds -- |
|
449 scan_count (attribute tds Symtab.empty) i >> pair name)) >> |
|
450 (fn fns => declare_functions verbose fns thy)) |
|
451 |
|
452 fun axioms verbose tds fds = Scan.depend (fn thy => |
|
453 Scan.repeat (scan_line "axiom" num :|-- (fn i => |
|
454 expr tds fds --| scan_count (attribute tds fds) i)) >> |
|
455 (fn axs => (add_axioms verbose axs thy, ()))) |
|
456 |
|
457 fun var_decls tds fds = Scan.depend (fn thy => |
|
458 Scan.repeat (scan_line "var-decl" (str -- num) :|-- (fn (_, i) => |
|
459 typ tds -- scan_count (attribute tds fds) i >> K ())) >> K (thy, ())) |
|
460 |
|
461 fun vcs verbose tds fds = Scan.depend (fn thy => |
|
462 Scan.repeat (scan_line "vc" (str -- num) -- |
|
463 (expr tds fds >> Sign.cert_term thy)) >> |
|
464 (fn vcs => ((), add_vcs verbose vcs thy))) |
|
465 |
|
466 fun parse verbose thy = Scan.pass thy |
|
467 (type_decls verbose :|-- (fn tds => |
|
468 fun_decls verbose tds :|-- (fn fds => |
|
469 axioms verbose tds fds |-- |
|
470 var_decls tds fds |-- |
|
471 vcs verbose tds fds))) |
|
472 |
|
473 fun load_b2i verbose path thy = finite (parse verbose thy) path |
|
474 |
|
475 end |