| author | haftmann |
| Thu, 29 Apr 2010 15:00:42 +0200 | |
| changeset 36534 | 0090b04432f7 |
| parent 34960 | 1d5ee19ef940 |
| child 36893 | 48cf03469dc6 |
| permissions | -rw-r--r-- |
| 33010 | 1 |
(* Title: HOL/SMT/Tools/z3_proof.ML |
2 |
Author: Sascha Boehme, TU Muenchen |
|
3 |
||
4 |
Proof reconstruction for proofs found by Z3. |
|
5 |
*) |
|
6 |
||
7 |
signature Z3_PROOF = |
|
8 |
sig |
|
9 |
val reconstruct: Proof.context -> thm list option -> SMT_Translate.recon -> |
|
10 |
string list -> thm |
|
11 |
end |
|
12 |
||
13 |
structure Z3_Proof: Z3_PROOF = |
|
14 |
struct |
|
15 |
||
16 |
structure T = Z3_Proof_Terms |
|
17 |
structure R = Z3_Proof_Rules |
|
18 |
||
|
33418
1312e8337ce5
added a specific SMT exception captured by smt_tac (prevents the SMT method from failing with an exception),
boehmes
parents:
33243
diff
changeset
|
19 |
fun z3_exn msg = raise SMT_Solver.SMT ("Z3 proof reconstruction: " ^ msg)
|
| 33010 | 20 |
|
21 |
||
22 |
fun lift f (x, y) = apsnd (pair x) (f y) |
|
23 |
fun lift' f v (x, y) = apsnd (rpair y) (f v x) |
|
24 |
||
25 |
fun $$ s = lift (Scan.$$ s) |
|
26 |
fun this s = lift (Scan.this_string s) |
|
27 |
||
28 |
fun blank s = lift (Scan.many1 Symbol.is_ascii_blank) s |
|
29 |
||
30 |
fun par scan = $$ "(" |-- scan --| $$ ")"
|
|
31 |
fun bra scan = $$ "[" |-- scan --| $$ "]" |
|
32 |
||
33 |
val digit = (fn |
|
34 |
"0" => SOME 0 | "1" => SOME 1 | "2" => SOME 2 | "3" => SOME 3 | |
|
35 |
"4" => SOME 4 | "5" => SOME 5 | "6" => SOME 6 | "7" => SOME 7 | |
|
36 |
"8" => SOME 8 | "9" => SOME 9 | _ => NONE) |
|
37 |
||
38 |
val nat_num = Scan.repeat1 (Scan.some digit) >> |
|
39 |
(fn ds => fold (fn d => fn i => i * 10 + d) ds 0) |
|
40 |
val int_num = Scan.optional (Scan.$$ "-" >> K (fn i => ~i)) I :|-- |
|
41 |
(fn sign => nat_num >> sign) |
|
42 |
||
43 |
val is_char = Symbol.is_ascii_letter orf Symbol.is_ascii_digit orf |
|
44 |
member (op =) (explode "_+*-/%~=<>$&|?!.@^#") |
|
45 |
val name = Scan.many1 is_char >> implode |
|
46 |
||
47 |
datatype sym = Sym of string * sym list |
|
48 |
||
49 |
datatype context = Context of {
|
|
50 |
Ttab: typ Symtab.table, |
|
| 33243 | 51 |
ttab: cterm Symtab.table, |
| 33010 | 52 |
etab: T.preterm Inttab.table, |
53 |
ptab: R.proof Inttab.table, |
|
54 |
nctxt: Name.context } |
|
55 |
||
56 |
fun make_context (Ttab, ttab, etab, ptab, nctxt) = |
|
57 |
Context {Ttab=Ttab, ttab=ttab, etab=etab, ptab=ptab, nctxt=nctxt}
|
|
58 |
||
|
33017
4fb8a33f74d6
eliminated extraneous wrapping of public records,
boehmes
parents:
33010
diff
changeset
|
59 |
fun empty_context thy ({typs, terms=ttab} : SMT_Translate.recon) =
|
| 33010 | 60 |
let |
61 |
val ttab' = Symtab.map (fn @{term True} => @{term "~False"} | t => t) ttab
|
|
62 |
val ns = Symtab.fold (Term.add_free_names o snd) ttab' [] |
|
63 |
val nctxt = Name.make_context ns |
|
64 |
val tt = Symtab.map (Thm.cterm_of thy) ttab' |
|
65 |
in make_context (typs, tt, Inttab.empty, Inttab.empty, nctxt) end |
|
66 |
||
67 |
fun map_context f (Context {Ttab, ttab, etab, ptab, nctxt}) =
|
|
68 |
make_context (f (Ttab, ttab, etab, ptab, nctxt)) |
|
69 |
||
70 |
fun map_type_tab f = map_context (fn (Ttab, ttab, etab, ptab, nctxt) => |
|
71 |
(f Ttab, ttab, etab, ptab, nctxt)) |
|
72 |
||
73 |
fun map_term_tab f = map_context (fn (Ttab, ttab, etab, ptab, nctxt) => |
|
74 |
(Ttab, f ttab, etab, ptab, nctxt)) |
|
75 |
||
76 |
fun map_expr_tab f = map_context (fn (Ttab, ttab, etab, ptab, nctxt) => |
|
77 |
(Ttab, ttab, f etab, ptab, nctxt)) |
|
78 |
||
79 |
fun map_proof_tab f = map_context (fn (Ttab, ttab, etab, ptab, nctxt) => |
|
80 |
(Ttab, ttab, etab, f ptab, nctxt)) |
|
81 |
||
82 |
val free_prefix = "f" |
|
83 |
||
84 |
fun fresh_name (cx as Context {nctxt, ...}) =
|
|
85 |
let val (n, nctxt') = yield_singleton Name.variants free_prefix nctxt |
|
86 |
in |
|
87 |
(n, map_context (fn (Ttab, ttab, etab, ptab, _) => |
|
88 |
(Ttab, ttab, etab, ptab, nctxt')) cx) |
|
89 |
end |
|
90 |
||
91 |
fun typ_of_sort name (cx as Context {Ttab, ...}) =
|
|
92 |
(case Symtab.lookup Ttab name of |
|
93 |
SOME T => (T, cx) |
|
94 |
| _ => cx |> fresh_name |-> (fn n => |
|
95 |
let val T = TFree ("'" ^ n, @{sort type})
|
|
96 |
in pair T o map_type_tab (Symtab.update (name, T)) end)) |
|
97 |
||
98 |
fun lookup_expr id (cx as Context {etab, ...}) =
|
|
99 |
(case Inttab.lookup etab id of |
|
100 |
SOME e => (e, cx) |
|
101 |
| _ => z3_exn ("unknown term id: " ^ quote (string_of_int id)))
|
|
102 |
||
103 |
fun add_expr k t = map_expr_tab (Inttab.update (k, t)) |
|
104 |
||
105 |
fun add_proof thy k ((r, ps), t) (cx as Context {nctxt, ...}) =
|
|
106 |
let val p = R.make_proof r ps (T.compile thy nctxt t) |
|
107 |
in (k, map_proof_tab (Inttab.update (k, p)) cx) end |
|
108 |
||
109 |
fun mk_app app (cx as Context {ttab, ...}) =
|
|
110 |
let |
|
111 |
val mk = |
|
112 |
(fn |
|
113 |
(Sym ("true", _), _) => T.mk_true
|
|
114 |
| (Sym ("false", _), _) => T.mk_false
|
|
115 |
| (Sym ("=", _), [t, u]) => T.mk_eq t u
|
|
116 |
| (Sym ("distinct", _), ts) => T.mk_distinct ts
|
|
117 |
| (Sym ("ite", _), [s, t, u]) => T.mk_if s t u
|
|
118 |
| (Sym ("and", _), ts) => T.mk_and ts
|
|
119 |
| (Sym ("or", _), ts) => T.mk_or ts
|
|
120 |
| (Sym ("iff", _), [t, u]) => T.mk_iff t u
|
|
121 |
| (Sym ("xor", _), [t, u]) => T.mk_not (T.mk_iff t u)
|
|
122 |
| (Sym ("not", _), [t]) => T.mk_not t
|
|
123 |
| (Sym ("implies", _), [t, u]) => T.mk_implies t u
|
|
124 |
| (Sym ("~", _), [t, u]) => T.mk_eq t u
|
|
125 |
| (Sym ("<", _), [t, u]) => T.mk_lt t u
|
|
126 |
| (Sym ("<=", _), [t, u]) => T.mk_le t u
|
|
127 |
| (Sym (">", _), [t, u]) => T.mk_lt u t
|
|
128 |
| (Sym (">=", _), [t, u]) => T.mk_le u t
|
|
129 |
| (Sym ("+", _), [t, u]) => T.mk_add t u
|
|
130 |
| (Sym ("-", _), [t, u]) => T.mk_sub t u
|
|
131 |
| (Sym ("-", _), [t]) => T.mk_uminus t
|
|
132 |
| (Sym ("*", _), [t, u]) => T.mk_mul t u
|
|
133 |
| (Sym ("/", _), [t, u]) => T.mk_real_div t u
|
|
134 |
| (Sym ("div", _), [t, u]) => T.mk_int_div t u
|
|
135 |
| (Sym ("mod", _), [t, u]) => T.mk_mod t u
|
|
136 |
| (Sym ("rem", _), [t, u]) => T.mk_rem t u
|
|
137 |
| (Sym ("select", _), [m, k]) => T.mk_access m k
|
|
138 |
| (Sym ("store", _), [m, k, v]) => T.mk_update m k v
|
|
|
34960
1d5ee19ef940
support skolem constant for extensional arrays in Z3 proofs
boehmes
parents:
33418
diff
changeset
|
139 |
| (Sym ("array-ext", _), [m1, m2]) => T.mk_array_ext m1 m2
|
| 33010 | 140 |
| (Sym ("pattern", _), _) => T.mk_true
|
141 |
| (Sym (n, _), ts) => |
|
142 |
(case Symtab.lookup ttab n of |
|
143 |
SOME ct => T.mk_fun ct ts |
|
144 |
| NONE => z3_exn ("unknown function: " ^ quote n)))
|
|
145 |
in (mk app, cx) end |
|
146 |
||
147 |
fun add_decl thy (n, T) (cx as Context {ttab, ...}) =
|
|
148 |
(case Symtab.lookup ttab n of |
|
149 |
SOME _ => cx |
|
150 |
| _ => cx |> fresh_name |-> (fn n' => |
|
151 |
map_term_tab (Symtab.update (n, Thm.cterm_of thy (Free (n', T)))))) |
|
152 |
||
153 |
||
154 |
fun sep scan_sep scan = scan ::: Scan.repeat (scan_sep |-- scan) |
|
155 |
fun bsep scan = Scan.repeat (blank |-- scan) |
|
156 |
fun bsep1 scan = Scan.repeat1 (blank |-- scan) |
|
157 |
||
158 |
val id = Scan.$$ "#" |-- int_num |
|
159 |
||
160 |
fun sym s = |
|
161 |
(lift name -- Scan.optional (bra (sep ($$ ":") sym)) [] >> Sym) s |
|
162 |
||
163 |
fun sort st = Scan.first [ |
|
164 |
this "bool" >> K @{typ bool},
|
|
165 |
this "int" >> K @{typ int},
|
|
166 |
this "real" >> K @{typ real},
|
|
167 |
this "bv" |-- bra (lift int_num) >> T.wordT, |
|
168 |
this "array" |-- bra (sort --| $$ ":" -- sort) >> (op -->), |
|
169 |
par (this "->" |-- bsep1 sort) >> ((op --->) o split_last), |
|
170 |
lift name #-> lift' typ_of_sort] st |
|
171 |
||
172 |
fun bound thy = |
|
173 |
par (this ":var" -- blank |-- lift int_num --| blank -- sort) >> |
|
174 |
uncurry (T.mk_bound thy) |
|
175 |
||
176 |
val number = |
|
177 |
int_num -- Scan.option (Scan.$$ "/" |-- int_num) --| |
|
178 |
Scan.this_string "::" :|-- (fn num as (n, _) => |
|
179 |
Scan.this_string "int" >> K (T.mk_int_num n) || |
|
180 |
Scan.this_string "real" >> K (T.mk_real_frac_num num)) |
|
181 |
||
182 |
fun bv_number thy = |
|
183 |
this "bv" |-- bra (lift (int_num --| Scan.$$ ":" -- int_num)) >> |
|
184 |
uncurry (T.mk_bv_num thy) |
|
185 |
||
186 |
val constant = sym #-> lift' (mk_app o rpair []) |
|
187 |
||
188 |
fun arg thy = Scan.first [lift id #-> lift' lookup_expr, |
|
189 |
lift number, bv_number thy, constant] |
|
190 |
||
191 |
fun application thy = |
|
192 |
par (sym -- bsep1 (arg thy)) #-> lift' mk_app |
|
193 |
||
194 |
val variables = |
|
195 |
par (this "vars" |-- bsep1 (par ((lift name >> K "x") --| blank -- sort))) |
|
| 33047 | 196 |
fun patterns st = |
197 |
bsep (par ((this ":pat" || this ":nopat") |-- bsep1 (lift id))) st |
|
198 |
fun quant_kind st = |
|
199 |
(this "forall" >> K T.mk_forall || |
|
200 |
this "exists" >> K T.mk_exists) st |
|
| 33010 | 201 |
fun quantifier thy = par (quant_kind --| blank -- |
202 |
variables --| patterns --| blank -- arg thy) >> |
|
203 |
(fn ((q, vs), body) => fold_rev (q thy) vs body) |
|
204 |
||
205 |
fun expr thy k = Scan.first [bound thy, quantifier thy, application thy, |
|
206 |
lift number, bv_number thy, constant] #-> apfst o add_expr k |
|
207 |
||
208 |
fun rule_name name = |
|
209 |
(case R.rule_of_string name of |
|
210 |
SOME r => r |
|
211 |
| NONE => z3_exn ("unknown proof rule: " ^ quote name))
|
|
212 |
||
213 |
fun rule thy k = |
|
214 |
bra (lift (name >> rule_name) -- bsep (lift id)) --| |
|
215 |
($$ ":" -- blank) -- arg thy #-> lift' (add_proof thy k) |
|
216 |
||
217 |
fun decl thy = ((this "decl" -- blank) |-- lift name --| |
|
218 |
(blank -- this "::" -- blank) -- sort) #-> apfst o add_decl thy |
|
219 |
||
220 |
fun def st = (lift id --| (blank -- this ":=" -- blank)) st |
|
221 |
||
222 |
fun node thy = |
|
223 |
decl thy #> pair NONE || |
|
224 |
def :|-- (fn k => expr thy k #> pair NONE || rule thy k #>> K NONE) || |
|
225 |
rule thy ~1 #>> SOME |
|
226 |
||
|
33418
1312e8337ce5
added a specific SMT exception captured by smt_tac (prevents the SMT method from failing with an exception),
boehmes
parents:
33243
diff
changeset
|
227 |
fun handle_errors line_no scan (st as (_, xs)) = |
|
1312e8337ce5
added a specific SMT exception captured by smt_tac (prevents the SMT method from failing with an exception),
boehmes
parents:
33243
diff
changeset
|
228 |
(case try scan st of |
|
1312e8337ce5
added a specific SMT exception captured by smt_tac (prevents the SMT method from failing with an exception),
boehmes
parents:
33243
diff
changeset
|
229 |
SOME y => y |
|
1312e8337ce5
added a specific SMT exception captured by smt_tac (prevents the SMT method from failing with an exception),
boehmes
parents:
33243
diff
changeset
|
230 |
| NONE => z3_exn ("parse error at line " ^ string_of_int line_no ^ ": " ^
|
|
1312e8337ce5
added a specific SMT exception captured by smt_tac (prevents the SMT method from failing with an exception),
boehmes
parents:
33243
diff
changeset
|
231 |
quote (implode xs))) |
| 33010 | 232 |
|
233 |
fun parse_line thy l (st as (stop, line_no, cx)) = |
|
234 |
if is_some stop then st |
|
235 |
else |
|
236 |
(cx, explode l) |
|
237 |
|> handle_errors line_no (Scan.finite' Symbol.stopper (node thy)) |
|
238 |
|> (fn (stop', (cx', _)) => (stop', line_no + 1, cx')) |
|
239 |
||
240 |
fun reconstruct ctxt assms recon output = |
|
241 |
let |
|
242 |
val _ = T.var_prefix <> free_prefix orelse error "Same prefixes" |
|
243 |
||
244 |
val thy = ProofContext.theory_of ctxt |
|
245 |
in |
|
246 |
(case fold (parse_line thy) output (NONE, 1, empty_context thy recon) of |
|
247 |
(SOME p, _, Context {ptab, ...}) => R.prove ctxt assms ptab p
|
|
248 |
| _ => z3_exn "bad proof") |
|
249 |
end |
|
250 |
||
251 |
end |