7 signature EXHAUSTIVE_GENERATORS = |
7 signature EXHAUSTIVE_GENERATORS = |
8 sig |
8 sig |
9 val compile_generator_expr: |
9 val compile_generator_expr: |
10 Proof.context -> (term * term list) list -> int list -> term list option * Quickcheck.report option |
10 Proof.context -> (term * term list) list -> int list -> term list option * Quickcheck.report option |
11 val compile_generator_exprs: Proof.context -> term list -> (int -> term list option) list |
11 val compile_generator_exprs: Proof.context -> term list -> (int -> term list option) list |
|
12 val compile_validator_exprs: Proof.context -> term list -> (int -> bool) list |
12 val put_counterexample: (unit -> int -> int -> term list option) |
13 val put_counterexample: (unit -> int -> int -> term list option) |
13 -> Proof.context -> Proof.context |
14 -> Proof.context -> Proof.context |
14 val put_counterexample_batch: (unit -> (int -> term list option) list) |
15 val put_counterexample_batch: (unit -> (int -> term list option) list) |
15 -> Proof.context -> Proof.context |
16 -> Proof.context -> Proof.context |
16 val smart_quantifier : bool Config.T; |
17 val put_validator_batch: (unit -> (int -> bool) list) -> Proof.context -> Proof.context |
17 val quickcheck_pretty : bool Config.T; |
18 val smart_quantifier : bool Config.T |
|
19 val quickcheck_pretty : bool Config.T |
18 val setup: theory -> theory |
20 val setup: theory -> theory |
19 end; |
21 end; |
20 |
22 |
21 structure Exhaustive_Generators : EXHAUSTIVE_GENERATORS = |
23 structure Exhaustive_Generators : EXHAUSTIVE_GENERATORS = |
22 struct |
24 struct |
236 val none_t = @{term "None :: term list option"} |
238 val none_t = @{term "None :: term list option"} |
237 fun mk_safe_if (cond, then_t, else_t) = |
239 fun mk_safe_if (cond, then_t, else_t) = |
238 @{term "Quickcheck_Exhaustive.catch_match :: term list option => term list option => term list option"} $ |
240 @{term "Quickcheck_Exhaustive.catch_match :: term list option => term list option => term list option"} $ |
239 (@{term "If :: bool => term list option => term list option => term list option"} |
241 (@{term "If :: bool => term list option => term list option => term list option"} |
240 $ cond $ then_t $ else_t) $ none_t; |
242 $ cond $ then_t $ else_t) $ none_t; |
241 fun strip_imp (Const(@{const_name HOL.implies},_) $ A $ B) = apfst (cons A) (strip_imp B) |
|
242 | strip_imp A = ([], A) |
|
243 fun lookup v = the (AList.lookup (op =) (names ~~ (frees ~~ term_vars)) v) |
243 fun lookup v = the (AList.lookup (op =) (names ~~ (frees ~~ term_vars)) v) |
244 fun mk_naive_test_term t = |
244 fun mk_naive_test_term t = |
245 fold_rev mk_exhaustive_closure (frees ~~ term_vars) (mk_safe_if (t, none_t, return)) |
245 fold_rev mk_exhaustive_closure (frees ~~ term_vars) (mk_safe_if (t, none_t, return)) |
246 fun mk_smart_test_term' concl bound_vars assms = |
246 fun mk_smart_test_term' concl bound_vars assms = |
247 let |
247 let |
253 in |
253 in |
254 fold_rev mk_exhaustive_closure (map lookup vars) (mk_safe_if check) |
254 fold_rev mk_exhaustive_closure (map lookup vars) (mk_safe_if check) |
255 end |
255 end |
256 fun mk_smart_test_term t = |
256 fun mk_smart_test_term t = |
257 let |
257 let |
258 val (assms, concl) = strip_imp t |
258 val (assms, concl) = Quickcheck_Common.strip_imp t |
259 in |
259 in |
260 mk_smart_test_term' concl [] assms |
260 mk_smart_test_term' concl [] assms |
261 end |
261 end |
262 val mk_test_term = |
262 val mk_test_term = |
263 if Config.get ctxt smart_quantifier then mk_smart_test_term else mk_naive_test_term |
263 if Config.get ctxt smart_quantifier then mk_smart_test_term else mk_naive_test_term |
265 |
265 |
266 val mk_parametric_generator_expr = |
266 val mk_parametric_generator_expr = |
267 Quickcheck_Common.gen_mk_parametric_generator_expr |
267 Quickcheck_Common.gen_mk_parametric_generator_expr |
268 ((mk_generator_expr, absdummy (@{typ "code_numeral"}, @{term "None :: term list option"})), |
268 ((mk_generator_expr, absdummy (@{typ "code_numeral"}, @{term "None :: term list option"})), |
269 @{typ "code_numeral => term list option"}) |
269 @{typ "code_numeral => term list option"}) |
270 |
270 |
|
271 fun mk_validator_expr ctxt t = |
|
272 let |
|
273 fun bounded_forallT T = (T --> @{typ bool}) --> @{typ code_numeral} --> @{typ bool} |
|
274 val thy = ProofContext.theory_of ctxt |
|
275 val ctxt' = Variable.auto_fixes t ctxt |
|
276 val ([depth_name], ctxt'') = Variable.variant_fixes ["depth"] ctxt' |
|
277 val depth = Free (depth_name, @{typ code_numeral}) |
|
278 fun mk_bounded_forall (s, T) t = |
|
279 Const (@{const_name "Quickcheck_Exhaustive.bounded_forall_class.bounded_forall"}, bounded_forallT T) |
|
280 $ lambda (Free (s, T)) t $ depth |
|
281 fun mk_implies (cond, then_t) = |
|
282 @{term "If :: bool => bool => bool => bool"} $ cond $ then_t $ @{term False} |
|
283 fun mk_naive_test_term t = fold_rev mk_bounded_forall (Term.add_frees t []) t |
|
284 fun mk_smart_test_term' concl bound_frees assms = |
|
285 let |
|
286 fun vars_of t = subtract (op =) bound_frees (Term.add_frees t []) |
|
287 val (vars, check) = |
|
288 case assms of [] => (vars_of concl, concl) |
|
289 | assm :: assms => (vars_of assm, mk_implies (assm, |
|
290 mk_smart_test_term' concl (union (op =) (vars_of assm) bound_frees) assms)) |
|
291 in |
|
292 fold_rev mk_bounded_forall vars check |
|
293 end |
|
294 fun mk_smart_test_term t = |
|
295 let |
|
296 val (assms, concl) = Quickcheck_Common.strip_imp t |
|
297 in |
|
298 mk_smart_test_term' concl [] assms |
|
299 end |
|
300 val mk_test_term = |
|
301 if Config.get ctxt smart_quantifier then mk_smart_test_term else mk_naive_test_term |
|
302 in lambda depth (mk_test_term t) end |
|
303 |
|
304 |
271 (** generator compiliation **) |
305 (** generator compiliation **) |
272 |
306 |
273 structure Counterexample = Proof_Data |
307 structure Counterexample = Proof_Data |
274 ( |
308 ( |
275 type T = unit -> int -> int -> term list option |
309 type T = unit -> int -> int -> term list option |
284 (* FIXME avoid user error with non-user text *) |
318 (* FIXME avoid user error with non-user text *) |
285 fun init _ () = error "Counterexample" |
319 fun init _ () = error "Counterexample" |
286 ); |
320 ); |
287 val put_counterexample_batch = Counterexample_Batch.put; |
321 val put_counterexample_batch = Counterexample_Batch.put; |
288 |
322 |
|
323 structure Validator_Batch = Proof_Data |
|
324 ( |
|
325 type T = unit -> (int -> bool) list |
|
326 (* FIXME avoid user error with non-user text *) |
|
327 fun init _ () = error "Counterexample" |
|
328 ); |
|
329 val put_validator_batch = Validator_Batch.put; |
|
330 |
|
331 |
289 val target = "Quickcheck"; |
332 val target = "Quickcheck"; |
290 |
333 |
291 fun compile_generator_expr ctxt ts = |
334 fun compile_generator_expr ctxt ts = |
292 let |
335 let |
293 val thy = ProofContext.theory_of ctxt |
336 val thy = ProofContext.theory_of ctxt |
294 val t' = mk_parametric_generator_expr ctxt ts; |
337 val t' = mk_parametric_generator_expr ctxt ts; |
295 val compile = Code_Runtime.dynamic_value_strict |
338 val compile = Code_Runtime.dynamic_value_strict |
296 (Counterexample.get, put_counterexample, "Exhaustive_Generators.put_counterexample") |
339 (Counterexample.get, put_counterexample, "Exhaustive_Generators.put_counterexample") |
297 thy (SOME target) (fn proc => fn g => |
340 thy (SOME target) (fn proc => fn g => |
298 fn card => fn size => g card size |> (Option.map o map) proc) t' []; |
341 fn card => fn size => g card size |> (Option.map o map) proc) t' [] |
299 in |
342 in |
300 fn [card, size] => rpair NONE (compile card size |> |
343 fn [card, size] => rpair NONE (compile card size |> |
301 (if Config.get ctxt quickcheck_pretty then |
344 (if Config.get ctxt quickcheck_pretty then |
302 Option.map (map Quickcheck_Common.post_process_term) else I)) |
345 Option.map (map Quickcheck_Common.post_process_term) else I)) |
303 end; |
346 end; |
308 val ts' = map (fn t => mk_generator_expr ctxt (t, [])) ts; |
351 val ts' = map (fn t => mk_generator_expr ctxt (t, [])) ts; |
309 val compiles = Code_Runtime.dynamic_value_strict |
352 val compiles = Code_Runtime.dynamic_value_strict |
310 (Counterexample_Batch.get, put_counterexample_batch, |
353 (Counterexample_Batch.get, put_counterexample_batch, |
311 "Exhaustive_Generators.put_counterexample_batch") |
354 "Exhaustive_Generators.put_counterexample_batch") |
312 thy (SOME target) (fn proc => map (fn g => g #> (Option.map o map) proc)) |
355 thy (SOME target) (fn proc => map (fn g => g #> (Option.map o map) proc)) |
313 (HOLogic.mk_list @{typ "code_numeral => term list option"} ts') []; |
356 (HOLogic.mk_list @{typ "code_numeral => term list option"} ts') [] |
314 in |
357 in |
315 map (fn compile => fn size => compile size |
358 map (fn compile => fn size => compile size |
316 |> Option.map (map Quickcheck_Common.post_process_term)) compiles |
359 |> Option.map (map Quickcheck_Common.post_process_term)) compiles |
317 end; |
360 end; |
318 |
361 |
|
362 fun compile_validator_exprs ctxt ts = |
|
363 let |
|
364 val thy = ProofContext.theory_of ctxt |
|
365 val ts' = map (mk_validator_expr ctxt) ts; |
|
366 in |
|
367 Code_Runtime.dynamic_value_strict |
|
368 (Validator_Batch.get, put_validator_batch, "Exhaustive_Generators.put_validator_batch") |
|
369 thy (SOME target) (K I) (HOLogic.mk_list @{typ "code_numeral => bool"} ts') [] |
|
370 end; |
|
371 |
319 (** setup **) |
372 (** setup **) |
320 |
373 |
321 val setup = |
374 val setup = |
322 Datatype.interpretation |
375 Datatype.interpretation |
323 (Quickcheck_Common.ensure_sort_datatype (@{sort exhaustive}, instantiate_exhaustive_datatype)) |
376 (Quickcheck_Common.ensure_sort_datatype (@{sort exhaustive}, instantiate_exhaustive_datatype)) |
324 #> setup_smart_quantifier |
377 #> setup_smart_quantifier |
325 #> setup_quickcheck_pretty |
378 #> setup_quickcheck_pretty |
326 #> Context.theory_map (Quickcheck.add_generator ("exhaustive", compile_generator_expr)) |
379 #> Context.theory_map (Quickcheck.add_generator ("exhaustive", compile_generator_expr)) |
327 #> Context.theory_map (Quickcheck.add_batch_generator ("exhaustive", compile_generator_exprs)); |
380 #> Context.theory_map (Quickcheck.add_batch_generator ("exhaustive", compile_generator_exprs)) |
|
381 #> Context.theory_map (Quickcheck.add_batch_validator ("exhaustive", compile_validator_exprs)); |
328 |
382 |
329 end; |
383 end; |