|
| 1 | +(* -------------------------------------------------------------------- *) |
| 2 | +open EcUtils |
| 3 | +open EcLocation |
| 4 | +open EcFol |
| 5 | +open EcEnv |
| 6 | +open EcProvers |
| 7 | + |
| 8 | +(* -------------------------------------------------------------------- *) |
| 9 | +module WCall_provers = Why3.Call_provers |
| 10 | +module WConf = Why3.Whyconf |
| 11 | +module WDriver = Why3.Driver |
| 12 | +module WEnv = Why3.Env |
| 13 | +module WTask = Why3.Task |
| 14 | + |
| 15 | +(* -------------------------------------------------------------------- *) |
| 16 | +type coqenv = { |
| 17 | + config : WConf.config; |
| 18 | + main : WConf.main; |
| 19 | + driver : WDriver.driver; |
| 20 | + cnfprv : WConf.config_prover; |
| 21 | + prover : WConf.prover; |
| 22 | +} |
| 23 | + |
| 24 | +(* -------------------------------------------------------------------- *) |
| 25 | +let call_prover_task ~(coqenv : coqenv) (call : WCall_provers.prover_call) = |
| 26 | + EcUtils.try_finally |
| 27 | + (fun () -> |
| 28 | + match (Why3.Call_provers.wait_on_call call).pr_answer with |
| 29 | + | Valid -> Some `Valid |
| 30 | + | Invalid -> Some `Invalid |
| 31 | + | _ -> None) |
| 32 | + (fun () -> |
| 33 | + WCall_provers.interrupt_call ~config:coqenv.main call) |
| 34 | + |
| 35 | +(* -------------------------------------------------------------------- *) |
| 36 | +let run_batch ~(script : string) ~(coqenv : coqenv) (task : WTask.task) = |
| 37 | + let config = Why3.Whyconf.get_main coqenv.config in |
| 38 | + let config_mem = Why3.Whyconf.memlimit config in |
| 39 | + let config_time = Why3.Whyconf.timelimit config in |
| 40 | + let limit = |
| 41 | + Why3.Call_provers.{ |
| 42 | + limit_time = config_time; |
| 43 | + limit_steps = 0; |
| 44 | + limit_mem = config_mem; |
| 45 | + } |
| 46 | + in |
| 47 | + let command = |
| 48 | + Why3.Whyconf.get_complete_command |
| 49 | + coqenv.cnfprv ~with_steps:false in |
| 50 | + let call = |
| 51 | + Why3.Driver.prove_task_prepared |
| 52 | + ~old:script ~inplace:true |
| 53 | + ~command ~limit ~config coqenv.driver task |
| 54 | + in call_prover_task ~coqenv call |
| 55 | + |
| 56 | +(* -------------------------------------------------------------------- *) |
| 57 | +let editor_command ~(coqenv : coqenv) : string = |
| 58 | + try |
| 59 | + let editors = Why3.Whyconf.get_editors coqenv.config in |
| 60 | + let editors = |
| 61 | + Why3.Whyconf.Meditor.filter |
| 62 | + (fun _ a -> a.Why3.Whyconf.editor_name = "Emacs/ProofGeneral/Coq") |
| 63 | + editors |
| 64 | + in |
| 65 | + let _, ed = Why3.Whyconf.Meditor.max_binding editors in |
| 66 | + String.concat " " (ed.editor_command :: ed.editor_options) |
| 67 | + |
| 68 | + with Not_found -> |
| 69 | + Why3.Whyconf.(default_editor (get_main coqenv.config)) |
| 70 | + |
| 71 | +(* -------------------------------------------------------------------- *) |
| 72 | +let script_file ~(name : string located) ~(ext : string) = |
| 73 | + let { pl_loc = loc; pl_desc = name; } = name in |
| 74 | + let file = loc.loc_fname in |
| 75 | + let path = Filename.dirname file in |
| 76 | + let path = |
| 77 | + if Filename.is_relative path then |
| 78 | + Filename.concat (Sys.getcwd ()) path |
| 79 | + else path in |
| 80 | + let path = Filename.concat path ".interactive" in |
| 81 | + let name = |
| 82 | + if String.is_empty name then |
| 83 | + let name = Filename.basename file in |
| 84 | + let name = Filename.remove_extension name in |
| 85 | + let l, r = loc.loc_start in |
| 86 | + Format.sprintf "%s-%d-%d" name l r |
| 87 | + else name |
| 88 | + in |
| 89 | + Format.sprintf "%s/%s%s" path name ext |
| 90 | + |
| 91 | +(* -------------------------------------------------------------------- *) |
| 92 | +let update_script |
| 93 | + ~(script : string) |
| 94 | + ~(coqenv : coqenv) |
| 95 | + (task : WTask.task) |
| 96 | += |
| 97 | + let backup = Format.sprintf "%s~" script in |
| 98 | + Sys.rename script backup; |
| 99 | + |
| 100 | + let old = open_in backup in |
| 101 | + EcUtils.try_finally |
| 102 | + (fun () -> |
| 103 | + IO.pp_to_file ~filename:script |
| 104 | + (fun fmt -> ignore @@ |
| 105 | + Why3.Driver.print_task_prepared ~old coqenv.driver fmt task)) |
| 106 | + (fun () -> close_in old) |
| 107 | + |
| 108 | +(* -------------------------------------------------------------------- *) |
| 109 | +let editor |
| 110 | + ~(script : string) |
| 111 | + ~(merge : bool) |
| 112 | + ~(coqenv : coqenv) |
| 113 | + (task : WTask.task) |
| 114 | += |
| 115 | + if merge then update_script ~script ~coqenv task; |
| 116 | + let command = editor_command ~coqenv in |
| 117 | + let config = WConf.get_main coqenv.config in |
| 118 | + let call = WCall_provers.call_editor ~command ~config script in |
| 119 | + ignore @@ call_prover_task ~coqenv call |
| 120 | + |
| 121 | +(* -------------------------------------------------------------------- *) |
| 122 | +let prepare |
| 123 | + ~(name : string located) |
| 124 | + ~(coqenv : coqenv) |
| 125 | + (task : WTask.task) |
| 126 | += |
| 127 | + let ext = Why3.Driver.file_of_task coqenv.driver "S" "T" task in |
| 128 | + let ext = Filename.extension ext in |
| 129 | + let script = script_file ~name ~ext in |
| 130 | + |
| 131 | + if Sys.file_exists script then |
| 132 | + (script, true) |
| 133 | + else begin |
| 134 | + EcUtils.makedirs (Filename.dirname script); |
| 135 | + EcUtils.IO.pp_to_file ~filename:script |
| 136 | + (fun fmt -> ignore @@ |
| 137 | + Why3.Driver.print_task_prepared coqenv.driver fmt task); |
| 138 | + (script, false) |
| 139 | + end |
| 140 | + |
| 141 | +(* -------------------------------------------------------------------- *) |
| 142 | +let interactive |
| 143 | + ~(name : string located) |
| 144 | + ~(coqmode : coq_mode) |
| 145 | + ~(coqenv : coqenv) |
| 146 | + (task : WTask.task) |
| 147 | += |
| 148 | + let script, merge = prepare ~name ~coqenv task in |
| 149 | + |
| 150 | + if merge then |
| 151 | + update_script ~script ~coqenv task; |
| 152 | + match coqmode with |
| 153 | + | Check -> |
| 154 | + run_batch ~script ~coqenv task |
| 155 | + |
| 156 | + | Edit -> |
| 157 | + editor ~script ~merge ~coqenv task; |
| 158 | + run_batch ~script ~coqenv task |
| 159 | + |
| 160 | + | Fix -> begin |
| 161 | + match run_batch ~script ~coqenv task with |
| 162 | + | Some `Valid as answer -> |
| 163 | + answer |
| 164 | + | _ -> |
| 165 | + editor ~script ~merge ~coqenv task; |
| 166 | + run_batch ~script ~coqenv task |
| 167 | + end |
| 168 | + |
| 169 | +(* -------------------------------------------------------------------- *) |
| 170 | +let is_trivial (t : Why3.Task.task) = |
| 171 | + let goal = Why3.Task.task_goal_fmla t in |
| 172 | + Why3.Term.t_equal goal Why3.Term.t_true |
| 173 | + |
| 174 | +(* -------------------------------------------------------------------- *) |
| 175 | +let build_proof_task |
| 176 | + ~(notify : notify option) |
| 177 | + ~(name : string located) |
| 178 | + ~(coqmode : coq_mode) |
| 179 | + ~(config : WConf.config) |
| 180 | + ~(env : WEnv.env) |
| 181 | + (task : WTask.task) |
| 182 | += |
| 183 | + let exception CoqNotFound in |
| 184 | + |
| 185 | + try |
| 186 | + let coqenv = |
| 187 | + let (prover, cnfprv) = |
| 188 | + let fp = Why3.Whyconf.parse_filter_prover "Coq" in |
| 189 | + let provers = Why3.Whyconf.filter_provers config fp in |
| 190 | + begin |
| 191 | + match Why3.Whyconf.Mprover.is_empty provers with |
| 192 | + | false -> Why3.Whyconf.Mprover.max_binding provers |
| 193 | + | true -> raise CoqNotFound |
| 194 | + end |
| 195 | + in |
| 196 | + let main = Why3.Whyconf.get_main config in |
| 197 | + let driver = |
| 198 | + Why3.Driver.load_driver_for_prover |
| 199 | + main env cnfprv |
| 200 | + in { config; main; driver; cnfprv; prover; } in |
| 201 | + |
| 202 | + let task = Why3.Driver.prepare_task coqenv.driver task in |
| 203 | + |
| 204 | + if is_trivial task then |
| 205 | + Some `Valid |
| 206 | + else |
| 207 | + interactive ~name ~coqmode ~coqenv task |
| 208 | + |
| 209 | + with |
| 210 | + | CoqNotFound -> |
| 211 | + notify |> oiter (fun notify -> notify `Critical (lazy ( |
| 212 | + Format.asprintf "Prover Coq not installed or not configured" |
| 213 | + ))); |
| 214 | + None |
| 215 | + |
| 216 | + | exn -> |
| 217 | + notify |> oiter (fun notify -> notify `Critical (lazy ( |
| 218 | + Format.asprintf "[Why3 Error] %a" Why3.Exn_printer.exn_printer exn |
| 219 | + ))); |
| 220 | + None |
| 221 | + |
| 222 | +(* -------------------------------------------------------------------- *) |
| 223 | +let check |
| 224 | + ~(loc : EcLocation.t) |
| 225 | + ~(name : string) |
| 226 | + ?(notify : notify option) |
| 227 | + (pi : prover_infos) |
| 228 | + ?(coqmode : coq_mode = Edit) |
| 229 | + (hyps : LDecl.hyps) |
| 230 | + (concl : form) |
| 231 | += |
| 232 | + EcProvers.maybe_start_why3_server pi; |
| 233 | + |
| 234 | + let config = EcProvers.get_w3_conf () in |
| 235 | + let env = EcProvers.get_w3_env () in |
| 236 | + let ec_env, hyps, tenv, decl = EcSmt.init hyps concl in |
| 237 | + |
| 238 | + let execute_task toadd = |
| 239 | + let task = EcSmt.make_task tenv toadd decl in |
| 240 | + let result = |
| 241 | + build_proof_task ~notify ~name:(mk_loc loc name) ~coqmode ~config ~env task in |
| 242 | + Option.map (fun r -> r = `Valid) result |
| 243 | + in EcSmt.select ec_env pi hyps concl execute_task |
0 commit comments