| author | paulson <lp15@cam.ac.uk> | 
| Sun, 15 Jan 2023 15:58:05 +0000 | |
| changeset 76953 | f70d431b5016 | 
| parent 76884 | a004c5322ea4 | 
| child 78705 | fde0b195cb7d | 
| permissions | -rw-r--r-- | 
| 59720 | 1 | (* Title: HOL/Library/code_test.ML | 
| 64577 | 2 | Author: Andreas Lochbihler, ETH Zürich | 
| 58039 | 3 | |
| 64577 | 4 | Test infrastructure for the code generator. | 
| 58039 | 5 | *) | 
| 6 | ||
| 64577 | 7 | signature CODE_TEST = | 
| 8 | sig | |
| 9 | val add_driver: | |
| 10 | string * ((Proof.context -> (string * string) list * string -> Path.T -> string) * string) -> | |
| 11 | theory -> theory | |
| 72284 | 12 | val debug: bool Config.T | 
| 64577 | 13 | val test_terms: Proof.context -> term list -> string -> unit | 
| 64580 | 14 | val test_code_cmd: string list -> string list -> Proof.context -> unit | 
| 64577 | 15 | val eval_term: string -> Proof.context -> term -> term | 
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 16 | val check_settings: string -> string -> string -> unit | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 17 | val compile: string -> string -> unit | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 18 | val evaluate: string -> string -> string | 
| 64577 | 19 | val evaluate_in_polyml: Proof.context -> (string * string) list * string -> Path.T -> string | 
| 20 | val evaluate_in_mlton: Proof.context -> (string * string) list * string -> Path.T -> string | |
| 21 | val evaluate_in_smlnj: Proof.context -> (string * string) list * string -> Path.T -> string | |
| 22 | val evaluate_in_ocaml: Proof.context -> (string * string) list * string -> Path.T -> string | |
| 23 | val ghc_options: string Config.T | |
| 24 | val evaluate_in_ghc: Proof.context -> (string * string) list * string -> Path.T -> string | |
| 25 | val evaluate_in_scala: Proof.context -> (string * string) list * string -> Path.T -> string | |
| 66284 
378895354604
new derived targets for evaluating Haskell and Scala programs
 Andreas Lochbihler parents: 
65905diff
changeset | 26 | val target_Scala: string | 
| 
378895354604
new derived targets for evaluating Haskell and Scala programs
 Andreas Lochbihler parents: 
65905diff
changeset | 27 | val target_Haskell: string | 
| 58039 | 28 | end | 
| 29 | ||
| 64577 | 30 | structure Code_Test: CODE_TEST = | 
| 31 | struct | |
| 58039 | 32 | |
| 33 | (* convert a list of terms into nested tuples and back *) | |
| 64577 | 34 | |
| 74636 | 35 | fun mk_tuples [] = \<^Const>\<open>Unity\<close> | 
| 58039 | 36 | | mk_tuples [t] = t | 
| 37 | | mk_tuples (t :: ts) = HOLogic.mk_prod (t, mk_tuples ts) | |
| 38 | ||
| 74636 | 39 | fun dest_tuples \<^Const_>\<open>Pair _ _ for l r\<close> = l :: dest_tuples r | 
| 58039 | 40 | | dest_tuples t = [t] | 
| 41 | ||
| 42 | ||
| 43 | fun last_field sep str = | |
| 44 | let | |
| 64577 | 45 | val n = size sep | 
| 46 | val len = size str | |
| 58039 | 47 | fun find i = | 
| 48 | if i < 0 then NONE | |
| 49 | else if String.substring (str, i, n) = sep then SOME i | |
| 64577 | 50 | else find (i - 1) | 
| 58039 | 51 | in | 
| 52 | (case find (len - n) of | |
| 53 | NONE => NONE | |
| 54 | | SOME i => SOME (String.substring (str, 0, i), String.extract (str, i + n, NONE))) | |
| 64577 | 55 | end | 
| 58039 | 56 | |
| 57 | fun split_first_last start stop s = | |
| 64577 | 58 | (case first_field start s of | 
| 59 | NONE => NONE | |
| 60 | | SOME (initial, rest) => | |
| 61 | (case last_field stop rest of | |
| 62 | NONE => NONE | |
| 63 | | SOME (middle, tail) => SOME (initial, middle, tail))) | |
| 58039 | 64 | |
| 64577 | 65 | |
| 66 | (* data slot for drivers *) | |
| 58039 | 67 | |
| 68 | structure Drivers = Theory_Data | |
| 69 | ( | |
| 64577 | 70 | type T = | 
| 71 | (string * ((Proof.context -> (string * string) list * string -> Path.T -> string) * string)) list | |
| 72 | val empty = [] | |
| 73 | fun merge data : T = AList.merge (op =) (K true) data | |
| 58039 | 74 | ) | 
| 75 | ||
| 64577 | 76 | val add_driver = Drivers.map o AList.update (op =) | 
| 77 | val get_driver = AList.lookup (op =) o Drivers.get | |
| 58039 | 78 | |
| 79 | (* | |
| 80 | Test drivers must produce output of the following format: | |
| 64577 | 81 | |
| 72307 | 82 | The start of the relevant data is marked with start_marker, | 
| 83 | its end with end_marker. | |
| 58039 | 84 | |
| 85 | Between these two markers, every line corresponds to one test. | |
| 72307 | 86 | Lines of successful tests start with success, failures start with failure. | 
| 87 | The failure failure may continue with the YXML encoding of the evaluated term. | |
| 58039 | 88 | There must not be any additional whitespace in between. | 
| 89 | *) | |
| 90 | ||
| 64577 | 91 | |
| 92 | (* parsing of results *) | |
| 58039 | 93 | |
| 72307 | 94 | val success = "True" | 
| 95 | val failure = "False" | |
| 96 | val start_marker = "*@*Isabelle/Code_Test-start*@*" | |
| 97 | val end_marker = "*@*Isabelle/Code_Test-end*@*" | |
| 58039 | 98 | |
| 99 | fun parse_line line = | |
| 72307 | 100 | if String.isPrefix success line then (true, NONE) | 
| 101 | else if String.isPrefix failure line then (false, | |
| 102 | if size line > size failure then | |
| 103 | String.extract (line, size failure, NONE) | |
| 58039 | 104 | |> YXML.parse_body | 
| 70784 
799437173553
Term_XML.Encode/Decode.term uses Const "typargs";
 wenzelm parents: 
69950diff
changeset | 105 | |> Term_XML.Decode.term_raw | 
| 58039 | 106 | |> dest_tuples | 
| 107 | |> SOME | |
| 108 | else NONE) | |
| 109 |   else raise Fail ("Cannot parse result of evaluation:\n" ^ line)
 | |
