(************************************************************************) (* * The Rocq Prover / The Rocq Development Team *) (* v * Copyright INRIA, CNRS and contributors *) (* <O___,, * (see version control and CREDITS file for authors & dates) *) (* \VV/ **************************************************************) (* // * This file is distributed under the terms of the *) (* * GNU Lesser General Public License Version 2.1 *) (* * (see LICENSE file for the text of the license) *) (************************************************************************)
open Ideutils open Preferences
let ideslave_rocqtop_flags = ref None
(** * Version *)
let get_version () = try (* the following makes sense only when running with local layout *) let rocqroot = Filename.concat
(Filename.dirname Sys.executable_name)
Filename.parent_dir_name in let ch = open_in (Filename.concat rocqroot "revision") in let ver = input_line ch in let rev = input_line ch in
close_in ch;
Printf.sprintf "%s (%s)" ver rev with _ -> Coq_config.version
let short_version () =
Printf.sprintf "The Coq Proof Assistant, version %s\n" (get_version ())
let version () =
Printf.sprintf "The Coq Proof Assistant, version %s\
\nArchitecture %s running %s operating system\
\nGtk version is %s\
\nThis is %s \n"
(get_version ())
Coq_config.arch Sys.os_type
(let x,y,z = GMain.Main.version in Printf.sprintf "%d.%d.%d" x y z)
(Filename.basename Sys.executable_name)
(** * Initial checks by launching test rocqtop processes *)
let rec read_all_lines in_chan = try let arg = input_line in_chan in let len = String.length arg in let arg = if len > 0 && arg.[len - 1] = '\r'then String.sub arg 0 (len - 1) else arg in
arg::(read_all_lines in_chan) with End_of_file -> []
let rocq_error_popup ~code ~msg data = let callback _ = exit code in let popup = GWindow.dialog () in let button = GButton.button ~stock:`OK ~packing:popup#action_area#add () in let _ = GMisc.label ~text:msg ~packing:popup#vbox#add () in let container = GPack.notebook ~packing:popup#vbox#add () in let iter (label, data) = let scroll = GBin.scrolled_window ~hpolicy:`NEVER ~vpolicy:`AUTOMATIC ~height:500 () in let label = GMisc.label ~text:label () in let text = GText.view ~editable:false ~packing:scroll#add_with_viewport () in let () = text#buffer#set_text data in
ignore (container#append_page ~tab_label:label#coerce scroll#coerce) in let () = List.iter iter data in let _ = popup#connect#destroy ~callback in let _ = button#connect#clicked ~callback in let _ = popup#run () in
callback ()
let display_rocqtop_answer cmd lines =
rocq_error_popup ~code:0 ~msg:"The coqtop process has exited."
[ "Command", cmd; "Answer", String.concat "\n" lines;
]
let rec filter_rocq_opts args = let argstr = String.concat " " (List.map Filename.quote args) in let cmd = Filename.quote (rocqtop_path ()) ^" -q -nois -batch " ^ argstr in let cmd = requote cmd in let filtered_args = ref [] in let errlines = ref [] in try let oc,ic,ec = Unix.open_process_full cmd (Unix.environment ()) in
filtered_args := read_all_lines oc;
errlines := read_all_lines ec; match Unix.close_process_full (oc,ic,ec) with
| Unix.WEXITED 0 -> !filtered_args
| Unix.WEXITED 127 -> asks_for_rocqtop args
| _ -> display_rocqtop_answer cmd (!filtered_args @ !errlines) with Sys_error _ -> asks_for_rocqtop args
| e -> connection_error cmd (!filtered_args @ !errlines) e
and asks_for_rocqtop args = let pb_mes = GWindow.message_dialog
~message:"Failed to load coqidetop. Reset the preference to default?"
~message_type:`QUESTION ~buttons:GWindow.Buttons.yes_no () in match pb_mes#run () with
| `YES -> let () = cmd_rocqtop#set None in let () = custom_rocqtop := None in let () = pb_mes#destroy () in
filter_rocq_opts args
| `DELETE_EVENT | `NO -> let file = select_file_for_open
~title:"coqidetop to execute (edit your preference then)"
~filter:false
~filename:(rocqtop_path ()) () in match file with
| [file] -> let () = custom_rocqtop := Some file in
filter_rocq_opts args
| _ -> exit 0
exception WrongExitStatus ofstring
let print_status = function
| Unix.WEXITED n -> "WEXITED "^string_of_int n
| Unix.WSIGNALED n -> "WSIGNALED "^string_of_int n
| Unix.WSTOPPED n -> "WSTOPPED "^string_of_int n
let check_connection args = let lines = ref [] in let argstr = String.concat " " (List.map Filename.quote args) in let cmd = Filename.quote (rocqtop_path ()) ^ " -batch " ^ argstr in let cmd = requote cmd in try let oc,ic,ec = Unix.open_process_full cmd (Unix.environment ()) in
lines := read_all_lines oc @ read_all_lines ec; match Unix.close_process_full (oc,ic,ec) with
| Unix.WEXITED 0 -> () (* rocqtop seems ok *)
| st -> raise (WrongExitStatus (print_status st)) with e -> connection_error cmd !lines e
(** Useful stuff *)
let ignore_error f arg = try ignore (f arg) with _ -> ()
(** An abstract copy of unit. Thiswillhelpensuringthatwedonotforgettofinallycall
continuations when building tasks in other modules. *)
type void = Void
(** ccb : existential type for a (call + callback) type.
Reference:http://alan.petitepomme.net/cwn/2004.01.13.html
To rewrite someday with GADT. *)
let mk_ccb poly = { open_ccb = fun scope -> scope.bind_ccb poly } let with_ccb ccb e = ccb.open_ccb e
(* overridden on Windows; see file rocqide_WIN32.c.in *) let interrupter = ref (fun pid -> Unix.kill pid Sys.sigint)
(* todo: does not work on windows (sigusr1 not supported) *) let breaker = ref (fun pid -> Unix.kill pid Sys.sigusr1)
(** * The structure describing a rocqtop sub-process *)
module GlibMainLoop = struct type async_chan = Glib.Io.channel type watch_id = Glib.Io.id type condition = Glib.Io.condition let add_watch ~callback chan =
Glib.Io.add_watch ~cond:[`ERR; `HUP; `IN; `NVAL; `PRI] ~callback chan let remove_watch x = try Glib.Io.remove x with Glib.GError _ -> () let read_all = Ideutils.io_read_all let async_chan_of_file_or_socket fd = Glib.Io.channel_of_descr fd end
type rocqtop = { (* non quoted command-line arguments of rocqtop *)
mutable sup_args : stringlist; (* called whenever rocqtop dies *)
mutable reset_handler : unit task; (* called whenever rocqtop sends a feedback message *)
mutable feedback_handler : Feedback.feedback -> unit; (* called when the Ltac debugger sends an input prompt message *)
mutable debug_prompt_handler : tag:string -> Pp.t -> unit; (* actual rocqtop process and its status *)
mutable handle : handle;
mutable status : status;
mutable stopped_in_debugger : bool; (* i.e., RocqIDE has received a prompt message *)
mutable do_when_ready : (unit -> unit) Queue.t; (* for debug msgs only; functions are called when rocqtop is Ready *)
mutable basename : string;
mutable set_script_editable : bool -> unit;
mutable restore_bpts : unit -> unit
}
let return (x : 'a) : 'a task =
(); fun _ k -> k x
let bind (m : 'a task) (f : 'a -> 'b task) : 'b task =
(); fun h k -> m h (fun x -> f x h k)
let seq (m : unit task) (n : 'a task) : 'a task =
(); fun h k -> m h (fun () -> n h k)
let lift (f : unit -> 'a) : 'a task =
(); fun _ k -> k (f ())
(** * Starting / signaling / ending a real rocqtop sub-process *)
(** We simulate a Unix.open_process that also returns the pid of thecreatedprocess.Note:thisusesUnix.create_process,which doesn'tcallbin/sh,soargsshouldn'tbequoted.Theprocess cannotbeterminatedbyaUnix.close_process,butratherbya killofthepid.
let handle_feedback feedback_processor xml = let feedback = Xmlprotocol.to_feedback xml in
feedback_processor feedback
let handle_ltac_debug ltac_debug_processor xml = let tag, msg = Xmlprotocol.to_ltac_debug_answer xml in
ltac_debug_processor ~tag msg
let handle_final_answer handle xml = let () = Minilib.log "Handling rocqtop answer"in let ccb = matchhandle.db_waiting_for, handle.waiting_for with
| None, None -> raise (AnswerWithoutRequest (Xml_printer.to_string_fmt xml))
| Some c, _ -> handle.db_waiting_for <- None; c
| None, Some c -> handle.waiting_for <- None; c in
with_ccb ccb { bind_ccb = fun (c, f) -> f (Xmlprotocol.to_answer c xml) }
type input_state = {
mutable fragment : string;
mutable lexerror : int option;
}
let unsafe_handle_input handle (feedback_processor, ltac_debug_processor) state conds ~read_all =
check_errors conds; let s = read_all () in ifString.length s = 0thenraise (TubeError "EMPTY"); let s = state.fragment ^ s in
state.fragment <- s; let lex = Lexing.from_string s in let p = Xml_parser.make (Xml_parser.SLexbuf lex) in let rec loop () = let xml = Xml_parser.parse ~canonicalize:false p in let l_end = Lexing.lexeme_end lex in
state.fragment <- String.sub s l_end (String.length s - l_end);
state.lexerror <- None; match Xmlprotocol.msg_kind xml with
| Xmlprotocol.Feedback ->
handle_feedback feedback_processor xml;
loop ()
| Xmlprotocol.LtacDebugInfo ->
handle_ltac_debug ltac_debug_processor xml;
loop ()
| Xmlprotocol.Other ->
ignore (handle_final_answer handle xml); (* request completed *) ifhandle.waiting_for <> None then(* i.e., just finished a db request *)
loop () in try loop () with Xml_parser.Error _ as e -> (* Parsing error at the end of s : we have only received a part of
an xml answer. We store the current fragment for later *) let l_end = Lexing.lexeme_end lex in (* Heuristic hack not to reimplement the lexer: if ever the lexer dies
twice at the same place, then this is a non-recoverable error *) if state.lexerror = Some l_end thenraise e;
state.lexerror <- Some l_end
let print_exception = function
| Xml_parser.Error e -> Xml_parser.error e
| Serialize.Marshal_error(expected,actual) -> "Protocol violation. Expected: " ^ expected ^ " Actual: "
^ Xml_printer.to_string actual
| e -> Printexc.to_string e
let input_watch handle respawner processors = let state = { fragment = ""; lexerror = None; } in
(fun conds ~read_all -> let h = handle () in ifnot h.alive thenfalse else try unsafe_handle_input h processors state conds ~read_all; true with e ->
Minilib.log ("Rocqtop reader failed, resetting: "^print_exception e);
respawner (); false)
let bind_self_as f = let me = ref None in let get_me () = Option.get !me in
me := Some(f get_me); Option.get !me
(** This launches a fresh handle from its command line arguments. *) let spawn_handle args respawner processors = let prog = rocqtop_path () in let async_default = (* disable async processing by default in Windows *) ifList.mem Sys.os_type ["Win32"; "Cygwin"] then "off" else "on" in let args = Array.of_list ("--xml_format=Ppcmds" :: "-async-proofs" :: async_default :: args) in let env = match !ideslave_rocqtop_flags with
| None -> None
| Some s -> letopen Str in letopen Array in let opts = split_delim (regexp ",") s in begintry let erex = regexp "^extra-env="in let echunk = List.find (fun s -> string_match erex s 0) opts in
Some (append
(of_list (split_delim (regexp ";") (replace_first erex "" echunk)))
(Unix.environment ())) with Not_found -> None endin
bind_self_as (funhandle -> let proc, oc =
RocqTop.spawn ?env prog args (input_watch handle respawner processors) in
{
proc;
xml_oc = Xml_printer.make (Xml_printer.TChannel oc);
alive = true;
waiting_for = None;
db_waiting_for = None;
})
(** This clears any potentially remaining open garbage. *) let clear_handle h = if h.alive thenbegin (* invalidate the old handle *)
RocqTop.kill h.proc;
ignore(RocqTop.wait h.proc);
h.alive <- false;
h.db_waiting_for <- None; end
let pstatus = function
| Closed -> "Closed"
| Busy -> "Busy"
| Ready -> "Ready"
| New -> "New"
let can_send_db_msg rocqtop =
rocqtop.handle.db_waiting_for = None && match rocqtop.status with
| Busy -> rocqtop.stopped_in_debugger
| Ready -> true
| _ -> false
let add_do_when_ready rocqtop hook = let q = rocqtop.do_when_ready in
Queue.add hook q; ifnot (Queue.is_empty q) && can_send_db_msg rocqtop then let f = Queue.pop q in f ()
let mkready rocqtop db = fun () -> ifnot db thenbegin
rocqtop.status <- Ready;
rocqtop.set_script_editable true end; if rocqtop.status = Ready || (db && can_send_db_msg rocqtop) thenbegin let q = rocqtop.do_when_ready in ifnot (Queue.is_empty q) then let f = Queue.pop q in f () end;
Void
let save_all = ref (fun () -> assert false)
let rec respawn_rocqtop ?(why=Unexpected) rocqtop = let () = match why with
| Unexpected -> let title = "Warning"in let icon = (warn_image ())#coerce in let buttons = ["Reset"; "Save all and quit"; "Quit without saving"] in let ans = GToolbox.question_box ~title ~buttons ~icon (rocqtop.basename ^ ": coqidetop died.") in if ans = 2then (!save_all (); GtkMain.Main.quit ()) elseif ans = 3then GtkMain.Main.quit ()
| Planned -> () in
clear_handle rocqtop.handle;
Queue.clear rocqtop.do_when_ready;
ignore_error (fun () -> let processors = (rocqtop.feedback_handler, rocqtop.debug_prompt_handler) in
rocqtop.handle <-
spawn_handle
rocqtop.sup_args
(fun () -> respawn_rocqtop rocqtop)
processors) (); (* Normally, the handle is now a fresh one.
If not, there isn't much we can do ... *)
assert (rocqtop.handle.alive = true);
rocqtop.status <- New;
rocqtop.set_script_editable true;
ignore (rocqtop.reset_handler rocqtop.handle (mkready rocqtop false));
rocqtop.restore_bpts () (* queue call to restore previous breakpoints *)
let set_restore_bpts rocqtop f = rocqtop.restore_bpts <- f
let set_reset_handler rocqtop hook = rocqtop.reset_handler <- hook
let set_feedback_handler rocqtop hook = rocqtop.feedback_handler <- hook let set_debug_prompt_handler rocqtop hook = rocqtop.debug_prompt_handler <- hook
let setup_script_editable rocqtop f = rocqtop.set_script_editable <- f
let is_computing rocqtop = (rocqtop.status = Busy)
let is_stopped_in_debugger rocqtop = rocqtop.stopped_in_debugger
let is_ready_or_stopped_in_debugger rocqtop =
rocqtop.status = Ready || (is_stopped_in_debugger rocqtop)
let set_stopped_in_debugger rocqtop v =
rocqtop.stopped_in_debugger <- v
(* For closing a rocqtop, we don't try to send it a Quit call anymore, butrathercloseitschannels: -alisteningrocqtopwillhandlethisjustasaQuitcall
- a busy rocqtop will anyway have to be killed *)
let close_rocqtop rocqtop =
rocqtop.status <- Closed;
clear_handle rocqtop.handle
let reset_rocqtop rocqtop = respawn_rocqtop ~why:Planned rocqtop
let get_arguments rocqtop = rocqtop.sup_args
let set_arguments rocqtop args =
rocqtop.sup_args <- args;
reset_rocqtop rocqtop
let process_task ?(db=false) rocqtop task = (* todo: queuing is probably better than assert. *) ifnot (rocqtop.status = Ready || rocqtop.status = New || (db && rocqtop.status = Busy)) then
Printf.printf "Assert failure in process_task rocqtop.status = %s db = %b\n" (pstatus rocqtop.status) db;
assert (rocqtop.status = Ready || rocqtop.status = New || (db && rocqtop.status = Busy)); ifnot db thenbegin
rocqtop.status <- Busy;
rocqtop.set_script_editable false end; try ignore (task rocqtop.handle (mkready rocqtop db)) with e ->
Minilib.log ("Rocqtop writer failed, resetting: " ^ Printexc.to_string e); if rocqtop.status <> Closed then respawn_rocqtop rocqtop
(* todo: logic for functions such as "forward one" should not rely on try_grab todiscardtherequest;asmallamountofqueuingwouldmakethisatrivial
routine, perhaps not even needed. The "abort" should go away. *) let try_grab ?(db=false) rocqtop task abort = match rocqtop.status with
| _ when db && rocqtop.handle.db_waiting_for <> None -> (abort (); false)
| Closed -> abort (); false
| Busy when db -> if(*rocqtop.stopped_in_debugger &&*) rocqtop.handle.db_waiting_for = None then
(process_task ~db rocqtop task; true) else
(abort (); false)
| Busy | New -> abort (); false
| Ready -> process_task ~db rocqtop task; true
(* todo: it's too easy to fail the asserts here when changing the code. Weshouldlookatmakinghandle.waiting_forandhandle.db_waiting_forinto queues,perhapshaveaqueueforthecalltoXml_printer.printwithflow control.Thenrocqtop.do_when_readywouldnotbeneededandother
logic such as mkready and rocqtop initialization might be simplified. *)
let eval_call ?(db=false) call handle k = (* Send messages to rocqtop and prepare the decoding of the answer *) let in_db = if db then"db "else""in
Minilib.log ("Start " ^ in_db ^ "eval_call " ^ Xmlprotocol.pr_call call); if db thenbegin
assert (handle.alive && handle.db_waiting_for = None); handle.db_waiting_for <- Some (mk_ccb (call,k)) endelsebegin
assert (handle.alive && handle.waiting_for = None); handle.waiting_for <- Some (mk_ccb (call,k)) end;
Xml_printer.printhandle.xml_oc (Xmlprotocol.of_call call);
Minilib.log ("End " ^ in_db ^ "eval_call");
Void
let add x = eval_call (Xmlprotocol.add x) let edit_at i = eval_call (Xmlprotocol.edit_at i) let query x = eval_call (Xmlprotocol.query x) let mkcases s = eval_call (Xmlprotocol.mkcases s) let status force = eval_call (Xmlprotocol.status force) let hints x = eval_call (Xmlprotocol.hints x) let search flags = eval_call (Xmlprotocol.search flags) let init x = eval_call (Xmlprotocol.init x) let stop_worker x = eval_call (Xmlprotocol.stop_worker x) let proof_diff x = eval_call (Xmlprotocol.proof_diff x) let db_cmd x = eval_call ~db:true (Xmlprotocol.db_cmd x) let db_upd_bpts x = eval_call ~db:true (Xmlprotocol.db_upd_bpts x) let db_continue x = eval_call ~db:true (Xmlprotocol.db_continue x) let db_stack x = eval_call ~db:true (Xmlprotocol.db_stack x) let db_vars x = eval_call ~db:true (Xmlprotocol.db_vars x) let db_configd x = eval_call ~db:true (Xmlprotocol.db_configd x)
let interrupt_rocqtop rocqtop workers = if rocqtop.status = Busy then try !interrupter (RocqTop.unixpid rocqtop.handle.proc) with _ -> Minilib.log "Error while sending Ctrl-C" else let rec aux = function
| [] -> Void
| w :: ws -> stop_worker w rocqtop.handle (fun _ -> aux ws) in let Void = aux workers in ()
let send_break rocqtop = try
!breaker (RocqTop.unixpid rocqtop.handle.proc) with _ -> Minilib.log "Error while sending Break"
module PrintOpt = struct type _ t =
| BoolOpt : stringlist -> bool t
| StringOpt : stringlist -> string t
let opt_name (type a) : a t -> stringlist = function
| BoolOpt l -> l
| StringOpt l -> l
let opt_data (type a) (key : a t) (v : a) = match key with
| BoolOpt l -> Interface.BoolValue v
| StringOpt l -> Interface.StringValue v
(* Boolean options *)
let implicit = BoolOpt ["Printing"; "Implicit"] let coercions = BoolOpt ["Printing"; "Coercions"] let nested_matching = BoolOpt ["Printing"; "Matching"] let notations = BoolOpt ["Printing"; "Notations"] let parentheses = BoolOpt ["Printing"; "Parentheses"] let all_basic = BoolOpt ["Printing"; "All"] let existential = BoolOpt ["Printing"; "Existential"; "Instances"] let universes = BoolOpt ["Printing"; "Universes"] let unfocused = BoolOpt ["Printing"; "Unfocused"] let goal_names = BoolOpt ["Printing"; "Goal"; "Names"] let record = BoolOpt ["Printing"; "Records"] let synth = BoolOpt ["Printing"; "Synth"] let diff = StringOpt ["Diffs"]
type'a descr = { opts : 'a t list; init : 'a; label : string }
letset (type a) (opt : a t) (v : a) =
Hashtbl.replace current_state (opt_name opt) (opt_data opt v)
let get (type a) (opt : a t) =
Hashtbl.find current_state (opt_name opt)
let reset () = let init_descr d = List.iter (fun o -> set o d.init) d.opts in List.iter init_descr bool_items; List.iter (fun o -> set o diff_item.init) diff_item.opts
let _ = reset ()
let printing_unfocused () = let BoolOpt unfocused = unfocused in match Hashtbl.find current_state unfocused with
| Interface.BoolValue b -> b
| _ -> assert false
(** Transmitting options to rocqtop *)
(* todo: if Rocq hasn't processed any statements since the last enforce, it'sunnecessarytosendanother.Inparticular,"goals"and"evars"
below are generally called one after the other. *) let enforce h k = let mkopt o v acc = (o, v) :: acc in let opts = List.sort (fun a b -> String.compare (String.concat " " (fst a)) (String.concat " " (fst b)))
(Hashtbl.fold mkopt current_state []) in
eval_call (Xmlprotocol.set_options opts) h
(function
| Interface.Good () -> k ()
| _ -> failwith "Cannot set options. Resetting rocqtop")
end
let goals x h k =
PrintOpt.enforce h (fun () -> eval_call (Xmlprotocol.goals x) h k)
let subgoals x h k =
PrintOpt.enforce h (fun () -> eval_call (Xmlprotocol.subgoals x) h k)
let evars x h k =
PrintOpt.enforce h (fun () -> eval_call (Xmlprotocol.evars x) h k)
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-10)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.