src/HOL/Tools/Quickcheck/narrowing_generators.ML
author bulwahn
Fri Mar 18 18:19:42 2011 +0100 (2011-03-18)
changeset 42019 a9445d87bc3e
parent 41963 d8c3b26b3da4
child 42020 2da02764d523
permissions -rw-r--r--
adding size as static argument in quickcheck_narrowing compilation
     1 (*  Title:      HOL/Tools/Quickcheck/narrowing_generators.ML
     2     Author:     Lukas Bulwahn, TU Muenchen
     3 
     4 Narrowing-based counterexample generation.
     5 *)
     6 
     7 signature NARROWING_GENERATORS =
     8 sig
     9   val compile_generator_expr:
    10     Proof.context -> term -> int -> term list option * Quickcheck.report option
    11   val put_counterexample: (unit -> term list option) -> Proof.context -> Proof.context
    12   val setup: theory -> theory
    13 end;
    14 
    15 structure Narrowing_Generators : NARROWING_GENERATORS =
    16 struct
    17 
    18 fun mk_undefined T = Const(@{const_name undefined}, T)
    19 
    20 (* narrowing specific names and types *)
    21 
    22 exception FUNCTION_TYPE;
    23 
    24 val narrowingN = "narrowing";
    25 
    26 fun narrowingT T =
    27   @{typ Quickcheck_Narrowing.code_int} --> Type (@{type_name Quickcheck_Narrowing.cons}, [T])
    28 
    29 fun mk_empty T = Const (@{const_name Quickcheck_Narrowing.empty}, narrowingT T)
    30 
    31 fun mk_cons c T = Const (@{const_name Quickcheck_Narrowing.cons}, T --> narrowingT T) $ Const (c, T)
    32 
    33 fun mk_apply (T, t) (U, u) =
    34   let
    35     val (_, U') = dest_funT U
    36   in
    37     (U', Const (@{const_name Quickcheck_Narrowing.apply},
    38       narrowingT U --> narrowingT T --> narrowingT U') $ u $ t)
    39   end
    40   
    41 fun mk_sum (t, u) =
    42   let
    43     val T = fastype_of t
    44   in
    45     Const (@{const_name Quickcheck_Narrowing.sum}, T --> T --> T) $ t $ u
    46   end
    47 
    48 (* creating narrowing instances *)
    49 
    50 fun mk_equations descr vs tycos narrowings (Ts, Us) =
    51   let
    52     fun mk_call T =
    53       let
    54         val narrowing =
    55           Const (@{const_name "Quickcheck_Narrowing.narrowing_class.narrowing"}, narrowingT T)
    56       in
    57         (T, narrowing)
    58       end
    59     fun mk_aux_call fTs (k, _) (tyco, Ts) =
    60       let
    61         val T = Type (tyco, Ts)
    62         val _ = if not (null fTs) then raise FUNCTION_TYPE else ()
    63       in
    64         (T, nth narrowings k)
    65       end
    66     fun mk_consexpr simpleT (c, xs) =
    67       let
    68         val Ts = map fst xs
    69       in snd (fold mk_apply xs (Ts ---> simpleT, mk_cons c (Ts ---> simpleT))) end
    70     fun mk_rhs exprs = foldr1 mk_sum exprs
    71     val rhss =
    72       Datatype_Aux.interpret_construction descr vs
    73         { atyp = mk_call, dtyp = mk_aux_call }
    74       |> (map o apfst) Type
    75       |> map (fn (T, cs) => map (mk_consexpr T) cs)
    76       |> map mk_rhs
    77     val lhss = narrowings
    78     val eqs = map (HOLogic.mk_Trueprop o HOLogic.mk_eq) (lhss ~~ rhss)
    79   in
    80     eqs
    81   end
    82 
    83 
    84 fun instantiate_narrowing_datatype config descr vs tycos prfx (names, auxnames) (Ts, Us) thy =
    85   let
    86     val _ = Datatype_Aux.message config "Creating narrowing generators ...";
    87     val narrowingsN = map (prefix (narrowingN ^ "_")) (names @ auxnames);
    88   in
    89     thy
    90     |> Class.instantiation (tycos, vs, @{sort narrowing})
    91     |> (fold_map (fn (name, T) => Local_Theory.define
    92           ((Binding.conceal (Binding.name name), NoSyn),
    93             (apfst Binding.conceal Attrib.empty_binding, mk_undefined (narrowingT T)))
    94         #> apfst fst) (narrowingsN ~~ (Ts @ Us))
    95       #> (fn (narrowings, lthy) =>
    96         let
    97           val eqs_t = mk_equations descr vs tycos narrowings (Ts, Us)
    98           val eqs = map (fn eq => Goal.prove lthy ["f", "i"] [] eq
    99             (fn _ => Skip_Proof.cheat_tac (ProofContext.theory_of lthy))) eqs_t
   100         in
   101           fold (fn (name, eq) => Local_Theory.note
   102           ((Binding.conceal (Binding.qualify true prfx
   103              (Binding.qualify true name (Binding.name "simps"))),
   104              Code.add_default_eqn_attrib :: map (Attrib.internal o K)
   105                [Simplifier.simp_add, Nitpick_Simps.add]), [eq]) #> snd) (narrowingsN ~~ eqs) lthy
   106         end))
   107     |> Class.prove_instantiation_exit (K (Class.intro_classes_tac []))
   108   end;
   109 
   110 (* testing framework *)
   111 
   112 val target = "Haskell"
   113 
   114 (* invocation of Haskell interpreter *)
   115 
   116 val narrowing_engine = File.read (Path.explode "~~/src/HOL/Tools/Quickcheck/Narrowing_Engine.hs")
   117 
   118 fun exec verbose code =
   119   ML_Context.exec (fn () => Secure.use_text ML_Env.local_context (0, "generated code") verbose code)
   120 
   121 fun value ctxt (get, put, put_ml) (code, value) size =
   122   let
   123     val tmp_prefix = "Quickcheck_Narrowing"
   124     fun run in_path = 
   125       let
   126         val code_file = Path.append in_path (Path.basic "Code.hs")
   127         val narrowing_engine_file = Path.append in_path (Path.basic "Narrowing_Engine.hs")
   128         val main_file = Path.append in_path (Path.basic "Main.hs")
   129         val main = "module Main where {\n\n" ^
   130           "import Narrowing_Engine;\n" ^
   131           "import Code;\n\n" ^
   132           "main = Narrowing_Engine.smallCheck " ^ string_of_int size ^ " (Code.value ())\n\n" ^
   133           "}\n"
   134         val code' = prefix "module Code where {\n\ndata Typerep = Typerep String [Typerep];\n"
   135           (unprefix "module Code where {" code)
   136         val _ = File.write code_file code'
   137         val _ = File.write narrowing_engine_file narrowing_engine
   138         val _ = File.write main_file main
   139         val executable = File.shell_path (Path.append in_path (Path.basic "isa_lsc"))
   140         val cmd = "\"$ISABELLE_GHC\" -fglasgow-exts " ^
   141           (space_implode " " (map File.shell_path [code_file, narrowing_engine_file, main_file])) ^
   142           " -o " ^ executable ^ " && " ^ executable
   143           (* FIXME: should use bash command exec but does not work with && *) 
   144       in
   145         bash_output cmd
   146       end
   147     val result = Isabelle_System.with_tmp_dir tmp_prefix run
   148     val output_value = the_default "NONE"
   149       (try (snd o split_last o filter_out (fn s => s = "") o split_lines o fst) result)
   150     val ml_code = "\nval _ = Context.set_thread_data (SOME (Context.map_proof (" ^ put_ml
   151       ^ " (fn () => " ^ output_value ^ ")) (ML_Context.the_generic_context ())))";
   152     val ctxt' = ctxt
   153       |> put (fn () => error ("Bad evaluation for " ^ quote put_ml))
   154       |> Context.proof_map (exec false ml_code);
   155   in get ctxt' () end;
   156 
   157 fun evaluation cookie thy evaluator vs_t args size =
   158   let
   159     val ctxt = ProofContext.init_global thy;
   160     val (program_code, value_name) = evaluator vs_t;
   161     val value_code = space_implode " "
   162       (value_name :: "()" :: map (enclose "(" ")") args);
   163   in Exn.interruptible_capture (value ctxt cookie (program_code, value_code)) size end;
   164 
   165 fun dynamic_value_strict cookie thy postproc t args size =
   166   let
   167     fun evaluator naming program ((_, vs_ty), t) deps =
   168       evaluation cookie thy (Code_Target.evaluator thy target naming program deps) (vs_ty, t) args size;
   169   in Exn.release (Code_Thingol.dynamic_value thy (Exn.map_result o postproc) evaluator t) end;
   170 
   171 (* counterexample generator *)
   172   
   173 structure Counterexample = Proof_Data
   174 (
   175   type T = unit -> term list option
   176   fun init _ () = error "Counterexample"
   177 )
   178 
   179 val put_counterexample =  Counterexample.put
   180   
   181 fun compile_generator_expr ctxt t size =
   182   let
   183     val thy = ProofContext.theory_of ctxt
   184     fun ensure_testable t =
   185       Const (@{const_name Quickcheck_Narrowing.ensure_testable}, fastype_of t --> fastype_of t) $ t
   186     val t = dynamic_value_strict
   187       (Counterexample.get, Counterexample.put, "Narrowing_Generators.put_counterexample")
   188       thy (Option.map o map) (ensure_testable t) [] size
   189   in
   190     (t, NONE)
   191   end;
   192 
   193 
   194 val setup =
   195   Datatype.interpretation
   196     (Quickcheck_Common.ensure_sort_datatype (@{sort narrowing}, instantiate_narrowing_datatype))
   197   #> Context.theory_map
   198     (Quickcheck.add_generator ("narrowing", compile_generator_expr))
   199     
   200 end;