8 sig |
8 sig |
9 val compile_generator_expr: |
9 val compile_generator_expr: |
10 Proof.context -> (term * term list) list -> bool -> int list -> (bool * term list) option * Quickcheck.report option |
10 Proof.context -> (term * term list) list -> bool -> int list -> (bool * 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 compile_validator_exprs: Proof.context -> term list -> (int -> bool) list |
13 val put_counterexample: (unit -> int -> bool -> int -> (bool * term list) option) |
13 val put_counterexample: (unit -> Code_Numeral.natural -> bool -> Code_Numeral.natural -> (bool * term list) option) |
14 -> Proof.context -> Proof.context |
14 -> Proof.context -> Proof.context |
15 val put_counterexample_batch: (unit -> (int -> term list option) list) |
15 val put_counterexample_batch: (unit -> (Code_Numeral.natural -> term list option) list) |
16 -> Proof.context -> Proof.context |
16 -> Proof.context -> Proof.context |
17 val put_validator_batch: (unit -> (int -> bool) list) -> Proof.context -> Proof.context |
17 val put_validator_batch: (unit -> (Code_Numeral.natural -> bool) list) -> Proof.context -> Proof.context |
18 exception Counterexample of term list |
18 exception Counterexample of term list |
19 val smart_quantifier : bool Config.T |
19 val smart_quantifier : bool Config.T |
20 val optimise_equality : bool Config.T |
20 val optimise_equality : bool Config.T |
21 val quickcheck_pretty : bool Config.T |
21 val quickcheck_pretty : bool Config.T |
22 val setup_exhaustive_datatype_interpretation : theory -> theory |
22 val setup_exhaustive_datatype_interpretation : theory -> theory |
48 |
48 |
49 (** abstract syntax **) |
49 (** abstract syntax **) |
50 |
50 |
51 fun termifyT T = HOLogic.mk_prodT (T, @{typ "unit => Code_Evaluation.term"}); |
51 fun termifyT T = HOLogic.mk_prodT (T, @{typ "unit => Code_Evaluation.term"}); |
52 |
52 |
53 val size = @{term "i :: code_numeral"} |
53 val size = @{term "i :: natural"} |
54 val size_pred = @{term "(i :: code_numeral) - 1"} |
54 val size_pred = @{term "(i :: natural) - 1"} |
55 val size_ge_zero = @{term "(i :: code_numeral) > 0"} |
55 val size_ge_zero = @{term "(i :: natural) > 0"} |
56 |
56 |
57 fun mk_none_continuation (x, y) = |
57 fun mk_none_continuation (x, y) = |
58 let |
58 let |
59 val (T as Type(@{type_name "option"}, _)) = fastype_of x |
59 val (T as Type(@{type_name "option"}, _)) = fastype_of x |
60 in |
60 in |
79 val exhaustiveN = "exhaustive"; |
79 val exhaustiveN = "exhaustive"; |
80 val full_exhaustiveN = "full_exhaustive"; |
80 val full_exhaustiveN = "full_exhaustive"; |
81 val bounded_forallN = "bounded_forall"; |
81 val bounded_forallN = "bounded_forall"; |
82 |
82 |
83 fun fast_exhaustiveT T = (T --> @{typ unit}) |
83 fun fast_exhaustiveT T = (T --> @{typ unit}) |
84 --> @{typ code_numeral} --> @{typ unit} |
84 --> @{typ natural} --> @{typ unit} |
85 |
85 |
86 fun exhaustiveT T = (T --> resultT) --> @{typ code_numeral} --> resultT |
86 fun exhaustiveT T = (T --> resultT) --> @{typ natural} --> resultT |
87 |
87 |
88 fun bounded_forallT T = (T --> @{typ bool}) --> @{typ code_numeral} --> @{typ bool} |
88 fun bounded_forallT T = (T --> @{typ bool}) --> @{typ natural} --> @{typ bool} |
89 |
89 |
90 fun full_exhaustiveT T = (termifyT T --> resultT) --> @{typ code_numeral} --> resultT |
90 fun full_exhaustiveT T = (termifyT T --> resultT) --> @{typ natural} --> resultT |
91 |
91 |
92 fun check_allT T = (termifyT T --> resultT) --> resultT |
92 fun check_allT T = (termifyT T --> resultT) --> resultT |
93 |
93 |
94 fun mk_equation_terms generics (descr, vs, Ts) = |
94 fun mk_equation_terms generics (descr, vs, Ts) = |
95 let |
95 let |
321 val ctxt' = Variable.auto_fixes t ctxt |
321 val ctxt' = Variable.auto_fixes t ctxt |
322 val names = Term.add_free_names t [] |
322 val names = Term.add_free_names t [] |
323 val frees = map Free (Term.add_frees t []) |
323 val frees = map Free (Term.add_frees t []) |
324 fun lookup v = the (AList.lookup (op =) (names ~~ frees) v) |
324 fun lookup v = the (AList.lookup (op =) (names ~~ frees) v) |
325 val ([depth_name], _) = Variable.variant_fixes ["depth"] ctxt' |
325 val ([depth_name], _) = Variable.variant_fixes ["depth"] ctxt' |
326 val depth = Free (depth_name, @{typ code_numeral}) |
326 val depth = Free (depth_name, @{typ natural}) |
327 fun return _ = @{term "throw_Counterexample :: term list => unit"} $ |
327 fun return _ = @{term "throw_Counterexample :: term list => unit"} $ |
328 (HOLogic.mk_list @{typ "term"} |
328 (HOLogic.mk_list @{typ "term"} |
329 (map (fn t => HOLogic.mk_term_of (fastype_of t) t) (frees @ eval_terms))) |
329 (map (fn t => HOLogic.mk_term_of (fastype_of t) t) (frees @ eval_terms))) |
330 fun mk_exhaustive_closure (free as Free (_, T)) t = |
330 fun mk_exhaustive_closure (free as Free (_, T)) t = |
331 Const (@{const_name "Quickcheck_Exhaustive.fast_exhaustive_class.fast_exhaustive"}, |
331 Const (@{const_name "Quickcheck_Exhaustive.fast_exhaustive_class.fast_exhaustive"}, |
351 val names = Term.add_free_names t [] |
351 val names = Term.add_free_names t [] |
352 val frees = map Free (Term.add_frees t []) |
352 val frees = map Free (Term.add_frees t []) |
353 fun lookup v = the (AList.lookup (op =) (names ~~ frees) v) |
353 fun lookup v = the (AList.lookup (op =) (names ~~ frees) v) |
354 val ([depth_name, genuine_only_name], _) = |
354 val ([depth_name, genuine_only_name], _) = |
355 Variable.variant_fixes ["depth", "genuine_only"] ctxt' |
355 Variable.variant_fixes ["depth", "genuine_only"] ctxt' |
356 val depth = Free (depth_name, @{typ code_numeral}) |
356 val depth = Free (depth_name, @{typ natural}) |
357 val genuine_only = Free (genuine_only_name, @{typ bool}) |
357 val genuine_only = Free (genuine_only_name, @{typ bool}) |
358 val return = mk_return (HOLogic.mk_list @{typ "term"} |
358 val return = mk_return (HOLogic.mk_list @{typ "term"} |
359 (map (fn t => HOLogic.mk_term_of (fastype_of t) t) frees @ map mk_safe_term eval_terms)) |
359 (map (fn t => HOLogic.mk_term_of (fastype_of t) t) frees @ map mk_safe_term eval_terms)) |
360 fun mk_exhaustive_closure (free as Free (_, T)) t = |
360 fun mk_exhaustive_closure (free as Free (_, T)) t = |
361 Const (@{const_name "Quickcheck_Exhaustive.exhaustive_class.exhaustive"}, exhaustiveT T) |
361 Const (@{const_name "Quickcheck_Exhaustive.exhaustive_class.exhaustive"}, exhaustiveT T) |
373 val names = Term.add_free_names t [] |
373 val names = Term.add_free_names t [] |
374 val frees = map Free (Term.add_frees t []) |
374 val frees = map Free (Term.add_frees t []) |
375 val ([depth_name, genuine_only_name], ctxt'') = |
375 val ([depth_name, genuine_only_name], ctxt'') = |
376 Variable.variant_fixes ["depth", "genuine_only"] ctxt' |
376 Variable.variant_fixes ["depth", "genuine_only"] ctxt' |
377 val (term_names, _) = Variable.variant_fixes (map (prefix "t_") names) ctxt'' |
377 val (term_names, _) = Variable.variant_fixes (map (prefix "t_") names) ctxt'' |
378 val depth = Free (depth_name, @{typ code_numeral}) |
378 val depth = Free (depth_name, @{typ natural}) |
379 val genuine_only = Free (genuine_only_name, @{typ bool}) |
379 val genuine_only = Free (genuine_only_name, @{typ bool}) |
380 val term_vars = map (fn n => Free (n, @{typ "unit => term"})) term_names |
380 val term_vars = map (fn n => Free (n, @{typ "unit => term"})) term_names |
381 fun lookup v = the (AList.lookup (op =) (names ~~ (frees ~~ term_vars)) v) |
381 fun lookup v = the (AList.lookup (op =) (names ~~ (frees ~~ term_vars)) v) |
382 val return = mk_return (HOLogic.mk_list @{typ term} |
382 val return = mk_return (HOLogic.mk_list @{typ term} |
383 (map (fn v => v $ @{term "()"}) term_vars @ map mk_safe_term eval_terms)) |
383 (map (fn v => v $ @{term "()"}) term_vars @ map mk_safe_term eval_terms)) |
400 in lambda genuine_only (lambda depth (mk_test_term t)) end |
400 in lambda genuine_only (lambda depth (mk_test_term t)) end |
401 |
401 |
402 fun mk_parametric_generator_expr mk_generator_expr = |
402 fun mk_parametric_generator_expr mk_generator_expr = |
403 Quickcheck_Common.gen_mk_parametric_generator_expr |
403 Quickcheck_Common.gen_mk_parametric_generator_expr |
404 ((mk_generator_expr, |
404 ((mk_generator_expr, |
405 absdummy @{typ bool} (absdummy @{typ "code_numeral"} (Const (@{const_name "None"}, resultT)))), |
405 absdummy @{typ bool} (absdummy @{typ natural} (Const (@{const_name "None"}, resultT)))), |
406 @{typ bool} --> @{typ "code_numeral"} --> resultT) |
406 @{typ bool} --> @{typ natural} --> resultT) |
407 |
407 |
408 fun mk_validator_expr ctxt t = |
408 fun mk_validator_expr ctxt t = |
409 let |
409 let |
410 fun bounded_forallT T = (T --> @{typ bool}) --> @{typ code_numeral} --> @{typ bool} |
410 fun bounded_forallT T = (T --> @{typ bool}) --> @{typ natural} --> @{typ bool} |
411 val ctxt' = Variable.auto_fixes t ctxt |
411 val ctxt' = Variable.auto_fixes t ctxt |
412 val names = Term.add_free_names t [] |
412 val names = Term.add_free_names t [] |
413 val frees = map Free (Term.add_frees t []) |
413 val frees = map Free (Term.add_frees t []) |
414 fun lookup v = the (AList.lookup (op =) (names ~~ frees) v) |
414 fun lookup v = the (AList.lookup (op =) (names ~~ frees) v) |
415 val ([depth_name], _) = Variable.variant_fixes ["depth"] ctxt' |
415 val ([depth_name], _) = Variable.variant_fixes ["depth"] ctxt' |
416 val depth = Free (depth_name, @{typ code_numeral}) |
416 val depth = Free (depth_name, @{typ natural}) |
417 fun mk_bounded_forall (Free (s, T)) t = |
417 fun mk_bounded_forall (Free (s, T)) t = |
418 Const (@{const_name "Quickcheck_Exhaustive.bounded_forall_class.bounded_forall"}, bounded_forallT T) |
418 Const (@{const_name "Quickcheck_Exhaustive.bounded_forall_class.bounded_forall"}, bounded_forallT T) |
419 $ lambda (Free (s, T)) t $ depth |
419 $ lambda (Free (s, T)) t $ depth |
420 fun mk_safe_if (cond, then_t, else_t) genuine = mk_if (cond, then_t, else_t genuine) |
420 fun mk_safe_if (cond, then_t, else_t) genuine = mk_if (cond, then_t, else_t genuine) |
421 fun mk_let _ def v_opt t e = mk_let_expr (the_default def v_opt, t, e) |
421 fun mk_let _ def v_opt t e = mk_let_expr (the_default def v_opt, t, e) |
441 |
441 |
442 (* FIXME just one data slot (record) per program unit *) |
442 (* FIXME just one data slot (record) per program unit *) |
443 |
443 |
444 structure Counterexample = Proof_Data |
444 structure Counterexample = Proof_Data |
445 ( |
445 ( |
446 type T = unit -> int -> bool -> int -> (bool * term list) option |
446 type T = unit -> Code_Numeral.natural -> bool -> Code_Numeral.natural -> (bool * term list) option |
447 (* FIXME avoid user error with non-user text *) |
447 (* FIXME avoid user error with non-user text *) |
448 fun init _ () = error "Counterexample" |
448 fun init _ () = error "Counterexample" |
449 ); |
449 ); |
450 val put_counterexample = Counterexample.put; |
450 val put_counterexample = Counterexample.put; |
451 |
451 |
452 structure Counterexample_Batch = Proof_Data |
452 structure Counterexample_Batch = Proof_Data |
453 ( |
453 ( |
454 type T = unit -> (int -> term list option) list |
454 type T = unit -> (Code_Numeral.natural -> term list option) list |
455 (* FIXME avoid user error with non-user text *) |
455 (* FIXME avoid user error with non-user text *) |
456 fun init _ () = error "Counterexample" |
456 fun init _ () = error "Counterexample" |
457 ); |
457 ); |
458 val put_counterexample_batch = Counterexample_Batch.put; |
458 val put_counterexample_batch = Counterexample_Batch.put; |
459 |
459 |
460 structure Validator_Batch = Proof_Data |
460 structure Validator_Batch = Proof_Data |
461 ( |
461 ( |
462 type T = unit -> (int -> bool) list |
462 type T = unit -> (Code_Numeral.natural -> bool) list |
463 (* FIXME avoid user error with non-user text *) |
463 (* FIXME avoid user error with non-user text *) |
464 fun init _ () = error "Counterexample" |
464 fun init _ () = error "Counterexample" |
465 ); |
465 ); |
466 val put_validator_batch = Validator_Batch.put; |
466 val put_validator_batch = Validator_Batch.put; |
467 |
467 |
468 |
468 |
469 val target = "Quickcheck"; |
469 val target = "Quickcheck"; |
470 |
470 |
471 fun compile_generator_expr ctxt ts = |
471 fun compile_generator_expr_raw ctxt ts = |
472 let |
472 let |
473 val thy = Proof_Context.theory_of ctxt |
473 val thy = Proof_Context.theory_of ctxt |
474 val mk_generator_expr = |
474 val mk_generator_expr = |
475 if Config.get ctxt fast then mk_fast_generator_expr |
475 if Config.get ctxt fast then mk_fast_generator_expr |
476 else if Config.get ctxt bounded_forall then mk_bounded_forall_generator_expr |
476 else if Config.get ctxt bounded_forall then mk_bounded_forall_generator_expr |
485 fn genuine_only => fn [card, size] => rpair NONE (compile card genuine_only size |> |
485 fn genuine_only => fn [card, size] => rpair NONE (compile card genuine_only size |> |
486 (if Config.get ctxt quickcheck_pretty then |
486 (if Config.get ctxt quickcheck_pretty then |
487 Option.map (apsnd (map Quickcheck_Common.post_process_term)) else I)) |
487 Option.map (apsnd (map Quickcheck_Common.post_process_term)) else I)) |
488 end; |
488 end; |
489 |
489 |
490 fun compile_generator_exprs ctxt ts = |
490 fun compile_generator_expr ctxt ts = |
|
491 let |
|
492 val compiled = compile_generator_expr_raw ctxt ts; |
|
493 in fn genuine_only => fn [card, size] => |
|
494 compiled genuine_only [Code_Numeral.natural_of_integer card, Code_Numeral.natural_of_integer size] |
|
495 end; |
|
496 |
|
497 fun compile_generator_exprs_raw ctxt ts = |
491 let |
498 let |
492 val thy = Proof_Context.theory_of ctxt |
499 val thy = Proof_Context.theory_of ctxt |
493 val ts' = map (fn t => mk_generator_expr ctxt (t, [])) ts; |
500 val ts' = map (fn t => mk_generator_expr ctxt (t, [])) ts; |
494 val compiles = Code_Runtime.dynamic_value_strict |
501 val compiles = Code_Runtime.dynamic_value_strict |
495 (Counterexample_Batch.get, put_counterexample_batch, |
502 (Counterexample_Batch.get, put_counterexample_batch, |
496 "Exhaustive_Generators.put_counterexample_batch") |
503 "Exhaustive_Generators.put_counterexample_batch") |
497 thy (SOME target) (fn proc => map (fn g => g #> (Option.map o map) proc)) |
504 thy (SOME target) (fn proc => map (fn g => g #> (Option.map o map) proc)) |
498 (HOLogic.mk_list @{typ "code_numeral => term list option"} ts') [] |
505 (HOLogic.mk_list @{typ "natural => term list option"} ts') [] |
499 in |
506 in |
500 map (fn compile => fn size => compile size |
507 map (fn compile => fn size => compile size |
501 |> Option.map (map Quickcheck_Common.post_process_term)) compiles |
508 |> (Option.map o map) Quickcheck_Common.post_process_term) compiles |
502 end; |
509 end; |
503 |
510 |
504 fun compile_validator_exprs ctxt ts = |
511 fun compile_generator_exprs ctxt ts = |
|
512 compile_generator_exprs_raw ctxt ts |
|
513 |> map (fn f => fn size => f (Code_Numeral.natural_of_integer size)); |
|
514 |
|
515 fun compile_validator_exprs_raw ctxt ts = |
505 let |
516 let |
506 val thy = Proof_Context.theory_of ctxt |
517 val thy = Proof_Context.theory_of ctxt |
507 val ts' = map (mk_validator_expr ctxt) ts |
518 val ts' = map (mk_validator_expr ctxt) ts |
508 in |
519 in |
509 Code_Runtime.dynamic_value_strict |
520 Code_Runtime.dynamic_value_strict |
510 (Validator_Batch.get, put_validator_batch, "Exhaustive_Generators.put_validator_batch") |
521 (Validator_Batch.get, put_validator_batch, "Exhaustive_Generators.put_validator_batch") |
511 thy (SOME target) (K I) (HOLogic.mk_list @{typ "code_numeral => bool"} ts') [] |
522 thy (SOME target) (K I) (HOLogic.mk_list @{typ "natural => bool"} ts') [] |
512 end; |
523 end; |
|
524 |
|
525 fun compile_validator_exprs ctxt ts = |
|
526 compile_validator_exprs_raw ctxt ts |
|
527 |> map (fn f => fn size => f (Code_Numeral.natural_of_integer size)); |
513 |
528 |
514 fun size_matters_for thy Ts = not (forall (fn T => Sign.of_sort thy (T, @{sort check_all})) Ts) |
529 fun size_matters_for thy Ts = not (forall (fn T => Sign.of_sort thy (T, @{sort check_all})) Ts) |
515 |
530 |
516 val test_goals = |
531 val test_goals = |
517 Quickcheck_Common.generator_test_goal_terms ("exhaustive", (size_matters_for, compile_generator_expr)); |
532 Quickcheck_Common.generator_test_goal_terms ("exhaustive", (size_matters_for, compile_generator_expr)); |