src/HOL/Library/code_test.ML
changeset 64577 0288a566c966
parent 63806 c54a53ef1873
child 64578 7b20f9f94f4e
equal deleted inserted replaced
64576:ce8802dc3145 64577:0288a566c966
     1 (*  Title:      HOL/Library/code_test.ML
     1 (*  Title:      HOL/Library/code_test.ML
     2     Author:     Andreas Lochbihler, ETH Zurich
     2     Author:     Andreas Lochbihler, ETH Zürich
     3 
     3 
     4 Test infrastructure for the code generator
     4 Test infrastructure for the code generator.
     5 *)
     5 *)
     6 
     6 
     7 signature CODE_TEST = sig
     7 signature CODE_TEST =
     8   val add_driver : string * ((Proof.context -> (string * string) list * string -> Path.T -> string) * string) -> theory -> theory
     8 sig
     9   val get_driver : theory -> string -> ((Proof.context -> (string * string) list * string -> Path.T -> string) * string) option
     9   val add_driver:
    10   val overlord : bool Config.T
    10     string * ((Proof.context -> (string * string) list * string -> Path.T -> string) * string) ->
    11   val successN : string
    11     theory -> theory
    12   val failureN : string
    12   val get_driver: theory -> string ->
    13   val start_markerN : string
    13     ((Proof.context -> (string * string) list * string -> Path.T -> string) * string) option
    14   val end_markerN : string
    14   val overlord: bool Config.T
    15   val test_terms : Proof.context -> term list -> string -> unit
    15   val successN: string
    16   val test_targets : Proof.context -> term list -> string list -> unit list
    16   val failureN: string
    17   val test_code_cmd : string list -> string list -> Toplevel.state -> unit
    17   val start_markerN: string
    18 
    18   val end_markerN: string
    19   val eval_term : string -> Proof.context -> term -> term
    19   val test_terms: Proof.context -> term list -> string -> unit
    20 
    20   val test_targets: Proof.context -> term list -> string list -> unit list
    21   val gen_driver :
    21   val test_code_cmd: string list -> string list -> Toplevel.state -> unit
       
    22 
       
    23   val eval_term: string -> Proof.context -> term -> term
       
    24 
       
    25   val gen_driver:
    22    (theory -> Path.T -> string list -> string ->
    26    (theory -> Path.T -> string list -> string ->
    23     {files : (Path.T * string) list,
    27     {files: (Path.T * string) list,
    24      compile_cmd : string option, run_cmd : string, mk_code_file : string -> Path.T})
    28      compile_cmd: string option,
    25    -> string -> string -> string
    29      run_cmd: string,
    26    -> theory -> (string * string) list * string -> Path.T -> string
    30      mk_code_file: string -> Path.T}) ->
    27 
    31    string -> string -> string -> theory -> (string * string) list * string -> Path.T -> string
    28   val ISABELLE_POLYML : string
    32 
    29   val polymlN : string
    33   val ISABELLE_POLYML: string
    30   val evaluate_in_polyml : Proof.context -> (string * string) list * string -> Path.T -> string
    34   val polymlN: string
    31 
    35   val evaluate_in_polyml: Proof.context -> (string * string) list * string -> Path.T -> string
    32   val mltonN : string
    36 
    33   val ISABELLE_MLTON : string
    37   val mltonN: string
    34   val evaluate_in_mlton : Proof.context -> (string * string) list * string -> Path.T -> string
    38   val ISABELLE_MLTON: string
    35 
    39   val evaluate_in_mlton: Proof.context -> (string * string) list * string -> Path.T -> string
    36   val smlnjN : string
    40 
    37   val ISABELLE_SMLNJ : string
    41   val smlnjN: string
    38   val evaluate_in_smlnj : Proof.context -> (string * string) list * string -> Path.T -> string
    42   val ISABELLE_SMLNJ: string
    39 
    43   val evaluate_in_smlnj: Proof.context -> (string * string) list * string -> Path.T -> string
    40   val ocamlN : string
    44 
    41   val ISABELLE_OCAMLC : string
    45   val ocamlN: string
    42   val evaluate_in_ocaml : Proof.context -> (string * string) list * string -> Path.T -> string
    46   val ISABELLE_OCAMLC: string
    43 
    47   val evaluate_in_ocaml: Proof.context -> (string * string) list * string -> Path.T -> string
    44   val ghcN : string
    48 
    45   val ISABELLE_GHC : string
    49   val ghcN: string
    46   val ghc_options : string Config.T
    50   val ISABELLE_GHC: string
    47   val evaluate_in_ghc : Proof.context -> (string * string) list * string -> Path.T -> string
    51   val ghc_options: string Config.T
    48 
    52   val evaluate_in_ghc: Proof.context -> (string * string) list * string -> Path.T -> string
    49   val scalaN : string
    53 
    50   val ISABELLE_SCALA : string
    54   val scalaN: string
    51   val evaluate_in_scala : Proof.context -> (string * string) list * string -> Path.T -> string
    55   val ISABELLE_SCALA: string
       
    56   val evaluate_in_scala: Proof.context -> (string * string) list * string -> Path.T -> string
    52 end
    57 end
    53 
    58 
    54 structure Code_Test : CODE_TEST = struct
    59 structure Code_Test: CODE_TEST =
       
    60 struct
    55 
    61 
    56 (* convert a list of terms into nested tuples and back *)
    62 (* convert a list of terms into nested tuples and back *)
       
    63 
    57 fun mk_tuples [] = @{term "()"}
    64 fun mk_tuples [] = @{term "()"}
    58   | mk_tuples [t] = t
    65   | mk_tuples [t] = t
    59   | mk_tuples (t :: ts) = HOLogic.mk_prod (t, mk_tuples ts)
    66   | mk_tuples (t :: ts) = HOLogic.mk_prod (t, mk_tuples ts)
    60 
    67 
    61 fun dest_tuples (Const (@{const_name Pair}, _) $ l $ r) = l :: dest_tuples r
    68 fun dest_tuples (Const (@{const_name Pair}, _) $ l $ r) = l :: dest_tuples r
    65 fun map_option _ NONE = NONE
    72 fun map_option _ NONE = NONE
    66   | map_option f (SOME x) = SOME (f x)
    73   | map_option f (SOME x) = SOME (f x)
    67 
    74 
    68 fun last_field sep str =
    75 fun last_field sep str =
    69   let
    76   let
    70     val n = size sep;
    77     val n = size sep
    71     val len = size str;
    78     val len = size str
    72     fun find i =
    79     fun find i =
    73       if i < 0 then NONE
    80       if i < 0 then NONE
    74       else if String.substring (str, i, n) = sep then SOME i
    81       else if String.substring (str, i, n) = sep then SOME i
    75       else find (i - 1);
    82       else find (i - 1)
    76   in
    83   in
    77     (case find (len - n) of
    84     (case find (len - n) of
    78       NONE => NONE
    85       NONE => NONE
    79     | SOME i => SOME (String.substring (str, 0, i), String.extract (str, i + n, NONE)))
    86     | SOME i => SOME (String.substring (str, 0, i), String.extract (str, i + n, NONE)))
    80   end;
    87   end
    81 
    88 
    82 fun split_first_last start stop s =
    89 fun split_first_last start stop s =
    83   case first_field start s
    90   (case first_field start s of
    84    of NONE => NONE
    91     NONE => NONE
    85     | SOME (initial, rest) =>
    92   | SOME (initial, rest) =>
    86       case last_field stop rest
    93       (case last_field stop rest of
    87        of NONE => NONE
    94         NONE => NONE
    88         | SOME (middle, tail) => SOME (initial, middle, tail);
    95       | SOME (middle, tail) => SOME (initial, middle, tail)))
    89 
    96 
    90 (* Data slot for drivers *)
    97 
       
    98 (* data slot for drivers *)
    91 
    99 
    92 structure Drivers = Theory_Data
   100 structure Drivers = Theory_Data
    93 (
   101 (
    94   type T = (string * ((Proof.context -> (string * string) list * string -> Path.T -> string) * string)) list;
   102   type T =
    95   val empty = [];
   103     (string * ((Proof.context -> (string * string) list * string -> Path.T -> string) * string)) list
    96   val extend = I;
   104   val empty = []
    97   fun merge data : T = AList.merge (op =) (K true) data;
   105   val extend = I
       
   106   fun merge data : T = AList.merge (op =) (K true) data
    98 )
   107 )
    99 
   108 
   100 val add_driver = Drivers.map o AList.update (op =);
   109 val add_driver = Drivers.map o AList.update (op =)
   101 val get_driver = AList.lookup (op =) o Drivers.get;
   110 val get_driver = AList.lookup (op =) o Drivers.get
   102 
   111 
   103 (*
   112 (*
   104   Test drivers must produce output of the following format:
   113   Test drivers must produce output of the following format:
   105   
   114 
   106   The start of the relevant data is marked with start_markerN,
   115   The start of the relevant data is marked with start_markerN,
   107   its end with end_markerN.
   116   its end with end_markerN.
   108 
   117 
   109   Between these two markers, every line corresponds to one test.
   118   Between these two markers, every line corresponds to one test.
   110   Lines of successful tests start with successN, failures start with failureN.
   119   Lines of successful tests start with successN, failures start with failureN.
   111   The failure failureN may continue with the YXML encoding of the evaluated term.
   120   The failure failureN may continue with the YXML encoding of the evaluated term.
   112   There must not be any additional whitespace in between.
   121   There must not be any additional whitespace in between.
   113 *)
   122 *)
   114 
   123 
   115 (* Parsing of results *)
   124 
       
   125 (* parsing of results *)
   116 
   126 
   117 val successN = "True"
   127 val successN = "True"
   118 val failureN = "False"
   128 val failureN = "False"
   119 val start_markerN = "*@*Isabelle/Code_Test-start*@*"
   129 val start_markerN = "*@*Isabelle/Code_Test-start*@*"
   120 val end_markerN = "*@*Isabelle/Code_Test-end*@*"
   130 val end_markerN = "*@*Isabelle/Code_Test-end*@*"
   121 
   131 
   122 fun parse_line line =
   132 fun parse_line line =
   123   if String.isPrefix successN line then (true, NONE)
   133   if String.isPrefix successN line then (true, NONE)
   124   else if String.isPrefix failureN line then (false, 
   134   else if String.isPrefix failureN line then (false,
   125     if size line > size failureN then
   135     if size line > size failureN then
   126       String.extract (line, size failureN, NONE)
   136       String.extract (line, size failureN, NONE)
   127       |> YXML.parse_body
   137       |> YXML.parse_body
   128       |> Term_XML.Decode.term
   138       |> Term_XML.Decode.term
   129       |> dest_tuples
   139       |> dest_tuples
   130       |> SOME
   140       |> SOME
   131     else NONE)
   141     else NONE)
   132   else raise Fail ("Cannot parse result of evaluation:\n" ^ line)
   142   else raise Fail ("Cannot parse result of evaluation:\n" ^ line)
   133 
   143 
   134 fun parse_result target out =
   144 fun parse_result target out =
   135   case split_first_last start_markerN end_markerN out
   145   (case split_first_last start_markerN end_markerN out of
   136     of NONE => error ("Evaluation failed for " ^ target ^ "!\nBash output:\n" ^ out)
   146     NONE => error ("Evaluation failed for " ^ target ^ "!\nBash output:\n" ^ out)
   137      | SOME (_, middle, _) => middle |> trim_line |> split_lines |> map parse_line
   147   | SOME (_, middle, _) => middle |> trim_line |> split_lines |> map parse_line)
   138 
   148 
   139 (* Pretty printing of test results *)
   149 
       
   150 (* pretty printing of test results *)
   140 
   151 
   141 fun pretty_eval _ NONE _ = []
   152 fun pretty_eval _ NONE _ = []
   142   | pretty_eval ctxt (SOME evals) ts = 
   153   | pretty_eval ctxt (SOME evals) ts =
   143     [Pretty.fbrk,
   154       [Pretty.fbrk,
   144      Pretty.big_list "Evaluated terms"
   155        Pretty.big_list "Evaluated terms"
   145        (map (fn (t, eval) => Pretty.block 
   156          (map (fn (t, eval) => Pretty.block
   146          [Syntax.pretty_term ctxt t, Pretty.brk 1, Pretty.str "=", Pretty.brk 1,
   157            [Syntax.pretty_term ctxt t, Pretty.brk 1, Pretty.str "=", Pretty.brk 1,
   147           Syntax.pretty_term ctxt eval])
   158             Syntax.pretty_term ctxt eval])
   148        (ts ~~ evals))]
   159          (ts ~~ evals))]
   149 
   160 
   150 fun pretty_failure ctxt target (((_, evals), query), eval_ts) =
   161 fun pretty_failure ctxt target (((_, evals), query), eval_ts) =
   151   Pretty.block (Pretty.text ("Test in " ^ target ^ " failed for")
   162   Pretty.block (Pretty.text ("Test in " ^ target ^ " failed for")
   152     @ [Pretty.brk 1, Pretty.quote (Syntax.pretty_term ctxt query)]
   163     @ [Pretty.brk 1, Pretty.quote (Syntax.pretty_term ctxt query)]
   153     @ pretty_eval ctxt evals eval_ts)
   164     @ pretty_eval ctxt evals eval_ts)
   154 
   165 
   155 fun pretty_failures ctxt target failures =
   166 fun pretty_failures ctxt target failures =
   156   Pretty.blk (0, Pretty.fbreaks (map (pretty_failure ctxt target) failures))
   167   Pretty.blk (0, Pretty.fbreaks (map (pretty_failure ctxt target) failures))
   157 
   168 
   158 (* Driver invocation *)
   169 
   159 
   170 (* driver invocation *)
   160 val overlord = Attrib.setup_config_bool @{binding "code_test_overlord"} (K false);
   171 
       
   172 val overlord = Attrib.setup_config_bool @{binding "code_test_overlord"} (K false)
   161 
   173 
   162 fun with_overlord_dir name f =
   174 fun with_overlord_dir name f =
   163   let
   175   let
   164     val path = Path.append (Path.explode "$ISABELLE_HOME_USER") (Path.basic (name ^ serial_string ()))
   176     val path =
   165     val _ = Isabelle_System.mkdirs path;
   177       Path.append (Path.explode "$ISABELLE_HOME_USER") (Path.basic (name ^ serial_string ()))
   166   in
   178     val _ = Isabelle_System.mkdirs path
   167     Exn.release (Exn.capture f path)
   179   in Exn.release (Exn.capture f path) end
   168   end;
       
   169 
   180 
   170 fun dynamic_value_strict ctxt t compiler =
   181 fun dynamic_value_strict ctxt t compiler =
   171   let
   182   let
   172     val thy = Proof_Context.theory_of ctxt
   183     val thy = Proof_Context.theory_of ctxt
   173     val (driver, target) = case get_driver thy compiler
   184     val (driver, target) =
   174      of NONE => error ("No driver for target " ^ compiler)
   185       (case get_driver thy compiler of
   175       | SOME f => f;
   186         NONE => error ("No driver for target " ^ compiler)
       
   187       | SOME f => f)
   176     val debug = Config.get (Proof_Context.init_global thy) overlord
   188     val debug = Config.get (Proof_Context.init_global thy) overlord
   177     val with_dir = if debug then with_overlord_dir else Isabelle_System.with_tmp_dir
   189     val with_dir = if debug then with_overlord_dir else Isabelle_System.with_tmp_dir
   178     fun evaluate f = with_dir "Code_Test" (driver ctxt f) |> parse_result compiler
   190     fun evaluate f = with_dir "Code_Test" (driver ctxt f) |> parse_result compiler
   179     fun evaluator program _ vs_ty deps =
   191     fun evaluator program _ vs_ty deps =
   180       Exn.interruptible_capture evaluate (Code_Target.computation_text ctxt target program deps true vs_ty);
   192       Exn.interruptible_capture evaluate
       
   193         (Code_Target.computation_text ctxt target program deps true vs_ty)
   181     fun postproc f = map (apsnd (map_option (map f)))
   194     fun postproc f = map (apsnd (map_option (map f)))
   182   in
   195   in Exn.release (Code_Thingol.dynamic_value ctxt (Exn.map_res o postproc) evaluator t) end
   183     Exn.release (Code_Thingol.dynamic_value ctxt (Exn.map_res o postproc) evaluator t)
   196 
   184   end;
   197 
   185 
   198 (* term preprocessing *)
   186 (* Term preprocessing *)
       
   187 
   199 
   188 fun add_eval (Const (@{const_name Trueprop}, _) $ t) = add_eval t
   200 fun add_eval (Const (@{const_name Trueprop}, _) $ t) = add_eval t
   189   | add_eval (Const (@{const_name "HOL.eq"}, _) $ lhs $ rhs) = (fn acc =>
   201   | add_eval (Const (@{const_name "HOL.eq"}, _) $ lhs $ rhs) = (fn acc =>
   190     acc
   202       acc
   191     |> add_eval rhs
   203       |> add_eval rhs
   192     |> add_eval lhs
   204       |> add_eval lhs
   193     |> cons rhs
   205       |> cons rhs
   194     |> cons lhs)
   206       |> cons lhs)
   195   | add_eval (Const (@{const_name "Not"}, _) $ t) = add_eval t
   207   | add_eval (Const (@{const_name "Not"}, _) $ t) = add_eval t
   196   | add_eval (Const (@{const_name "Orderings.ord_class.less_eq"}, _) $ lhs $ rhs) = (fn acc =>
   208   | add_eval (Const (@{const_name "Orderings.ord_class.less_eq"}, _) $ lhs $ rhs) = (fn acc =>
   197     lhs :: rhs :: acc)
   209       lhs :: rhs :: acc)
   198   | add_eval (Const (@{const_name "Orderings.ord_class.less"}, _) $ lhs $ rhs) = (fn acc =>
   210   | add_eval (Const (@{const_name "Orderings.ord_class.less"}, _) $ lhs $ rhs) = (fn acc =>
   199     lhs :: rhs :: acc)
   211       lhs :: rhs :: acc)
   200   | add_eval _ = I
   212   | add_eval _ = I
   201 
   213 
   202 fun mk_term_of [] = @{term "None :: (unit \<Rightarrow> yxml_of_term) option"}
   214 fun mk_term_of [] = @{term "None :: (unit \<Rightarrow> yxml_of_term) option"}
   203   | mk_term_of ts =
   215   | mk_term_of ts =
   204   let
   216       let
   205     val tuple = mk_tuples ts
   217         val tuple = mk_tuples ts
   206     val T = fastype_of tuple
   218         val T = fastype_of tuple
   207   in
   219       in
   208     @{term "Some :: (unit \<Rightarrow> yxml_of_term) \<Rightarrow> (unit \<Rightarrow> yxml_of_term) option"} $
   220         @{term "Some :: (unit \<Rightarrow> yxml_of_term) \<Rightarrow> (unit \<Rightarrow> yxml_of_term) option"} $
   209       (absdummy @{typ unit} (@{const yxml_string_of_term} $
   221           (absdummy @{typ unit} (@{const yxml_string_of_term} $
   210         (Const (@{const_name Code_Evaluation.term_of}, T --> @{typ term}) $ tuple)))
   222             (Const (@{const_name Code_Evaluation.term_of}, T --> @{typ term}) $ tuple)))
   211   end
   223       end
   212 
   224 
   213 fun test_terms ctxt ts target =
   225 fun test_terms ctxt ts target =
   214   let
   226   let
   215     val thy = Proof_Context.theory_of ctxt
   227     val thy = Proof_Context.theory_of ctxt
   216 
   228 
   217     fun term_of t = Sign.of_sort thy (fastype_of t, @{sort term_of})
   229     fun term_of t = Sign.of_sort thy (fastype_of t, @{sort term_of})
   218 
   230 
   219     fun ensure_bool t = case fastype_of t of @{typ bool} => ()
   231     fun ensure_bool t =
   220       | _ => error ("Test case not of type bool: " ^ Pretty.string_of (Syntax.pretty_term ctxt t))
   232       (case fastype_of t of
       
   233         @{typ bool} => ()
       
   234       | _ => error ("Test case not of type bool: " ^ Pretty.string_of (Syntax.pretty_term ctxt t)))
   221 
   235 
   222     val _ = map ensure_bool ts
   236     val _ = map ensure_bool ts
   223 
   237 
   224     val evals = map (fn t => filter term_of (add_eval t [])) ts
   238     val evals = map (fn t => filter term_of (add_eval t [])) ts
   225     val eval = map mk_term_of evals
   239     val eval = map mk_term_of evals
   226 
   240 
   227     val T = HOLogic.mk_prodT (@{typ bool}, Type (@{type_name option}, [@{typ unit} --> @{typ yxml_of_term}]))
   241     val T =
       
   242       HOLogic.mk_prodT (@{typ bool},
       
   243         Type (@{type_name option}, [@{typ unit} --> @{typ yxml_of_term}]))
   228     val t = HOLogic.mk_list T (map HOLogic.mk_prod (ts ~~ eval))
   244     val t = HOLogic.mk_list T (map HOLogic.mk_prod (ts ~~ eval))
   229 
   245 
   230     val result = dynamic_value_strict ctxt t target;
   246     val result = dynamic_value_strict ctxt t target
   231 
   247 
   232     val failed =
   248     val failed =
   233       filter_out (fst o fst o fst) (result ~~ ts ~~ evals)
   249       filter_out (fst o fst o fst) (result ~~ ts ~~ evals)
   234       handle ListPair.UnequalLengths => 
   250       handle ListPair.UnequalLengths =>
   235         error ("Evaluation failed!\nWrong number of test results: " ^ Int.toString (length result))
   251         error ("Evaluation failed!\nWrong number of test results: " ^ Int.toString (length result))
   236     val _ = case failed of [] => () 
   252     val _ = case failed of [] => ()
   237       | _ => error (Pretty.string_of (pretty_failures ctxt target failed))
   253       | _ => error (Pretty.string_of (pretty_failures ctxt target failed))
   238   in
   254   in
   239     ()
   255     ()
   240   end
   256   end
   241 
   257 
   242 fun test_targets ctxt = map o test_terms ctxt
   258 fun test_targets ctxt = map o test_terms ctxt
   243 
   259 
   244 fun test_code_cmd raw_ts targets state =
   260 fun test_code_cmd raw_ts targets state =
   245   let
   261   let
   246     val ctxt = Toplevel.context_of state;
   262     val ctxt = Toplevel.context_of state
   247     val ts = Syntax.read_terms ctxt raw_ts;
   263     val ts = Syntax.read_terms ctxt raw_ts
   248     val frees = fold Term.add_free_names ts []
   264     val frees = fold Term.add_free_names ts []
   249     val _ = if frees = [] then () else
   265     val _ = if frees = [] then () else
   250       error ("Terms contain free variables: " ^
   266       error ("Terms contain free variables: " ^
   251       Pretty.string_of (Pretty.block (Pretty.commas (map Pretty.str frees))))
   267       Pretty.string_of (Pretty.block (Pretty.commas (map Pretty.str frees))))
   252   in
   268   in
   261       Pretty.string_of (Pretty.block (Pretty.commas (map Pretty.str frees))))
   277       Pretty.string_of (Pretty.block (Pretty.commas (map Pretty.str frees))))
   262 
   278 
   263     val thy = Proof_Context.theory_of ctxt
   279     val thy = Proof_Context.theory_of ctxt
   264 
   280 
   265     val T_t = fastype_of t
   281     val T_t = fastype_of t
   266     val _ = if Sign.of_sort thy (T_t, @{sort term_of}) then () else error 
   282     val _ = if Sign.of_sort thy (T_t, @{sort term_of}) then () else error
   267       ("Type " ^ Pretty.string_of (Syntax.pretty_typ ctxt T_t) ^ 
   283       ("Type " ^ Pretty.string_of (Syntax.pretty_typ ctxt T_t) ^
   268        " of term not of sort " ^ Pretty.string_of (Syntax.pretty_sort ctxt @{sort term_of}))
   284        " of term not of sort " ^ Pretty.string_of (Syntax.pretty_sort ctxt @{sort term_of}))
   269 
   285 
   270     val T = HOLogic.mk_prodT (@{typ bool}, Type (@{type_name option}, [@{typ unit} --> @{typ yxml_of_term}]))
   286     val T = HOLogic.mk_prodT (@{typ bool}, Type (@{type_name option}, [@{typ unit} --> @{typ yxml_of_term}]))
   271     val t' = HOLogic.mk_list T [HOLogic.mk_prod (@{term "False"}, mk_term_of [t])]
   287     val t' = HOLogic.mk_list T [HOLogic.mk_prod (@{term "False"}, mk_term_of [t])]
   272 
   288 
   273     val result = dynamic_value_strict ctxt t' target;
   289     val result = dynamic_value_strict ctxt t' target
   274   in
   290   in (case result of [(_, SOME [t])] => t | _ => error "Evaluation failed") end
   275     case result of [(_, SOME [t])] => t | _ => error "Evaluation failed"
   291 
   276   end
   292 
   277 
   293 (* generic driver *)
   278 (* Generic driver *)
       
   279 
   294 
   280 fun gen_driver mk_driver env_var env_var_dest compilerN ctxt (code_files, value_name) =
   295 fun gen_driver mk_driver env_var env_var_dest compilerN ctxt (code_files, value_name) =
   281   let
   296   let
   282     val compiler = getenv env_var
   297     val compiler = getenv env_var
   283     val _ = if compiler <> "" then () else error (Pretty.string_of (Pretty.para 
   298     val _ =
       
   299       if compiler <> "" then ()
       
   300       else error (Pretty.string_of (Pretty.para
   284          ("Environment variable " ^ env_var ^ " is not set. To test code generation with " ^
   301          ("Environment variable " ^ env_var ^ " is not set. To test code generation with " ^
   285          compilerN ^ ", set this variable to your " ^ env_var_dest ^ " in the settings file.")))
   302          compilerN ^ ", set this variable to your " ^ env_var_dest ^ " in the settings file.")))
   286 
   303 
   287     fun compile NONE = ()
   304     fun compile NONE = ()
   288       | compile (SOME cmd) =
   305       | compile (SOME cmd) =
   289         let
   306           let
   290           val (out, ret) = Isabelle_System.bash_output cmd
   307             val (out, ret) = Isabelle_System.bash_output cmd
   291         in
   308           in
   292           if ret = 0 then () else error
   309             if ret = 0 then () else error
   293             ("Compilation with " ^ compilerN ^ " failed:\n" ^ cmd ^ "\n" ^ out)
   310               ("Compilation with " ^ compilerN ^ " failed:\n" ^ cmd ^ "\n" ^ out)
   294         end
   311           end
   295 
   312 
   296     fun run (path : Path.T)= 
   313     fun run path =
   297       let
   314       let
   298         val modules = map fst code_files
   315         val modules = map fst code_files
   299         val {files, compile_cmd, run_cmd, mk_code_file}
   316         val {files, compile_cmd, run_cmd, mk_code_file} = mk_driver ctxt path modules value_name
   300           =  mk_driver ctxt path modules value_name
       
   301 
   317 
   302         val _ = map (fn (name, code) => File.write (mk_code_file name) code) code_files
   318         val _ = map (fn (name, code) => File.write (mk_code_file name) code) code_files
   303         val _ = map (fn (name, content) => File.write name content) files
   319         val _ = map (fn (name, content) => File.write name content) files
   304 
   320 
   305         val _ = compile compile_cmd
   321         val _ = compile compile_cmd
   306 
   322 
   307         val (out, res) = Isabelle_System.bash_output run_cmd
   323         val (out, res) = Isabelle_System.bash_output run_cmd
   308         val _ = if res = 0 then () else error
   324         val _ = if res = 0 then () else error
   309           ("Evaluation for " ^ compilerN ^ " terminated with error code " ^ Int.toString res ^
   325           ("Evaluation for " ^ compilerN ^ " terminated with error code " ^ Int.toString res ^
   310            "\nBash output:\n" ^ out)
   326            "\nBash output:\n" ^ out)
   311       in
   327       in out end
   312         out
   328   in run end
   313       end
   329 
   314   in
   330 
   315     run
   331 (* driver for PolyML *)
   316   end
       
   317 
       
   318 (* Driver for PolyML *)
       
   319 
   332 
   320 val ISABELLE_POLYML = "ISABELLE_POLYML"
   333 val ISABELLE_POLYML = "ISABELLE_POLYML"
   321 val polymlN = "PolyML";
   334 val polymlN = "PolyML"
   322 
   335 
   323 fun mk_driver_polyml _ path _ value_name =
   336 fun mk_driver_polyml _ path _ value_name =
   324   let
   337   let
   325     val generatedN = "generated.sml"
   338     val generatedN = "generated.sml"
   326     val driverN = "driver.sml"
   339     val driverN = "driver.sml"
   327 
   340 
   328     val code_path = Path.append path (Path.basic generatedN)
   341     val code_path = Path.append path (Path.basic generatedN)
   329     val driver_path = Path.append path (Path.basic driverN)
   342     val driver_path = Path.append path (Path.basic driverN)
   330     val driver = 
   343     val driver =
   331       "fun main prog_name = \n" ^
   344       "fun main prog_name = \n" ^
   332       "  let\n" ^
   345       "  let\n" ^
   333       "    fun format_term NONE = \"\"\n" ^ 
   346       "    fun format_term NONE = \"\"\n" ^
   334       "      | format_term (SOME t) = t ();\n" ^
   347       "      | format_term (SOME t) = t ();\n" ^
   335       "    fun format (true, _) = \"" ^ successN ^ "\\n\"\n" ^
   348       "    fun format (true, _) = \"" ^ successN ^ "\\n\"\n" ^
   336       "      | format (false, to) = \"" ^ failureN ^ "\" ^ format_term to ^ \"\\n\";\n" ^
   349       "      | format (false, to) = \"" ^ failureN ^ "\" ^ format_term to ^ \"\\n\";\n" ^
   337       "    val result = " ^ value_name ^ " ();\n" ^
   350       "    val result = " ^ value_name ^ " ();\n" ^
   338       "    val _ = print \"" ^ start_markerN ^ "\";\n" ^
   351       "    val _ = print \"" ^ start_markerN ^ "\";\n" ^
   340       "    val _ = print \"" ^ end_markerN ^ "\";\n" ^
   353       "    val _ = print \"" ^ end_markerN ^ "\";\n" ^
   341       "  in\n" ^
   354       "  in\n" ^
   342       "    ()\n" ^
   355       "    ()\n" ^
   343       "  end;\n"
   356       "  end;\n"
   344     val cmd =
   357     val cmd =
   345       "echo \"use \\\"" ^ Path.implode code_path ^ "\\\"; use \\\"" ^ 
   358       "echo \"use \\\"" ^ Path.implode code_path ^ "\\\"; use \\\"" ^
   346       Path.implode driver_path ^ "\\\"; main ();\" | " ^ 
   359       Path.implode driver_path ^ "\\\"; main ();\" | " ^
   347       Path.implode (Path.variable ISABELLE_POLYML)
   360       Path.implode (Path.variable ISABELLE_POLYML)
   348   in
   361   in
   349     {files = [(driver_path, driver)], compile_cmd = NONE, run_cmd = cmd, mk_code_file = K code_path}
   362     {files = [(driver_path, driver)], compile_cmd = NONE, run_cmd = cmd, mk_code_file = K code_path}
   350   end
   363   end
   351 
   364 
   352 fun evaluate_in_polyml ctxt =
   365 fun evaluate_in_polyml ctxt =
   353   gen_driver mk_driver_polyml ISABELLE_POLYML "PolyML executable" polymlN ctxt
   366   gen_driver mk_driver_polyml ISABELLE_POLYML "PolyML executable" polymlN ctxt
   354 
   367 
   355 (* Driver for mlton *)
   368 
       
   369 (* driver for mlton *)
   356 
   370 
   357 val mltonN = "MLton"
   371 val mltonN = "MLton"
   358 val ISABELLE_MLTON = "ISABELLE_MLTON"
   372 val ISABELLE_MLTON = "ISABELLE_MLTON"
   359 
   373 
   360 fun mk_driver_mlton _ path _ value_name =
   374 fun mk_driver_mlton _ path _ value_name =
   365     val ml_basisN = projectN ^ ".mlb"
   379     val ml_basisN = projectN ^ ".mlb"
   366 
   380 
   367     val code_path = Path.append path (Path.basic generatedN)
   381     val code_path = Path.append path (Path.basic generatedN)
   368     val driver_path = Path.append path (Path.basic driverN)
   382     val driver_path = Path.append path (Path.basic driverN)
   369     val ml_basis_path = Path.append path (Path.basic ml_basisN)
   383     val ml_basis_path = Path.append path (Path.basic ml_basisN)
   370     val driver = 
   384     val driver =
   371       "fun format_term NONE = \"\"\n" ^ 
   385       "fun format_term NONE = \"\"\n" ^
   372       "  | format_term (SOME t) = t ();\n" ^
   386       "  | format_term (SOME t) = t ();\n" ^
   373       "fun format (true, _) = \"" ^ successN ^ "\\n\"\n" ^
   387       "fun format (true, _) = \"" ^ successN ^ "\\n\"\n" ^
   374       "  | format (false, to) = \"" ^ failureN ^ "\" ^ format_term to ^ \"\\n\";\n" ^
   388       "  | format (false, to) = \"" ^ failureN ^ "\" ^ format_term to ^ \"\\n\";\n" ^
   375       "val result = " ^ value_name ^ " ();\n" ^
   389       "val result = " ^ value_name ^ " ();\n" ^
   376       "val _ = print \"" ^ start_markerN ^ "\";\n" ^
   390       "val _ = print \"" ^ start_markerN ^ "\";\n" ^
   391   end
   405   end
   392 
   406 
   393 fun evaluate_in_mlton ctxt =
   407 fun evaluate_in_mlton ctxt =
   394   gen_driver mk_driver_mlton ISABELLE_MLTON "MLton executable" mltonN ctxt
   408   gen_driver mk_driver_mlton ISABELLE_MLTON "MLton executable" mltonN ctxt
   395 
   409 
   396 (* Driver for SML/NJ *)
   410 
       
   411 (* driver for SML/NJ *)
   397 
   412 
   398 val smlnjN = "SMLNJ"
   413 val smlnjN = "SMLNJ"
   399 val ISABELLE_SMLNJ = "ISABELLE_SMLNJ"
   414 val ISABELLE_SMLNJ = "ISABELLE_SMLNJ"
   400 
   415 
   401 fun mk_driver_smlnj _ path _ value_name =
   416 fun mk_driver_smlnj _ path _ value_name =
   403     val generatedN = "generated.sml"
   418     val generatedN = "generated.sml"
   404     val driverN = "driver.sml"
   419     val driverN = "driver.sml"
   405 
   420 
   406     val code_path = Path.append path (Path.basic generatedN)
   421     val code_path = Path.append path (Path.basic generatedN)
   407     val driver_path = Path.append path (Path.basic driverN)
   422     val driver_path = Path.append path (Path.basic driverN)
   408     val driver = 
   423     val driver =
   409       "structure Test = struct\n" ^
   424       "structure Test = struct\n" ^
   410       "fun main prog_name =\n" ^
   425       "fun main prog_name =\n" ^
   411       "  let\n" ^
   426       "  let\n" ^
   412       "    fun format_term NONE = \"\"\n" ^ 
   427       "    fun format_term NONE = \"\"\n" ^
   413       "      | format_term (SOME t) = t ();\n" ^
   428       "      | format_term (SOME t) = t ();\n" ^
   414       "    fun format (true, _) = \"" ^ successN ^ "\\n\"\n" ^
   429       "    fun format (true, _) = \"" ^ successN ^ "\\n\"\n" ^
   415       "      | format (false, to) = \"" ^ failureN ^ "\" ^ format_term to ^ \"\\n\";\n" ^
   430       "      | format (false, to) = \"" ^ failureN ^ "\" ^ format_term to ^ \"\\n\";\n" ^
   416       "    val result = " ^ value_name ^ " ();\n" ^
   431       "    val result = " ^ value_name ^ " ();\n" ^
   417       "    val _ = print \"" ^ start_markerN ^ "\";\n" ^
   432       "    val _ = print \"" ^ start_markerN ^ "\";\n" ^
   430   end
   445   end
   431 
   446 
   432 fun evaluate_in_smlnj ctxt =
   447 fun evaluate_in_smlnj ctxt =
   433   gen_driver mk_driver_smlnj ISABELLE_SMLNJ "SMLNJ executable" smlnjN ctxt
   448   gen_driver mk_driver_smlnj ISABELLE_SMLNJ "SMLNJ executable" smlnjN ctxt
   434 
   449 
   435 (* Driver for OCaml *)
   450 
       
   451 (* driver for OCaml *)
   436 
   452 
   437 val ocamlN = "OCaml"
   453 val ocamlN = "OCaml"
   438 val ISABELLE_OCAMLC = "ISABELLE_OCAMLC"
   454 val ISABELLE_OCAMLC = "ISABELLE_OCAMLC"
   439 
   455 
   440 fun mk_driver_ocaml _ path _ value_name =
   456 fun mk_driver_ocaml _ path _ value_name =
   442     val generatedN = "generated.ml"
   458     val generatedN = "generated.ml"
   443     val driverN = "driver.ml"
   459     val driverN = "driver.ml"
   444 
   460 
   445     val code_path = Path.append path (Path.basic generatedN)
   461     val code_path = Path.append path (Path.basic generatedN)
   446     val driver_path = Path.append path (Path.basic driverN)
   462     val driver_path = Path.append path (Path.basic driverN)
   447     val driver = 
   463     val driver =
   448       "let format_term = function\n" ^
   464       "let format_term = function\n" ^
   449       "  | None -> \"\"\n" ^ 
   465       "  | None -> \"\"\n" ^
   450       "  | Some t -> t ();;\n" ^
   466       "  | Some t -> t ();;\n" ^
   451       "let format = function\n" ^
   467       "let format = function\n" ^
   452       "  | (true, _) -> \"" ^ successN ^ "\\n\"\n" ^
   468       "  | (true, _) -> \"" ^ successN ^ "\\n\"\n" ^
   453       "  | (false, x) -> \"" ^ failureN ^ "\" ^ format_term x ^ \"\\n\";;\n" ^
   469       "  | (false, x) -> \"" ^ failureN ^ "\" ^ format_term x ^ \"\\n\";;\n" ^
   454       "let result = " ^ ("Generated." ^ value_name) ^ " ();;\n" ^
   470       "let result = " ^ ("Generated." ^ value_name) ^ " ();;\n" ^
   471   end
   487   end
   472 
   488 
   473 fun evaluate_in_ocaml ctxt =
   489 fun evaluate_in_ocaml ctxt =
   474   gen_driver mk_driver_ocaml ISABELLE_OCAMLC "ocamlc executable" ocamlN ctxt
   490   gen_driver mk_driver_ocaml ISABELLE_OCAMLC "ocamlc executable" ocamlN ctxt
   475 
   491 
   476 (* Driver for GHC *)
   492 
       
   493 (* driver for GHC *)
   477 
   494 
   478 val ghcN = "GHC"
   495 val ghcN = "GHC"
   479 val ISABELLE_GHC = "ISABELLE_GHC"
   496 val ISABELLE_GHC = "ISABELLE_GHC"
   480 
   497 
   481 val ghc_options = Attrib.setup_config_string @{binding code_test_ghc} (K "")
   498 val ghc_options = Attrib.setup_config_string @{binding code_test_ghc} (K "")
   484   let
   501   let
   485     val driverN = "Main.hs"
   502     val driverN = "Main.hs"
   486 
   503 
   487     fun mk_code_file name = Path.append path (Path.basic (name ^ ".hs"))
   504     fun mk_code_file name = Path.append path (Path.basic (name ^ ".hs"))
   488     val driver_path = Path.append path (Path.basic driverN)
   505     val driver_path = Path.append path (Path.basic driverN)
   489     val driver = 
   506     val driver =
   490       "module Main where {\n" ^
   507       "module Main where {\n" ^
   491       String.concat (map (fn module => "import qualified " ^ module ^ ";\n") modules) ^
   508       String.concat (map (fn module => "import qualified " ^ module ^ ";\n") modules) ^
   492       "main = do {\n" ^
   509       "main = do {\n" ^
   493       "    let {\n" ^
   510       "    let {\n" ^
   494       "      format_term Nothing = \"\";\n" ^ 
   511       "      format_term Nothing = \"\";\n" ^
   495       "      format_term (Just t) = t ();\n" ^
   512       "      format_term (Just t) = t ();\n" ^
   496       "      format (True, _) = \"" ^ successN ^ "\\n\";\n" ^
   513       "      format (True, _) = \"" ^ successN ^ "\\n\";\n" ^
   497       "      format (False, to) = \"" ^ failureN ^ "\" ++ format_term to ++ \"\\n\";\n" ^
   514       "      format (False, to) = \"" ^ failureN ^ "\" ++ format_term to ++ \"\\n\";\n" ^
   498       "      result = " ^ value_name ^ " ();\n" ^
   515       "      result = " ^ value_name ^ " ();\n" ^
   499       "    };\n" ^
   516       "    };\n" ^
   516   end
   533   end
   517 
   534 
   518 fun evaluate_in_ghc ctxt =
   535 fun evaluate_in_ghc ctxt =
   519   gen_driver mk_driver_ghc ISABELLE_GHC "GHC executable" ghcN ctxt
   536   gen_driver mk_driver_ghc ISABELLE_GHC "GHC executable" ghcN ctxt
   520 
   537 
   521 (* Driver for Scala *)
   538 
       
   539 (* driver for Scala *)
   522 
   540 
   523 val scalaN = "Scala"
   541 val scalaN = "Scala"
   524 val ISABELLE_SCALA = "ISABELLE_SCALA"
   542 val ISABELLE_SCALA = "ISABELLE_SCALA"
   525 
   543 
   526 fun mk_driver_scala _ path _ value_name =
   544 fun mk_driver_scala _ path _ value_name =
   528     val generatedN = "Generated_Code"
   546     val generatedN = "Generated_Code"
   529     val driverN = "Driver.scala"
   547     val driverN = "Driver.scala"
   530 
   548 
   531     val code_path = Path.append path (Path.basic (generatedN ^ ".scala"))
   549     val code_path = Path.append path (Path.basic (generatedN ^ ".scala"))
   532     val driver_path = Path.append path (Path.basic driverN)
   550     val driver_path = Path.append path (Path.basic driverN)
   533     val driver = 
   551     val driver =
   534       "import " ^ generatedN ^ "._\n" ^
   552       "import " ^ generatedN ^ "._\n" ^
   535       "object Test {\n" ^
   553       "object Test {\n" ^
   536       "  def format_term(x : Option[Unit => String]) : String = x match {\n" ^
   554       "  def format_term(x : Option[Unit => String]) : String = x match {\n" ^
   537       "    case None => \"\"\n" ^
   555       "    case None => \"\"\n" ^
   538       "    case Some(x) => x(())\n" ^
   556       "    case Some(x) => x(())\n" ^
   565 fun evaluate_in_scala ctxt =
   583 fun evaluate_in_scala ctxt =
   566   gen_driver mk_driver_scala ISABELLE_SCALA "Scala directory" scalaN ctxt
   584   gen_driver mk_driver_scala ISABELLE_SCALA "Scala directory" scalaN ctxt
   567 
   585 
   568 val test_codeP = Scan.repeat1 Parse.prop -- (@{keyword "in"} |-- Scan.repeat1 Parse.name)
   586 val test_codeP = Scan.repeat1 Parse.prop -- (@{keyword "in"} |-- Scan.repeat1 Parse.name)
   569 
   587 
   570 val _ = 
   588 val _ =
   571   Outer_Syntax.command @{command_keyword test_code}
   589   Outer_Syntax.command @{command_keyword test_code}
   572     "compile test cases to target languages, execute them and report results"
   590     "compile test cases to target languages, execute them and report results"
   573       (test_codeP >> (fn (raw_ts, targets) => Toplevel.keep (test_code_cmd raw_ts targets)))
   591       (test_codeP >> (fn (raw_ts, targets) => Toplevel.keep (test_code_cmd raw_ts targets)))
   574 
   592 
   575 val _ = Theory.setup (fold add_driver
   593 val _ = Theory.setup (fold add_driver
   578    (smlnjN, (evaluate_in_smlnj, Code_ML.target_SML)),
   596    (smlnjN, (evaluate_in_smlnj, Code_ML.target_SML)),
   579    (ocamlN, (evaluate_in_ocaml, Code_ML.target_OCaml)),
   597    (ocamlN, (evaluate_in_ocaml, Code_ML.target_OCaml)),
   580    (ghcN, (evaluate_in_ghc, Code_Haskell.target)),
   598    (ghcN, (evaluate_in_ghc, Code_Haskell.target)),
   581    (scalaN, (evaluate_in_scala, Code_Scala.target))]
   599    (scalaN, (evaluate_in_scala, Code_Scala.target))]
   582   #> fold (fn target => Value_Command.add_evaluator (target, eval_term target))
   600   #> fold (fn target => Value_Command.add_evaluator (target, eval_term target))
   583     [polymlN, mltonN, smlnjN, ocamlN, ghcN, scalaN]);
   601     [polymlN, mltonN, smlnjN, ocamlN, ghcN, scalaN])
   584 
   602 
   585 end
   603 end
   586