diff options
author | 1998-10-22 14:28:12 +0000 | |
---|---|---|
committer | 1998-10-22 14:28:12 +0000 | |
commit | c7a9508798891c67346ee7a6341922286248466a (patch) | |
tree | f483b9d6924d2e9ca1970456bef591c7b1ea8e6f /generic | |
parent | 18021e2580cd8e667bf40a850bc2b30b3c54e0cf (diff) |
Split proof.el into proof-config.el, proof-script.el, proof-shell.el
Diffstat (limited to 'generic')
-rw-r--r-- | generic/proof-config.el | 483 | ||||
-rw-r--r-- | generic/proof-script.el | 1423 | ||||
-rw-r--r-- | generic/proof-shell.el | 951 | ||||
-rw-r--r-- | generic/proof.el | 2849 |
4 files changed, 2905 insertions, 2801 deletions
diff --git a/generic/proof-config.el b/generic/proof-config.el new file mode 100644 index 00000000..f5fb761d --- /dev/null +++ b/generic/proof-config.el @@ -0,0 +1,483 @@ +;; proof-config.el Proof General configuration for proof assistant. +;; +;; Copyright (C) 1994 - 1998 LFCS Edinburgh. +;; Authors: David Aspinall, Yves Bertot, Healfdene Goguen, +;; Thomas Kleymann and Dilip Sequeira +;; +;; Maintainer: Proof General maintainer <proofgen@dcs.ed.ac.uk> +;; +;; $Id$ +;; +;; +;; This file declares all prover-specific configuration variables. +;; These are used variously by the proof script mode and the proof +;; shell mode. +;; +;; To customize Proof General for a new proof assistant, you +;; should read this file carefully! +;; +;; 0. Major modes +;; 1. Menus, user-level commands. +;; 2. Script mode configuration +;; 3. Shell mode configuration +;; 3a. commands +;; 3b. regexps +;; 3c. hooks +;; + + +(defgroup prover-config nil + "Configuration of Proof General for the prover in use." + :group 'proof-internal) + +;; The variables in the "prover-config" (NB: not "proof config"!!) +;; customize group are those which are intended to be set by the +;; prover specific elisp, i.e. constants set on a per-prover basis. + +;; Putting these in a customize group is useful for documenting +;; this type of variable, and for developing a new instantiation +;; of Proof General. +;; But it is *not* useful for final user-level customization! +;; The reason is that saving these customizations across a session is +;; not liable to work, because the prover specific elisp usually +;; overrides with a series of setq's in <assistant>-mode-config type +;; functions. This is why prover-config appears under the +;; proof-internal group. + +;; Note: The XEmacs customization menus would be nicer if the +;; variables in prover-config group were uniformly renamed +;; prover-config-* (and similarly for other variables/groups). +;; But it's somewhat of a horror in the code! + + +(defcustom proof-assistant "" + "Name of the proof assistant Proof General is using. +This is set automatically by the mode stub defined in proof-site, +from the name given in proof-internal-assistant-table." + :type 'string + :group 'prover-config) + + + + +;; +;; 0. The major modes used by Proof General. +;; + +(defcustom proof-mode-for-shell nil + "Mode for proof shell buffers. +Suggestion: this can be set in proof-pre-shell-start-hook." + :type 'function + :group 'prover-config) + +(defcustom proof-mode-for-pbp nil + "Mode for proof state display buffers. +Suggestion: this can be set in proof-pre-shell-start-hook." + :type 'function + :group 'prover-config) + +(defcustom proof-mode-for-script nil + "Major mode for proof script buffers." + :type 'function + :group 'prover-config) + + + + + +;; +;; 1. Configuration for menus, user-level commands, etc. +;; + +(defcustom proof-www-home-page "" + "Web address for information on proof assistant" + :type 'string + :group 'prover-config) + +(defcustom proof-ctxt-string "" + "*Command to display the context in proof assistant." + :type 'string + :group 'proof) + +(defcustom proof-help-string "" + "*Command to ask for help in proof assistant." + :type 'string + :group 'proof) + +(defcustom proof-prf-string "" + "Command to display proof state in proof assistant." + :type 'string + :group 'prover-) + +(defcustom proof-goal-command nil + "Command to set a goal in the proof assistant. String or fn. +If a string, the format character %s will be replaced by the +goal string. If a function, should return a command string to +insert when called interactively." + :type '(choice string function) + :group 'prover-config) + +(defcustom proof-save-command nil + "Command to save a proved theorem in the proof assistant. String or fn. +If a string, the format character %s will be replaced by the +theorem name. If a function, should return a command string to +insert when called interactively." + :type '(choice string function) + :group 'prover-config) + + +;; FIXME: allow this to be set in the mode fn instead. +(defcustom proof-tags-support t + "If non-nil, include tags functions in menus. +This variable should be set before requiring proof.el" + :type 'boolean + :group 'prover-config) + + + +;; +;; 2. Configuration for proof script mode +;; + +;; +;; The following variables should be set before proof-config-done +;; is called. These configure the mode for the script buffer, +;; including highlighting, etc. +;; + +(defgroup proof-script nil + "Proof General configuration of scripting buffer mode." + :group 'prover-config) + + +(defcustom proof-terminal-char nil + "Character which terminates a proof command in a script buffer." + :type 'character + :group 'proof-script) + +(defcustom proof-comment-start "" + "String which starts a comment in the proof assistant command language. +The script buffer's comment-start is set to this string plus a space." + :type 'string + :group 'proof-script) + +(defcustom proof-comment-end "" + "String which starts a comment in the proof assistant command language. +The script buffer's comment-end is set to this string plus a space." + :type 'string + :group 'proof-script) + +(defcustom proof-save-command-regexp nil + "Matches a save command" + :type 'regexp + :group 'prover-config) + +(defcustom proof-save-with-hole-regexp nil + "Matches a named save command" + :type 'regexp + :group 'proof-script) + +(defcustom proof-goal-with-hole-regexp nil + "Matches a saved goal command" + :type 'regexp + :group 'proof-script) + +(defcustom proof-goal-command-p nil + "Is this a goal" + :type 'function + :group 'proof-script) + +(defcustom proof-lift-global nil + "This function lifts local lemmas from inside goals out to top level. +This function takes the local goalsave span as an argument. Set this +to `nil' of the proof assistant does not support nested goals." + :type '(choice nil function) + :group 'proof-script) + +(defcustom proof-count-undos-fn nil + "Compute number of undos in a target segment" + :type 'function + :group 'proof-script) + +(defcustom proof-atomic-sequence-lists nil + "list of instructions for setting up atomic sequences of commands (ACS). + +Each instruction is +a list of the form `(END START &optional FORGET-COMMAND)'. END is a +regular expression to recognise the last command in an ACS. START +is a function. Its input is the last command of an ACS. Its output +is a regular exression to recognise the first command of the ACS. +It is evaluated once and the output is successively matched agains +previously processed commands until a match occurs (or the +beginning of the current buffer is reached). The region determined +by (START,END) is locked as an ACS. Optionally, the ACS is +annotated with the actual command to retract the ACS. This is +computed by applying FORGET-COMMAND to the first and last command +of the ACS." + :type '(repeat (cons regexp function (choice (const nil) function))) + :group 'proof-shell) + + +(defconst proof-no-command "COMMENT" + "String used as a nullary action (send no command to the proof assistant). +Only relevant for proof-find-and-forget-fn.") + +(defcustom proof-find-and-forget-fn nil + "Function returning a command string to forget back to before its argument span. +The special string proof-no-command means there is nothing to do." + :type 'function + :group 'proof-script) + +(defcustom proof-goal-hyp-fn nil + "A function which returns cons cell if point is at a goal/hypothesis. +First element of cell is a symbol, 'goal' or 'hyp'. The second +element is a string: the goal or hypothesis itself. This is used +when parsing the proofstate output" + :type 'function + :group 'proof-script) + +(defcustom proof-kill-goal-command "" + "Command to kill a goal." + :type 'string + :group 'proof-script) + +(defcustom proof-global-p nil + "Whether a command is a global declaration. Predicate on strings or nil. +This is used to handle nested goals allowed by some provers." + :type 'function + :group 'proof-script) + +(defcustom proof-state-preserving-p nil + "A predicate, non-nil if its argument preserves the proof state." + :type 'function + :group 'proof-script) + +(defcustom pbp-change-goal nil + "Command to change to the goal %s" + :type 'string + :group 'prover-config) + +(defcustom proof-shell-process-output-system-specific nil + "Set this variable to handle system specific output. +Errors, start of proofs, abortions of proofs and completions of +proofs are recognised in the function `proof-shell-process-output'. +All other output from the proof engine is simply reported to the +user in the RESPONSE buffer. + +To catch further special cases, set this variable to a pair of +functions '(condf . actf). Both are given (cmd string) as arguments. +`cmd' is a string containing the currently processed command. +`string' is the response from the proof system. To change the +behaviour of `proof-shell-process-output', (condf cmd string) must +return a non-nil value. Then (actf cmd string) is invoked. See the +documentation of `proof-shell-process-output' for the required +output format." + :type '(cons (function function)) + :group 'prover-config) + +(defcustom proof-activate-scripting-hook nil + "Hook run when a buffer is switched into scripting mode. +The current buffer will be the newly active scripting buffer. + +This hook may be useful for synchronizing with the proof +assistant, for example, to switch to a new theory." + :type '(repeat function) + :group 'prover-config) + + + + + +;; +;; 3. Configuration for proof shell +;; +;; The variables in this section concern the proof shell mode. +;; The first group of variables are hooks invoked at various points. +;; The second group of variables are concerned with matching the output +;; from the proof assistant. +;; +;; Variables here are put into the customize group 'proof-shell'. +;; +;; These should be set in the shell mode configuration, again, +;; before proof-shell-config-done is called. +;; + +(defgroup proof-shell nil + "Settings for output from the proof assistant in the proof shell." + :group 'prover-config) + + +;; +;; 3a. commands +;; + +(defcustom proof-prog-name nil + "System command to run program name in proof shell. +Suggestion: this can be set in proof-pre-shell-start-hook." + :type 'string + :group 'proof-shell) + +(defcustom proof-shell-init-cmd "" + "The command for initially configuring the proof process." + :type '(choice string (const nil)) + :group 'proof-shell) + +(defcustom proof-shell-cd nil + "Command to the proof assistant to change the working directory." + :type 'string + :group 'proof-shell) + + +;; +;; 3b. Regexp variables for matching output from proof process. +;; + +(defcustom proof-shell-prompt-pattern nil + "Proof shell's value for comint-prompt-pattern, which see." + :type 'regexp + :group 'proof-shell) + +;; FIXME da: replace this with wakeup-regexp or prompt-regexp? +;; May not need next variable. +(defcustom proof-shell-wakeup-char nil + "A special character which terminates an annotated prompt. +Set to nil if proof assistant does not support annotated prompts." + :type '(choice character (const nil)) + :group 'proof-shell) + +(defcustom proof-shell-annotated-prompt-regexp "" + "Regexp matching a (possibly annotated) prompt pattern. +Output is grabbed between pairs of lines matching this regexp. +To help matching you may be able to annotate the proof assistant +prompt with a special character not appearing in ordinary output. +The special character should appear in this regexp, should +be the value of proof-shell-wakeup-char." + :type 'regexp + :group 'proof-shell) + +(defcustom proof-shell-abort-goal-regexp nil + "Regexp matching output from an aborted proof." + :type 'regexp + :group 'proof-shell) + +(defcustom proof-shell-error-regexp nil + "Regexp matching an error report from the proof assistant. +We assume that an error message corresponds to a failure +in the last proof command executed. (So don't match +mere warning messages with this regexp)." + :type 'regexp + :group 'proof-shell) + +(defcustom proof-shell-interrupt-regexp nil + "Regexp matching output indicating the assistant was interrupted." + :type 'regexp + :group 'proof-shell) + +(defcustom proof-shell-proof-completed-regexp nil + "Regexp matching output indicating a finished proof." + :type 'regexp + :group 'proof-shell) + +(defcustom proof-shell-result-start "" + "Regexp matching start of an output from the prover after pbp commands. +In particular, after a `pbp-goal-command' or a `pbp-hyp-command'." + :type 'regexp + :group 'proof-shell) + +(defcustom proof-shell-result-end "" + "Regexp matching end of output from the prover after pbp commands. +In particular, after a `pbp-goal-command' or a `pbp-hyp-command'." + :type 'regexp + :group 'proof-shell) + +(defcustom proof-shell-start-goals-regexp "" + "Regexp matching the start of the proof state output." + :type 'regexp + :group 'proof-shell) + +(defcustom proof-shell-end-goals-regexp "" + "Regexp matching the end of the proof state output." + :type 'regexp + :group 'proof-shell) + +(defcustom pbp-error-regexp nil + "Regexp indicating that the proof process has identified an error." + :type 'regexp + :group 'proof-shell) + +;; +;; 3c. hooks +;; + +(defcustom proof-shell-insert-hook nil + "Hooks run by proof-shell-insert before inserting a command. +Can be used to configure the proof assistant to the interface in +various ways (for example, setting the line width of output). +Any text inserted by these hooks will be sent to the proof process +before every command issued by Proof General." + :type '(repeat function) + :group 'proof-shell) + +(defcustom proof-pre-shell-start-hook nil + "Hooks run before proof shell is started. +Usually this is set to a function which configures the proof shell +variables." + :type '(repeat function) + :group 'proof-shell) + +(defcustom proof-shell-handle-delayed-output-hook + '(proof-pbp-focus-on-first-goal) + "Hooks run after new output has been displayed in the RESPONSE buffer." + :type '(repeat function) + :group 'proof-shell) + +(defcustom proof-shell-handle-error-hook + '(proof-goto-end-of-locked-if-pos-not-visible-in-window) + "Hooks run after an error has been reported in the RESPONSE buffer." + :type '(repeat function) + :group 'proof-shell) + +(defcustom proof-shell-process-file nil + "A tuple of the form (regexp . function) to match a processed file name. + +If REGEXP matches output, then the function FUNCTION is invoked on the +output string chunk. It must return a script file name (with complete +path) the system is currently processing. In practice, FUNCTION is +likely to inspect the match data. If it returns the empty string, +the file name of the scripting buffer is used instead. If it +returns nil, no action is taken. + +Care has to be taken in case the prover only reports on compiled +versions of files it is processing. In this case, FUNCTION needs to +reconstruct the corresponding script file name. The new (true) file +name is added to the front of `proof-included-files-list'." + :type '(choice (cons regexp function) (const nil)) + :group 'proof-shell) + +(defcustom proof-shell-retract-files-regexp nil + "A REGEXP to match that the prover has retracted across file boundaries. + +At this stage, Proof General's view of the processed files is out of +date and needs to be updated with the help of the function +`proof-shell-compute-new-files-list'." + :type '(choice regexp (const nil)) + :group 'proof-shell) + +(defcustom proof-shell-compute-new-files-list nil + "Function to update `proof-included-files list'. + +It needs to return an up to date list of all processed files. Its +output is stored in `proof-included-files-list'. Its input is the +string of which `proof-shell-retract-files-regexp' matched a +substring. In practice, this function is likely to inspect the +previous (global) variable `proof-included-files-list' and the match +data triggered by `proof-shell-retract-files-regexp'." + :type '(choice function (const nil)) + :group 'proof-shell) + + + + + +;; End of proof-config.el +(provide 'proof-config) diff --git a/generic/proof-script.el b/generic/proof-script.el new file mode 100644 index 00000000..453b18db --- /dev/null +++ b/generic/proof-script.el @@ -0,0 +1,1423 @@ +;; proof-script.el Major mode for proof assistant script files. +;; +;; Copyright (C) 1994 - 1998 LFCS Edinburgh. +;; Authors: David Aspinall, Yves Bertot, Healfdene Goguen, +;; Thomas Kleymann and Dilip Sequeira +;; +;; Maintainer: Proof General maintainer <proofgen@dcs.ed.ac.uk> +;; +;; $Id$ +;; + +;; +;; Internal variables used by script mode +;; + +;; FIXME da: remove proof-terminal-string. At the moment some +;; commands need to have the terminal string, some don't. +;; We should assume commands are terminated properly at the +;; specific level. +(defvar proof-terminal-string nil + "End-of-line string for proof process.") + +(defvar proof-re-end-of-cmd nil + "Regexp matching end of command. Based on proof-terminal-string. +Set in proof-config-done.") + +(defvar proof-re-term-or-comment nil + "Regexp matching terminator, comment start, or comment end. +Set in proof-config-done.") + +(defvar proof-marker nil + "Marker in proof shell buffer pointing to previous command input.") + +(defvar proof-shell-buffer nil + "Process buffer where the proof assistant is run.") + +(defvar proof-script-buffer-list nil + "Scripting buffers with locked regions. +The first element is current and in scripting minor mode. +The cdr of the list of corresponding file names is a subset of +`proof-included-files-list'.") + +(defvar proof-pbp-buffer nil + "The goals buffer (also known as the pbp buffer).") + +(defvar proof-response-buffer nil + "The response buffer.") + +(defvar proof-shell-busy nil + "A lock indicating that the proof shell is processing. +When this is non-nil, proof-check-process-available will give +an error.") + +(deflocal proof-buffer-type nil + "Symbol indicating the type of this buffer: 'script, 'shell, or 'pbp.") + +(defvar proof-action-list nil "action list") + +(defvar proof-included-files-list nil + "List of files currently included in proof process. +Whenever a new file is being processed, it gets added to the front of +the list. When the prover retracts across file boundaries, this list +is resynchronised. It contains files in canonical truename format") + +(deflocal proof-active-buffer-fake-minor-mode nil + "An indication in the modeline that this is the *active* script buffer") + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; A few small utilities ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun proof-match-find-next-function-name (buffer) + "General next function name in BUFFER finder using match. +The regexp is assumed to be a two item list the car of which is the regexp +to use, and the cdr of which is the match position of the function +name. Moves point after the match. + +The package fume-func provides the function +`fume-match-find-next-function-name' with the same specification. +However, fume-func's version is incorrec" + ;; DO NOT USE save-excursion; we need to move point! + ;; save-excursion is called at a higher level in the func-menu + ;; package + (set-buffer buffer) + (let ((r (car fume-function-name-regexp)) + (p (cdr fume-function-name-regexp))) + (and (re-search-forward r nil t) + (cons (buffer-substring (setq p (match-beginning p)) (point)) p)))) + +(defun proof-message (str) + "Output STR in minibuffer." + (message (concat "[" proof-assistant "] " str))) + +;; append-element in tl-list +(defun proof-append-element (ls elt) + "Append ELT to last of LS if ELT is not nil. [proof.el] +This function coincides with `append-element' in the package +[tl-list.el.]" + (if elt + (append ls (list elt)) + ls)) + +(defun proof-define-keys (map kbl) + "Adds keybindings KBL in MAP. +The argument KBL is a list of tuples (k . f) where `k' is a keybinding +(vector) and `f' the designated function." + (mapcar + (lambda (kbl) + (let ((k (car kbl)) (f (cdr kbl))) + (define-key map k f))) + kbl)) + +(defun proof-string-to-list (s separator) + "Return the list of words in S separated by SEPARATOR." + (let ((end-of-word-occurence (string-match (concat separator "+") s))) + (if (not end-of-word-occurence) + (if (string= s "") + nil + (list s)) + (cons (substring s 0 end-of-word-occurence) + (proof-string-to-list + (substring s + (string-match (concat "[^" separator "]") + s end-of-word-occurence)) separator))))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Basic code for the locked region and the queue region ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; FIXME: da: doc below needs fixing. +(defvar proof-locked-hwm nil + "Upper limit of the locked region") + +(defvar proof-queue-loose-end nil + "Limit of the queue region that is not equal to proof-locked-hwm.") + +(defvar proof-locked-span nil + "Upper limit of the locked region") + +(defvar proof-queue-span nil + "Upper limit of the locked region") + +(make-variable-buffer-local 'proof-locked-span) +(make-variable-buffer-local 'proof-queue-span) + +(defface proof-queue-face + '((((type x) (class color) (background light)) + (:background "mistyrose")) + (((type x) (class color) (background dark)) + (:background "mediumvioletred")) + (t + (:foreground "white" :background "black"))) + "Face for commands in proof script waiting to be processed." + :group 'proof) + +(defface proof-locked-face + '((((type x) (class color) (background light)) + (:background "lavender")) + (((type x) (class color) (background dark)) + (:background "navy")) + (t + (:underline t))) + "Face for locked region of proof script (processed commands)." + :group 'proof) + +(defun proof-init-segmentation () + (setq proof-queue-loose-end nil) + (if (not proof-queue-span) + (setq proof-queue-span (make-span 1 1))) + (set-span-property proof-queue-span 'start-closed t) + (set-span-property proof-queue-span 'end-open t) + (span-read-only proof-queue-span) + + (set-span-property proof-queue-span 'face 'proof-queue-face) + + (detach-span proof-queue-span) + + (setq proof-locked-hwm nil) + (if (not proof-locked-span) + (setq proof-locked-span (make-span 1 1))) + (set-span-property proof-locked-span 'start-closed t) + (set-span-property proof-locked-span 'end-open t) + (span-read-only proof-locked-span) + + (set-span-property proof-locked-span 'face 'proof-locked-face) + + (detach-span proof-locked-span)) + +(defsubst proof-lock-unlocked () + "Make the locked region read only." + (span-read-only proof-locked-span)) + +(defsubst proof-unlock-locked () + "Make the locked region read-write." + (span-read-write proof-locked-span)) + +(defsubst proof-set-queue-endpoints (start end) + "Set the queue span to be START, END." + (set-span-endpoints proof-queue-span start end)) + +(defsubst proof-set-locked-endpoints (start end) + "Set the locked span to be START, END." + (set-span-endpoints proof-locked-span start end)) + +(defsubst proof-detach-queue () + "Remove the span for the queue region." + (and proof-queue-span (detach-span proof-queue-span))) + +(defsubst proof-detach-locked () + "Remove the span for the locked region." + (and proof-locked-span (detach-span proof-locked-span))) + +(defsubst proof-set-queue-start (start) + "Set the queue span to begin at START." + (set-span-start proof-queue-span start)) + +(defsubst proof-set-queue-end (end) + "Set the queue span to end at END." + (set-span-end proof-queue-span end)) + +(defun proof-detach-segments (&optional buffer) + "Remove locked and queue region from BUFFER. +Defaults to current buffer when BUFFER is nil." + (let ((buffer (or buffer (current-buffer)))) + (save-excursion + (proof-detach-queue) + (proof-detach-locked)))) + +(defsubst proof-set-locked-end (end) + "Set the end of the locked region to be END, sort of? FIXME. +If END is past the end of the buffer, remove the locked region. +Otherwise set the locked region to be from the start of the +buffer to END." + (if (>= (point-min) end) + (proof-detach-locked) + (set-span-endpoints proof-locked-span (point-min) end))) + +(defun proof-unprocessed-begin () + "Return end of locked region in current buffer or (point-min) otherwise." + (or + (and (member (current-buffer) proof-script-buffer-list) + proof-locked-span (span-end proof-locked-span)) + (point-min))) + +(defun proof-locked-end () + "Return end of the locked region of the current buffer. +Only call this from a scripting buffer." + (if (member (current-buffer) proof-script-buffer-list) + (proof-unprocessed-begin) + (error "bug: proof-locked-end called from wrong buffer"))) + +(defsubst proof-end-of-queue () + "Return the end of the queue region, or nil if none." + (and proof-queue-span (span-end proof-queue-span))) + +(defun proof-dont-show-annotations () + "Set display values of annotations (characters 128-255) to be invisible." + (let ((disp (make-display-table)) + (i 128)) + (while (< i 256) + (aset disp i []) + (incf i)) + (cond ((fboundp 'add-spec-to-specifier) + (add-spec-to-specifier current-display-table disp + (current-buffer))) + ((boundp 'buffer-display-table) + (setq buffer-display-table disp))))) + +(defun proof-mark-buffer-atomic (buffer) + "Mark BUFFER as having been processed in a single step. + +If buffer already contains a locked region, only the remainder of the +buffer is closed off atomically." + (save-excursion + (set-buffer buffer) + (let ((span (make-span (proof-unprocessed-begin) (point-max))) + cmd) + ;; compute first command of buffer + (goto-char (point-min)) + (proof-find-next-terminator) + (let ((cmd-list (member-if + (lambda (entry) (equal (car entry) 'cmd)) + (proof-segment-up-to (point))))) + (proof-init-segmentation) + (if cmd-list + (progn + (setq cmd (second (car cmd-list))) + (set-span-property span 'type 'vanilla) + (set-span-property span 'cmd cmd)) + ;; If there was no command in the buffer, atomic + ;; span becomes a comment. + (set-span-property span 'type 'comment))) + (proof-set-locked-end (point-max)) + (or (member buffer proof-script-buffer-list) + (setq proof-script-buffer-list + (append proof-script-buffer-list (list buffer))))))) + +(defun proof-file-truename (filename) + "Returns the true name of the file FILENAME or nil." + (and filename (file-exists-p filename) (file-truename filename))) + +(defun proof-register-possibly-new-processed-file (file) + "Register a possibly new FILE as having been processed by the prover." + (let* ((cfile (file-truename file)) + (buffer (proof-file-to-buffer cfile))) + (if (not (member cfile proof-included-files-list)) + (progn + (setq proof-included-files-list + (cons cfile proof-included-files-list)) + (or (equal cfile + (file-truename (buffer-file-name + (car proof-script-buffer-list)))) + (not buffer) + (proof-mark-buffer-atomic buffer)))))) + + + + +;;; begin messy COMPATIBILITY HACKING for FSFmacs. +;;; +;;; In case Emacs is not aware of the function read-shell-command, +;;; and read-shell-command-map, we duplicate some code adjusted from +;;; minibuf.el distributed with XEmacs 20.4. +;;; +;;; This code is still required as of FSF Emacs 20.2. +;;; +;;; I think bothering with this just to give completion for +;;; when proof-prog-name-ask-p=t is a big overkill! - da. +;;; +(defvar read-shell-command-map + (let ((map (make-sparse-keymap 'read-shell-command-map))) + (if (not (fboundp 'set-keymap-parents)) + (if (fboundp 'set-keymap-parent) + ;; FSF Emacs 20.2 + (set-keymap-parent map minibuffer-local-map) + ;; Earlier FSF Emacs + (setq map (append minibuffer-local-map map)) + ;; XEmacs versions? + (set-keymap-parents map minibuffer-local-map))) + (define-key map "\t" 'comint-dynamic-complete) + (define-key map "\M-\t" 'comint-dynamic-complete) + (define-key map "\M-?" 'comint-dynamic-list-completions) + map) + "Minibuffer keymap used by shell-command and related commands.") + +(or (fboundp 'read-shell-command) + (defun read-shell-command (prompt &optional initial-input history) + "Just like read-string, but uses read-shell-command-map: +\\{read-shell-command-map}" + (let ((minibuffer-completion-table nil)) + (read-from-minibuffer prompt initial-input read-shell-command-map + nil (or history + 'shell-command-history))))) + + +;;; end messy COMPATIBILITY HACKING + + +;; +;; Misc movement functions +;; + +;; FIXME da: these next two functions slightly different, why? +;; (unprocessed-begin vs proof-locked-end) +(defun proof-goto-end-of-locked-interactive () + "Jump to the end of the locked region." + (interactive) + (switch-to-buffer (car proof-script-buffer-list)) + (goto-char (proof-locked-end))) + +(defun proof-goto-end-of-locked () + "Jump to the end of the locked region." + (goto-char (proof-unprocessed-begin))) + +(defun proof-in-locked-region-p () + "Non-nil if point is in locked region. Assumes proof script buffer current." + (< (point) (proof-locked-end))) + +(defun proof-goto-end-of-locked-if-pos-not-visible-in-window () + "If the end of the locked region is not visible, jump to the end of it." + (interactive) + (let* ((proof-script-buffer (car proof-script-buffer-list)) + (pos (save-excursion + (set-buffer proof-script-buffer) + (proof-locked-end)))) + (or (pos-visible-in-window-p pos (get-buffer-window + proof-script-buffer t)) + (proof-goto-end-of-locked-interactive)))) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; User Commands ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Script management uses two major segments: Locked, which marks text +; which has been sent to the proof assistant and cannot be altered +; without being retracted, and Queue, which contains stuff being +; queued for processing. proof-action-list contains a list of +; (span,command,action) triples. The loop looks like: Execute the +; command, and if it's successful, do action on span. If the +; command's not successful, we bounce the rest of the queue and do +; some error processing. +; +; when a span has been processed, we classify it as follows: +; 'goalsave - denoting a 'goalsave pair in the locked region +; a 'goalsave region has a 'name property which is the name of the goal +; 'comment - denoting a comment +; 'pbp - denoting a span created by pbp +; 'vanilla - denoting any other span. +; 'pbp & 'vanilla spans have a property 'cmd, which says what +; command they contain. + +; We don't allow commands while the queue has anything in it. So we +; do configuration by concatenating the config command on the front in +; proof-send + +;; proof-assert-until-point, and various gunk for its ;; +;; setup and callback ;; + + +(defun proof-check-atomic-sequents-lists (span cmd end) + "Check if CMD is the final command in an ACS. + +If CMD is matched by the end regexp in `proof-atomic-sequents-list', +the ACS is marked in the current buffer. If CMD does not match any, +`nil' is returned, otherwise non-nil." + ;;FIXME tms: needs implementation + nil) + +(defun proof-done-advancing (span) + "The callback function for assert-until-point." + (let ((end (span-end span)) nam gspan next cmd) + (proof-set-locked-end end) + (proof-set-queue-start end) + (setq cmd (span-property span 'cmd)) + (cond + ((eq (span-property span 'type) 'comment) + (set-span-property span 'mouse-face 'highlight)) + ((proof-check-atomic-sequents-lists span cmd end)) + ((string-match proof-save-command-regexp cmd) + ;; In coq, we have the invariant that if we've done a save and + ;; there's a top-level declaration then it must be the + ;; associated goal. (Notice that because it's a callback it + ;; must have been approved by the theorem prover.) + (if (string-match proof-save-with-hole-regexp cmd) + (setq nam (match-string 2 cmd))) + (setq gspan span) + (while (or (eq (span-property gspan 'type) 'comment) + (not (funcall proof-goal-command-p + (setq cmd (span-property gspan 'cmd))))) + (setq next (prev-span gspan 'type)) + (delete-span gspan) + (setq gspan next)) + + (if (null nam) + (if (string-match proof-goal-with-hole-regexp + (span-property gspan 'cmd)) + (setq nam (match-string 2 (span-property gspan 'cmd))) + ;; This only works for Coq, but LEGO raises an error if + ;; there's no name. + ;; FIXME: a nonsense for Isabelle. + (setq nam "Unnamed_thm"))) + + (set-span-end gspan end) + (set-span-property gspan 'mouse-face 'highlight) + (set-span-property gspan 'type 'goalsave) + (set-span-property gspan 'name nam) + + (and proof-lift-global + (funcall proof-lift-global gspan))) + (t + (set-span-property span 'mouse-face 'highlight) + (and proof-global-p + (funcall proof-global-p cmd) + proof-lift-global + (funcall proof-lift-global span)))))) + + + +;; FIXME da: Below it would probably be faster to use the primitive +;; skip-chars-forward rather than scanning character-by-character +;; with a lisp loop over the whole region. Also I'm not convinced that +;; Emacs should be better at skipping whitespace and comments than the +;; proof process itself! + +(defun proof-segment-up-to (pos &optional next-command-end) + "Create a list of (type,int,string) tuples from end of locked region to POS. +Each tuple denotes the command and the position of its terminator, +type is one of 'comment, or 'cmd. 'unclosed-comment may be consed onto +the start if the segment finishes with an unclosed comment. +If optional NEXT-COMMAND-END is non-nil, we contine past POS until +the next command end." + (save-excursion + ;; depth marks number of nested comments. + ;; quote-parity is false if we're inside ""'s. + ;; Only one of (depth > 0) and (not quote-parity) + ;; should be true at once. -- hhg + (let ((str (make-string (- (buffer-size) + (proof-unprocessed-begin) -10) ?x)) + (i 0) (depth 0) (quote-parity t) done alist c) + (proof-goto-end-of-locked) + (while (not done) + (cond + ;; Case 1. We've reached POS, not allowed to go past it, + ;; and are inside a comment + ((and (not next-command-end) (= (point) pos) (> depth 0)) + (setq done t alist (cons 'unclosed-comment alist))) + ;; Case 2. We've reached the end of the buffer while + ;; scanning inside a comment or string + ((= (point) (point-max)) + (cond + ((not quote-parity) + (message "Warning: unclosed quote")) + ((> depth 0) + (setq done t alist (cons 'unclosed-comment alist)))) + (setq done t)) + ;; Case 3. Found a comment end, not inside a string + ((and (looking-at (regexp-quote proof-comment-end)) quote-parity) + (if (= depth 0) + (progn + (message "Warning: extraneous comment end") + (setq done t)) + (setq depth (- depth 1)) + (forward-char (length proof-comment-end)) + (if (eq i 0) + (setq alist (cons (list 'comment "" (point)) alist)) + (aset str i ?\ ) + (incf i)))) + ;; Case 4. Found a comment start, not inside a string + ((and (looking-at (regexp-quote proof-comment-start)) quote-parity) + (setq depth (+ depth 1)) + (forward-char (length proof-comment-start))) + ;; Case 5. Inside a comment. + ((> depth 0) + (forward-char)) + ;; Case 6. Anything else + (t + ;; Skip whitespace before the start of a command, otherwise + ;; other characters in the accumulator string str + (setq c (char-after (point))) + (if (or (> i 0) (not (= (char-syntax c) ?\ ))) + (progn + (aset str i c) + (incf i))) + + (if (looking-at "\"") ; FIXME da: should this be more generic? + (setq quote-parity (not quote-parity))) + + (forward-char) + + ;; Found the end of a command + (if (and (= c proof-terminal-char) quote-parity) + (progn + (setq alist + (cons (list 'cmd (substring str 0 i) (point)) alist)) + (if (>= (point) pos) + (setq done t) + (setq i 0))))))) + alist))) + +(defun proof-semis-to-vanillas (semis &optional callback-fn) + "Convert a sequence of semicolon positions (returned by the above +function) to a set of vanilla extents." + (let ((ct (proof-unprocessed-begin)) span alist semi) + (while (not (null semis)) + (setq semi (car semis) + span (make-span ct (nth 2 semi)) + ct (nth 2 semi)) + (if (eq (car (car semis)) 'cmd) + (progn + (set-span-property span 'type 'vanilla) + (set-span-property span 'cmd (nth 1 semi)) + (setq alist (cons (list span (nth 1 semi) + (or callback-fn 'proof-done-advancing)) + alist))) + (set-span-property span 'type 'comment) + (setq alist (cons (list span proof-no-command 'proof-done-advancing) alist))) + (setq semis (cdr semis))) + (nreverse alist))) + +;; +;; Two commands for moving forwards in proof scripts. +;; Moving forward for a "new" command may insert spaces +;; or new lines. Moving forward for the "next" command +;; does not. +;; + +(defun proof-script-new-command-advance () + "Move point to a nice position for a new command. +Assumes that point is at the end of a command." + (interactive) + (if proof-one-command-per-line + ;; One command per line: move to next new line, + ;; creating one if at end of buffer or at the + ;; start of a blank line. (This has the pleasing + ;; effect that blank regions of the buffer are + ;; automatically extended when inserting new commands). + (cond + ((eq (forward-line) 1) + (newline)) + ((eolp) + (newline) + (forward-line -1))) + ;; Multiple commands per line: skip spaces at point, + ;; and insert the same number of spaces that were + ;; skipped in front of point (at least one). + ;; This has the pleasing effect that the spacing + ;; policy of the current line is copied: e.g. + ;; <command>; <command>; + ;; Tab columns don't work properly, however. + ;; Instead of proof-one-command-per-line we could + ;; introduce a "proof-command-separator" to improve + ;; this. + (let ((newspace (max (skip-chars-forward " \t") 1)) + (p (point))) + (insert-char ?\ newspace) + (goto-char p)))) + +(defun proof-script-next-command-advance () + "Move point to the beginning of the next command if it's nearby. +Assumes that point is at the end of a command." + (interactive) + ;; skip whitespace on this line + (skip-chars-forward " \t") + (if (and proof-one-command-per-line (eolp)) + ;; go to the next line if we have one command per line + (forward-line))) + + + +; Assert until point - We actually use this to implement the +; assert-until-point, active terminator keypress, and find-next-terminator. +; In different cases we want different things, but usually the information +; (i.e. are we inside a comment) isn't available until we've actually run +; proof-segment-up-to (point), hence all the different options when we've +; done so. + +;; FIXME da: this command doesn't behave as the doc string says when +;; inside comments. Also is unhelpful at the start of commands, and +;; in the locked region. I prefer the new version below. + +(defun proof-assert-until-point + (&optional unclosed-comment-fun ignore-proof-process-p) + "Process the region from the end of the locked-region until point. + Default action if inside a comment is just to go until the start of + the comment. If you want something different, put it inside + unclosed-comment-fun. If ignore-proof-process-p is set, no commands + will be added to the queue." + (interactive) + (let ((pt (point)) + (crowbar (or ignore-proof-process-p (proof-check-process-available))) + semis) + (save-excursion + (if (not (re-search-backward "\\S-" (proof-unprocessed-begin) t)) + (progn (goto-char pt) + (error "I don't know what I should be doing in this buffer!"))) + (setq semis (proof-segment-up-to (point)))) + (if (and unclosed-comment-fun (eq 'unclosed-comment (car semis))) + (funcall unclosed-comment-fun) + (if (eq 'unclosed-comment (car semis)) (setq semis (cdr semis))) + (if (and (not ignore-proof-process-p) (not crowbar) (null semis)) + (error "I don't know what I should be doing in this buffer!")) + (goto-char (nth 2 (car semis))) + (and (not ignore-proof-process-p) + (let ((vanillas (proof-semis-to-vanillas (nreverse semis)))) +; (if crowbar (setq vanillas (cons crowbar vanillas))) + (proof-start-queue (proof-unprocessed-begin) (point) vanillas)))))) + + +;; da: This is my alternative version of the above. +;; It works from the locked region too. +;; I find it more convenient to assert up to the current command (command +;; point is inside), and move to the next command. +;; This means proofs can be easily replayed with point at the start +;; of lines. Above function gives stupid "nothing to do error." when +;; point is on the start of line or in the locked region. + +;; FIXME: behaviour inside comments may be odd at the moment. (it +;; doesn't behave as docstring suggests, same prob as +;; proof-assert-until-point) +;; FIXME: polish the undo behaviour and quit behaviour of this +;; command (should inhibit quit somewhere or other). + +(defun proof-assert-next-command + (&optional unclosed-comment-fun ignore-proof-process-p + dont-move-forward for-new-command) + "Process until the end of the next unprocessed command after point. +If inside a comment, just to go the start of +the comment. If you want something different, put it inside +UNCLOSED-COMMENT-FUN. If IGNORE-PROOF-PROCESS-P is set, no commands +will be added to the queue. +Afterwards, move forward to near the next command afterwards, unless +DONT-MOVE-FORWARD is non-nil. If FOR-NEW-COMMAND is non-nil, +a space or newline will be inserted automatically." + (interactive) + (let ((pt (point)) + (crowbar (or ignore-proof-process-p (proof-check-process-available))) + semis) + (save-excursion + ;; CHANGE from old proof-assert-until-point: don't bother check + ;; for non-whitespace between locked region and point. + ;; CHANGE: ask proof-segment-up-to to scan until command end + ;; (which it used to do anyway, except in the case of a comment) + (setq semis (proof-segment-up-to (point) t))) + ;; old code: + ;;(if (not (re-search-backward "\\S-" (proof-unprocessed-begin) t)) + ;; (progn (goto-char pt) + ;; (error "I don't know what I should be doing in this buffer!"))) + ;; (setq semis (proof-segment-up-to (point)))) + (if (and unclosed-comment-fun (eq 'unclosed-comment (car semis))) + (funcall unclosed-comment-fun) + (if (eq 'unclosed-comment (car semis)) + ;; CHANGE: if inside a comment, do as the documentation + ;; suggests. + (setq semis nil)) + (if (and (not ignore-proof-process-p) (not crowbar) (null semis)) + (error "I don't know what I should be doing in this buffer!")) + (goto-char (nth 2 (car semis))) + (if (not ignore-proof-process-p) + (let ((vanillas (proof-semis-to-vanillas (nreverse semis)))) +; (if crowbar (setq vanillas (cons crowbar vanillas))) + (proof-start-queue (proof-unprocessed-begin) (point) vanillas))) + ;; This is done after the queuing to be polite: it means the + ;; spacing policy enforced here is not put into the locked + ;; region so the user can re-edit. + (if (not dont-move-forward) + (if for-new-command + (proof-script-new-command-advance) + (proof-script-next-command-advance)))))) + +;; insert-pbp-command - an advancing command, for use when ;; +;; PbpHyp or Pbp has executed in LEGO, and returned a ;; +;; command for us to run ;; + +(defun proof-insert-pbp-command (cmd) + (proof-check-process-available) + (let (span) + (proof-goto-end-of-locked) + (insert cmd) + (setq span (make-span (proof-locked-end) (point))) + (set-span-property span 'type 'pbp) + (set-span-property span 'cmd cmd) + (proof-start-queue (proof-unprocessed-begin) (point) + (list (list span cmd 'proof-done-advancing))))) + + +;; proof-retract-until-point and associated gunk ;; +;; most of the hard work (i.e computing the commands to do ;; +;; the retraction) is implemented in the customisation ;; +;; module (lego.el or coq.el) which is why this looks so ;; +;; straightforward ;; + + +(defun proof-done-retracting (span) + "Updates display after proof process has reset its state. See also +the documentation for `proof-retract-until-point'. It optionally +deletes the region corresponding to the proof sequence." + (let ((start (span-start span)) + (end (span-end span)) + (kill (span-property span 'delete-me))) + (proof-set-locked-end start) + (proof-set-queue-end start) + (delete-spans start end 'type) + (delete-span span) + (if kill (delete-region start end)))) + +(defun proof-setup-retract-action (start end proof-command delete-region) + (let ((span (make-span start end))) + (set-span-property span 'delete-me delete-region) + (list (list span proof-command 'proof-done-retracting)))) + +(defun proof-retract-target (target delete-region) + (let ((end (proof-locked-end)) + (start (span-start target)) + (span (proof-last-goal-or-goalsave)) + actions) + + (if (and span (not (eq (span-property span 'type) 'goalsave))) + (if (< (span-end span) (span-end target)) + (progn + (setq span target) + (while (and span (eq (span-property span 'type) 'comment)) + (setq span (next-span span 'type))) + (setq actions (proof-setup-retract-action + start end + (if (null span) proof-no-command + (funcall proof-count-undos-fn span)) + delete-region) + end start)) + + (setq actions (proof-setup-retract-action (span-start span) end + proof-kill-goal-command + delete-region) + end (span-start span)))) + + (if (> end start) + (setq actions + (nconc actions (proof-setup-retract-action + start end + (funcall proof-find-and-forget-fn target) + delete-region)))) + + (proof-start-queue (min start end) (proof-locked-end) actions))) + +;; FIXME da: I would rather that this function moved point to +;; the start of the region retracted? + +(defun proof-retract-until-point (&optional delete-region) + "Sets up the proof process for retracting until point. In + particular, it sets a flag for the filter process to call + `proof-done-retracting' after the proof process has actually + successfully reset its state. It optionally deletes the region in + the proof script corresponding to the proof command sequence. If + this function is invoked outside a locked region, the last + successfully processed command is undone." + (interactive) + (proof-check-process-available) + (let ((span (span-at (point) 'type))) + (if (null (proof-locked-end)) (error "No locked region")) + (and (null span) + (progn (proof-goto-end-of-locked) (backward-char) + (setq span (span-at (point) 'type)))) + (proof-retract-target span delete-region))) + +;; proof-try-command ;; +;; this isn't really in the spirit of script management, ;; +;; but sometimes the user wants to just try an expression ;; +;; without having to undo it in order to try something ;; +;; different. Of course you can easily lose sync by doing ;; +;; something here which changes the proof state ;; + +(defun proof-done-trying (span) + (delete-span span) + (proof-detach-queue)) + +(defun proof-try-command + (&optional unclosed-comment-fun) + + "Process the command at point, + but don't add it to the locked region. This will only happen if + the command satisfies proof-state-preserving-p. + + Default action if inside a comment is just to go until the start of + the comment. If you want something different, put it inside + unclosed-comment-fun." + (interactive) + (let ((pt (point)) semis crowbar test) + (setq crowbar (proof-check-process-available)) + (save-excursion + (if (not (re-search-backward "\\S-" (proof-locked-end) t)) + (progn (goto-char pt) + (error "I don't know what I should be doing in this buffer!"))) + (setq semis (proof-segment-up-to (point)))) + (if (and unclosed-comment-fun (eq 'unclosed-comment (car semis))) + (funcall unclosed-comment-fun) + (if (eq 'unclosed-comment (car semis)) (setq semis (cdr semis))) + (if (and (not crowbar) (null semis)) (error "I don't know what I should be doing in this buffer!")) + (setq test (car semis)) + (if (not (funcall proof-state-preserving-p (nth 1 test))) + (error "Command is not state preserving")) + (goto-char (nth 2 test)) + (let ((vanillas (proof-semis-to-vanillas (list test) + 'proof-done-trying))) +; (if crowbar (setq vanillas (cons crowbar vanillas))) + (proof-start-queue (proof-unprocessed-begin) (point) vanillas))))) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; misc other user functions ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +(defun proof-undo-last-successful-command (&optional no-delete) + "Undo last successful command at end of locked region. +Unless optional NO-DELETE is set, the text is also deleted from +the proof script." + (interactive "p") + (let ((lastspan (span-at-before (proof-locked-end) 'type))) + (if lastspan + (progn + (goto-char (span-start lastspan)) + (proof-retract-until-point (not no-delete))) + (error "Nothing to undo!")))) + +(defun proof-interrupt-process () + (interactive) + (if (not (proof-shell-live-buffer)) + (error "Proof Process Not Started!")) + (if (not (member (current-buffer) proof-script-buffer-list)) + (error "Don't own process!")) + (if (not proof-shell-busy) + (error "Proof Process Not Active!")) + (save-excursion + (set-buffer proof-shell-buffer) + (comint-interrupt-subjob))) + + +(defun proof-find-next-terminator () + "Set point after next `proof-terminal-char'." + (interactive) + (let ((cmd (span-at (point) 'type))) + (if cmd (goto-char (span-end cmd)) + (and (re-search-forward "\\S-" nil t) + (proof-assert-until-point nil 'ignore-proof-process))))) + +(defun proof-goto-command-start () + "Move point to start of current command." + (interactive) + (let ((cmd (span-at (point) 'type))) + (if cmd (goto-char (span-start cmd)) ; BUG: only works for unclosed proofs. + (let ((semis (proof-segment-up-to (point) t))) + (if (eq 'unclosed-comment (car semis)) (setq semis (cdr semis))) + (if (and semis (car semis) (car (car semis))) + (progn + (goto-char (nth 2 (car (car semis)))) + (skip-chars-forward " \t\n"))))))) + +(defun proof-process-buffer () + "Process the current buffer and set point at the end of the buffer." + (interactive) + (goto-char (point-max)) + (proof-assert-until-point)) + +(defun proof-restart-buffer (buffer) + "Remove all extents in BUFFER and update `proof-script-buffer-list'." + (save-excursion + (if (buffer-live-p buffer) + (progn + (set-buffer buffer) + (setq proof-active-buffer-fake-minor-mode nil) + (delete-spans (point-min) (point-max) 'type) + (proof-detach-segments))) + (setq proof-script-buffer-list + (remove buffer proof-script-buffer-list)))) + +(defun proof-restart-buffers (bufferlist) + "Remove all extents in BUFFERLIST and update `proof-script-buffer-list'." + (mapcar 'proof-restart-buffer bufferlist)) + +;; For when things go horribly wrong +;; FIXME: this needs to politely stop the process by sending +;; an EOF or customizable command. (maybe timeout waiting for +;; it to die before killing the buffer) +;; maybe better: +;; Put a funciton to remove all extents in buffers into +;; the kill-buffer-hook for the proof shell. Then this +;; function just stops the shell nicely (see proof-stop-shell). +(defun proof-restart-script () + "Restart Proof General. +Kill off the proof assistant process and remove all markings in the +script buffers." + (interactive) + (proof-restart-buffers proof-script-buffer-list) + ;; { (equal proof-script-buffer-list nil) } + (setq proof-shell-busy nil + proof-included-files-list nil + proof-shell-proof-completed nil) + (if (buffer-live-p proof-shell-buffer) + (kill-buffer proof-shell-buffer)) + (if (buffer-live-p proof-pbp-buffer) + (kill-buffer proof-pbp-buffer)) + (and (buffer-live-p proof-response-buffer) + (kill-buffer proof-response-buffer))) + +;; For when things go not-quite-so-horribly wrong +;; FIXME: this may need work, and maybe isn't needed at +;; all (proof-retract-file when it appears may be enough). +(defun proof-restart-script-same-process () + (interactive) + (save-excursion + (if (buffer-live-p proof-script-buffer) + (progn + (set-buffer proof-script-buffer) + (setq proof-active-buffer-fake-minor-mode nil) + (delete-spans (point-min) (point-max) 'type) + (proof-detach-segments))))) + +;; A command for making things go horribly wrong - it moves the +;; end-of-locked-region marker backwards, so user had better move it +;; correctly to sync with the proof state, or things will go all +;; pear-shaped. + +(defun proof-frob-locked-end () + (interactive) + "Move the end of the locked region backwards. +Only for use by consenting adults." + (cond + ((not (member (current-buffer) proof-script-buffer-list)) + (error "Not in proof buffer")) + ((> (point) (proof-locked-end)) + (error "Can only move backwards")) + (t (proof-set-locked-end (point)) + (delete-spans (proof-locked-end) (point-max) 'type)))) + +(defvar proof-minibuffer-history nil + "History of proof commands read from the minibuffer") + +(defun proof-execute-minibuffer-cmd () + (interactive) + (let (cmd) + (proof-check-process-available 'relaxed) + (setq cmd (read-string "Command: " nil 'proof-minibuffer-history)) + (proof-invisible-command cmd 'relaxed))) + +;; da: below functions for input history simulation are quick hacks. +;; Could certainly be made more efficient. + +(defvar proof-command-history nil + "History used by proof-previous-matching-command and friends.") + +(defun proof-build-command-history () + "Construct proof-command-history from script buffer. +Based on position of point." + ;; let + ) + +(defun proof-previous-matching-command (arg) + "Search through previous commands for new command matching current input." + (interactive)) + ;;(if (not (memq last-command '(proof-previous-matching-command + ;; proof-next-matching-command))) + ;; Start a new search + + + +(defun proof-next-matching-command (arg) + "Search through following commands for new command matching current input." + (interactive "p") + (proof-previous-matching-command (- arg))) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Popup and Pulldown Menu ;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; Menu commands for the underlying proof assistant + +;;; These are defcustom'd so that users may re-configure +;;; the system to their liking. +;;; Functions using the above + +(defun proof-ctxt () + "List context." + (interactive) + (proof-invisible-command (concat proof-ctxt-string proof-terminal-string))) + +(defun proof-help () + "Print help message giving syntax." + (interactive) + (proof-invisible-command (concat proof-help-string proof-terminal-string))) + +(defun proof-prf () + "List proof state." + (interactive) + (proof-invisible-command (concat proof-prf-string proof-terminal-string))) + + + +;; FIXME: da: add more general function for inserting into the +;; script buffer and the proof process together, and using +;; a choice of minibuffer prompting (hated by power users, perfect +;; for novices). +;; Add named goals. +;; TODO: +;; Add named goals. +;; Coherent policy for movement here and elsewhere based on +;; proof-one-command-per-line user option. +;; Coherent policy for sending to process after writing into +;; script buffer. Could have one without the other. +;; For example, may be more easy to edit complex goal string +;; first in the script buffer. Ditto for tactics, etc. + +(defvar proof-issue-goal-history nil + "History of goals for proof-issue-goal.") + +(defun proof-issue-goal (goal-cmd) + "Insert a goal command into the script buffer, issue it to prover." + (interactive + (if proof-goal-command + (if (stringp proof-goal-command) + (list (format proof-goal-command + (read-string "Goal: " "" + proof-issue-goal-history))) + (proof-goal-command)) + (error + "Proof General not configured for goals: set proof-goal-command."))) + (let ((proof-one-command-per-line t)) ; Goals always start at a new line + (proof-issue-new-command goal-cmd))) + +(defvar proof-issue-save-history nil + "History of theorem names for proof-issue-save.") + +(defun proof-issue-save (thmsave-cmd) + "Insert a save theorem command into the script buffer, issue it." + (interactive + (if proof-save-command + (if (stringp proof-save-command) + (list (format proof-save-command + (read-string "Save as: " "" + proof-issue-save-history))) + (proof-save-command)) + (error + "Proof General not configured for save theorem: set proof-save-command."))) + (let ((proof-one-command-per-line t)) ; Goals always start at a new line + (proof-issue-new-command thmsave-cmd))) + +(defun proof-issue-new-command (cmd) + "Insert CMD into the script buffer and issue it to the proof assistant. +If point is in the locked region, move to the end of it first. +Start up the proof assistant if necessary." + ;; FIXME: da: I think we need a (proof-script-switch-to-buffer) + ;; function (so there is some control over display). + ;; (switch-to-buffer proof-script-buffer) + (if (proof-shell-live-buffer) + (if (proof-in-locked-region-p) + (proof-goto-end-of-locked-interactive))) + (proof-script-new-command-advance) + ;; FIXME: fixup behaviour of undo here. Really want + ;; to temporarily disable undo for insertion. + ;; (buffer-disable-undo) this trashes whole undo list! + (insert cmd) + ;; FIXME: could do proof-indent-line here, but let's + ;; wait until indentation is fixed. + (proof-assert-until-point)) + + + + +;;; To be called from menu +(defun proof-info-mode () + "Call info on Proof General." + (interactive) + (info "ProofGeneral")) + +(defun proof-exit () + "Exit Proof-assistant." + (interactive) + (proof-restart-script)) + +(defvar proof-help-menu + `("Help" + [,(concat proof-assistant " web page") + (browse-url proof-www-home-page) t] + ["Proof General home page" + (browse-url proof-general-home-page) t] + ["Proof General Info" proof-info-mode t] + ) + "The Help Menu in Script Management.") + +(defvar proof-shared-menu + (proof-append-element + (append + (list + ["Display context" proof-ctxt + :active (proof-shell-live-buffer)] + ["Display proof state" proof-prf + :active (proof-shell-live-buffer)] + ["Exit proof assistant" proof-exit + :active (proof-shell-live-buffer)]) + (if proof-tags-support + (list + "----" + ["Find definition/declaration" find-tag-other-window t]) + nil)) + proof-help-menu)) + +(defvar proof-menu + (append '("Commands" + ["Toggle active terminator" proof-active-terminator-minor-mode + :active t + :style toggle + :selected proof-active-terminator-minor-mode] + "----") + (list (if (string-match "XEmacs 19.1[2-9]" emacs-version) + "--:doubleLine" "----")) + proof-shared-menu + ) + "*The menu for the proof assistant.") + +(defvar proof-shell-menu proof-shared-menu + "The menu for the Proof-assistant shell") + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Active terminator minor mode ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(deflocal proof-active-terminator-minor-mode nil + "Active terminator minor mode flag") + +(defun proof-active-terminator-minor-mode (&optional arg) + "Toggle PROOF's Active Terminator minor mode. +With arg, turn on the Active Terminator minor mode if and only if arg +is positive. + +If Active terminator mode is enabled, a terminator will process the +current command." + + (interactive "P") + +;; has this minor mode been registered as such? + (or (assq 'proof-active-terminator-minor-mode minor-mode-alist) + (setq minor-mode-alist + (append minor-mode-alist + (list '(proof-active-terminator-minor-mode + (concat " " proof-terminal-string)))))) + + (setq proof-active-terminator-minor-mode + (if (null arg) (not proof-active-terminator-minor-mode) + (> (prefix-numeric-value arg) 0))) + (if (fboundp 'redraw-modeline) + (redraw-modeline) + (force-mode-line-update))) + +(defun proof-process-active-terminator () + "Insert the proof command terminator, and assert up to it. +Fire up proof process if necessary." + (let ((mrk (point)) ins incomment) + (if (looking-at "\\s-\\|\\'\\|\\w") + (if (not (re-search-backward "\\S-" (proof-unprocessed-begin) t)) + (error "I don't know what I should be doing in this buffer!"))) + (if (not (= (char-after (point)) proof-terminal-char)) + (progn (forward-char) (insert proof-terminal-string) (setq ins t))) + (proof-assert-until-point + (function (lambda () + (setq incomment t) + (if ins (backward-delete-char 1)) + (goto-char mrk) + (insert proof-terminal-string)))) + (or incomment + (proof-script-next-command-advance)))) + +(defun proof-active-terminator () + "Insert the terminator, perhaps sending the command to the assistant. +If proof-active-terminator-minor-mode is non-nil, the command will be +sent to the assistant." + (interactive) + (if proof-active-terminator-minor-mode + (proof-process-active-terminator) + (self-insert-command 1))) + + + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Proof General scripting mode definition ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +;; da: this isn't so nice if a scripting buffer should inherit +;; from another mode: e.g. for Isabelle, we would like to use +;; sml-mode. +;; FIXME: add more doc to the mode below, to give hints on +;; configuring for new assistants. +(define-derived-mode proof-mode fundamental-mode + proof-mode-name + "Proof General major mode for proof scripts. +\\{proof-mode-map} +" + (setq proof-buffer-type 'script) + + ;; Has buffer already been processed? + (and (member buffer-file-truename proof-included-files-list) + (proof-mark-buffer-atomic (current-buffer))) + + (make-local-variable 'kill-buffer-hook) + (add-hook 'kill-buffer-hook + (lambda () + (setq proof-script-buffer-list + (remove (current-buffer) proof-script-buffer-list))))) + +;; FIXME: da: could we put these into another keymap already? +;; FIXME: da: it's offensive to experience users to rebind global stuff +;; like meta-tab, this should be removed. +(defconst proof-universal-keys + (append + '(([(control c) (control c)] . proof-interrupt-process) + ([(control c) (control v)] . proof-execute-minibuffer-cmd)) + (if proof-tags-support + '(([(meta tab)] . tag-complete-symbol)) + nil)) +"List of keybindings which for the script and response buffer. +Elements of the list are tuples (k . f) +where `k' is a keybinding (vector) and `f' the designated function.") + +;; Fixed definitions in proof-mode-map, which don't depend on +;; prover configuration. +;;; INDENT HACK: font-lock only recognizes define-key at start of line +(let ((map proof-mode-map)) +(define-key map [(control c) (control e)] 'proof-find-next-terminator) +(define-key map [(control c) (control a)] 'proof-goto-command-start) +(define-key map [(control c) (control n)] 'proof-assert-next-command) +(define-key map [(control c) (return)] 'proof-assert-next-command) +(define-key map [(control c) (control t)] 'proof-try-command) +(define-key map [(control c) ?u] 'proof-retract-until-point) +(define-key map [(control c) (control u)] 'proof-undo-last-successful-command) +(define-key map [(control c) ?\'] 'proof-goto-end-of-locked-interactive) +(define-key map [(control button1)] 'proof-send-span) +(define-key map [(control c) (control b)] 'proof-process-buffer) +(define-key map [(control c) (control z)] 'proof-frob-locked-end) +(define-key map [(control c) (control p)] 'proof-prf) +(define-key map [(control c) ?c] 'proof-ctxt) +(define-key map [(control c) ?h] 'proof-help) +(define-key map [(meta p)] 'proof-previous-matching-command) +(define-key map [(meta n)] 'proof-next-matching-command) +;; FIXME da: this shouldn't need setting, because it works +;; via indent-line-function which is set below. Check this. +(define-key map [tab] 'proof-indent-line) +(proof-define-keys map proof-universal-keys)) + + + +;; the following callback is an irritating hack - there should be some +;; elegant mechanism for computing constants after the child has +;; configured. + +(defun proof-config-done () + "Finish setup of Proof General scripting mode. +Call this function in the derived mode for the proof assistant to +finish setup which depends on specific proof assistant configuration." + ;; calculate some strings and regexps for searching + (setq proof-terminal-string (char-to-string proof-terminal-char)) + + ;; FIXME: these are LEGO specific! + (setq pbp-goal-command (concat "Pbp %s" proof-terminal-string)) + (setq pbp-hyp-command (concat "PbpHyp %s" proof-terminal-string)) + + ;; FIXME da: I'm not sure we ought to add spaces here, but if + ;; we don't, there would be trouble overloading these settings + ;; to also use as regexps for finding comments. + ;; + (make-local-variable 'comment-start) + (setq comment-start (concat proof-comment-start " ")) + (make-local-variable 'comment-end) + (setq comment-end (concat " " proof-comment-end)) + (make-local-variable 'comment-start-skip) + (setq comment-start-skip + (concat (regexp-quote proof-comment-start) "+\\s_?")) + + (setq proof-re-end-of-cmd (concat "\\s_*" proof-terminal-string "\\s_*\\\'")) + (setq proof-re-term-or-comment + (concat proof-terminal-string "\\|" (regexp-quote proof-comment-start) + "\\|" (regexp-quote proof-comment-end))) + + ;; func-menu --- Jump to a goal within a buffer + (and (boundp 'fume-function-name-regexp-alist) + (defvar fume-function-name-regexp-proof + (cons proof-goal-with-hole-regexp 2)) + (push (cons major-mode 'fume-function-name-regexp-proof) + fume-function-name-regexp-alist)) + (and (boundp 'fume-find-function-name-method-alist) + (push (cons major-mode 'proof-match-find-next-function-name) + fume-find-function-name-method-alist)) + + ;; Additional key definitions which depend on configuration for + ;; specific proof assistant. + (define-key proof-mode-map + (vconcat [(control c)] (vector proof-terminal-char)) + 'proof-active-terminator-minor-mode) + + (define-key proof-mode-map (vector proof-terminal-char) + 'proof-active-terminator) + + (make-local-variable 'indent-line-function) + (setq indent-line-function 'proof-indent-line) + + ;; Toolbar + (if (featurep 'proof-toolbar) + (proof-toolbar-setup)) + + ;; Menu + (easy-menu-define proof-mode-menu + proof-mode-map + "Menu used in Proof General scripting mode." + (cons proof-mode-name + (append + (cdr proof-menu) + ;; begin UGLY COMPATIBILTY HACK + ;; older/non-existent customize doesn't have + ;; this function. + (if (fboundp 'customize-menu-create) + (list (customize-menu-create 'proof) + (customize-menu-create 'proof-internal + "Internals")) + nil) + ;; end UGLY COMPATIBILTY HACK + ))) + (easy-menu-add proof-mode-menu proof-mode-map) + + ;; For fontlock + + ;; setting font-lock-defaults explicitly is required by FSF Emacs + ;; 20.2's version of font-lock + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '(font-lock-keywords)) + + ;; FIXME (da): zap commas support is too specific, should be enabled + ;; by each proof assistant as it likes. + (remove-hook 'font-lock-after-fontify-buffer-hook 'proof-zap-commas-buffer t) + (add-hook 'font-lock-after-fontify-buffer-hook + 'proof-zap-commas-buffer nil t) + (remove-hook 'font-lock-mode-hook 'proof-unfontify-separator t) + (add-hook 'font-lock-mode-hook 'proof-unfontify-separator nil t) + + ;; if we don't have the following, zap-commas fails to work. + ;; FIXME (da): setting this to t everywhere is too brutal. Should + ;; probably make it local. + (and (boundp 'font-lock-always-fontify-immediately) + (setq font-lock-always-fontify-immediately t)) + + (run-hooks 'proof-mode-hook)) + + + +(provide 'proof-script) +;; proof-script.el ends here. diff --git a/generic/proof-shell.el b/generic/proof-shell.el new file mode 100644 index 00000000..1657b049 --- /dev/null +++ b/generic/proof-shell.el @@ -0,0 +1,951 @@ +;; proof-shell.el Major mode for proof assistant script files. +;; +;; Copyright (C) 1994 - 1998 LFCS Edinburgh. +;; Authors: David Aspinall, Yves Bertot, Healfdene Goguen, +;; Thomas Kleymann and Dilip Sequeira +;; +;; Maintainer: Proof General maintainer <proofgen@dcs.ed.ac.uk> +;; +;; $Id$ +;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Starting and stopping the proof-system shell ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun proof-shell-live-buffer () + "Return buffer of active proof assistant, or nil if none running." + (and proof-shell-buffer + (comint-check-proc proof-shell-buffer) + proof-shell-buffer)) + +(defun proof-start-shell () + "Initialise a shell-like buffer for a proof assistant. + +Also generates goal and response buffers. +Initialises current buffer for scripting. +Does nothing if proof assistant is already running." + (if (proof-shell-live-buffer) + () + (run-hooks 'proof-pre-shell-start-hook) + (setq proof-included-files-list nil) + (if proof-prog-name-ask-p + (save-excursion + (setq proof-prog-name (read-shell-command "Run process: " + proof-prog-name)))) + (let ((proc + (concat "Inferior " + (substring proof-prog-name + (string-match "[^/]*$" proof-prog-name))))) + (while (get-buffer (concat "*" proc "*")) + (if (string= (substring proc -1) ">") + (aset proc (- (length proc) 2) + (+ 1 (aref proc (- (length proc) 2)))) + (setq proc (concat proc "<2>")))) + + (message (format "Starting %s process..." proc)) + + ;; Starting the inferior process (asynchronous) + (let ((prog-name-list (proof-string-to-list proof-prog-name " "))) + (apply 'make-comint (append (list proc (car prog-name-list) nil) + (cdr prog-name-list)))) + ;; To send any initialisation commands to the inferior process, + ;; consult proof-shell-config-done... + + (setq proof-shell-buffer (get-buffer (concat "*" proc "*")) + proof-pbp-buffer (get-buffer-create (concat "*" proc "-goals*")) + proof-response-buffer (get-buffer-create (concat "*" proc + "-response*"))) + + (save-excursion + (set-buffer proof-shell-buffer) + (funcall proof-mode-for-shell) + (set-buffer proof-pbp-buffer) + (funcall proof-mode-for-pbp)) + + (setq proof-script-buffer-list (list (current-buffer))) + (proof-init-segmentation) + (setq proof-active-buffer-fake-minor-mode t) + (run-hooks 'proof-activate-scripting-hook) + + (if (fboundp 'redraw-modeline) + (redraw-modeline) + (force-mode-line-update)) + + (or (assq 'proof-active-buffer-fake-minor-mode minor-mode-alist) + (setq minor-mode-alist + (append minor-mode-alist + (list '(proof-active-buffer-fake-minor-mode + " Scripting"))))) + + (message + (format "Starting %s process... done." proc))))) + + +;; Should this be the same as proof-restart-script? +;; Perhaps this is redundant. +(defun proof-stop-shell () + "Exit the proof process. Runs proof-shell-exit-hook if non-nil" + (interactive) + (save-excursion + (let ((buffer (proof-shell-live-buffer)) + proc) + (if buffer + (progn + (save-excursion + (set-buffer buffer) + (setq proc (process-name (get-buffer-process))) + (comint-send-eof) + (mapcar 'proof-detach-segments proof-script-buffer-list) + (kill-buffer)) + (run-hooks 'proof-shell-exit-hook) + + ;;it is important that the hooks are + ;;run after the buffer has been killed. In the reverse + ;;order e.g., intall-shell-fonts causes problems and it + ;;is impossible to restart the PROOF shell + + (message (format "%s process terminated." proc))))))) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Proof by pointing ;; +;; All very lego-specific at present ;; +;; To make sense of this code, you should read the ;; +;; relevant LFCS tech report by tms, yb, and djs ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +(defvar pbp-goal-command nil + "Command informing the prover that `pbp-button-action' has been + requested on a goal.") + +(defvar pbp-hyp-command nil + "Command informing the prover that `pbp-button-action' has been + requested on an assumption.") + +(defun pbp-button-action (event) + (interactive "e") + (mouse-set-point event) + (pbp-construct-command)) + +; Using the spans in a mouse behavior is quite simple: from the +; mouse position, find the relevant span, then get its annotation +; and produce a piece of text that will be inserted in the right +; buffer. + +(defun proof-expand-path (string) + (let ((a 0) (l (length string)) ls) + (while (< a l) + (setq ls (cons (int-to-string (aref string a)) + (cons " " ls))) + (incf a)) + (apply 'concat (nreverse ls)))) + +(defun proof-send-span (event) + (interactive "e") + (let* ((span (span-at (mouse-set-point event) 'type)) + (str (span-property span 'cmd))) + (cond ((and (member (current-buffer) proof-script-buffer-list) (not (null span))) + (proof-goto-end-of-locked) + (cond ((eq (span-property span 'type) 'vanilla) + (insert str))))))) + +(defun pbp-construct-command () + (let* ((span (span-at (point) 'proof)) + (top-span (span-at (point) 'proof-top-element)) + top-info) + (if (null top-span) () + (setq top-info (span-property top-span 'proof-top-element)) + (pop-to-buffer (car proof-script-buffer-list)) + (cond + (span + (proof-invisible-command + (format (if (eq 'hyp (car top-info)) pbp-hyp-command + pbp-goal-command) + (concat (cdr top-info) (proof-expand-path + (span-property span 'proof)))))) + ((eq (car top-info) 'hyp) + (proof-invisible-command (format pbp-hyp-command (cdr top-info)))) + (t + (proof-insert-pbp-command (format pbp-change-goal (cdr top-info)))))) + )) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Turning annotated output into pbp goal set ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar proof-shell-first-special-char nil "where the specials start") +(defvar proof-shell-goal-char nil "goal mark") +(defvar proof-shell-start-char nil "annotation start") +(defvar proof-shell-end-char nil "annotation end") +(defvar proof-shell-field-char nil "annotated field end") + +(defvar proof-shell-eager-annotation-start nil + "Eager annotation field start. A regular expression or nil. +An eager annotation indicates to Emacs that some following output +should be displayed immediately and not accumulated for parsing. +Set to nil if the proof to disable this feature.") + +(defvar proof-shell-eager-annotation-end "$" + "Eager annotation field end. A regular expression or nil. +An eager annotation indicates to Emacs that some following output +should be displayed immediately and not accumulated for parsing. +Set to nil if the proof to disable this feature. + +The default value is \"$\" to match up to the end of the line.") + +(defvar proof-shell-assumption-regexp nil + "A regular expression matching the name of assumptions.") + +;; FIXME da: where is this variable used? +;; dropped in favour of goal-char? +;; Answer: this is used in *specific* modes, see e.g. +;; lego-goal-hyp. This stuff needs making more generic. +(defvar proof-shell-goal-regexp nil + "A regular expression matching the identifier of a goal.") + +(defvar proof-shell-noise-regexp nil + "Unwanted information output from the proof process within + `proof-start-goals-regexp' and `proof-end-goals-regexp'.") + +(defun pbp-make-top-span (start end) + (let (span name) + (goto-char start) + ;; FIXME da: why name? This is a character function + (setq name (funcall proof-goal-hyp-fn)) + (beginning-of-line) + (setq start (point)) + (goto-char end) + (beginning-of-line) + (backward-char) + (setq span (make-span start (point))) + (set-span-property span 'mouse-face 'highlight) + (set-span-property span 'proof-top-element name))) + +;; Need this for processing error strings and so forth + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; The filter. First some functions that handle those few ;; +;; occasions when the glorious illusion that is script-management ;; +;; is temporarily suspended ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Output from the proof process is handled lazily, so that only +;; the output from the last of multiple commands is actually +;; processed (assuming they're all successful) + +(defvar proof-shell-delayed-output nil + "The last interesting output the proof process output, and what to do + with it.") + +(defvar proof-shell-proof-completed nil + "Flag indicating that a completed proof has just been observed.") + +(defvar proof-analyse-using-stack nil + "Are annotations sent by proof assistant local or global") + +(defun proof-shell-analyse-structure (string) + (save-excursion + (let* ((ip 0) (op 0) ap (l (length string)) + (ann (make-string (length string) ?x)) + (stack ()) (topl ()) + (out (make-string l ?x)) c span) + (while (< ip l) + (if (< (setq c (aref string ip)) 128) + (progn (aset out op c) + (incf op))) + (incf ip)) + (display-buffer (set-buffer proof-pbp-buffer)) + (erase-buffer) + (insert (substring out 0 op)) + + (setq ip 0 + op 1) + (while (< ip l) + (setq c (aref string ip)) + (cond + ((= c proof-shell-goal-char) + (setq topl (cons op topl)) + (setq ap 0)) + ((= c proof-shell-start-char) + (if proof-analyse-using-stack + (setq ap (- ap (- (aref string (incf ip)) 128))) + (setq ap (- (aref string (incf ip)) 128))) + (incf ip) + (while (not (= (setq c (aref string ip)) proof-shell-end-char)) + (aset ann ap (- c 128)) + (incf ap) + (incf ip)) + (setq stack (cons op (cons (substring ann 0 ap) stack)))) + ((= c proof-shell-field-char) + (setq span (make-span (car stack) op)) + (set-span-property span 'mouse-face 'highlight) + (set-span-property span 'proof (car (cdr stack))) + ;; Pop annotation off stack + (and proof-analyse-using-stack + (progn + (setq ap 0) + (while (< ap (length (cadr stack))) + (aset ann ap (aref (cadr stack) ap)) + (incf ap)))) + ;; Finish popping annotations + (setq stack (cdr (cdr stack)))) + (t (incf op))) + (incf ip)) + (setq topl (reverse (cons (point-max) topl))) + ;; If we want Coq pbp: (setq coq-current-goal 1) + (while (setq ip (car topl) + topl (cdr topl)) + (pbp-make-top-span ip (car topl)))))) + +(defun proof-shell-strip-annotations (string) + (let* ((ip 0) (op 0) (l (length string)) (out (make-string l ?x ))) + (while (< ip l) + (if (>= (aref string ip) proof-shell-first-special-char) + (if (char-equal (aref string ip) proof-shell-start-char) + (progn (incf ip) + (while (< (aref string ip) proof-shell-first-special-char) + (incf ip)))) + (aset out op (aref string ip)) + (incf op)) + (incf ip)) + (substring out 0 op))) + +(defun proof-shell-handle-output (start-regexp end-regexp append-face) + "Pretty print output in process buffer in specified region. +The region begins with the match for START-REGEXP and is delimited by +END-REGEXP. The match for END-REGEXP is not part of the specified region. +Removes any annotations in the region. +Fontifies the region. +Appends APPEND-FACE to the text property of the region . +Returns the string (with faces) in the specified region." + (let (start end string) + (save-excursion + (set-buffer proof-shell-buffer) + (goto-char (point-max)) + (setq start (search-backward-regexp start-regexp)) + (save-excursion (setq end (- (search-forward-regexp end-regexp) + (length (match-string 0))))) + (setq string + (proof-shell-strip-annotations (buffer-substring start end))) + (delete-region start end) + (insert string) + (setq end (+ start (length string))) + ;; This doesn't work with FSF Emacs 20.2's version of Font-lock + ;; because there are no known keywords in the process buffer + ;; Never mind. In a forthcoming version, the process buffer will + ;; not be tempered with. Fontification will take place in a + ;; separate response buffer. + ;; (font-lock-fontify-region start end) + (font-lock-append-text-property start end 'face append-face) + (buffer-substring start end)))) + +(defun proof-shell-handle-delayed-output () + (let ((ins (car proof-shell-delayed-output)) + (str (cdr proof-shell-delayed-output))) + (display-buffer proof-pbp-buffer) + (save-excursion + (cond + ((eq ins 'insert) + (setq str (proof-shell-strip-annotations str)) + (set-buffer proof-pbp-buffer) + (erase-buffer) + (insert str)) + ((eq ins 'analyse) + (proof-shell-analyse-structure str)) + (t (set-buffer proof-pbp-buffer) + (insert "\n\nbug???"))))) + (run-hooks 'proof-shell-handle-delayed-output-hook) + (setq proof-shell-delayed-output (cons 'insert "done"))) + + +;; Notes on markup in the scripting buffer. (da) +;; +;; The locked region is covered by a collection of non-overlaping +;; spans (our abstraction of extents/overlays). +;; +;; For an unfinished proof, there is one extent for each command +;; or comment outside a command. For a finished proof, there +;; is one extent for the whole proof. +;; +;; Each command span has a 'type property, one of: +;; +;; 'vanilla Initialised in proof-semis-to-vanillas, for +;; 'goalsave +;; 'comment A comment outside a command. +;; +;; All spans except those of type 'comment have a 'cmd property, +;; which is set to a string of its command. This is the +;; text in the buffer stripped of leading whitespace and any comments. +;; +;; + +(defun proof-shell-handle-error (cmd string) + "React on an error message triggered by the prover. +We display the process buffer, scroll to the end, beep and fontify the +error message. At the end it calls `proof-shell-handle-error-hook'. " + ;; We extract all text between text matching + ;; `proof-shell-error-regexp' and the following prompt. + ;; Alternatively one could higlight all output between the + ;; previous and the current prompt. + ;; +/- of our approach + ;; + Previous not so relevent output is not highlighted + ;; - Proof systems have to conform to error messages whose start can be + ;; detected by a regular expression. + (proof-shell-handle-output + proof-shell-error-regexp proof-shell-annotated-prompt-regexp + 'font-lock-error-face) + (save-excursion (display-buffer proof-shell-buffer) + (beep) + + ;; unwind script buffer + (set-buffer (car proof-script-buffer-list)) + (proof-detach-queue) + (delete-spans (proof-locked-end) (point-max) 'type) + (proof-release-lock) + (run-hooks 'proof-shell-handle-error-hook))) + +(defun proof-shell-handle-interrupt () + (save-excursion + (display-buffer (set-buffer proof-pbp-buffer)) + (goto-char (point-max)) + (newline 2) + (insert-string + "Interrupt: Script Management may be in an inconsistent state\n") + (beep) + (set-buffer (car proof-script-buffer-list))) + (if proof-shell-busy + (progn (proof-detach-queue) + (delete-spans (proof-locked-end) (point-max) 'type) + (proof-release-lock)))) + + +(defun proof-goals-pos (span maparg) + "Given a span, this function returns the start of it if corresponds + to a goal and nil otherwise." + (and (eq 'goal (car (span-property span 'proof-top-element))) + (span-start span))) + +(defun proof-pbp-focus-on-first-goal () + "If the `proof-pbp-buffer' contains goals, the first one is brought + into view." + (and (fboundp 'map-extents) + (let + ((pos (map-extents 'proof-goals-pos proof-pbp-buffer + nil nil nil nil 'proof-top-element))) + (and pos (set-window-point + (get-buffer-window proof-pbp-buffer t) pos))))) + + + +(defun proof-shell-process-output (cmd string) + "Process shell output by matching on STRING. +This function deals with errors, start of proofs, abortions of +proofs and completions of proofs. All other output from the proof +engine is simply reported to the user in the response buffer. +To extend this function, set +`proof-shell-process-output-system-specific'. + +This function - it can return one of 4 things: 'error, 'interrupt, +'loopback, or nil. 'loopback means this was output from pbp, and +should be inserted into the script buffer and sent back to the proof +assistant." + (cond + ((string-match proof-shell-error-regexp string) + (cons 'error (proof-shell-strip-annotations + (substring string + (match-beginning 0))))) + + ((string-match proof-shell-interrupt-regexp string) + 'interrupt) + + ((and proof-shell-abort-goal-regexp + (string-match proof-shell-abort-goal-regexp string)) + (setq proof-shell-delayed-output (cons 'insert "\n\nAborted")) + ()) + + ((string-match proof-shell-proof-completed-regexp string) + (setq proof-shell-proof-completed t) + (setq proof-shell-delayed-output + (cons 'insert (concat "\n" (match-string 0 string))))) + + ((string-match proof-shell-start-goals-regexp string) + (setq proof-shell-proof-completed nil) + (let (start end) + (while (progn (setq start (match-end 0)) + (string-match proof-shell-start-goals-regexp + string start))) + (setq end (string-match proof-shell-end-goals-regexp string start)) + (setq proof-shell-delayed-output + (cons 'analyse (substring string start end))))) + + ((string-match proof-shell-result-start string) + (setq proof-shell-proof-completed nil) + (let (start end) + (setq start (+ 1 (match-end 0))) + (string-match proof-shell-result-end string) + (setq end (- (match-beginning 0) 1)) + (cons 'loopback (substring string start end)))) + + ;; hook to tailor proof-shell-process-output to a specific proof + ;; system + ((and proof-shell-process-output-system-specific + (funcall (car proof-shell-process-output-system-specific) + cmd string)) + (funcall (cdr proof-shell-process-output-system-specific) + cmd string)) + + (t (setq proof-shell-delayed-output (cons 'insert string))))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Low-level commands for shell communication ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; FIXME da: Should this kind of ordinary input go onto the queue of +;; things to do? Then errors during processing would prevent it being +;; sent. Also the proof shell lock would be set automatically, which +;; might be nice? +(defun proof-shell-insert (string) + (save-excursion + (set-buffer proof-shell-buffer) + (goto-char (point-max)) + + (run-hooks 'proof-shell-insert-hook) + (insert string) + + ;; xemacs and emacs19 have different semantics for what happens when + ;; shell input is sent next to a marker + ;; the following code accommodates both definitions + (if (marker-position proof-marker) + (let ((inserted (point))) + (comint-send-input) + (set-marker proof-marker inserted)) + (comint-send-input)))) + +;; da: This function strips carriage returns from string, then +;; sends it. (Why strip CRs?) +(defun proof-send (string) + (let ((l (length string)) (i 0)) + (while (< i l) + (if (= (aref string i) ?\n) (aset string i ?\ )) + (incf i))) + (save-excursion (proof-shell-insert string))) + +(defun proof-check-process-available (&optional relaxed) + "Adjust internal variables for scripting of current buffer. + +Signals an error if current buffer is not a proof script or if the +proof process is not idle. If RELAXED is set, nothing more is done. No +changes are necessary if the current buffer is already in Scripting +minor mode. Otherwise, the current buffer will become the current +scripting buffer provided the current scripting buffer has either no +locked region or everything in it has been processed." + (proof-start-shell) + (cond + ((not (or relaxed (eq proof-buffer-type 'script))) + (error "Must be running in a script buffer")) + (proof-shell-busy (error "Proof Process Busy!")) + (relaxed ()) ;exit cond + + ;; No buffer is in Scripting minor mode. + ;; We assume the setup is done somewhere else + ;; so this should never happen + ((null proof-script-buffer-list) (assert nil)) + + ;; The current buffer is in Scripting minor mode + ;; so there's nothing to do + ((equal (current-buffer) (car proof-script-buffer-list))) + + (t + (let ((script-buffer (car proof-script-buffer-list)) + (current-buffer (current-buffer))) + (save-excursion + (set-buffer script-buffer) + ;; We only allow switching of the Scripting buffer if the + ;; locked region is either empty or full for all buffers. + ;; Here we check the current Scripting buffer's status. + (let + ((locked-is-empty (eq (proof-unprocessed-begin) (point-min))) + (locked-is-full (progn + (goto-char (point-max)) + (skip-chars-backward " \t\n") + (>= (proof-unprocessed-begin) (point))))) + (if locked-is-full + ;; We're switching from a buffer which has been + ;; completely processed. Make sure that it's + ;; registered on the included files list, in + ;; case it has been processed piecemeal. + (if buffer-file-name + (proof-register-possibly-new-processed-file + buffer-file-name))) + + (if (or locked-is-full locked-is-empty) + ;; we are changing the scripting buffer + (progn + (setq proof-active-buffer-fake-minor-mode nil) + (set-buffer current-buffer) + + ;; does the current buffer contain locked regions already? + (if (member current-buffer proof-script-buffer-list) + (setq proof-script-buffer-list + (remove current-buffer proof-script-buffer-list)) + (proof-init-segmentation)) + (setq proof-script-buffer-list + (cons current-buffer proof-script-buffer-list)) + (setq proof-active-buffer-fake-minor-mode t) + (run-hooks 'proof-activate-scripting-hook)) + ;; Locked region covers only part of the buffer + ;; FIXME da: ponder alternative of trying to complete rest + ;; of current scripting buffer? + (error "Cannot deal with two unfinished script buffers!")))))))) + + +(defun proof-grab-lock (&optional relaxed) + (proof-start-shell) + (proof-check-process-available relaxed) + (setq proof-shell-busy t)) + +(defun proof-release-lock () + (if (proof-shell-live-buffer) + (progn + (if (not proof-shell-busy) + ; (error "Bug in proof-release-lock: Proof process not busy") + (message "Nag, nag, nag: proof-release-lock: Proof process not busy")) + (if (not (member (current-buffer) proof-script-buffer-list)) + (error "Bug in proof-release-lock: Don't own process")) + (setq proof-shell-busy nil)))) + +; Pass start and end as nil if the cmd isn't in the buffer. + +(defun proof-start-queue (start end alist &optional relaxed) + (if start + (proof-set-queue-endpoints start end)) + (let (item) + (while (and alist (string= + (nth 1 (setq item (car alist))) + proof-no-command)) + (funcall (nth 2 item) (car item)) + (setq alist (cdr alist))) + (if alist + (progn + (proof-grab-lock relaxed) + (setq proof-shell-delayed-output (cons 'insert "Done.")) + (setq proof-action-list alist) + (proof-send (nth 1 item)))))) + +; returns t if it's run out of input + + +;; +;; The loop looks like: Execute the +; command, and if it's successful, do action on span. If the +; command's not successful, we bounce the rest of the queue and do +; some error processing. + +(defun proof-shell-exec-loop () +"Process the proof-action-list. +proof-action-list contains a list of (span,command,action) triples. +This function is called with a non-empty proof-action-list, where the +head of the list is the previously executed command which succeeded. +We execute (action span) on the first item, then (action span) on +following items which have proof-no-command as their cmd components. +Return non-nil if the action list becomes empty; unlock the process +and removes the queue region. Otherwise send the next command to the +proof process." + (save-excursion + (set-buffer (car proof-script-buffer-list)) + ;; (if (null proof-action-list) + ;; (error "proof-shell-exec-loop: called with empty proof-action-list.")) + ;; Be more relaxed about calls with empty action list + (if proof-action-list + (let ((item (car proof-action-list))) + ;; Do (action span) on first item in list + (funcall (nth 2 item) (car item)) + (setq proof-action-list (cdr proof-action-list)) + ;; Process following items in list with the form + ;; ("COMMENT" cmd) by calling (cmd "COMMENT") + (while (and proof-action-list + (string= + (nth 1 (setq item (car proof-action-list))) + proof-no-command)) + ;; Do (action span) on comments + (funcall (nth 2 item) (car item)) + (setq proof-action-list (cdr proof-action-list))) + ;; If action list is empty now, release the process lock + (if (null proof-action-list) + (progn (proof-release-lock) + (proof-detach-queue) + ;; indicate finished + t) + ;; Send the next command to the process + (proof-send (nth 1 item)) + ;; indicate not finished + nil)) + ;; Just indicate finished if called with empty proof-action-list. + t))) + +(defun proof-shell-insert-loopback-cmd (cmd) + "Insert command sequence triggered by the proof process +at the end of locked region (after inserting a newline and indenting)." + (save-excursion + (set-buffer (car proof-script-buffer-list)) + (let (span) + (proof-goto-end-of-locked) + (newline-and-indent) + (insert cmd) + (setq span (make-span (proof-locked-end) (point))) + (set-span-property span 'type 'pbp) + (set-span-property span 'cmd cmd) + (proof-set-queue-endpoints (proof-locked-end) (point)) + (setq proof-action-list + (cons (car proof-action-list) + (cons (list span cmd 'proof-done-advancing) + (cdr proof-action-list))))))) + +;; ******** NB ********** +;; While we're using pty communication, this code is OK, since all +;; eager annotations are one line long, and we get input a line at a +;; time. If we go over to piped communication, it will break. + +;; FIXME: highlight eager annotation-end : fix proof-shell-handle-output +;; to highlight whole string. +(defun proof-shell-popup-eager-annotation () + "Process urgent messages. +Eager annotations are annotations which the proof system produces +while it's doing something (e.g. loading libraries) to say how much +progress it's made. Obviously we need to display these as soon as they +arrive." + (let ((str (proof-shell-handle-output + proof-shell-eager-annotation-start + proof-shell-eager-annotation-end + 'font-lock-eager-annotation-face)) + file module) + (proof-message str))) + +(defun proof-response-buffer-display (str face) + "Display STR with FACE in response buffer." + (let ((start)) + (save-excursion + (set-buffer proof-response-buffer) + (setq start (goto-char (point-max))) + (insert str) + (font-lock-fontify-region start (point-max)) + (font-lock-append-text-property start (point-max) 'face face) + (insert "\n")))) + +(defun proof-file-to-buffer (filename) + "Converts a FILENAME into a buffer name" + (let* ((buffers (buffer-list)) + (pos + (position (file-truename filename) + (mapcar 'proof-file-truename + (mapcar 'buffer-file-name + buffers)) + :test 'equal))) + (and pos (nth pos buffers)))) + +(defun proof-files-to-buffers (filenames) + "Converts a list of FILENAMES into a list of BUFFERS." + (if (null filenames) nil + (let* ((buffer (proof-file-to-buffer (car filenames))) + (rest (proof-files-to-buffers (cdr filenames)))) + (if buffer (cons buffer rest) rest)))) + + +(defun proof-shell-process-urgent-message (message) + "Display and analyse urgent MESSAGE for asserting or retracting files." + + ;; Is the prover processing a file? + (cond ((and proof-shell-process-file + (string-match (car proof-shell-process-file) message)) + (let + ((file (funcall (cdr proof-shell-process-file) message))) + (if (string= file "") + (setq file (buffer-file-name (car proof-script-buffer-list)))) + (if file + (proof-register-possibly-new-processed-file file)))) + + ;; Is the prover retracting across files? + ((and proof-shell-retract-files-regexp + (string-match proof-shell-retract-files-regexp message)) + (let ((current-included proof-included-files-list)) + (setq proof-included-files-list + (funcall proof-shell-compute-new-files-list message)) + (proof-restart-buffers + (remove (car proof-script-buffer-list) + (proof-files-to-buffers + (set-difference current-included + proof-included-files-list)))))) + (t + (proof-message message) + (proof-response-buffer-display message + 'font-lock-eager-annotation-face)))) + +(defun proof-shell-process-urgent-messages (str) + "Scan the process output for urgent messages. +Display them immediately in the Response buffer. +Keep `proof-included-files-list' up to date. +The input STR is the most recent chunk of output sent to the process +filter. We assume that urgent messages are fully contained in STR." + (let ((start (string-match proof-shell-eager-annotation-start str)) + end) + (and start + (setq end + (string-match proof-shell-eager-annotation-end str + start)) + (progn + (proof-shell-process-urgent-message + (proof-shell-strip-annotations (substring str start end))) + (proof-shell-process-urgent-messages (substring str end)))))) + +(defun proof-shell-filter (str) + "Filter for the proof assistant shell-process. +We sleep until we get a wakeup-char in the input, then run +proof-shell-process-output, and set proof-marker to keep track of +how far we've got." + (and proof-shell-eager-annotation-start + (proof-shell-process-urgent-messages str)) + + (if (or + ;; Some proof systems can be hacked to have annotated prompts; + ;; for these we set proof-shell-wakeup-char to the annotation special. + (and proof-shell-wakeup-char + (string-match (char-to-string proof-shell-wakeup-char) str)) + ;; Others rely on a standard top-level (e.g. SML) whose prompt can't + ;; be hacked. For those we use the annotated prompt regexp. + (string-match proof-shell-annotated-prompt-regexp str)) + (if (null (marker-position proof-marker)) + ;; Set marker to first prompt in output buffer, and sleep again. + (progn + (goto-char (point-min)) + (re-search-forward proof-shell-annotated-prompt-regexp) + (set-marker proof-marker (point))) + ;; We've matched against a second prompt in str now. Search + ;; the output buffer for the second prompt after the one previously + ;; marked. + (let (string res cmd) + (goto-char (marker-position proof-marker)) + (re-search-forward proof-shell-annotated-prompt-regexp nil t) + (backward-char (- (match-end 0) (match-beginning 0))) + (setq string (buffer-substring (marker-position proof-marker) + (point))) + (goto-char (point-max)) ; da: assumed to be after a prompt? + (setq cmd (nth 1 (car proof-action-list))) + (save-excursion + ;; + (setq res (proof-shell-process-output cmd string)) + ;; da: Added this next line to redisplay, for proof-toolbar + ;; FIXME: should do this for all frames displaying the script + ;; buffer! + ;; ODDITY: has strange effect on highlighting, leave it. + ;; (redisplay-frame (window-buffer t) + (cond + ((eq (car-safe res) 'error) + (proof-shell-handle-error cmd (cdr res))) + ((eq res 'interrupt) + (proof-shell-handle-interrupt)) + ((eq (car-safe res) 'loopback) + (proof-shell-insert-loopback-cmd (cdr res)) + (proof-shell-exec-loop)) + (t (if (proof-shell-exec-loop) + (proof-shell-handle-delayed-output))))))))) + +(defun proof-last-goal-or-goalsave () + (save-excursion + (let ((span (span-at-before (proof-locked-end) 'type))) + (while (and span + (not (eq (span-property span 'type) 'goalsave)) + (or (eq (span-property span 'type) 'comment) + (not (funcall proof-goal-command-p + (span-property span 'cmd))))) + (setq span (prev-span span 'type))) + span))) + +(defun proof-done-invisible (span) ()) + +(defun proof-invisible-command (cmd &optional relaxed) + "Send cmd to the proof process without responding to the user." + (proof-check-process-available relaxed) + (if (not (string-match proof-re-end-of-cmd cmd)) + (setq cmd (concat cmd proof-terminal-string))) + (proof-start-queue nil nil (list (list nil cmd + 'proof-done-invisible)) relaxed)) + + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Proof General shell mode definition ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; This has to come after proof-mode is defined +(define-derived-mode proof-shell-mode comint-mode + "proof-shell" "Proof shell mode - not standalone" + (setq proof-buffer-type 'shell) + (setq proof-shell-busy nil) + (setq proof-shell-delayed-output (cons 'insert "done")) + (setq proof-shell-proof-completed nil) + (make-local-variable 'proof-shell-insert-hook) + + ;; comint customisation. comint-prompt-regexp is used by + ;; comint-get-old-input, comint-skip-prompt, comint-bol, backward + ;; matching input, etc. + (setq comint-prompt-regexp proof-shell-prompt-pattern) + + ;; An article by Helen Lowe in UITP'96 suggests that the user should + ;; not see all output produced by the proof process. + (remove-hook 'comint-output-filter-functions + 'comint-postoutput-scroll-to-bottom 'local) + + (add-hook 'comint-output-filter-functions 'proof-shell-filter nil 'local) + (setq comint-get-old-input (function (lambda () ""))) + (proof-dont-show-annotations) + (setq proof-marker (make-marker))) + +(easy-menu-define proof-shell-menu + proof-shell-mode-map + "Menu used in Proof General shell mode." + (cons proof-mode-name (cdr proof-shell-menu))) + +(defun proof-shell-config-done () + (accept-process-output (get-buffer-process (current-buffer))) + + ;; If the proof process in invoked on a different machine e.g., + ;; for proof-prog-name="rsh fastmachine proofprocess", one needs + ;; to adjust the directory: + (and proof-shell-cd + (proof-shell-insert (format proof-shell-cd + ;; under Emacs 19.34 default-directory contains "~" which causes + ;; problems with LEGO's internal Cd command + (expand-file-name default-directory)))) + + (if proof-shell-init-cmd + (proof-shell-insert proof-shell-init-cmd)) + + ;; Note that proof-marker actually gets set in proof-shell-filter. + ;; This is manifestly a hack, but finding somewhere more convenient + ;; to do the setup is tricky. + + (while (null (marker-position proof-marker)) + (if (accept-process-output (get-buffer-process (current-buffer)) 15) + () + (error "Failed to initialise proof process")))) + +(define-derived-mode pbp-mode fundamental-mode + proof-mode-name "Proof by Pointing" + ;; defined-derived-mode pbp-mode initialises pbp-mode-map + (setq proof-buffer-type 'pbp) + (suppress-keymap pbp-mode-map 'all) +; (define-key pbp-mode-map [(button2)] 'pbp-button-action) + (proof-define-keys pbp-mode-map proof-universal-keys) + (erase-buffer)) + + + +(provide 'proof-shell) +;; proof-shell.el ends here.
\ No newline at end of file diff --git a/generic/proof.el b/generic/proof.el index 91036383..8a5b00f2 100644 --- a/generic/proof.el +++ b/generic/proof.el @@ -12,36 +12,46 @@ ;; ;; $Id$ ;; -;; PENDING: + +(require 'proof-site) + + ;; -;; da: I propose splitting this file as follows: ;; -;; proof.el Controls loading. Requires proof-script, proof-shell. -;; Retains misc utils, user options, splash screen. -;; proof-script.el Script management mode. -;; proof-shell.el Proof shell mode. +;; User options for proof mode ;; -;; The shell mode and script mode are already fairly independent in -;; this file. +;; See proof-config.el for configuration variables. ;; - -(require 'proof-site) - ;; -;; Here is the first configuration variable, proof-assistant. -;; Others follow below, along with notes about the prover-config -;; customization group. +;; The following variables are user options for Proof General. +;; They appear in the 'proof' customize group and should +;; not normally be touched by prover specific code. ;; -(defgroup prover-config nil - "Configuration of Proof General for the prover in use." - :group 'proof-internal) -(defcustom proof-assistant "" - "Name of the proof assistant Proof General is using. -This is set automatically by the mode stub defined in proof-site, -from the name given in proof-internal-assistant-table." +(defcustom proof-prog-name-ask-p nil + "*If non-nil, query user which program to run for the inferior process." + :type 'boolean + :group 'proof) + +(defcustom proof-one-command-per-line nil + "*If non-nil, format for newlines after each proof command in a script." + :type 'boolean + :group 'proof) + +(defcustom proof-general-home-page + "http://www.dcs.ed.ac.uk/home/proofgen" + "*Web address for Proof General" :type 'string - :group 'proof-config) + :group 'proof-internal) + +;; configuration variables +(require 'proof-config) + +;; A global constant +(defconst proof-mode-name "Proof-General" + "Root name for proof script mode. +Used internally and in menu titles.") + ;;; @@ -180,27 +190,33 @@ No effect if proof-display-splash-time is zero." )) ;;; End splash screen code + ;;; -;;; Requires for included modules. +;;; Load included modules. ;;; -;; FIXME da: I think some of these should be autoloaded (etags,...) + +;; FIXME da: I think these ones should be autoloaded!! (require 'cl) (require 'compile) (require 'comint) (require 'etags) +(require 'easymenu) + + +;; Spans are our abstraction of extents/overlays. (cond ((fboundp 'make-extent) (require 'span-extent)) ((fboundp 'make-overlay) (require 'span-overlay)) (t nil)) -(require 'easymenu) ; present in XEmacs 20.4 by default. + (require 'proof-syntax) (require 'proof-indent) ;; browse-url function doesn't seem to be autoloaded in -;; XEmacs 20.4, is in FSF Emacs 20.2. +;; XEmacs 20.4, but it is in FSF Emacs 20.2. (or (fboundp 'browse-url) (autoload 'browse-url "browse-url" "Ask a WWW browser to load URL." t)) @@ -212,2785 +228,16 @@ No effect if proof-display-splash-time is zero." (list 'make-variable-buffer-local (list 'quote var)) (list 'setq-default var value))) -;; Load toolbar code if toolbars available. -(if (featurep 'toolbar) - (require 'proof-toolbar)) - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; User options ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; The following variables are user options for Proof General. -;; They appear in the 'proof' customize group and should -;; not normally be touched by prover specific code. -;; -;; -(defcustom proof-prog-name-ask-p nil - "*If non-nil, query user which program to run for the inferior process." - :type 'boolean - :group 'proof) - -(defcustom proof-one-command-per-line nil - "*If non-nil, format for newlines after each proof command in a script." - :type 'boolean - :group 'proof) - -(defcustom proof-general-home-page - "http://www.dcs.ed.ac.uk/home/proofgen" - "*Web address for Proof General" - :type 'string - :group 'proof-internal) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Configuration for proof assistant ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; The variables in the "prover-config" customize group are those -;; which are intended to be set by the prover specific elisp, -;; i.e. constants set on a per-prover basis. -;; -;; Putting these in a customize group is useful for documenting -;; this type of variable, and for developing a new instantiation -;; of Proof General. -;; But it is *not* useful for final user-level customization! -;; The reason is that saving these customizations across a session is -;; not liable to work, because the prover specific elisp usually -;; overrides with a series of setq's in <assistant>-mode-config type -;; functions. This is why prover-config appears under the -;; proof-internal group. -;; -;; - -;; The Customization menus would be nicer if the variables in -;; prover-config group were uniformly renamed prover-config-* -;; (and similarly for other variables/groups). But it's -;; somewhat of a horror in the code? - -(defcustom proof-www-home-page "" - "Web address for information on proof assistant" - :type 'string - :group 'prover-config) - -(defcustom proof-shell-cd nil - "Command to the inferior process to change the directory." - :type 'string - :group 'prover-config) - -(defcustom proof-tags-support t - "If non-nil, include tags functions in menus. -This variable should be set before requiring proof.el" - :type 'boolean - :group 'prover-config) - -(defcustom proof-shell-process-output-system-specific nil - "Set this variable to handle system specific output. -Errors, start of proofs, abortions of proofs and completions of -proofs are recognised in the function `proof-shell-process-output'. -All other output from the proof engine is simply reported to the -user in the RESPONSE buffer. - -To catch further special cases, set this variable to a pair of -functions '(condf . actf). Both are given (cmd string) as arguments. -`cmd' is a string containing the currently processed command. -`string' is the response from the proof system. To change the -behaviour of `proof-shell-process-output', (condf cmd string) must -return a non-nil value. Then (actf cmd string) is invoked. See the -documentation of `proof-shell-process-output' for the required -output format." - :type '(cons (function function)) - :group 'prover-config) - -(defcustom proof-activate-scripting-hook nil - "Hook run when a buffer is switched into scripting mode. -The current buffer will be the newly active scripting buffer. - -This hook may be useful for synchronizing with the proof -assistant, for example, to switch to a new theory.") - -;; -;; The following variables should be set before proof-config-done -;; is called. These configure the mode for the script buffer, -;; including highlighting, etc. -;; - -(defgroup proof-script nil - "Proof General configuration of scripting buffer mode." - :group 'prover-config) - - -(defcustom proof-terminal-char nil - "Character which terminates a proof command in a script buffer." - :type 'character - :group 'proof-script) - -(defcustom proof-comment-start "" - "String which starts a comment in the proof assistant command language. -The script buffer's comment-start is set to this string plus a space." - :type 'string - :group 'proof-script) - -(defcustom proof-comment-end "" - "String which starts a comment in the proof assistant command language. -The script buffer's comment-end is set to this string plus a space." - :type 'string - :group 'proof-script) - -(defcustom proof-save-command-regexp nil - "Matches a save command" - :type 'regexp - :group 'prover-config) - -(defcustom proof-save-with-hole-regexp nil - "Matches a named save command" - :type 'regexp - :group 'proof-script) - -(defcustom proof-goal-with-hole-regexp nil - "Matches a saved goal command" - :type 'regexp - :group 'proof-script) - -(defcustom proof-goal-command-p nil - "Is this a goal" - :type 'function - :group 'proof-script) - -(defcustom proof-lift-global nil - "This function lifts local lemmas from inside goals out to top level. -This function takes the local goalsave span as an argument. Set this -to `nil' of the proof assistant does not support nested goals." - :type '(choice nil function) - :group 'proof-script) - -(defcustom proof-count-undos-fn nil - "Compute number of undos in a target segment" - :type 'function - :group 'proof-script) - -(defcustom proof-atomic-sequence-lists nil - "list of instructions for setting up atomic sequences of commands (ACS). - -Each instruction is -a list of the form `(END START &optional FORGET-COMMAND)'. END is a -regular expression to recognise the last command in an ACS. START -is a function. Its input is the last command of an ACS. Its output -is a regular exression to recognise the first command of the ACS. -It is evaluated once and the output is successively matched agains -previously processed commands until a match occurs (or the -beginning of the current buffer is reached). The region determined -by (START,END) is locked as an ACS. Optionally, the ACS is -annotated with the actual command to retract the ACS. This is -computed by applying FORGET-COMMAND to the first and last command -of the ACS." - :type '(repeat (cons regexp function (choice (const nil) function))) - :group 'proof-shell) - - -(defconst proof-no-command "COMMENT" - "String used as a nullary action (send no command to the proof assistant). -Only relevant for proof-find-and-forget-fn.") - -(defcustom proof-find-and-forget-fn nil - "Function returning a command string to forget back to before its argument span. -The special string proof-no-command means there is nothing to do." - :type 'function - :group 'proof-script) - -(defcustom proof-goal-hyp-fn nil - "A function which returns cons cell if point is at a goal/hypothesis. -First element of cell is a symbol, 'goal' or 'hyp'. The second -element is a string: the goal or hypothesis itself. This is used -when parsing the proofstate output" - :type 'function - :group 'proof-script) - -(defcustom proof-kill-goal-command "" - "Command to kill a goal." - :type 'string - :group 'proof-script) - -(defcustom proof-global-p nil - "Whether a command is a global declaration. Predicate on strings or nil. -This is used to handle nested goals allowed by some provers." - :type 'function - :group 'proof-script) - -(defcustom proof-state-preserving-p nil - "A predicate, non-nil if its argument preserves the proof state." - :type 'function - :group 'proof-script) - -(defcustom pbp-change-goal nil - "Command to change to the goal %s" - :type 'string - :group 'prover-config) - -;; -;; The following variables should be set in -;; proof-pre-shell-start-hook. -;; - -;; FIXME da: these could be put in another customize group? - -(defcustom proof-prog-name nil - "Command to run program name in proof shell" - :type 'string - :group 'prover-config) - -(defcustom proof-mode-for-shell nil - "Mode for proof shell" - :type 'function - :group 'prover-config) - -(defcustom proof-mode-for-pbp nil - "Mode for Proof-by-Pointing." - :type 'function - :group 'prover-config) - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Generic config for proof shell ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; The variables in this section concern the proof shell mode. -;; The first group of variables are hooks invoked at various points. -;; The second group of variables are concerned with matching the output -;; from the proof assistant. -;; -;; Variables here are put into the customize group 'proof-shell'. -;; -;; These should be set in the shell mode configuration, again, -;; before proof-shell-config-done is called. -;; - -(defgroup proof-shell nil - "Settings for output from the proof assistant in the proof shell." - :group 'prover-config) - -;; -;; Hooks for the proof shell -;; - -(defcustom proof-shell-insert-hook nil - "Hooks run by proof-shell-insert before inserting a command. -Can be used to configure the proof assistant to the interface in -various ways (for example, setting the line width of output). -Any text inserted by these hooks will be sent to the proof process -before every command issued by Proof General." - :type '(repeat function) - :group 'proof-shell) - -(defcustom proof-pre-shell-start-hook nil - "Hooks run before proof shell is started. -Usually this is set to a function which configures the proof shell -variables." - :type '(repeat function) - :group 'proof-shell) - -(defcustom proof-shell-init-cmd "" - "The command for initially configuring the proof process." - :type '(choice string (const nil)) - :group 'proof-shell) - -(defcustom proof-shell-handle-delayed-output-hook - '(proof-pbp-focus-on-first-goal) - "Hooks run after new output has been displayed in the RESPONSE buffer." - :type '(repeat function) - :group 'proof-shell) - -(defcustom proof-shell-handle-error-hook - '(proof-goto-end-of-locked-if-pos-not-visible-in-window) - "Hooks run after an error has been reported in the RESPONSE buffer." - :type '(repeat function) - :group 'proof-shell) - ;; -;; Regexp variables for matching output from proof process. +;; Rest of code ;; -(defcustom proof-shell-prompt-pattern nil - "Proof shell's value for comint-prompt-pattern, which see." - :type 'regexp - :group 'proof-shell) - -;; FIXME da: replace this with wakeup-regexp or prompt-regexp? -;; May not need next variable. -(defcustom proof-shell-wakeup-char nil - "A special character which terminates an annotated prompt. -Set to nil if proof assistant does not support annotated prompts." - :type '(choice character (const nil)) - :group 'proof-shell) - -(defcustom proof-shell-annotated-prompt-regexp "" - "Regexp matching a (possibly annotated) prompt pattern. -Output is grabbed between pairs of lines matching this regexp. -To help matching you may be able to annotate the proof assistant -prompt with a special character not appearing in ordinary output. -The special character should appear in this regexp, should -be the value of proof-shell-wakeup-char." - :type 'regexp - :group 'proof-shell) - -(defcustom proof-shell-abort-goal-regexp nil - "Regexp matching output from an aborted proof." - :type 'regexp - :group 'proof-shell) - -(defcustom proof-shell-error-regexp nil - "Regexp matching an error report from the proof assistant. -We assume that an error message corresponds to a failure -in the last proof command executed. (So don't match -mere warning messages with this regexp)." - :type 'regexp - :group 'proof-shell) - -(defcustom proof-shell-interrupt-regexp nil - "Regexp matching output indicating the assistant was interrupted." - :type 'regexp - :group 'proof-shell) - -(defcustom proof-shell-proof-completed-regexp nil - "Regexp matching output indicating a finished proof." - :type 'regexp - :group 'proof-shell) - -(defcustom proof-shell-result-start "" - "Regexp matching start of an output from the prover after pbp commands. -In particular, after a `pbp-goal-command' or a `pbp-hyp-command'." - :type 'regexp - :group 'proof-shell) - -(defcustom proof-shell-result-end "" - "Regexp matching end of output from the prover after pbp commands. -In particular, after a `pbp-goal-command' or a `pbp-hyp-command'." - :type 'regexp - :group 'proof-shell) - -(defcustom proof-shell-start-goals-regexp "" - "Regexp matching the start of the proof state output." - :type 'regexp - :group 'proof-shell) - -(defcustom proof-shell-end-goals-regexp "" - "Regexp matching the end of the proof state output." - :type 'regexp - :group 'proof-shell) - -(defcustom pbp-error-regexp nil - "Regexp indicating that the proof process has identified an error." - :type 'regexp - :group 'proof-shell) - -(defcustom proof-shell-process-file nil - "A tuple of the form (regexp . function) to match a processed file name. - -If REGEXP matches output, then the function FUNCTION is invoked on the -output string chunk. It must return a script file name (with complete -path) the system is currently processing. In practice, FUNCTION is -likely to inspect the match data. If it returns the empty string, -the file name of the scripting buffer is used instead. If it -returns nil, no action is taken. - -Care has to be taken in case the prover only reports on compiled -versions of files it is processing. In this case, FUNCTION needs to -reconstruct the corresponding script file name. The new (true) file -name is added to the front of `proof-included-files-list'." - :type '(choice (cons regexp function) (const nil)) - :group 'proof-shell) - -(defcustom proof-shell-retract-files-regexp nil - "A REGEXP to match that the prover has retracted across file boundaries. - -At this stage, Proof General's view of the processed files is out of -date and needs to be updated with the help of the function -`proof-shell-compute-new-files-list'." - :type '(choice regexp (const nil)) - :group 'proof-shell) - -(defcustom proof-shell-compute-new-files-list nil - "Function to update `proof-included-files list'. - -It needs to return an up to date list of all processed files. Its -output is stored in `proof-included-files-list'. Its input is the -string of which `proof-shell-retract-files-regexp' matched a -substring. In practice, this function is likely to inspect the -previous (global) variable `proof-included-files-list' and the match -data triggered by `proof-shell-retract-files-regexp'." - :type '(choice function (const nil)) - :group 'proof-shell) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Internal variables used by scripting and pbp ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defconst proof-mode-name "Proof-General") - -;; FIXME da: remove proof-terminal-string. At the moment some -;; commands need to have the terminal string, some don't. -;; We should assume commands are terminated properly at the -;; specific level. -(defvar proof-terminal-string nil - "End-of-line string for proof process.") - -(defvar proof-re-end-of-cmd nil - "Regexp matching end of command. Based on proof-terminal-string. -Set in proof-config-done.") - -(defvar proof-re-term-or-comment nil - "Regexp matching terminator, comment start, or comment end. -Set in proof-config-done.") - -(defvar proof-marker nil - "Marker in proof shell buffer pointing to previous command input.") - -(defvar proof-shell-buffer nil - "Process buffer where the proof assistant is run.") - -(defvar proof-script-buffer-list nil - "Scripting buffers with locked regions. -The first element is current and in scripting minor mode. -The cdr of the list of corresponding file names is a subset of -`proof-included-files-list'.") - -(defvar proof-pbp-buffer nil - "The goals buffer (also known as the pbp buffer).") - -(defvar proof-response-buffer nil - "The response buffer.") - -(defvar proof-shell-busy nil - "A lock indicating that the proof shell is processing. -When this is non-nil, proof-check-process-available will give -an error.") - -(deflocal proof-buffer-type nil - "Symbol indicating the type of this buffer: 'script, 'shell, or 'pbp.") - -(defvar proof-action-list nil "action list") - -(defvar proof-included-files-list nil - "List of files currently included in proof process. -Whenever a new file is being processed, it gets added to the front of -the list. When the prover retracts across file boundaries, this list -is resynchronised. It contains files in canonical truename format") - -(deflocal proof-active-buffer-fake-minor-mode nil - "An indication in the modeline that this is the *active* script buffer") - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; A few small utilities ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun proof-match-find-next-function-name (buffer) - "General next function name in BUFFER finder using match. -The regexp is assumed to be a two item list the car of which is the regexp -to use, and the cdr of which is the match position of the function -name. Moves point after the match. - -The package fume-func provides the function -`fume-match-find-next-function-name' with the same specification. -However, fume-func's version is incorrec" - ;; DO NOT USE save-excursion; we need to move point! - ;; save-excursion is called at a higher level in the func-menu - ;; package - (set-buffer buffer) - (let ((r (car fume-function-name-regexp)) - (p (cdr fume-function-name-regexp))) - (and (re-search-forward r nil t) - (cons (buffer-substring (setq p (match-beginning p)) (point)) p)))) - -(defun proof-message (str) - "Output STR in minibuffer." - (message (concat "[" proof-assistant "] " str))) - -;; append-element in tl-list -(defun proof-append-element (ls elt) - "Append ELT to last of LS if ELT is not nil. [proof.el] -This function coincides with `append-element' in the package -[tl-list.el.]" - (if elt - (append ls (list elt)) - ls)) - -(defun proof-define-keys (map kbl) - "Adds keybindings KBL in MAP. -The argument KBL is a list of tuples (k . f) where `k' is a keybinding -(vector) and `f' the designated function." - (mapcar - (lambda (kbl) - (let ((k (car kbl)) (f (cdr kbl))) - (define-key map k f))) - kbl)) - -(defun proof-string-to-list (s separator) - "Return the list of words in S separated by SEPARATOR." - (let ((end-of-word-occurence (string-match (concat separator "+") s))) - (if (not end-of-word-occurence) - (if (string= s "") - nil - (list s)) - (cons (substring s 0 end-of-word-occurence) - (proof-string-to-list - (substring s - (string-match (concat "[^" separator "]") - s end-of-word-occurence)) separator))))) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Basic code for the locked region and the queue region ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; FIXME: da: doc below needs fixing. -(defvar proof-locked-hwm nil - "Upper limit of the locked region") - -(defvar proof-queue-loose-end nil - "Limit of the queue region that is not equal to proof-locked-hwm.") - -(defvar proof-locked-span nil - "Upper limit of the locked region") - -(defvar proof-queue-span nil - "Upper limit of the locked region") - -(make-variable-buffer-local 'proof-locked-span) -(make-variable-buffer-local 'proof-queue-span) - -(defface proof-queue-face - '((((type x) (class color) (background light)) - (:background "mistyrose")) - (((type x) (class color) (background dark)) - (:background "mediumvioletred")) - (t - (:foreground "white" :background "black"))) - "Face for commands in proof script waiting to be processed." - :group 'proof) - -(defface proof-locked-face - '((((type x) (class color) (background light)) - (:background "lavender")) - (((type x) (class color) (background dark)) - (:background "navy")) - (t - (:underline t))) - "Face for locked region of proof script (processed commands)." - :group 'proof) - -(defun proof-init-segmentation () - (setq proof-queue-loose-end nil) - (if (not proof-queue-span) - (setq proof-queue-span (make-span 1 1))) - (set-span-property proof-queue-span 'start-closed t) - (set-span-property proof-queue-span 'end-open t) - (span-read-only proof-queue-span) - - (set-span-property proof-queue-span 'face 'proof-queue-face) - - (detach-span proof-queue-span) - - (setq proof-locked-hwm nil) - (if (not proof-locked-span) - (setq proof-locked-span (make-span 1 1))) - (set-span-property proof-locked-span 'start-closed t) - (set-span-property proof-locked-span 'end-open t) - (span-read-only proof-locked-span) - - (set-span-property proof-locked-span 'face 'proof-locked-face) - - (detach-span proof-locked-span)) - -(defsubst proof-lock-unlocked () - "Make the locked region read only." - (span-read-only proof-locked-span)) - -(defsubst proof-unlock-locked () - "Make the locked region read-write." - (span-read-write proof-locked-span)) - -(defsubst proof-set-queue-endpoints (start end) - "Set the queue span to be START, END." - (set-span-endpoints proof-queue-span start end)) - -(defsubst proof-set-locked-endpoints (start end) - "Set the locked span to be START, END." - (set-span-endpoints proof-locked-span start end)) - -(defsubst proof-detach-queue () - "Remove the span for the queue region." - (and proof-queue-span (detach-span proof-queue-span))) - -(defsubst proof-detach-locked () - "Remove the span for the locked region." - (and proof-locked-span (detach-span proof-locked-span))) - -(defsubst proof-set-queue-start (start) - "Set the queue span to begin at START." - (set-span-start proof-queue-span start)) - -(defsubst proof-set-queue-end (end) - "Set the queue span to end at END." - (set-span-end proof-queue-span end)) - -(defun proof-detach-segments (&optional buffer) - "Remove locked and queue region from BUFFER. -Defaults to current buffer when BUFFER is nil." - (let ((buffer (or buffer (current-buffer)))) - (save-excursion - (proof-detach-queue) - (proof-detach-locked)))) - -(defsubst proof-set-locked-end (end) - "Set the end of the locked region to be END, sort of? FIXME. -If END is past the end of the buffer, remove the locked region. -Otherwise set the locked region to be from the start of the -buffer to END." - (if (>= (point-min) end) - (proof-detach-locked) - (set-span-endpoints proof-locked-span (point-min) end))) - -(defun proof-unprocessed-begin () - "Return end of locked region in current buffer or (point-min) otherwise." - (or - (and (member (current-buffer) proof-script-buffer-list) - proof-locked-span (span-end proof-locked-span)) - (point-min))) - -(defun proof-locked-end () - "Return end of the locked region of the current buffer. -Only call this from a scripting buffer." - (if (member (current-buffer) proof-script-buffer-list) - (proof-unprocessed-begin) - (error "bug: proof-locked-end called from wrong buffer"))) - -(defsubst proof-end-of-queue () - "Return the end of the queue region, or nil if none." - (and proof-queue-span (span-end proof-queue-span))) - -(defun proof-dont-show-annotations () - "Set display values of annotations (characters 128-255) to be invisible." - (let ((disp (make-display-table)) - (i 128)) - (while (< i 256) - (aset disp i []) - (incf i)) - (cond ((fboundp 'add-spec-to-specifier) - (add-spec-to-specifier current-display-table disp - (current-buffer))) - ((boundp 'buffer-display-table) - (setq buffer-display-table disp))))) - -(defun proof-mark-buffer-atomic (buffer) - "Mark BUFFER as having been processed in a single step. - -If buffer already contains a locked region, only the remainder of the -buffer is closed off atomically." - (save-excursion - (set-buffer buffer) - (let ((span (make-span (proof-unprocessed-begin) (point-max))) - cmd) - ;; compute first command of buffer - (goto-char (point-min)) - (proof-find-next-terminator) - (let ((cmd-list (member-if - (lambda (entry) (equal (car entry) 'cmd)) - (proof-segment-up-to (point))))) - (proof-init-segmentation) - (if cmd-list - (progn - (setq cmd (second (car cmd-list))) - (set-span-property span 'type 'vanilla) - (set-span-property span 'cmd cmd)) - ;; If there was no command in the buffer, atomic - ;; span becomes a comment. - (set-span-property span 'type 'comment))) - (proof-set-locked-end (point-max)) - (or (member buffer proof-script-buffer-list) - (setq proof-script-buffer-list - (append proof-script-buffer-list (list buffer))))))) - -(defun proof-file-truename (filename) - "Returns the true name of the file FILENAME or nil." - (and filename (file-exists-p filename) (file-truename filename))) - -(defun proof-register-possibly-new-processed-file (file) - "Register a possibly new FILE as having been processed by the prover." - (let* ((cfile (file-truename file)) - (buffer (proof-file-to-buffer cfile))) - (if (not (member cfile proof-included-files-list)) - (progn - (setq proof-included-files-list - (cons cfile proof-included-files-list)) - (or (equal cfile - (file-truename (buffer-file-name - (car proof-script-buffer-list)))) - (not buffer) - (proof-mark-buffer-atomic buffer)))))) - - -;;; begin messy COMPATIBILITY HACKING for FSFmacs. -;;; -;;; In case Emacs is not aware of the function read-shell-command, -;;; and read-shell-command-map, we duplicate some code adjusted from -;;; minibuf.el distributed with XEmacs 20.4. -;;; -;;; This code is still required as of FSF Emacs 20.2. -;;; -;;; I think bothering with this just to give completion for -;;; when proof-prog-name-ask-p=t is a big overkill! - da. -;;; -(defvar read-shell-command-map - (let ((map (make-sparse-keymap 'read-shell-command-map))) - (if (not (fboundp 'set-keymap-parents)) - (if (fboundp 'set-keymap-parent) - ;; FSF Emacs 20.2 - (set-keymap-parent map minibuffer-local-map) - ;; Earlier FSF Emacs - (setq map (append minibuffer-local-map map)) - ;; XEmacs versions? - (set-keymap-parents map minibuffer-local-map))) - (define-key map "\t" 'comint-dynamic-complete) - (define-key map "\M-\t" 'comint-dynamic-complete) - (define-key map "\M-?" 'comint-dynamic-list-completions) - map) - "Minibuffer keymap used by shell-command and related commands.") - -(or (fboundp 'read-shell-command) - (defun read-shell-command (prompt &optional initial-input history) - "Just like read-string, but uses read-shell-command-map: -\\{read-shell-command-map}" - (let ((minibuffer-completion-table nil)) - (read-from-minibuffer prompt initial-input read-shell-command-map - nil (or history - 'shell-command-history))))) - - -;;; end messy COMPATIBILITY HACKING - - -;; -;; Misc movement functions -;; - -;; FIXME da: these next two functions slightly different, why? -;; (unprocessed-begin vs proof-locked-end) -(defun proof-goto-end-of-locked-interactive () - "Jump to the end of the locked region." - (interactive) - (switch-to-buffer (car proof-script-buffer-list)) - (goto-char (proof-locked-end))) - -(defun proof-goto-end-of-locked () - "Jump to the end of the locked region." - (goto-char (proof-unprocessed-begin))) - -(defun proof-in-locked-region-p () - "Non-nil if point is in locked region. Assumes proof script buffer current." - (< (point) (proof-locked-end))) - -(defun proof-goto-end-of-locked-if-pos-not-visible-in-window () - "If the end of the locked region is not visible, jump to the end of it." - (interactive) - (let* ((proof-script-buffer (car proof-script-buffer-list)) - (pos (save-excursion - (set-buffer proof-script-buffer) - (proof-locked-end)))) - (or (pos-visible-in-window-p pos (get-buffer-window - proof-script-buffer t)) - (proof-goto-end-of-locked-interactive)))) - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Starting and stopping the proof-system shell ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun proof-shell-live-buffer () - "Return buffer of active proof assistant, or nil if none running." - (and proof-shell-buffer - (comint-check-proc proof-shell-buffer) - proof-shell-buffer)) - -(defun proof-start-shell () - "Initialise a shell-like buffer for a proof assistant. - -Also generates goal and response buffers. -Initialises current buffer for scripting. -Does nothing if proof assistant is already running." - (if (proof-shell-live-buffer) - () - (run-hooks 'proof-pre-shell-start-hook) - (setq proof-included-files-list nil) - (if proof-prog-name-ask-p - (save-excursion - (setq proof-prog-name (read-shell-command "Run process: " - proof-prog-name)))) - (let ((proc - (concat "Inferior " - (substring proof-prog-name - (string-match "[^/]*$" proof-prog-name))))) - (while (get-buffer (concat "*" proc "*")) - (if (string= (substring proc -1) ">") - (aset proc (- (length proc) 2) - (+ 1 (aref proc (- (length proc) 2)))) - (setq proc (concat proc "<2>")))) - - (message (format "Starting %s process..." proc)) - - ;; Starting the inferior process (asynchronous) - (let ((prog-name-list (proof-string-to-list proof-prog-name " "))) - (apply 'make-comint (append (list proc (car prog-name-list) nil) - (cdr prog-name-list)))) - ;; To send any initialisation commands to the inferior process, - ;; consult proof-shell-config-done... - - (setq proof-shell-buffer (get-buffer (concat "*" proc "*")) - proof-pbp-buffer (get-buffer-create (concat "*" proc "-goals*")) - proof-response-buffer (get-buffer-create (concat "*" proc - "-response*"))) - - (save-excursion - (set-buffer proof-shell-buffer) - (funcall proof-mode-for-shell) - (set-buffer proof-pbp-buffer) - (funcall proof-mode-for-pbp)) - - (setq proof-script-buffer-list (list (current-buffer))) - (proof-init-segmentation) - (setq proof-active-buffer-fake-minor-mode t) - (run-hooks 'proof-activate-scripting-hook) - - (if (fboundp 'redraw-modeline) - (redraw-modeline) - (force-mode-line-update)) - - (or (assq 'proof-active-buffer-fake-minor-mode minor-mode-alist) - (setq minor-mode-alist - (append minor-mode-alist - (list '(proof-active-buffer-fake-minor-mode - " Scripting"))))) - - (message - (format "Starting %s process... done." proc))))) - - -;; Should this be the same as proof-restart-script? -;; Perhaps this is redundant. -(defun proof-stop-shell () - "Exit the proof process. Runs proof-shell-exit-hook if non-nil" - (interactive) - (save-excursion - (let ((buffer (proof-shell-live-buffer)) - proc) - (if buffer - (progn - (save-excursion - (set-buffer buffer) - (setq proc (process-name (get-buffer-process))) - (comint-send-eof) - (mapcar 'proof-detach-segments proof-script-buffer-list) - (kill-buffer)) - (run-hooks 'proof-shell-exit-hook) - - ;;it is important that the hooks are - ;;run after the buffer has been killed. In the reverse - ;;order e.g., intall-shell-fonts causes problems and it - ;;is impossible to restart the PROOF shell - - (message (format "%s process terminated." proc))))))) - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Proof by pointing ;; -;; All very lego-specific at present ;; -;; To make sense of this code, you should read the ;; -;; relevant LFCS tech report by tms, yb, and djs ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -(defvar pbp-goal-command nil - "Command informing the prover that `pbp-button-action' has been - requested on a goal.") - -(defvar pbp-hyp-command nil - "Command informing the prover that `pbp-button-action' has been - requested on an assumption.") - -(defun pbp-button-action (event) - (interactive "e") - (mouse-set-point event) - (pbp-construct-command)) - -; Using the spans in a mouse behavior is quite simple: from the -; mouse position, find the relevant span, then get its annotation -; and produce a piece of text that will be inserted in the right -; buffer. - -(defun proof-expand-path (string) - (let ((a 0) (l (length string)) ls) - (while (< a l) - (setq ls (cons (int-to-string (aref string a)) - (cons " " ls))) - (incf a)) - (apply 'concat (nreverse ls)))) - -(defun proof-send-span (event) - (interactive "e") - (let* ((span (span-at (mouse-set-point event) 'type)) - (str (span-property span 'cmd))) - (cond ((and (member (current-buffer) proof-script-buffer-list) (not (null span))) - (proof-goto-end-of-locked) - (cond ((eq (span-property span 'type) 'vanilla) - (insert str))))))) - -(defun pbp-construct-command () - (let* ((span (span-at (point) 'proof)) - (top-span (span-at (point) 'proof-top-element)) - top-info) - (if (null top-span) () - (setq top-info (span-property top-span 'proof-top-element)) - (pop-to-buffer (car proof-script-buffer-list)) - (cond - (span - (proof-invisible-command - (format (if (eq 'hyp (car top-info)) pbp-hyp-command - pbp-goal-command) - (concat (cdr top-info) (proof-expand-path - (span-property span 'proof)))))) - ((eq (car top-info) 'hyp) - (proof-invisible-command (format pbp-hyp-command (cdr top-info)))) - (t - (proof-insert-pbp-command (format pbp-change-goal (cdr top-info)))))) - )) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Turning annotated output into pbp goal set ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defvar proof-shell-first-special-char nil "where the specials start") -(defvar proof-shell-goal-char nil "goal mark") -(defvar proof-shell-start-char nil "annotation start") -(defvar proof-shell-end-char nil "annotation end") -(defvar proof-shell-field-char nil "annotated field end") - -(defvar proof-shell-eager-annotation-start nil - "Eager annotation field start. A regular expression or nil. -An eager annotation indicates to Emacs that some following output -should be displayed immediately and not accumulated for parsing. -Set to nil if the proof to disable this feature.") - -(defvar proof-shell-eager-annotation-end "$" - "Eager annotation field end. A regular expression or nil. -An eager annotation indicates to Emacs that some following output -should be displayed immediately and not accumulated for parsing. -Set to nil if the proof to disable this feature. - -The default value is \"$\" to match up to the end of the line.") - -(defvar proof-shell-assumption-regexp nil - "A regular expression matching the name of assumptions.") - -;; FIXME da: where is this variable used? -;; dropped in favour of goal-char? -;; Answer: this is used in *specific* modes, see e.g. -;; lego-goal-hyp. This stuff needs making more generic. -(defvar proof-shell-goal-regexp nil - "A regular expression matching the identifier of a goal.") - -(defvar proof-shell-noise-regexp nil - "Unwanted information output from the proof process within - `proof-start-goals-regexp' and `proof-end-goals-regexp'.") - -(defun pbp-make-top-span (start end) - (let (span name) - (goto-char start) - ;; FIXME da: why name? This is a character function - (setq name (funcall proof-goal-hyp-fn)) - (beginning-of-line) - (setq start (point)) - (goto-char end) - (beginning-of-line) - (backward-char) - (setq span (make-span start (point))) - (set-span-property span 'mouse-face 'highlight) - (set-span-property span 'proof-top-element name))) - -;; Need this for processing error strings and so forth - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; The filter. First some functions that handle those few ;; -;; occasions when the glorious illusion that is script-management ;; -;; is temporarily suspended ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Output from the proof process is handled lazily, so that only -;; the output from the last of multiple commands is actually -;; processed (assuming they're all successful) - -(defvar proof-shell-delayed-output nil - "The last interesting output the proof process output, and what to do - with it.") - -(defvar proof-shell-proof-completed nil - "Flag indicating that a completed proof has just been observed.") - -(defvar proof-analyse-using-stack nil - "Are annotations sent by proof assistant local or global") - -(defun proof-shell-analyse-structure (string) - (save-excursion - (let* ((ip 0) (op 0) ap (l (length string)) - (ann (make-string (length string) ?x)) - (stack ()) (topl ()) - (out (make-string l ?x)) c span) - (while (< ip l) - (if (< (setq c (aref string ip)) 128) - (progn (aset out op c) - (incf op))) - (incf ip)) - (display-buffer (set-buffer proof-pbp-buffer)) - (erase-buffer) - (insert (substring out 0 op)) - - (setq ip 0 - op 1) - (while (< ip l) - (setq c (aref string ip)) - (cond - ((= c proof-shell-goal-char) - (setq topl (cons op topl)) - (setq ap 0)) - ((= c proof-shell-start-char) - (if proof-analyse-using-stack - (setq ap (- ap (- (aref string (incf ip)) 128))) - (setq ap (- (aref string (incf ip)) 128))) - (incf ip) - (while (not (= (setq c (aref string ip)) proof-shell-end-char)) - (aset ann ap (- c 128)) - (incf ap) - (incf ip)) - (setq stack (cons op (cons (substring ann 0 ap) stack)))) - ((= c proof-shell-field-char) - (setq span (make-span (car stack) op)) - (set-span-property span 'mouse-face 'highlight) - (set-span-property span 'proof (car (cdr stack))) - ;; Pop annotation off stack - (and proof-analyse-using-stack - (progn - (setq ap 0) - (while (< ap (length (cadr stack))) - (aset ann ap (aref (cadr stack) ap)) - (incf ap)))) - ;; Finish popping annotations - (setq stack (cdr (cdr stack)))) - (t (incf op))) - (incf ip)) - (setq topl (reverse (cons (point-max) topl))) - ;; If we want Coq pbp: (setq coq-current-goal 1) - (while (setq ip (car topl) - topl (cdr topl)) - (pbp-make-top-span ip (car topl)))))) - -(defun proof-shell-strip-annotations (string) - (let* ((ip 0) (op 0) (l (length string)) (out (make-string l ?x ))) - (while (< ip l) - (if (>= (aref string ip) proof-shell-first-special-char) - (if (char-equal (aref string ip) proof-shell-start-char) - (progn (incf ip) - (while (< (aref string ip) proof-shell-first-special-char) - (incf ip)))) - (aset out op (aref string ip)) - (incf op)) - (incf ip)) - (substring out 0 op))) - -(defun proof-shell-handle-output (start-regexp end-regexp append-face) - "Pretty print output in process buffer in specified region. -The region begins with the match for START-REGEXP and is delimited by -END-REGEXP. The match for END-REGEXP is not part of the specified region. -Removes any annotations in the region. -Fontifies the region. -Appends APPEND-FACE to the text property of the region . -Returns the string (with faces) in the specified region." - (let (start end string) - (save-excursion - (set-buffer proof-shell-buffer) - (goto-char (point-max)) - (setq start (search-backward-regexp start-regexp)) - (save-excursion (setq end (- (search-forward-regexp end-regexp) - (length (match-string 0))))) - (setq string - (proof-shell-strip-annotations (buffer-substring start end))) - (delete-region start end) - (insert string) - (setq end (+ start (length string))) - ;; This doesn't work with FSF Emacs 20.2's version of Font-lock - ;; because there are no known keywords in the process buffer - ;; Never mind. In a forthcoming version, the process buffer will - ;; not be tempered with. Fontification will take place in a - ;; separate response buffer. - ;; (font-lock-fontify-region start end) - (font-lock-append-text-property start end 'face append-face) - (buffer-substring start end)))) - -(defun proof-shell-handle-delayed-output () - (let ((ins (car proof-shell-delayed-output)) - (str (cdr proof-shell-delayed-output))) - (display-buffer proof-pbp-buffer) - (save-excursion - (cond - ((eq ins 'insert) - (setq str (proof-shell-strip-annotations str)) - (set-buffer proof-pbp-buffer) - (erase-buffer) - (insert str)) - ((eq ins 'analyse) - (proof-shell-analyse-structure str)) - (t (set-buffer proof-pbp-buffer) - (insert "\n\nbug???"))))) - (run-hooks 'proof-shell-handle-delayed-output-hook) - (setq proof-shell-delayed-output (cons 'insert "done"))) - - -;; Notes on markup in the scripting buffer. (da) -;; -;; The locked region is covered by a collection of non-overlaping -;; spans (our abstraction of extents/overlays). -;; -;; For an unfinished proof, there is one extent for each command -;; or comment outside a command. For a finished proof, there -;; is one extent for the whole proof. -;; -;; Each command span has a 'type property, one of: -;; -;; 'vanilla Initialised in proof-semis-to-vanillas, for -;; 'goalsave -;; 'comment A comment outside a command. -;; -;; All spans except those of type 'comment have a 'cmd property, -;; which is set to a string of its command. This is the -;; text in the buffer stripped of leading whitespace and any comments. -;; -;; - -(defun proof-shell-handle-error (cmd string) - "React on an error message triggered by the prover. -We display the process buffer, scroll to the end, beep and fontify the -error message. At the end it calls `proof-shell-handle-error-hook'. " - ;; We extract all text between text matching - ;; `proof-shell-error-regexp' and the following prompt. - ;; Alternatively one could higlight all output between the - ;; previous and the current prompt. - ;; +/- of our approach - ;; + Previous not so relevent output is not highlighted - ;; - Proof systems have to conform to error messages whose start can be - ;; detected by a regular expression. - (proof-shell-handle-output - proof-shell-error-regexp proof-shell-annotated-prompt-regexp - 'font-lock-error-face) - (save-excursion (display-buffer proof-shell-buffer) - (beep) - - ;; unwind script buffer - (set-buffer (car proof-script-buffer-list)) - (proof-detach-queue) - (delete-spans (proof-locked-end) (point-max) 'type) - (proof-release-lock) - (run-hooks 'proof-shell-handle-error-hook))) - -(defun proof-shell-handle-interrupt () - (save-excursion - (display-buffer (set-buffer proof-pbp-buffer)) - (goto-char (point-max)) - (newline 2) - (insert-string - "Interrupt: Script Management may be in an inconsistent state\n") - (beep) - (set-buffer (car proof-script-buffer-list))) - (if proof-shell-busy - (progn (proof-detach-queue) - (delete-spans (proof-locked-end) (point-max) 'type) - (proof-release-lock)))) - - -(defun proof-goals-pos (span maparg) - "Given a span, this function returns the start of it if corresponds - to a goal and nil otherwise." - (and (eq 'goal (car (span-property span 'proof-top-element))) - (span-start span))) - -(defun proof-pbp-focus-on-first-goal () - "If the `proof-pbp-buffer' contains goals, the first one is brought - into view." - (and (fboundp 'map-extents) - (let - ((pos (map-extents 'proof-goals-pos proof-pbp-buffer - nil nil nil nil 'proof-top-element))) - (and pos (set-window-point - (get-buffer-window proof-pbp-buffer t) pos))))) - - - -(defun proof-shell-process-output (cmd string) - "Process shell output by matching on STRING. -This function deals with errors, start of proofs, abortions of -proofs and completions of proofs. All other output from the proof -engine is simply reported to the user in the response buffer. -To extend this function, set -`proof-shell-process-output-system-specific'. - -This function - it can return one of 4 things: 'error, 'interrupt, -'loopback, or nil. 'loopback means this was output from pbp, and -should be inserted into the script buffer and sent back to the proof -assistant." - (cond - ((string-match proof-shell-error-regexp string) - (cons 'error (proof-shell-strip-annotations - (substring string - (match-beginning 0))))) - - ((string-match proof-shell-interrupt-regexp string) - 'interrupt) - - ((and proof-shell-abort-goal-regexp - (string-match proof-shell-abort-goal-regexp string)) - (setq proof-shell-delayed-output (cons 'insert "\n\nAborted")) - ()) - - ((string-match proof-shell-proof-completed-regexp string) - (setq proof-shell-proof-completed t) - (setq proof-shell-delayed-output - (cons 'insert (concat "\n" (match-string 0 string))))) - - ((string-match proof-shell-start-goals-regexp string) - (setq proof-shell-proof-completed nil) - (let (start end) - (while (progn (setq start (match-end 0)) - (string-match proof-shell-start-goals-regexp - string start))) - (setq end (string-match proof-shell-end-goals-regexp string start)) - (setq proof-shell-delayed-output - (cons 'analyse (substring string start end))))) - - ((string-match proof-shell-result-start string) - (setq proof-shell-proof-completed nil) - (let (start end) - (setq start (+ 1 (match-end 0))) - (string-match proof-shell-result-end string) - (setq end (- (match-beginning 0) 1)) - (cons 'loopback (substring string start end)))) - - ;; hook to tailor proof-shell-process-output to a specific proof - ;; system - ((and proof-shell-process-output-system-specific - (funcall (car proof-shell-process-output-system-specific) - cmd string)) - (funcall (cdr proof-shell-process-output-system-specific) - cmd string)) - - (t (setq proof-shell-delayed-output (cons 'insert string))))) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Low-level commands for shell communication ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; FIXME da: Should this kind of ordinary input go onto the queue of -;; things to do? Then errors during processing would prevent it being -;; sent. Also the proof shell lock would be set automatically, which -;; might be nice? -(defun proof-shell-insert (string) - (save-excursion - (set-buffer proof-shell-buffer) - (goto-char (point-max)) - - (run-hooks 'proof-shell-insert-hook) - (insert string) - - ;; xemacs and emacs19 have different semantics for what happens when - ;; shell input is sent next to a marker - ;; the following code accommodates both definitions - (if (marker-position proof-marker) - (let ((inserted (point))) - (comint-send-input) - (set-marker proof-marker inserted)) - (comint-send-input)))) - -;; da: This function strips carriage returns from string, then -;; sends it. (Why strip CRs?) -(defun proof-send (string) - (let ((l (length string)) (i 0)) - (while (< i l) - (if (= (aref string i) ?\n) (aset string i ?\ )) - (incf i))) - (save-excursion (proof-shell-insert string))) - -(defun proof-check-process-available (&optional relaxed) - "Adjust internal variables for scripting of current buffer. - -Signals an error if current buffer is not a proof script or if the -proof process is not idle. If RELAXED is set, nothing more is done. No -changes are necessary if the current buffer is already in Scripting -minor mode. Otherwise, the current buffer will become the current -scripting buffer provided the current scripting buffer has either no -locked region or everything in it has been processed." - (proof-start-shell) - (cond - ((not (or relaxed (eq proof-buffer-type 'script))) - (error "Must be running in a script buffer")) - (proof-shell-busy (error "Proof Process Busy!")) - (relaxed ()) ;exit cond - - ;; No buffer is in Scripting minor mode. - ;; We assume the setup is done somewhere else - ;; so this should never happen - ((null proof-script-buffer-list) (assert nil)) - - ;; The current buffer is in Scripting minor mode - ;; so there's nothing to do - ((equal (current-buffer) (car proof-script-buffer-list))) - - (t - (let ((script-buffer (car proof-script-buffer-list)) - (current-buffer (current-buffer))) - (save-excursion - (set-buffer script-buffer) - ;; We only allow switching of the Scripting buffer if the - ;; locked region is either empty or full for all buffers. - ;; Here we check the current Scripting buffer's status. - (let - ((locked-is-empty (eq (proof-unprocessed-begin) (point-min))) - (locked-is-full (progn - (goto-char (point-max)) - (skip-chars-backward " \t\n") - (>= (proof-unprocessed-begin) (point))))) - (if locked-is-full - ;; We're switching from a buffer which has been - ;; completely processed. Make sure that it's - ;; registered on the included files list, in - ;; case it has been processed piecemeal. - (if buffer-file-name - (proof-register-possibly-new-processed-file - buffer-file-name))) - - (if (or locked-is-full locked-is-empty) - ;; we are changing the scripting buffer - (progn - (setq proof-active-buffer-fake-minor-mode nil) - (set-buffer current-buffer) - - ;; does the current buffer contain locked regions already? - (if (member current-buffer proof-script-buffer-list) - (setq proof-script-buffer-list - (remove current-buffer proof-script-buffer-list)) - (proof-init-segmentation)) - (setq proof-script-buffer-list - (cons current-buffer proof-script-buffer-list)) - (setq proof-active-buffer-fake-minor-mode t) - (run-hooks 'proof-activate-scripting-hook)) - ;; Locked region covers only part of the buffer - ;; FIXME da: ponder alternative of trying to complete rest - ;; of current scripting buffer? - (error "Cannot deal with two unfinished script buffers!")))))))) - - -(defun proof-grab-lock (&optional relaxed) - (proof-start-shell) - (proof-check-process-available relaxed) - (setq proof-shell-busy t)) - -(defun proof-release-lock () - (if (proof-shell-live-buffer) - (progn - (if (not proof-shell-busy) - ; (error "Bug in proof-release-lock: Proof process not busy") - (message "Nag, nag, nag: proof-release-lock: Proof process not busy")) - (if (not (member (current-buffer) proof-script-buffer-list)) - (error "Bug in proof-release-lock: Don't own process")) - (setq proof-shell-busy nil)))) - -; Pass start and end as nil if the cmd isn't in the buffer. - -(defun proof-start-queue (start end alist &optional relaxed) - (if start - (proof-set-queue-endpoints start end)) - (let (item) - (while (and alist (string= - (nth 1 (setq item (car alist))) - proof-no-command)) - (funcall (nth 2 item) (car item)) - (setq alist (cdr alist))) - (if alist - (progn - (proof-grab-lock relaxed) - (setq proof-shell-delayed-output (cons 'insert "Done.")) - (setq proof-action-list alist) - (proof-send (nth 1 item)))))) - -; returns t if it's run out of input - - -;; -;; The loop looks like: Execute the -; command, and if it's successful, do action on span. If the -; command's not successful, we bounce the rest of the queue and do -; some error processing. - -(defun proof-shell-exec-loop () -"Process the proof-action-list. -proof-action-list contains a list of (span,command,action) triples. -This function is called with a non-empty proof-action-list, where the -head of the list is the previously executed command which succeeded. -We execute (action span) on the first item, then (action span) on -following items which have proof-no-command as their cmd components. -Return non-nil if the action list becomes empty; unlock the process -and removes the queue region. Otherwise send the next command to the -proof process." - (save-excursion - (set-buffer (car proof-script-buffer-list)) - ;; (if (null proof-action-list) - ;; (error "proof-shell-exec-loop: called with empty proof-action-list.")) - ;; Be more relaxed about calls with empty action list - (if proof-action-list - (let ((item (car proof-action-list))) - ;; Do (action span) on first item in list - (funcall (nth 2 item) (car item)) - (setq proof-action-list (cdr proof-action-list)) - ;; Process following items in list with the form - ;; ("COMMENT" cmd) by calling (cmd "COMMENT") - (while (and proof-action-list - (string= - (nth 1 (setq item (car proof-action-list))) - proof-no-command)) - ;; Do (action span) on comments - (funcall (nth 2 item) (car item)) - (setq proof-action-list (cdr proof-action-list))) - ;; If action list is empty now, release the process lock - (if (null proof-action-list) - (progn (proof-release-lock) - (proof-detach-queue) - ;; indicate finished - t) - ;; Send the next command to the process - (proof-send (nth 1 item)) - ;; indicate not finished - nil)) - ;; Just indicate finished if called with empty proof-action-list. - t))) - -(defun proof-shell-insert-loopback-cmd (cmd) - "Insert command sequence triggered by the proof process -at the end of locked region (after inserting a newline and indenting)." - (save-excursion - (set-buffer (car proof-script-buffer-list)) - (let (span) - (proof-goto-end-of-locked) - (newline-and-indent) - (insert cmd) - (setq span (make-span (proof-locked-end) (point))) - (set-span-property span 'type 'pbp) - (set-span-property span 'cmd cmd) - (proof-set-queue-endpoints (proof-locked-end) (point)) - (setq proof-action-list - (cons (car proof-action-list) - (cons (list span cmd 'proof-done-advancing) - (cdr proof-action-list))))))) - -;; ******** NB ********** -;; While we're using pty communication, this code is OK, since all -;; eager annotations are one line long, and we get input a line at a -;; time. If we go over to piped communication, it will break. - -;; FIXME: highlight eager annotation-end : fix proof-shell-handle-output -;; to highlight whole string. -(defun proof-shell-popup-eager-annotation () - "Process urgent messages. -Eager annotations are annotations which the proof system produces -while it's doing something (e.g. loading libraries) to say how much -progress it's made. Obviously we need to display these as soon as they -arrive." - (let ((str (proof-shell-handle-output - proof-shell-eager-annotation-start - proof-shell-eager-annotation-end - 'font-lock-eager-annotation-face)) - file module) - (proof-message str))) - -(defun proof-response-buffer-display (str face) - "Display STR with FACE in response buffer." - (let ((start)) - (save-excursion - (set-buffer proof-response-buffer) - (setq start (goto-char (point-max))) - (insert str) - (font-lock-fontify-region start (point-max)) - (font-lock-append-text-property start (point-max) 'face face) - (insert "\n")))) - -(defun proof-file-to-buffer (filename) - "Converts a FILENAME into a buffer name" - (let* ((buffers (buffer-list)) - (pos - (position (file-truename filename) - (mapcar 'proof-file-truename - (mapcar 'buffer-file-name - buffers)) - :test 'equal))) - (and pos (nth pos buffers)))) - -(defun proof-files-to-buffers (filenames) - "Converts a list of FILENAMES into a list of BUFFERS." - (if (null filenames) nil - (let* ((buffer (proof-file-to-buffer (car filenames))) - (rest (proof-files-to-buffers (cdr filenames)))) - (if buffer (cons buffer rest) rest)))) - - -(defun proof-shell-process-urgent-message (message) - "Display and analyse urgent MESSAGE for asserting or retracting files." - - ;; Is the prover processing a file? - (cond ((and proof-shell-process-file - (string-match (car proof-shell-process-file) message)) - (let - ((file (funcall (cdr proof-shell-process-file) message))) - (if (string= file "") - (setq file (buffer-file-name (car proof-script-buffer-list)))) - (if file - (proof-register-possibly-new-processed-file file)))) - - ;; Is the prover retracting across files? - ((and proof-shell-retract-files-regexp - (string-match proof-shell-retract-files-regexp message)) - (let ((current-included proof-included-files-list)) - (setq proof-included-files-list - (funcall proof-shell-compute-new-files-list message)) - (proof-restart-buffers - (remove (car proof-script-buffer-list) - (proof-files-to-buffers - (set-difference current-included - proof-included-files-list)))))) - (t - (proof-message message) - (proof-response-buffer-display message - 'font-lock-eager-annotation-face)))) - -(defun proof-shell-process-urgent-messages (str) - "Scan the process output for urgent messages. -Display them immediately in the Response buffer. -Keep `proof-included-files-list' up to date. -The input STR is the most recent chunk of output sent to the process -filter. We assume that urgent messages are fully contained in STR." - (let ((start (string-match proof-shell-eager-annotation-start str)) - end) - (and start - (setq end - (string-match proof-shell-eager-annotation-end str - start)) - (progn - (proof-shell-process-urgent-message - (proof-shell-strip-annotations (substring str start end))) - (proof-shell-process-urgent-messages (substring str end)))))) - -(defun proof-shell-filter (str) - "Filter for the proof assistant shell-process. -We sleep until we get a wakeup-char in the input, then run -proof-shell-process-output, and set proof-marker to keep track of -how far we've got." - (and proof-shell-eager-annotation-start - (proof-shell-process-urgent-messages str)) - - (if (or - ;; Some proof systems can be hacked to have annotated prompts; - ;; for these we set proof-shell-wakeup-char to the annotation special. - (and proof-shell-wakeup-char - (string-match (char-to-string proof-shell-wakeup-char) str)) - ;; Others rely on a standard top-level (e.g. SML) whose prompt can't - ;; be hacked. For those we use the annotated prompt regexp. - (string-match proof-shell-annotated-prompt-regexp str)) - (if (null (marker-position proof-marker)) - ;; Set marker to first prompt in output buffer, and sleep again. - (progn - (goto-char (point-min)) - (re-search-forward proof-shell-annotated-prompt-regexp) - (set-marker proof-marker (point))) - ;; We've matched against a second prompt in str now. Search - ;; the output buffer for the second prompt after the one previously - ;; marked. - (let (string res cmd) - (goto-char (marker-position proof-marker)) - (re-search-forward proof-shell-annotated-prompt-regexp nil t) - (backward-char (- (match-end 0) (match-beginning 0))) - (setq string (buffer-substring (marker-position proof-marker) - (point))) - (goto-char (point-max)) ; da: assumed to be after a prompt? - (setq cmd (nth 1 (car proof-action-list))) - (save-excursion - ;; - (setq res (proof-shell-process-output cmd string)) - ;; da: Added this next line to redisplay, for proof-toolbar - ;; FIXME: should do this for all frames displaying the script - ;; buffer! - ;; ODDITY: has strange effect on highlighting, leave it. - ;; (redisplay-frame (window-buffer t) - (cond - ((eq (car-safe res) 'error) - (proof-shell-handle-error cmd (cdr res))) - ((eq res 'interrupt) - (proof-shell-handle-interrupt)) - ((eq (car-safe res) 'loopback) - (proof-shell-insert-loopback-cmd (cdr res)) - (proof-shell-exec-loop)) - (t (if (proof-shell-exec-loop) - (proof-shell-handle-delayed-output))))))))) - -(defun proof-last-goal-or-goalsave () - (save-excursion - (let ((span (span-at-before (proof-locked-end) 'type))) - (while (and span - (not (eq (span-property span 'type) 'goalsave)) - (or (eq (span-property span 'type) 'comment) - (not (funcall proof-goal-command-p - (span-property span 'cmd))))) - (setq span (prev-span span 'type))) - span))) - -(defun proof-done-invisible (span) ()) - -(defun proof-invisible-command (cmd &optional relaxed) - "Send cmd to the proof process without responding to the user." - (proof-check-process-available relaxed) - (if (not (string-match proof-re-end-of-cmd cmd)) - (setq cmd (concat cmd proof-terminal-string))) - (proof-start-queue nil nil (list (list nil cmd - 'proof-done-invisible)) relaxed)) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; User Commands ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -; Script management uses two major segments: Locked, which marks text -; which has been sent to the proof assistant and cannot be altered -; without being retracted, and Queue, which contains stuff being -; queued for processing. proof-action-list contains a list of -; (span,command,action) triples. The loop looks like: Execute the -; command, and if it's successful, do action on span. If the -; command's not successful, we bounce the rest of the queue and do -; some error processing. -; -; when a span has been processed, we classify it as follows: -; 'goalsave - denoting a 'goalsave pair in the locked region -; a 'goalsave region has a 'name property which is the name of the goal -; 'comment - denoting a comment -; 'pbp - denoting a span created by pbp -; 'vanilla - denoting any other span. -; 'pbp & 'vanilla spans have a property 'cmd, which says what -; command they contain. - -; We don't allow commands while the queue has anything in it. So we -; do configuration by concatenating the config command on the front in -; proof-send - -;; proof-assert-until-point, and various gunk for its ;; -;; setup and callback ;; - - -(defun proof-check-atomic-sequents-lists (span cmd end) - "Check if CMD is the final command in an ACS. - -If CMD is matched by the end regexp in `proof-atomic-sequents-list', -the ACS is marked in the current buffer. If CMD does not match any, -`nil' is returned, otherwise non-nil." - ;;FIXME tms: needs implementation - nil) - -(defun proof-done-advancing (span) - "The callback function for assert-until-point." - (let ((end (span-end span)) nam gspan next cmd) - (proof-set-locked-end end) - (proof-set-queue-start end) - (setq cmd (span-property span 'cmd)) - (cond - ((eq (span-property span 'type) 'comment) - (set-span-property span 'mouse-face 'highlight)) - ((proof-check-atomic-sequents-lists span cmd end)) - ((string-match proof-save-command-regexp cmd) - ;; In coq, we have the invariant that if we've done a save and - ;; there's a top-level declaration then it must be the - ;; associated goal. (Notice that because it's a callback it - ;; must have been approved by the theorem prover.) - (if (string-match proof-save-with-hole-regexp cmd) - (setq nam (match-string 2 cmd))) - (setq gspan span) - (while (or (eq (span-property gspan 'type) 'comment) - (not (funcall proof-goal-command-p - (setq cmd (span-property gspan 'cmd))))) - (setq next (prev-span gspan 'type)) - (delete-span gspan) - (setq gspan next)) - - (if (null nam) - (if (string-match proof-goal-with-hole-regexp - (span-property gspan 'cmd)) - (setq nam (match-string 2 (span-property gspan 'cmd))) - ;; This only works for Coq, but LEGO raises an error if - ;; there's no name. - ;; FIXME: a nonsense for Isabelle. - (setq nam "Unnamed_thm"))) - - (set-span-end gspan end) - (set-span-property gspan 'mouse-face 'highlight) - (set-span-property gspan 'type 'goalsave) - (set-span-property gspan 'name nam) - - (and proof-lift-global - (funcall proof-lift-global gspan))) - (t - (set-span-property span 'mouse-face 'highlight) - (and proof-global-p - (funcall proof-global-p cmd) - proof-lift-global - (funcall proof-lift-global span)))))) - - - -;; FIXME da: Below it would probably be faster to use the primitive -;; skip-chars-forward rather than scanning character-by-character -;; with a lisp loop over the whole region. Also I'm not convinced that -;; Emacs should be better at skipping whitespace and comments than the -;; proof process itself! - -(defun proof-segment-up-to (pos &optional next-command-end) - "Create a list of (type,int,string) tuples from end of locked region to POS. -Each tuple denotes the command and the position of its terminator, -type is one of 'comment, or 'cmd. 'unclosed-comment may be consed onto -the start if the segment finishes with an unclosed comment. -If optional NEXT-COMMAND-END is non-nil, we contine past POS until -the next command end." - (save-excursion - ;; depth marks number of nested comments. - ;; quote-parity is false if we're inside ""'s. - ;; Only one of (depth > 0) and (not quote-parity) - ;; should be true at once. -- hhg - (let ((str (make-string (- (buffer-size) - (proof-unprocessed-begin) -10) ?x)) - (i 0) (depth 0) (quote-parity t) done alist c) - (proof-goto-end-of-locked) - (while (not done) - (cond - ;; Case 1. We've reached POS, not allowed to go past it, - ;; and are inside a comment - ((and (not next-command-end) (= (point) pos) (> depth 0)) - (setq done t alist (cons 'unclosed-comment alist))) - ;; Case 2. We've reached the end of the buffer while - ;; scanning inside a comment or string - ((= (point) (point-max)) - (cond - ((not quote-parity) - (message "Warning: unclosed quote")) - ((> depth 0) - (setq done t alist (cons 'unclosed-comment alist)))) - (setq done t)) - ;; Case 3. Found a comment end, not inside a string - ((and (looking-at (regexp-quote proof-comment-end)) quote-parity) - (if (= depth 0) - (progn - (message "Warning: extraneous comment end") - (setq done t)) - (setq depth (- depth 1)) - (forward-char (length proof-comment-end)) - (if (eq i 0) - (setq alist (cons (list 'comment "" (point)) alist)) - (aset str i ?\ ) - (incf i)))) - ;; Case 4. Found a comment start, not inside a string - ((and (looking-at (regexp-quote proof-comment-start)) quote-parity) - (setq depth (+ depth 1)) - (forward-char (length proof-comment-start))) - ;; Case 5. Inside a comment. - ((> depth 0) - (forward-char)) - ;; Case 6. Anything else - (t - ;; Skip whitespace before the start of a command, otherwise - ;; other characters in the accumulator string str - (setq c (char-after (point))) - (if (or (> i 0) (not (= (char-syntax c) ?\ ))) - (progn - (aset str i c) - (incf i))) - - (if (looking-at "\"") ; FIXME da: should this be more generic? - (setq quote-parity (not quote-parity))) - - (forward-char) - - ;; Found the end of a command - (if (and (= c proof-terminal-char) quote-parity) - (progn - (setq alist - (cons (list 'cmd (substring str 0 i) (point)) alist)) - (if (>= (point) pos) - (setq done t) - (setq i 0))))))) - alist))) - -(defun proof-semis-to-vanillas (semis &optional callback-fn) - "Convert a sequence of semicolon positions (returned by the above -function) to a set of vanilla extents." - (let ((ct (proof-unprocessed-begin)) span alist semi) - (while (not (null semis)) - (setq semi (car semis) - span (make-span ct (nth 2 semi)) - ct (nth 2 semi)) - (if (eq (car (car semis)) 'cmd) - (progn - (set-span-property span 'type 'vanilla) - (set-span-property span 'cmd (nth 1 semi)) - (setq alist (cons (list span (nth 1 semi) - (or callback-fn 'proof-done-advancing)) - alist))) - (set-span-property span 'type 'comment) - (setq alist (cons (list span proof-no-command 'proof-done-advancing) alist))) - (setq semis (cdr semis))) - (nreverse alist))) - -;; -;; Two commands for moving forwards in proof scripts. -;; Moving forward for a "new" command may insert spaces -;; or new lines. Moving forward for the "next" command -;; does not. -;; - -(defun proof-script-new-command-advance () - "Move point to a nice position for a new command. -Assumes that point is at the end of a command." - (interactive) - (if proof-one-command-per-line - ;; One command per line: move to next new line, - ;; creating one if at end of buffer or at the - ;; start of a blank line. (This has the pleasing - ;; effect that blank regions of the buffer are - ;; automatically extended when inserting new commands). - (cond - ((eq (forward-line) 1) - (newline)) - ((eolp) - (newline) - (forward-line -1))) - ;; Multiple commands per line: skip spaces at point, - ;; and insert the same number of spaces that were - ;; skipped in front of point (at least one). - ;; This has the pleasing effect that the spacing - ;; policy of the current line is copied: e.g. - ;; <command>; <command>; - ;; Tab columns don't work properly, however. - ;; Instead of proof-one-command-per-line we could - ;; introduce a "proof-command-separator" to improve - ;; this. - (let ((newspace (max (skip-chars-forward " \t") 1)) - (p (point))) - (insert-char ?\ newspace) - (goto-char p)))) - -(defun proof-script-next-command-advance () - "Move point to the beginning of the next command if it's nearby. -Assumes that point is at the end of a command." - (interactive) - ;; skip whitespace on this line - (skip-chars-forward " \t") - (if (and proof-one-command-per-line (eolp)) - ;; go to the next line if we have one command per line - (forward-line))) - - - -; Assert until point - We actually use this to implement the -; assert-until-point, active terminator keypress, and find-next-terminator. -; In different cases we want different things, but usually the information -; (i.e. are we inside a comment) isn't available until we've actually run -; proof-segment-up-to (point), hence all the different options when we've -; done so. - -;; FIXME da: this command doesn't behave as the doc string says when -;; inside comments. Also is unhelpful at the start of commands, and -;; in the locked region. I prefer the new version below. - -(defun proof-assert-until-point - (&optional unclosed-comment-fun ignore-proof-process-p) - "Process the region from the end of the locked-region until point. - Default action if inside a comment is just to go until the start of - the comment. If you want something different, put it inside - unclosed-comment-fun. If ignore-proof-process-p is set, no commands - will be added to the queue." - (interactive) - (let ((pt (point)) - (crowbar (or ignore-proof-process-p (proof-check-process-available))) - semis) - (save-excursion - (if (not (re-search-backward "\\S-" (proof-unprocessed-begin) t)) - (progn (goto-char pt) - (error "I don't know what I should be doing in this buffer!"))) - (setq semis (proof-segment-up-to (point)))) - (if (and unclosed-comment-fun (eq 'unclosed-comment (car semis))) - (funcall unclosed-comment-fun) - (if (eq 'unclosed-comment (car semis)) (setq semis (cdr semis))) - (if (and (not ignore-proof-process-p) (not crowbar) (null semis)) - (error "I don't know what I should be doing in this buffer!")) - (goto-char (nth 2 (car semis))) - (and (not ignore-proof-process-p) - (let ((vanillas (proof-semis-to-vanillas (nreverse semis)))) -; (if crowbar (setq vanillas (cons crowbar vanillas))) - (proof-start-queue (proof-unprocessed-begin) (point) vanillas)))))) - - -;; da: This is my alternative version of the above. -;; It works from the locked region too. -;; I find it more convenient to assert up to the current command (command -;; point is inside), and move to the next command. -;; This means proofs can be easily replayed with point at the start -;; of lines. Above function gives stupid "nothing to do error." when -;; point is on the start of line or in the locked region. - -;; FIXME: behaviour inside comments may be odd at the moment. (it -;; doesn't behave as docstring suggests, same prob as -;; proof-assert-until-point) -;; FIXME: polish the undo behaviour and quit behaviour of this -;; command (should inhibit quit somewhere or other). - -(defun proof-assert-next-command - (&optional unclosed-comment-fun ignore-proof-process-p - dont-move-forward for-new-command) - "Process until the end of the next unprocessed command after point. -If inside a comment, just to go the start of -the comment. If you want something different, put it inside -UNCLOSED-COMMENT-FUN. If IGNORE-PROOF-PROCESS-P is set, no commands -will be added to the queue. -Afterwards, move forward to near the next command afterwards, unless -DONT-MOVE-FORWARD is non-nil. If FOR-NEW-COMMAND is non-nil, -a space or newline will be inserted automatically." - (interactive) - (let ((pt (point)) - (crowbar (or ignore-proof-process-p (proof-check-process-available))) - semis) - (save-excursion - ;; CHANGE from old proof-assert-until-point: don't bother check - ;; for non-whitespace between locked region and point. - ;; CHANGE: ask proof-segment-up-to to scan until command end - ;; (which it used to do anyway, except in the case of a comment) - (setq semis (proof-segment-up-to (point) t))) - ;; old code: - ;;(if (not (re-search-backward "\\S-" (proof-unprocessed-begin) t)) - ;; (progn (goto-char pt) - ;; (error "I don't know what I should be doing in this buffer!"))) - ;; (setq semis (proof-segment-up-to (point)))) - (if (and unclosed-comment-fun (eq 'unclosed-comment (car semis))) - (funcall unclosed-comment-fun) - (if (eq 'unclosed-comment (car semis)) - ;; CHANGE: if inside a comment, do as the documentation - ;; suggests. - (setq semis nil)) - (if (and (not ignore-proof-process-p) (not crowbar) (null semis)) - (error "I don't know what I should be doing in this buffer!")) - (goto-char (nth 2 (car semis))) - (if (not ignore-proof-process-p) - (let ((vanillas (proof-semis-to-vanillas (nreverse semis)))) -; (if crowbar (setq vanillas (cons crowbar vanillas))) - (proof-start-queue (proof-unprocessed-begin) (point) vanillas))) - ;; This is done after the queuing to be polite: it means the - ;; spacing policy enforced here is not put into the locked - ;; region so the user can re-edit. - (if (not dont-move-forward) - (if for-new-command - (proof-script-new-command-advance) - (proof-script-next-command-advance)))))) - -;; insert-pbp-command - an advancing command, for use when ;; -;; PbpHyp or Pbp has executed in LEGO, and returned a ;; -;; command for us to run ;; - -(defun proof-insert-pbp-command (cmd) - (proof-check-process-available) - (let (span) - (proof-goto-end-of-locked) - (insert cmd) - (setq span (make-span (proof-locked-end) (point))) - (set-span-property span 'type 'pbp) - (set-span-property span 'cmd cmd) - (proof-start-queue (proof-unprocessed-begin) (point) - (list (list span cmd 'proof-done-advancing))))) - - -;; proof-retract-until-point and associated gunk ;; -;; most of the hard work (i.e computing the commands to do ;; -;; the retraction) is implemented in the customisation ;; -;; module (lego.el or coq.el) which is why this looks so ;; -;; straightforward ;; - - -(defun proof-done-retracting (span) - "Updates display after proof process has reset its state. See also -the documentation for `proof-retract-until-point'. It optionally -deletes the region corresponding to the proof sequence." - (let ((start (span-start span)) - (end (span-end span)) - (kill (span-property span 'delete-me))) - (proof-set-locked-end start) - (proof-set-queue-end start) - (delete-spans start end 'type) - (delete-span span) - (if kill (delete-region start end)))) - -(defun proof-setup-retract-action (start end proof-command delete-region) - (let ((span (make-span start end))) - (set-span-property span 'delete-me delete-region) - (list (list span proof-command 'proof-done-retracting)))) - -(defun proof-retract-target (target delete-region) - (let ((end (proof-locked-end)) - (start (span-start target)) - (span (proof-last-goal-or-goalsave)) - actions) - - (if (and span (not (eq (span-property span 'type) 'goalsave))) - (if (< (span-end span) (span-end target)) - (progn - (setq span target) - (while (and span (eq (span-property span 'type) 'comment)) - (setq span (next-span span 'type))) - (setq actions (proof-setup-retract-action - start end - (if (null span) proof-no-command - (funcall proof-count-undos-fn span)) - delete-region) - end start)) - - (setq actions (proof-setup-retract-action (span-start span) end - proof-kill-goal-command - delete-region) - end (span-start span)))) - - (if (> end start) - (setq actions - (nconc actions (proof-setup-retract-action - start end - (funcall proof-find-and-forget-fn target) - delete-region)))) - - (proof-start-queue (min start end) (proof-locked-end) actions))) - -;; FIXME da: I would rather that this function moved point to -;; the start of the region retracted? - -(defun proof-retract-until-point (&optional delete-region) - "Sets up the proof process for retracting until point. In - particular, it sets a flag for the filter process to call - `proof-done-retracting' after the proof process has actually - successfully reset its state. It optionally deletes the region in - the proof script corresponding to the proof command sequence. If - this function is invoked outside a locked region, the last - successfully processed command is undone." - (interactive) - (proof-check-process-available) - (let ((span (span-at (point) 'type))) - (if (null (proof-locked-end)) (error "No locked region")) - (and (null span) - (progn (proof-goto-end-of-locked) (backward-char) - (setq span (span-at (point) 'type)))) - (proof-retract-target span delete-region))) - -;; proof-try-command ;; -;; this isn't really in the spirit of script management, ;; -;; but sometimes the user wants to just try an expression ;; -;; without having to undo it in order to try something ;; -;; different. Of course you can easily lose sync by doing ;; -;; something here which changes the proof state ;; - -(defun proof-done-trying (span) - (delete-span span) - (proof-detach-queue)) - -(defun proof-try-command - (&optional unclosed-comment-fun) - - "Process the command at point, - but don't add it to the locked region. This will only happen if - the command satisfies proof-state-preserving-p. - - Default action if inside a comment is just to go until the start of - the comment. If you want something different, put it inside - unclosed-comment-fun." - (interactive) - (let ((pt (point)) semis crowbar test) - (setq crowbar (proof-check-process-available)) - (save-excursion - (if (not (re-search-backward "\\S-" (proof-locked-end) t)) - (progn (goto-char pt) - (error "I don't know what I should be doing in this buffer!"))) - (setq semis (proof-segment-up-to (point)))) - (if (and unclosed-comment-fun (eq 'unclosed-comment (car semis))) - (funcall unclosed-comment-fun) - (if (eq 'unclosed-comment (car semis)) (setq semis (cdr semis))) - (if (and (not crowbar) (null semis)) (error "I don't know what I should be doing in this buffer!")) - (setq test (car semis)) - (if (not (funcall proof-state-preserving-p (nth 1 test))) - (error "Command is not state preserving")) - (goto-char (nth 2 test)) - (let ((vanillas (proof-semis-to-vanillas (list test) - 'proof-done-trying))) -; (if crowbar (setq vanillas (cons crowbar vanillas))) - (proof-start-queue (proof-unprocessed-begin) (point) vanillas))))) - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; misc other user functions ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -(defun proof-undo-last-successful-command (&optional no-delete) - "Undo last successful command at end of locked region. -Unless optional NO-DELETE is set, the text is also deleted from -the proof script." - (interactive "p") - (let ((lastspan (span-at-before (proof-locked-end) 'type))) - (if lastspan - (progn - (goto-char (span-start lastspan)) - (proof-retract-until-point (not no-delete))) - (error "Nothing to undo!")))) - -(defun proof-interrupt-process () - (interactive) - (if (not (proof-shell-live-buffer)) - (error "Proof Process Not Started!")) - (if (not (member (current-buffer) proof-script-buffer-list)) - (error "Don't own process!")) - (if (not proof-shell-busy) - (error "Proof Process Not Active!")) - (save-excursion - (set-buffer proof-shell-buffer) - (comint-interrupt-subjob))) - - -(defun proof-find-next-terminator () - "Set point after next `proof-terminal-char'." - (interactive) - (let ((cmd (span-at (point) 'type))) - (if cmd (goto-char (span-end cmd)) - (and (re-search-forward "\\S-" nil t) - (proof-assert-until-point nil 'ignore-proof-process))))) - -(defun proof-goto-command-start () - "Move point to start of current command." - (interactive) - (let ((cmd (span-at (point) 'type))) - (if cmd (goto-char (span-start cmd)) ; BUG: only works for unclosed proofs. - (let ((semis (proof-segment-up-to (point) t))) - (if (eq 'unclosed-comment (car semis)) (setq semis (cdr semis))) - (if (and semis (car semis) (car (car semis))) - (progn - (goto-char (nth 2 (car (car semis)))) - (skip-chars-forward " \t\n"))))))) - -(defun proof-process-buffer () - "Process the current buffer and set point at the end of the buffer." - (interactive) - (goto-char (point-max)) - (proof-assert-until-point)) - -(defun proof-restart-buffer (buffer) - "Remove all extents in BUFFER and update `proof-script-buffer-list'." - (save-excursion - (if (buffer-live-p buffer) - (progn - (set-buffer buffer) - (setq proof-active-buffer-fake-minor-mode nil) - (delete-spans (point-min) (point-max) 'type) - (proof-detach-segments))) - (setq proof-script-buffer-list - (remove buffer proof-script-buffer-list)))) - -(defun proof-restart-buffers (bufferlist) - "Remove all extents in BUFFERLIST and update `proof-script-buffer-list'." - (mapcar 'proof-restart-buffer bufferlist)) - -;; For when things go horribly wrong -;; FIXME: this needs to politely stop the process by sending -;; an EOF or customizable command. (maybe timeout waiting for -;; it to die before killing the buffer) -;; maybe better: -;; Put a funciton to remove all extents in buffers into -;; the kill-buffer-hook for the proof shell. Then this -;; function just stops the shell nicely (see proof-stop-shell). -(defun proof-restart-script () - "Restart Proof General. -Kill off the proof assistant process and remove all markings in the -script buffers." - (interactive) - (proof-restart-buffers proof-script-buffer-list) - ;; { (equal proof-script-buffer-list nil) } - (setq proof-shell-busy nil - proof-included-files-list nil - proof-shell-proof-completed nil) - (if (buffer-live-p proof-shell-buffer) - (kill-buffer proof-shell-buffer)) - (if (buffer-live-p proof-pbp-buffer) - (kill-buffer proof-pbp-buffer)) - (and (buffer-live-p proof-response-buffer) - (kill-buffer proof-response-buffer))) - -;; For when things go not-quite-so-horribly wrong -;; FIXME: this may need work, and maybe isn't needed at -;; all (proof-retract-file when it appears may be enough). -(defun proof-restart-script-same-process () - (interactive) - (save-excursion - (if (buffer-live-p proof-script-buffer) - (progn - (set-buffer proof-script-buffer) - (setq proof-active-buffer-fake-minor-mode nil) - (delete-spans (point-min) (point-max) 'type) - (proof-detach-segments))))) - -;; A command for making things go horribly wrong - it moves the -;; end-of-locked-region marker backwards, so user had better move it -;; correctly to sync with the proof state, or things will go all -;; pear-shaped. - -(defun proof-frob-locked-end () - (interactive) - "Move the end of the locked region backwards. -Only for use by consenting adults." - (cond - ((not (member (current-buffer) proof-script-buffer-list)) - (error "Not in proof buffer")) - ((> (point) (proof-locked-end)) - (error "Can only move backwards")) - (t (proof-set-locked-end (point)) - (delete-spans (proof-locked-end) (point-max) 'type)))) - -(defvar proof-minibuffer-history nil - "History of proof commands read from the minibuffer") - -(defun proof-execute-minibuffer-cmd () - (interactive) - (let (cmd) - (proof-check-process-available 'relaxed) - (setq cmd (read-string "Command: " nil 'proof-minibuffer-history)) - (proof-invisible-command cmd 'relaxed))) - -;; da: below functions for input history simulation are quick hacks. -;; Could certainly be made more efficient. - -(defvar proof-command-history nil - "History used by proof-previous-matching-command and friends.") - -(defun proof-build-command-history () - "Construct proof-command-history from script buffer. -Based on position of point." - ;; let - ) - -(defun proof-previous-matching-command (arg) - "Search through previous commands for new command matching current input." - (interactive)) - ;;(if (not (memq last-command '(proof-previous-matching-command - ;; proof-next-matching-command))) - ;; Start a new search - - - -(defun proof-next-matching-command (arg) - "Search through following commands for new command matching current input." - (interactive "p") - (proof-previous-matching-command (- arg))) - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Popup and Pulldown Menu ;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;; Menu commands for the underlying proof assistant - -;;; These are defcustom'd so that users may re-configure -;;; the system to their liking. -(defcustom proof-ctxt-string "" - "*Command to display the context in proof assistant." - :type 'string - :group 'proof) - -(defcustom proof-help-string "" - "*Command to ask for help in proof assistant." - :type 'string - :group 'proof) - -(defcustom proof-prf-string "" - "Command to display proof state in proof assistant." - :type 'string - :group 'proof) - -(defvar proof-goal-command nil - "Command to set a goal in the proof assistant. String or fn. -If a string, the format character %s will be replaced by the -goal string. If a function, should return a command string to -insert when called interactively.") - -(defvar proof-save-command nil - "Command to save a proved theorem in the proof assistant. String or fn. -If a string, the format character %s will be replaced by the -theorem name. If a function, should return a command string to -insert when called interactively.") - -;;; Functions using the above - -(defun proof-ctxt () - "List context." - (interactive) - (proof-invisible-command (concat proof-ctxt-string proof-terminal-string))) - -(defun proof-help () - "Print help message giving syntax." - (interactive) - (proof-invisible-command (concat proof-help-string proof-terminal-string))) - -(defun proof-prf () - "List proof state." - (interactive) - (proof-invisible-command (concat proof-prf-string proof-terminal-string))) - - - -;; FIXME: da: add more general function for inserting into the -;; script buffer and the proof process together, and using -;; a choice of minibuffer prompting (hated by power users, perfect -;; for novices). -;; Add named goals. -;; TODO: -;; Add named goals. -;; Coherent policy for movement here and elsewhere based on -;; proof-one-command-per-line user option. -;; Coherent policy for sending to process after writing into -;; script buffer. Could have one without the other. -;; For example, may be more easy to edit complex goal string -;; first in the script buffer. Ditto for tactics, etc. - -(defvar proof-issue-goal-history nil - "History of goals for proof-issue-goal.") - -(defun proof-issue-goal (goal-cmd) - "Insert a goal command into the script buffer, issue it to prover." - (interactive - (if proof-goal-command - (if (stringp proof-goal-command) - (list (format proof-goal-command - (read-string "Goal: " "" - proof-issue-goal-history))) - (proof-goal-command)) - (error - "Proof General not configured for goals: set proof-goal-command."))) - (let ((proof-one-command-per-line t)) ; Goals always start at a new line - (proof-issue-new-command goal-cmd))) - -(defvar proof-issue-save-history nil - "History of theorem names for proof-issue-save.") - -(defun proof-issue-save (thmsave-cmd) - "Insert a save theorem command into the script buffer, issue it." - (interactive - (if proof-save-command - (if (stringp proof-save-command) - (list (format proof-save-command - (read-string "Save as: " "" - proof-issue-save-history))) - (proof-save-command)) - (error - "Proof General not configured for save theorem: set proof-save-command."))) - (let ((proof-one-command-per-line t)) ; Goals always start at a new line - (proof-issue-new-command thmsave-cmd))) - -(defun proof-issue-new-command (cmd) - "Insert CMD into the script buffer and issue it to the proof assistant. -If point is in the locked region, move to the end of it first. -Start up the proof assistant if necessary." - ;; FIXME: da: I think we need a (proof-script-switch-to-buffer) - ;; function (so there is some control over display). - ;; (switch-to-buffer proof-script-buffer) - (if (proof-shell-live-buffer) - (if (proof-in-locked-region-p) - (proof-goto-end-of-locked-interactive))) - (proof-script-new-command-advance) - ;; FIXME: fixup behaviour of undo here. Really want - ;; to temporarily disable undo for insertion. - ;; (buffer-disable-undo) this trashes whole undo list! - (insert cmd) - ;; FIXME: could do proof-indent-line here, but let's - ;; wait until indentation is fixed. - (proof-assert-until-point)) - - - - -;;; To be called from menu -(defun proof-info-mode () - "Call info on Proof General." - (interactive) - (info "ProofGeneral")) - -(defun proof-exit () - "Exit Proof-assistant." - (interactive) - (proof-restart-script)) - -(defvar proof-help-menu - `("Help" - [,(concat proof-assistant " web page") - (browse-url proof-www-home-page) t] - ["Proof General home page" - (browse-url proof-general-home-page) t] - ["Proof General Info" proof-info-mode t] - ) - "The Help Menu in Script Management.") - -(defvar proof-shared-menu - (proof-append-element - (append - (list - ["Display context" proof-ctxt - :active (proof-shell-live-buffer)] - ["Display proof state" proof-prf - :active (proof-shell-live-buffer)] - ["Exit proof assistant" proof-exit - :active (proof-shell-live-buffer)]) - (if proof-tags-support - (list - "----" - ["Find definition/declaration" find-tag-other-window t]) - nil)) - proof-help-menu)) - -(defvar proof-menu - (append '("Commands" - ["Toggle active terminator" proof-active-terminator-minor-mode - :active t - :style toggle - :selected proof-active-terminator-minor-mode] - "----") - (list (if (string-match "XEmacs 19.1[2-9]" emacs-version) - "--:doubleLine" "----")) - proof-shared-menu - ) - "*The menu for the proof assistant.") - -(defvar proof-shell-menu proof-shared-menu - "The menu for the Proof-assistant shell") - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Active terminator minor mode ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(deflocal proof-active-terminator-minor-mode nil - "Active terminator minor mode flag") - -(defun proof-active-terminator-minor-mode (&optional arg) - "Toggle PROOF's Active Terminator minor mode. -With arg, turn on the Active Terminator minor mode if and only if arg -is positive. - -If Active terminator mode is enabled, a terminator will process the -current command." - - (interactive "P") - -;; has this minor mode been registered as such? - (or (assq 'proof-active-terminator-minor-mode minor-mode-alist) - (setq minor-mode-alist - (append minor-mode-alist - (list '(proof-active-terminator-minor-mode - (concat " " proof-terminal-string)))))) - - (setq proof-active-terminator-minor-mode - (if (null arg) (not proof-active-terminator-minor-mode) - (> (prefix-numeric-value arg) 0))) - (if (fboundp 'redraw-modeline) - (redraw-modeline) - (force-mode-line-update))) - -(defun proof-process-active-terminator () - "Insert the proof command terminator, and assert up to it. -Fire up proof process if necessary." - (let ((mrk (point)) ins incomment) - (if (looking-at "\\s-\\|\\'\\|\\w") - (if (not (re-search-backward "\\S-" (proof-unprocessed-begin) t)) - (error "I don't know what I should be doing in this buffer!"))) - (if (not (= (char-after (point)) proof-terminal-char)) - (progn (forward-char) (insert proof-terminal-string) (setq ins t))) - (proof-assert-until-point - (function (lambda () - (setq incomment t) - (if ins (backward-delete-char 1)) - (goto-char mrk) - (insert proof-terminal-string)))) - (or incomment - (proof-script-next-command-advance)))) - -(defun proof-active-terminator () - "Insert the terminator, perhaps sending the command to the assistant. -If proof-active-terminator-minor-mode is non-nil, the command will be -sent to the assistant." - (interactive) - (if proof-active-terminator-minor-mode - (proof-process-active-terminator) - (self-insert-command 1))) - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Proof General scripting mode configuration ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;; da: this isn't so nice if a scripting buffer should inherit -;; from another mode: e.g. for Isabelle, we would like to use -;; sml-mode. -;; FIXME: add more doc to the mode below, to give hints on -;; configuring for new assistants. -(define-derived-mode proof-mode fundamental-mode - proof-mode-name - "Proof General major mode for proof scripts. -\\{proof-mode-map} -" - (setq proof-buffer-type 'script) - - ;; Has buffer already been processed? - (and (member buffer-file-truename proof-included-files-list) - (proof-mark-buffer-atomic (current-buffer))) - - (make-local-variable 'kill-buffer-hook) - (add-hook 'kill-buffer-hook - (lambda () - (setq proof-script-buffer-list - (remove (current-buffer) proof-script-buffer-list))))) - -;; FIXME: da: could we put these into another keymap already? -;; FIXME: da: it's offensive to experience users to rebind global stuff -;; like meta-tab, this should be removed. -(defconst proof-universal-keys - (append - '(([(control c) (control c)] . proof-interrupt-process) - ([(control c) (control v)] . proof-execute-minibuffer-cmd)) - (if proof-tags-support - '(([(meta tab)] . tag-complete-symbol)) - nil)) -"List of keybindings which for the script and response buffer. -Elements of the list are tuples (k . f) -where `k' is a keybinding (vector) and `f' the designated function.") - -;; Fixed definitions in proof-mode-map, which don't depend on -;; prover configuration. -;;; INDENT HACK: font-lock only recognizes define-key at start of line -(let ((map proof-mode-map)) -(define-key map [(control c) (control e)] 'proof-find-next-terminator) -(define-key map [(control c) (control a)] 'proof-goto-command-start) -(define-key map [(control c) (control n)] 'proof-assert-next-command) -(define-key map [(control c) (return)] 'proof-assert-next-command) -(define-key map [(control c) (control t)] 'proof-try-command) -(define-key map [(control c) ?u] 'proof-retract-until-point) -(define-key map [(control c) (control u)] 'proof-undo-last-successful-command) -(define-key map [(control c) ?\'] 'proof-goto-end-of-locked-interactive) -(define-key map [(control button1)] 'proof-send-span) -(define-key map [(control c) (control b)] 'proof-process-buffer) -(define-key map [(control c) (control z)] 'proof-frob-locked-end) -(define-key map [(control c) (control p)] 'proof-prf) -(define-key map [(control c) ?c] 'proof-ctxt) -(define-key map [(control c) ?h] 'proof-help) -(define-key map [(meta p)] 'proof-previous-matching-command) -(define-key map [(meta n)] 'proof-next-matching-command) -;; FIXME da: this shouldn't need setting, because it works -;; via indent-line-function which is set below. Check this. -(define-key map [tab] 'proof-indent-line) -(proof-define-keys map proof-universal-keys)) - - - -;; the following callback is an irritating hack - there should be some -;; elegant mechanism for computing constants after the child has -;; configured. - -(defun proof-config-done () - "Finish setup of Proof General scripting mode. -Call this function in the derived mode for the proof assistant to -finish setup which depends on specific proof assistant configuration." - ;; calculate some strings and regexps for searching - (setq proof-terminal-string (char-to-string proof-terminal-char)) - - ;; FIXME: these are LEGO specific! - (setq pbp-goal-command (concat "Pbp %s" proof-terminal-string)) - (setq pbp-hyp-command (concat "PbpHyp %s" proof-terminal-string)) - - ;; FIXME da: I'm not sure we ought to add spaces here, but if - ;; we don't, there would be trouble overloading these settings - ;; to also use as regexps for finding comments. - ;; - (make-local-variable 'comment-start) - (setq comment-start (concat proof-comment-start " ")) - (make-local-variable 'comment-end) - (setq comment-end (concat " " proof-comment-end)) - (make-local-variable 'comment-start-skip) - (setq comment-start-skip - (concat (regexp-quote proof-comment-start) "+\\s_?")) - - (setq proof-re-end-of-cmd (concat "\\s_*" proof-terminal-string "\\s_*\\\'")) - (setq proof-re-term-or-comment - (concat proof-terminal-string "\\|" (regexp-quote proof-comment-start) - "\\|" (regexp-quote proof-comment-end))) - - ;; func-menu --- Jump to a goal within a buffer - (and (boundp 'fume-function-name-regexp-alist) - (defvar fume-function-name-regexp-proof - (cons proof-goal-with-hole-regexp 2)) - (push (cons major-mode 'fume-function-name-regexp-proof) - fume-function-name-regexp-alist)) - (and (boundp 'fume-find-function-name-method-alist) - (push (cons major-mode 'proof-match-find-next-function-name) - fume-find-function-name-method-alist)) - - ;; Additional key definitions which depend on configuration for - ;; specific proof assistant. - (define-key proof-mode-map - (vconcat [(control c)] (vector proof-terminal-char)) - 'proof-active-terminator-minor-mode) - - (define-key proof-mode-map (vector proof-terminal-char) - 'proof-active-terminator) - - (make-local-variable 'indent-line-function) - (setq indent-line-function 'proof-indent-line) - - ;; Toolbar - (if (featurep 'proof-toolbar) - (proof-toolbar-setup)) - - ;; Menu - (easy-menu-define proof-mode-menu - proof-mode-map - "Menu used in Proof General scripting mode." - (cons proof-mode-name - (append - (cdr proof-menu) - ;; begin UGLY COMPATIBILTY HACK - ;; older/non-existent customize doesn't have - ;; this function. - (if (fboundp 'customize-menu-create) - (list (customize-menu-create 'proof) - (customize-menu-create 'proof-internal - "Internals")) - nil) - ;; end UGLY COMPATIBILTY HACK - ))) - (easy-menu-add proof-mode-menu proof-mode-map) - - ;; For fontlock - - ;; setting font-lock-defaults explicitly is required by FSF Emacs - ;; 20.2's version of font-lock - (make-local-variable 'font-lock-defaults) - (setq font-lock-defaults '(font-lock-keywords)) - - ;; FIXME (da): zap commas support is too specific, should be enabled - ;; by each proof assistant as it likes. - (remove-hook 'font-lock-after-fontify-buffer-hook 'proof-zap-commas-buffer t) - (add-hook 'font-lock-after-fontify-buffer-hook - 'proof-zap-commas-buffer nil t) - (remove-hook 'font-lock-mode-hook 'proof-unfontify-separator t) - (add-hook 'font-lock-mode-hook 'proof-unfontify-separator nil t) - - ;; if we don't have the following, zap-commas fails to work. - ;; FIXME (da): setting this to t everywhere is too brutal. Should - ;; probably make it local. - (and (boundp 'font-lock-always-fontify-immediately) - (setq font-lock-always-fontify-immediately t)) - - (run-hooks 'proof-mode-hook)) - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Proof General shell mode configuration ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; This has to come after proof-mode is defined -(define-derived-mode proof-shell-mode comint-mode - "proof-shell" "Proof shell mode - not standalone" - (setq proof-buffer-type 'shell) - (setq proof-shell-busy nil) - (setq proof-shell-delayed-output (cons 'insert "done")) - (setq proof-shell-proof-completed nil) - (make-local-variable 'proof-shell-insert-hook) - - ;; comint customisation. comint-prompt-regexp is used by - ;; comint-get-old-input, comint-skip-prompt, comint-bol, backward - ;; matching input, etc. - (setq comint-prompt-regexp proof-shell-prompt-pattern) - - ;; An article by Helen Lowe in UITP'96 suggests that the user should - ;; not see all output produced by the proof process. - (remove-hook 'comint-output-filter-functions - 'comint-postoutput-scroll-to-bottom 'local) - - (add-hook 'comint-output-filter-functions 'proof-shell-filter nil 'local) - (setq comint-get-old-input (function (lambda () ""))) - (proof-dont-show-annotations) - (setq proof-marker (make-marker))) - -(easy-menu-define proof-shell-menu - proof-shell-mode-map - "Menu used in Proof General shell mode." - (cons proof-mode-name (cdr proof-shell-menu))) - -(defun proof-shell-config-done () - (accept-process-output (get-buffer-process (current-buffer))) - - ;; If the proof process in invoked on a different machine e.g., - ;; for proof-prog-name="rsh fastmachine proofprocess", one needs - ;; to adjust the directory: - (and proof-shell-cd - (proof-shell-insert (format proof-shell-cd - ;; under Emacs 19.34 default-directory contains "~" which causes - ;; problems with LEGO's internal Cd command - (expand-file-name default-directory)))) - - (if proof-shell-init-cmd - (proof-shell-insert proof-shell-init-cmd)) - - ;; Note that proof-marker actually gets set in proof-shell-filter. - ;; This is manifestly a hack, but finding somewhere more convenient - ;; to do the setup is tricky. - - (while (null (marker-position proof-marker)) - (if (accept-process-output (get-buffer-process (current-buffer)) 15) - () - (error "Failed to initialise proof process")))) +;; Load toolbar code if toolbars available. +(if (featurep 'toolbar) + (require 'proof-toolbar)) -(define-derived-mode pbp-mode fundamental-mode - proof-mode-name "Proof by Pointing" - ;; defined-derived-mode pbp-mode initialises pbp-mode-map - (setq proof-buffer-type 'pbp) - (suppress-keymap pbp-mode-map 'all) -; (define-key pbp-mode-map [(button2)] 'pbp-button-action) - (proof-define-keys pbp-mode-map proof-universal-keys) - (erase-buffer)) +(require 'proof-script) +(require 'proof-shell) (provide 'proof) |