| 110 | ||
| 111 | fun parse_result target out = | |
| 72307 | 112 | (case split_first_last start_marker end_marker out of | 
| 72273 | 113 |     NONE => error ("Evaluation failed for " ^ target ^ "!\nCompiler output:\n" ^ out)
 | 
| 65904 
8411f1a2272c
permissive trim_line as in Scala, e.g. relevant for poly/TextIO.print output on Windows;
 wenzelm parents: 
65901diff
changeset | 114 | | SOME (_, middle, _) => middle |> trim_split_lines |> map parse_line) | 
| 58039 | 115 | |
| 64577 | 116 | |
| 117 | (* pretty printing of test results *) | |
| 58039 | 118 | |
| 119 | fun pretty_eval _ NONE _ = [] | |
| 64577 | 120 | | pretty_eval ctxt (SOME evals) ts = | 
| 121 | [Pretty.fbrk, | |
| 122 | Pretty.big_list "Evaluated terms" | |
| 123 | (map (fn (t, eval) => Pretty.block | |
| 124 | [Syntax.pretty_term ctxt t, Pretty.brk 1, Pretty.str "=", Pretty.brk 1, | |
| 125 | Syntax.pretty_term ctxt eval]) | |
| 126 | (ts ~~ evals))] | |
| 58039 | 127 | |
| 128 | fun pretty_failure ctxt target (((_, evals), query), eval_ts) = | |
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 129 |   Pretty.block (Pretty.text ("Test in " ^ target ^ " failed for") @
 | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 130 | [Pretty.brk 1, Pretty.quote (Syntax.pretty_term ctxt query)] @ | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 131 | pretty_eval ctxt evals eval_ts) | 
| 58039 | 132 | |
| 133 | fun pretty_failures ctxt target failures = | |
| 134 | Pretty.blk (0, Pretty.fbreaks (map (pretty_failure ctxt target) failures)) | |
| 135 | ||
| 136 | ||
| 64577 | 137 | (* driver invocation *) | 
| 138 | ||
| 72293 | 139 | val debug = Attrib.setup_config_bool \<^binding>\<open>test_code_debug\<close> (K false) | 
| 58039 | 140 | |
| 72284 | 141 | fun with_debug_dir name f = | 
| 72511 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 142 | (Path.explode "$ISABELLE_HOME_USER" + Path.basic (name ^ serial_string ())) | 
| 72376 | 143 | |> Isabelle_System.make_directory | 
| 144 | |> Exn.capture f | |
| 145 | |> Exn.release; | |
| 58039 | 146 | |
| 147 | fun dynamic_value_strict ctxt t compiler = | |
| 148 | let | |
| 149 | val thy = Proof_Context.theory_of ctxt | |
| 64577 | 150 | val (driver, target) = | 
| 151 | (case get_driver thy compiler of | |
| 152 |         NONE => error ("No driver for target " ^ compiler)
 | |
| 72272 | 153 | | SOME drv => drv) | 
| 72285 | 154 | val with_dir = if Config.get ctxt debug then with_debug_dir else Isabelle_System.with_tmp_dir | 
| 72274 | 155 | fun eval result = | 
| 75604 
39df30349778
more scalable generated files and code export, using Bytes.T;
 wenzelm parents: 
74636diff
changeset | 156 | with_dir "Code_Test" | 
| 
39df30349778
more scalable generated files and code export, using Bytes.T;
 wenzelm parents: 
74636diff
changeset | 157 | (driver ctxt ((apfst o map) (apfst Long_Name.implode #> apsnd Bytes.content) result)) | 
| 69623 
ef02c5e051e5
explicit model concerning files of generated code
 haftmann parents: 
69597diff
changeset | 158 | |> parse_result compiler | 
| 58039 | 159 | fun evaluator program _ vs_ty deps = | 
| 72274 | 160 | Exn.interruptible_capture eval | 
| 64957 | 161 | (Code_Target.compilation_text ctxt target program deps true vs_ty) | 
| 64578 | 162 | fun postproc f = map (apsnd (Option.map (map f))) | 
| 64577 | 163 | in Exn.release (Code_Thingol.dynamic_value ctxt (Exn.map_res o postproc) evaluator t) end | 
| 58039 | 164 | |
| 64577 | 165 | |
| 166 | (* term preprocessing *) | |
| 58039 | 167 | |
| 74636 | 168 | fun add_eval \<^Const_>\<open>Trueprop for t\<close> = add_eval t | 
| 169 | | add_eval \<^Const_>\<open>HOL.eq _ for lhs rhs\<close> = (fn acc => | |
| 64577 | 170 | acc | 
| 171 | |> add_eval rhs | |
| 172 | |> add_eval lhs | |
| 173 | |> cons rhs | |
| 174 | |> cons lhs) | |
| 74636 | 175 | | add_eval \<^Const_>\<open>Not for t\<close> = add_eval t | 
| 176 | | add_eval \<^Const_>\<open>less_eq _ for lhs rhs\<close> = (fn acc => lhs :: rhs :: acc) | |
| 177 | | add_eval \<^Const_>\<open>less _ for lhs rhs\<close> = (fn acc => lhs :: rhs :: acc) | |
| 58039 | 178 | | add_eval _ = I | 
| 179 | ||
| 74383 | 180 | fun mk_term_of [] = \<^Const>\<open>None \<^typ>\<open>unit \<Rightarrow> yxml_of_term\<close>\<close> | 
| 58039 | 181 | | mk_term_of ts = | 
| 64577 | 182 | let | 
| 183 | val tuple = mk_tuples ts | |
| 184 | val T = fastype_of tuple | |
| 185 | in | |
| 74636 | 186 | \<^Const>\<open>Some \<^typ>\<open>unit \<Rightarrow> yxml_of_term\<close> for | 
| 187 | \<open>absdummy \<^Type>\<open>unit\<close> | |
| 188 | \<^Const>\<open>yxml_string_of_term for \<^Const>\<open>Code_Evaluation.term_of T for tuple\<close>\<close>\<close>\<close> | |
| 64577 | 189 | end | 
| 58039 | 190 | |
| 191 | fun test_terms ctxt ts target = | |
| 192 | let | |
| 193 | val thy = Proof_Context.theory_of ctxt | |
| 194 | ||
| 69593 | 195 | fun term_of t = Sign.of_sort thy (fastype_of t, \<^sort>\<open>term_of\<close>) | 
| 58039 | 196 | |
| 64577 | 197 | fun ensure_bool t = | 
| 198 | (case fastype_of t of | |
| 74636 | 199 | \<^Type>\<open>bool\<close> => () | 
| 64579 | 200 | | _ => | 
| 201 | error (Pretty.string_of | |
| 202 | (Pretty.block [Pretty.str "Test case not of type bool:", Pretty.brk 1, | |
| 203 | Syntax.pretty_term ctxt t]))) | |
| 58039 | 204 | |
| 64578 | 205 | val _ = List.app ensure_bool ts | 
| 58039 | 206 | |
| 207 | val evals = map (fn t => filter term_of (add_eval t [])) ts | |
| 208 | val eval = map mk_term_of evals | |
| 209 | ||
| 64578 | 210 | val t = | 
| 69593 | 211 | HOLogic.mk_list \<^typ>\<open>bool \<times> (unit \<Rightarrow> yxml_of_term) option\<close> | 
| 64578 | 212 | (map HOLogic.mk_prod (ts ~~ eval)) | 
| 58039 | 213 | |
| 64577 | 214 | val result = dynamic_value_strict ctxt t target | 
| 58039 | 215 | |
| 216 | val failed = | |
| 217 | filter_out (fst o fst o fst) (result ~~ ts ~~ evals) | |
| 64577 | 218 | handle ListPair.UnequalLengths => | 
| 72272 | 219 |         error ("Evaluation failed!\nWrong number of test results: " ^ string_of_int (length result))
 | 
| 58039 | 220 | in | 
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 221 | (case failed of | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 222 | [] => () | 
| 64578 | 223 | | _ => error (Pretty.string_of (pretty_failures ctxt target failed))) | 
| 58039 | 224 | end | 
| 225 | ||
| 64580 | 226 | fun test_code_cmd raw_ts targets ctxt = | 
| 58039 | 227 | let | 
| 64577 | 228 | val ts = Syntax.read_terms ctxt raw_ts | 
| 64579 | 229 | val frees = fold Term.add_frees ts [] | 
| 64578 | 230 | val _ = | 
| 231 | if null frees then () | |
| 64579 | 232 | else error (Pretty.string_of | 
| 233 | (Pretty.block (Pretty.str "Terms contain free variables:" :: Pretty.brk 1 :: | |
| 72283 | 234 | Pretty.commas (map (Syntax.pretty_term ctxt o Free) frees)))) | 
| 235 | in List.app (test_terms ctxt ts) targets end | |
| 58039 | 236 | |
| 58348 
2d47c7d10b62
add target language evaluators for the value command;
 Andreas Lochbihler parents: 
58039diff
changeset | 237 | fun eval_term target ctxt t = | 
| 
2d47c7d10b62
add target language evaluators for the value command;
 Andreas Lochbihler parents: 
58039diff
changeset | 238 | let | 
| 64579 | 239 | val frees = Term.add_frees t [] | 
| 64578 | 240 | val _ = | 
| 241 | if null frees then () | |
| 64579 | 242 | else | 
| 243 | error (Pretty.string_of | |
| 244 | (Pretty.block (Pretty.str "Term contains free variables:" :: Pretty.brk 1 :: | |
| 72283 | 245 | Pretty.commas (map (Syntax.pretty_term ctxt o Free) frees)))) | 
| 58039 | 246 | |
| 64579 | 247 | val T = fastype_of t | 
| 64578 | 248 | val _ = | 
| 69593 | 249 | if Sign.of_sort (Proof_Context.theory_of ctxt) (T, \<^sort>\<open>term_of\<close>) then () | 
| 64579 | 250 |       else error ("Type " ^ Syntax.string_of_typ ctxt T ^
 | 
| 69593 | 251 | " of term not of sort " ^ Syntax.string_of_sort ctxt \<^sort>\<open>term_of\<close>) | 
| 58039 | 252 | |
| 64578 | 253 | val t' = | 
| 69593 | 254 | HOLogic.mk_list \<^typ>\<open>bool \<times> (unit \<Rightarrow> yxml_of_term) option\<close> | 
| 74636 | 255 | [HOLogic.mk_prod (\<^Const>\<open>False\<close>, mk_term_of [t])] | 
| 58039 | 256 | |
| 64577 | 257 | val result = dynamic_value_strict ctxt t' target | 
| 258 | in (case result of [(_, SOME [t])] => t | _ => error "Evaluation failed") end | |
| 58039 | 259 | |
| 64577 | 260 | |
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 261 | (* check and invoke compiler *) | 
| 58039 | 262 | |
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 263 | fun check_settings compiler var descr = | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 264 | if getenv var = "" then | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 265 | error (Pretty.string_of (Pretty.para | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 266 |       ("Environment variable " ^ var ^ " is not set. To test code generation with " ^
 | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 267 | compiler ^ ", set this variable to your " ^ descr ^ | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 268 | " in the $ISABELLE_HOME_USER/etc/settings file."))) | 
| 72306 | 269 | else () | 
| 58039 | 270 | |
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 271 | fun compile compiler cmd = | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 272 | let val (out, ret) = Isabelle_System.bash_output cmd in | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 273 | if ret = 0 then () | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 274 |     else error ("Compilation with " ^ compiler ^ " failed:\n" ^ cmd ^ "\n" ^ out)
 | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 275 | end | 
| 58039 | 276 | |
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 277 | fun evaluate compiler cmd = | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 278 | let val (out, res) = Isabelle_System.bash_output cmd in | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 279 | if res = 0 then out | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 280 |     else error ("Evaluation for " ^ compiler ^ " terminated with error code " ^
 | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 281 | string_of_int res ^ "\nCompiler output:\n" ^ out) | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 282 | end | 
| 58039 | 283 | |
| 64577 | 284 | |
| 285 | (* driver for PolyML *) | |
| 58039 | 286 | |
| 64577 | 287 | val polymlN = "PolyML" | 
| 58039 | 288 | |
| 72297 | 289 | fun evaluate_in_polyml (_: Proof.context) (code_files, value_name) dir = | 
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 290 | let | 
| 72511 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 291 | val code_path = dir + Path.basic "generated.sml" | 
| 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 292 | val driver_path = dir + Path.basic "driver.sml" | 
| 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 293 | val out_path = dir + Path.basic "out" | 
| 72297 | 294 | |
| 72306 | 295 | val code = #2 (the_single code_files) | 
| 72286 | 296 | val driver = \<^verbatim>\<open> | 
| 72290 | 297 | fun main () = | 
| 72286 | 298 | let | 
| 72307 | 299 | fun format (true, _) = \<close> ^ ML_Syntax.print_string success ^ \<^verbatim>\<open> ^ "\n" | 
| 300 | | format (false, NONE) = \<close> ^ ML_Syntax.print_string failure ^ \<^verbatim>\<open> ^ "\n" | |
| 301 | | format (false, SOME t) = \<close> ^ ML_Syntax.print_string failure ^ \<^verbatim>\<open> ^ t () ^ "\n" | |
| 72286 | 302 | val result = \<close> ^ value_name ^ \<^verbatim>\<open> () | 
| 303 | val result_text = \<close> ^ | |
| 72307 | 304 | ML_Syntax.print_string start_marker ^ | 
| 72286 | 305 | \<^verbatim>\<open> ^ String.concat (map format result) ^ \<close> ^ | 
| 72307 | 306 | ML_Syntax.print_string end_marker ^ \<^verbatim>\<open> | 
| 72288 | 307 | val out = BinIO.openOut \<close> ^ ML_Syntax.print_platform_path out_path ^ \<^verbatim>\<open> | 
| 72286 | 308 | val _ = BinIO.output (out, Byte.stringToBytes result_text) | 
| 309 | val _ = BinIO.closeOut out | |
| 310 | in () end; | |
| 311 | \<close> | |
| 72306 | 312 | val _ = File.write code_path code | 
| 313 | val _ = File.write driver_path driver | |
| 314 | val _ = | |
| 315 | ML_Context.eval | |
| 316 |         {environment = ML_Env.SML, redirect = false, verbose = false, debug = NONE,
 | |
| 317 | writeln = writeln, warning = warning} | |
| 318 | Position.none | |
| 76884 | 319 | (ML_Lex.read_text (code, Position.file (File.symbolic_path code_path)) @ | 
| 320 | ML_Lex.read_text (driver, Position.file (File.symbolic_path driver_path)) @ | |
| 72306 | 321 | ML_Lex.read "main ()") | 
| 72308 | 322 |       handle ERROR msg => error ("Evaluation for " ^ polymlN ^ " failed:\n" ^ msg)
 | 
| 72306 | 323 | in File.read out_path end | 
| 58039 | 324 | |
| 64577 | 325 | |
| 326 | (* driver for mlton *) | |
| 58039 | 327 | |
| 328 | val mltonN = "MLton" | |
| 329 | val ISABELLE_MLTON = "ISABELLE_MLTON" | |
| 330 | ||
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 331 | fun evaluate_in_mlton (_: Proof.context) (code_files, value_name) dir = | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 332 | let | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 333 | val compiler = mltonN | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 334 | val generatedN = "generated.sml" | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 335 | val driverN = "driver.sml" | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 336 | val projectN = "test" | 
| 58039 | 337 | |
| 72511 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 338 | val code_path = dir + Path.basic generatedN | 
| 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 339 | val driver_path = dir + Path.basic driverN | 
| 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 340 | val basis_path = dir + Path.basic (projectN ^ ".mlb") | 
| 72291 | 341 | val driver = \<^verbatim>\<open> | 
| 72307 | 342 | fun format (true, _) = \<close> ^ ML_Syntax.print_string success ^ \<^verbatim>\<open> ^ "\n" | 
| 343 | | format (false, NONE) = \<close> ^ ML_Syntax.print_string failure ^ \<^verbatim>\<open> ^ "\n" | |
| 344 | | format (false, SOME t) = \<close> ^ ML_Syntax.print_string failure ^ \<^verbatim>\<open> ^ t () ^ "\n" | |
| 72291 | 345 | val result = \<close> ^ value_name ^ \<^verbatim>\<open> () | 
| 72307 | 346 | val _ = print \<close> ^ ML_Syntax.print_string start_marker ^ \<^verbatim>\<open> | 
| 72291 | 347 | val _ = List.app (print o format) result | 
| 72307 | 348 | val _ = print \<close> ^ ML_Syntax.print_string end_marker ^ \<^verbatim>\<open> | 
| 72291 | 349 | \<close> | 
| 72306 | 350 | val _ = check_settings compiler ISABELLE_MLTON "MLton executable" | 
| 351 | val _ = List.app (File.write code_path o snd) code_files | |
| 352 | val _ = File.write driver_path driver | |
| 353 |     val _ = File.write basis_path ("$(SML_LIB)/basis/basis.mlb\n" ^ generatedN ^ "\n" ^ driverN)
 | |
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 354 | in | 
| 76181 
d27ed188e0c4
support for ISABELLE_MLTON_OPTIONS, notably for Ubuntu 22.04 with external mlton executable;
 wenzelm parents: 
75654diff
changeset | 355 | compile compiler | 
| 
d27ed188e0c4
support for ISABELLE_MLTON_OPTIONS, notably for Ubuntu 22.04 with external mlton executable;
 wenzelm parents: 
75654diff
changeset | 356 | (\<^verbatim>\<open>"$ISABELLE_MLTON" $ISABELLE_MLTON_OPTIONS -default-type intinf \<close> ^ | 
| 
d27ed188e0c4
support for ISABELLE_MLTON_OPTIONS, notably for Ubuntu 22.04 with external mlton executable;
 wenzelm parents: 
75654diff
changeset | 357 | File.bash_platform_path basis_path); | 
| 
d27ed188e0c4
support for ISABELLE_MLTON_OPTIONS, notably for Ubuntu 22.04 with external mlton executable;
 wenzelm parents: 
75654diff
changeset | 358 | evaluate compiler (File.bash_platform_path (dir + Path.basic projectN)) | 
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 359 | end | 
| 58039 | 360 | |
| 64577 | 361 | |
| 362 | (* driver for SML/NJ *) | |
| 58039 | 363 | |
| 364 | val smlnjN = "SMLNJ" | |
| 365 | val ISABELLE_SMLNJ = "ISABELLE_SMLNJ" | |
| 366 | ||
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 367 | fun evaluate_in_smlnj (_: Proof.context) (code_files, value_name) dir = | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 368 | let | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 369 | val compiler = smlnjN | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 370 | val generatedN = "generated.sml" | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 371 | val driverN = "driver.sml" | 
| 58039 | 372 | |
| 72511 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 373 | val code_path = dir + Path.basic generatedN | 
| 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 374 | val driver_path = dir + Path.basic driverN | 
| 72291 | 375 | val driver = \<^verbatim>\<open> | 
| 376 | structure Test = | |
| 377 | struct | |
| 378 | fun main () = | |
| 379 | let | |
| 72307 | 380 | fun format (true, _) = \<close> ^ ML_Syntax.print_string success ^ \<^verbatim>\<open> ^ "\n" | 
| 381 | | format (false, NONE) = \<close> ^ ML_Syntax.print_string failure ^ \<^verbatim>\<open> ^ "\n" | |
| 382 | | format (false, SOME t) = \<close> ^ ML_Syntax.print_string failure ^ \<^verbatim>\<open> ^ t () ^ "\n" | |
| 72291 | 383 | val result = \<close> ^ value_name ^ \<^verbatim>\<open> () | 
| 72307 | 384 | val _ = print \<close> ^ ML_Syntax.print_string start_marker ^ \<^verbatim>\<open> | 
| 72291 | 385 | val _ = List.app (print o format) result | 
| 72307 | 386 | val _ = print \<close> ^ ML_Syntax.print_string end_marker ^ \<^verbatim>\<open> | 
| 72291 | 387 | in 0 end | 
| 388 | end | |
| 389 | \<close> | |
| 72306 | 390 | val _ = check_settings compiler ISABELLE_SMLNJ "SMLNJ executable" | 
| 391 | val _ = List.app (File.write code_path o snd) code_files | |
| 392 | val _ = File.write driver_path driver | |
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 393 | val ml_source = | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 394 | "Control.MC.matchRedundantError := false; Control.MC.matchRedundantWarn := false;" ^ | 
| 72277 | 395 | "use " ^ ML_Syntax.print_string (File.platform_path code_path) ^ | 
| 396 | "; use " ^ ML_Syntax.print_string (File.platform_path driver_path) ^ | |
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 397 | "; Test.main ();" | 
| 72306 | 398 |   in evaluate compiler ("echo " ^ Bash.string ml_source ^ " | \"$ISABELLE_SMLNJ\"") end
 | 
| 58039 | 399 | |
| 64577 | 400 | |
| 401 | (* driver for OCaml *) | |
| 58039 | 402 | |
| 403 | val ocamlN = "OCaml" | |
| 69926 
110fff287217
access OCaml tools and libraries via ISABELLE_OCAMLFIND;
 wenzelm parents: 
69925diff
changeset | 404 | val ISABELLE_OCAMLFIND = "ISABELLE_OCAMLFIND" | 
| 58039 | 405 | |
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 406 | fun evaluate_in_ocaml (_: Proof.context) (code_files, value_name) dir = | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 407 | let | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 408 | val compiler = ocamlN | 
| 58039 | 409 | |
| 72511 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 410 | val code_path = dir + Path.basic "generated.ml" | 
| 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 411 | val driver_path = dir + Path.basic "driver.ml" | 
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 412 | val driver = | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 413 | "let format_term = function\n" ^ | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 414 | " | None -> \"\"\n" ^ | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 415 | " | Some t -> t ();;\n" ^ | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 416 | "let format = function\n" ^ | 
| 72307 | 417 | " | (true, _) -> \"" ^ success ^ "\\n\"\n" ^ | 
| 418 | " | (false, x) -> \"" ^ failure ^ "\" ^ format_term x ^ \"\\n\";;\n" ^ | |
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 419 |       "let result = " ^ ("Generated." ^ value_name) ^ " ();;\n" ^
 | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 420 | "let main x =\n" ^ | 
| 72307 | 421 | " let _ = print_string \"" ^ start_marker ^ "\" in\n" ^ | 
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 422 | " let _ = List.map (fun x -> print_string (format x)) result in\n" ^ | 
| 72307 | 423 | " print_string \"" ^ end_marker ^ "\";;\n" ^ | 
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 424 | "main ();;" | 
| 72511 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 425 | val compiled_path = dir + Path.basic "test" | 
| 72306 | 426 | |
| 427 | val _ = check_settings compiler ISABELLE_OCAMLFIND "ocamlfind executable" | |
| 428 | val _ = List.app (File.write code_path o snd) code_files | |
| 429 | val _ = File.write driver_path driver | |
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 430 | val cmd = | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 431 | "\"$ISABELLE_OCAMLFIND\" ocamlopt -w pu -package zarith -linkpkg" ^ | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 432 | " -o " ^ File.bash_path compiled_path ^ " -I " ^ File.bash_path dir ^ " " ^ | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 433 | File.bash_path code_path ^ " " ^ File.bash_path driver_path ^ " </dev/null" | 
| 72306 | 434 | in compile compiler cmd; evaluate compiler (File.bash_path compiled_path) end | 
| 58039 | 435 | |
| 64577 | 436 | |
| 437 | (* driver for GHC *) | |
| 58039 | 438 | |
| 439 | val ghcN = "GHC" | |
| 440 | val ISABELLE_GHC = "ISABELLE_GHC" | |
| 441 | ||
| 69593 | 442 | val ghc_options = Attrib.setup_config_string \<^binding>\<open>code_test_ghc\<close> (K "") | 
| 58039 | 443 | |
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 444 | fun evaluate_in_ghc ctxt (code_files, value_name) dir = | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 445 | let | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 446 | val compiler = ghcN | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 447 | val modules = map fst code_files | 
| 58039 | 448 | |
| 72511 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 449 | val driver_path = dir + Path.basic "Main.hs" | 
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 450 | val driver = | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 451 |       "module Main where {\n" ^
 | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 452 | implode (map (fn module => "import qualified " ^ unsuffix ".hs" module ^ ";\n") modules) ^ | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 453 |       "main = do {\n" ^
 | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 454 |       "    let {\n" ^
 | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 455 | " format_term Nothing = \"\";\n" ^ | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 456 | " format_term (Just t) = t ();\n" ^ | 
| 72307 | 457 | " format (True, _) = \"" ^ success ^ "\\n\";\n" ^ | 
| 458 | " format (False, to) = \"" ^ failure ^ "\" ++ format_term to ++ \"\\n\";\n" ^ | |
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 459 | " result = " ^ value_name ^ " ();\n" ^ | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 460 | " };\n" ^ | 
| 72307 | 461 | " Prelude.putStr \"" ^ start_marker ^ "\";\n" ^ | 
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 462 | " Prelude.mapM_ (putStr . format) result;\n" ^ | 
| 72307 | 463 | " Prelude.putStr \"" ^ end_marker ^ "\";\n" ^ | 
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 464 | " }\n" ^ | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 465 | "}\n" | 
| 58039 | 466 | |
| 72306 | 467 | val _ = check_settings compiler ISABELLE_GHC "GHC executable" | 
| 72511 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 468 | val _ = List.app (fn (name, code) => File.write (dir + Path.basic name) code) code_files | 
| 72306 | 469 | val _ = File.write driver_path driver | 
| 470 | ||
| 72511 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 471 | val compiled_path = dir + Path.basic "test" | 
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 472 | val cmd = | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 473 | "\"$ISABELLE_GHC\" " ^ Code_Haskell.language_params ^ " " ^ | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 474 | Config.get ctxt ghc_options ^ " -o " ^ | 
| 72278 | 475 | File.bash_platform_path compiled_path ^ " " ^ | 
| 476 | File.bash_platform_path driver_path ^ " -i" ^ | |
| 477 | File.bash_platform_path dir | |
| 72306 | 478 | in compile compiler cmd; evaluate compiler (File.bash_path compiled_path) end | 
| 58039 | 479 | |
| 64577 | 480 | |
| 481 | (* driver for Scala *) | |
| 58039 | 482 | |
| 483 | val scalaN = "Scala" | |
| 484 | ||
| 72297 | 485 | fun evaluate_in_scala (_: Proof.context) (code_files, value_name) dir = | 
| 72276 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 486 | let | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 487 | val generatedN = "Generated_Code" | 
| 
dfe150a246e6
misc tuning and clarification: prefer functions over data;
 wenzelm parents: 
72275diff
changeset | 488 | val driverN = "Driver.scala" | 
| 58039 | 489 | |
| 72511 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 490 | val code_path = dir + Path.basic (generatedN ^ ".scala") | 
| 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 491 | val driver_path = dir + Path.basic driverN | 
| 
460d743010bc
clarified signature: overloaded "+" for Path.append;
 wenzelm parents: 
72376diff
changeset | 492 | val out_path = dir + Path.basic "out" | 
| 72297 | 493 | |
| 72306 | 494 | val code = #2 (the_single code_files) | 
| 72296 | 495 | val driver = \<^verbatim>\<open> | 
| 496 | {
 | |
| 497 | val result = \<close> ^ value_name ^ \<^verbatim>\<open>(()) | |
| 498 | val result_text = | |
| 499 | result.map( | |
| 500 |       {
 | |
| 501 | case (true, _) => "True\n" | |
| 502 | case (false, None) => "False\n" | |
| 503 | case (false, Some(t)) => "False" + t(()) + "\n" | |
| 504 | }).mkString | |
| 505 | isabelle.File.write( | |
| 76882 | 506 | isabelle.Path.explode(\<close> ^ quote (File.standard_path out_path) ^ \<^verbatim>\<open>), | 
| 72307 | 507 | \<close> ^ quote start_marker ^ \<^verbatim>\<open> + result_text + \<close> ^ quote end_marker ^ \<^verbatim>\<open>) | 
| 72296 | 508 | }\<close> | 
| 72306 | 509 | val _ = File.write code_path code | 
| 510 | val _ = File.write driver_path driver | |
| 75654 | 511 | val _ = Scala_Compiler.toplevel (code ^ driver) | 
| 72308 | 512 |       handle ERROR msg => error ("Evaluation for " ^ scalaN ^ " failed:\n" ^ msg)
 | 
| 72306 | 513 | in File.read out_path end | 
| 58039 | 514 | |
| 64580 | 515 | |
| 516 | (* command setup *) | |
| 58039 | 517 | |
| 64577 | 518 | val _ = | 
| 69593 | 519 | Outer_Syntax.command \<^command_keyword>\<open>test_code\<close> | 
| 58039 | 520 | "compile test cases to target languages, execute them and report results" | 
| 69593 | 521 | (Scan.repeat1 Parse.prop -- (\<^keyword>\<open>in\<close> |-- Scan.repeat1 Parse.name) | 
| 64580 | 522 | >> (fn (ts, targets) => Toplevel.keep (test_code_cmd ts targets o Toplevel.context_of))) | 
| 58039 | 523 | |
| 66284 
378895354604
new derived targets for evaluating Haskell and Scala programs
 Andreas Lochbihler parents: 
65905diff
changeset | 524 | val target_Scala = "Scala_eval" | 
| 
378895354604
new derived targets for evaluating Haskell and Scala programs
 Andreas Lochbihler parents: 
65905diff
changeset | 525 | val target_Haskell = "Haskell_eval" | 
| 
378895354604
new derived targets for evaluating Haskell and Scala programs
 Andreas Lochbihler parents: 
65905diff
changeset | 526 | |
| 64580 | 527 | val _ = | 
| 72306 | 528 | Theory.setup | 
| 529 | (Code_Target.add_derived_target (target_Scala, [(Code_Scala.target, I)]) #> | |
| 530 | Code_Target.add_derived_target (target_Haskell, [(Code_Haskell.target, I)]) #> | |
| 531 | fold add_driver | |
| 532 | [(polymlN, (evaluate_in_polyml, Code_ML.target_SML)), | |
| 533 | (mltonN, (evaluate_in_mlton, Code_ML.target_SML)), | |
| 534 | (smlnjN, (evaluate_in_smlnj, Code_ML.target_SML)), | |
| 535 | (ocamlN, (evaluate_in_ocaml, Code_ML.target_OCaml)), | |
| 536 | (ghcN, (evaluate_in_ghc, target_Haskell)), | |
| 537 | (scalaN, (evaluate_in_scala, target_Scala))] #> | |
| 538 | fold (fn target => Value_Command.add_evaluator (Binding.name target, eval_term target) #> #2) | |
| 64580 | 539 | [polymlN, mltonN, smlnjN, ocamlN, ghcN, scalaN]) | 
| 59323 | 540 | |
| 58039 | 541 | end |