diff options
author | David Aspinall <da@inf.ed.ac.uk> | 2004-08-25 20:19:25 +0000 |
---|---|---|
committer | David Aspinall <da@inf.ed.ac.uk> | 2004-08-25 20:19:25 +0000 |
commit | 609a69821db204aad0f9bd3a3e7ecfebc455e4e1 (patch) | |
tree | 5a606f40ca4d7752aae9bc03a79ba2e8df7f30dc /generic/pg-pgip.el | |
parent | 417636c4fc274ba341335ec01d2775dab1fc630a (diff) |
Extended and updated PGIP support for PGIP 2.X
Diffstat (limited to 'generic/pg-pgip.el')
-rw-r--r-- | generic/pg-pgip.el | 825 |
1 files changed, 561 insertions, 264 deletions
diff --git a/generic/pg-pgip.el b/generic/pg-pgip.el index dc00426f..c8ee6523 100644 --- a/generic/pg-pgip.el +++ b/generic/pg-pgip.el @@ -10,153 +10,162 @@ ;; ;; Proof General Kit uses PGIP, an XML-message protocol ;; for interactive proof. This file contains functions -;; to process PGIP commands sent from the proof assistant. +;; to process PGIP commands sent from the proof assistant +;; and to construct PGIP commands to send out. ;; +;; TESTING NOTES: turn on `proof-show-debug-messages' for +;; useful tracing messages: (setq proof-show-debug-messages t). -;; Halt on errors during development: later may accumulate and ignore. -(defalias 'pg-pgip-error 'error) +(require 'pg-xml) +(require 'pg-pgip-old) ;; Handle some PGIP 1.X format messages + +(defalias 'pg-pgip-debug 'proof-debug) +(defalias 'pg-pgip-error 'error) +(defalias 'pg-pgip-warning 'pg-internal-warning) ;;;###autoload -(defun pg-pgip-process-packet (pgip) - "Process the command packet PGIP, which is parsed XML according to pg-xml-parse-*" +(defun pg-pgip-process-packet (pgips) + "Process the command packet PGIP, which is parsed XML according to pg-xml-parse-*. +The list PGIPS may contain one or more PGIP packets, whose contents are processed." ;; PGIP processing is split into two steps: ;; (1) process each command, altering internal data structures ;; (2) post-process for each command type, affecting external interface (menus, etc). (mapcar 'pg-pgip-post-process - (pg-pgip-process-cmds pgip))) - -(defun pg-pgip-process-cmds (pgips) - "Process the command(s) in PGIP, returning list of command symbols processed." - (let (cmdtys) - (while pgips - (let* ((pgip (car pgips)) - (elt (or (car-safe (car pgip)) ; normalise to symbol - (car pgip))) - ;; FIXME: this is wrong for self-closing elts, test with - ;; ProofGeneral.process_pgip("<pgip><askpgml/></pgip>"); - (attr (cdr-safe (car pgip))) - (attrs (and attr (if (listp (cdr attr)) ; normalise to list - attr (list attr)))) - (body (cdr pgip))) - (add-to-list 'cmdtys elt) - (cond - ;; <pgip> - ((eq elt 'pgip)) ;; ignore pgip attributes for now - ;; <usespgml> - ((eq elt 'usespgml) - (proof-debug "Received usespgml message, version %s" - (pg-pgip-get-version "usespgml" attrs))) - - ;; FIXME: this next will be replaced by current version of - ;; <haspref> in PG 4.0. The <oldhaspref> is for - ;; compatibility with Isabelle2004. - ;; (Isabelle2003 will break with PG 4.0) - ((eq elt 'haspref) - (pg-pgip-haspref attrs (car-safe body))) - - ;; <oldhaspref> - ((eq elt 'oldhaspref) - (pg-pgip-haspref attrs (car-safe body))) - - ;; <prefval> - ((eq elt 'prefval) - (pg-pgip-prefval attrs (car-safe body))) - - ;; <idtable> - ((eq elt 'idtable) - ) - ;; <addid> - ((eq elt 'addid) - ) - ;; <delid> - ((eq elt 'delid) - ) - ;; <menuadd> - ((eq elt 'menuadd) - ) - ((eq elt 'menudel) - ) - ;; <parsescript> - ((eq elt 'parsescript) - (pg-pgip-parsescript (car-safe body))) - ) - ;; Move on to next element - (setq pgips (cdr pgips)))) - ;; Return list of command types processed. - cmdtys)) - -(defun pg-pgip-post-process (pgip) - "Perform post-processing for a PGIP command type PGIP-ELT." - (cond - ((eq pgip 'pgip)) - ((eq pgip 'usespgml)) - ((or - (eq pgip 'haspref) - (eq pgip 'oldhaspref) ;; FIXME: see note above about oldhaspref - (eq pgip 'prefval)) - ;; Update preferences view/menu - (proof-assistant-menu-update)) - ((or - (eq pgip 'idtable) - (eq pgip 'addid) - (eq pgip 'delid)) - ;; Update completion tables/view - ) - ((or - (eq pgip 'menuadd) - (eq pgip 'menudel)) - ;; Update menus - ))) + (reduce 'union (mapcar 'pg-pgip-process-pgip pgips)))) + +;; TODO: use id's and sequence numbers to reconstruct streams of messages. +(defvar pg-pgip-last-seen-id nil) +(defvar pg-pgip-last-seen-seq nil) + +(defun pg-pgip-process-pgip (pgip) + "Process the commands in the PGIP XML-node PGIP." + (let* ((name (xml-node-name pgip)) + (origin (pg-xml-get-attr 'origin pgip 'optional)) + (id (pg-xml-get-attr 'id pgip 'optional)) + (class (pg-xml-get-attr 'class pgip)) + (refseq (pg-xml-get-attr 'refseq pgip 'optional)) + (refid (pg-xml-get-attr 'refid pgip 'optional)) + (seq (pg-xml-get-attr 'seq pgip))) + (setq pg-pgip-last-seen-id id) + (setq pg-pgip-last-seen-seq seq) + (if (eq name 'pgip) + ;; NB: schema currently allows only one message here + (mapcar 'pg-pgip-process-msg (xml-node-children pgip)) + (pg-internal-warning "pg-pgip-process-pgip: expected PGIP element, got %s" name)))) + +(defun pg-pgip-process-msg (pgipmsg) + "Process the PGIP message in the XML node PGIPMSG. +Return a symbol representing the PGIP command processed, or nil." + (let* ((name (xml-node-name pgipmsg)) + (fname (intern (concat "pg-pgip-process-" (symbol-name name))))) + (if (fboundp fname) + (progn + (pg-pgip-debug "Processing PGIP message seq %s, type %s" (pg-xml-get-attr 'seq pgip) name) + (funcall fname pgipmsg) + name) + (pg-internal-warning "!!! unrecognized/unimplemented PGIP message element `%s'" name) + nil))) + +(defvar pg-pgip-post-process-functions + '((hasprefs . proof-assistant-menu-update) + (menuadd . proof-assistant-menu-update) + (menudel . proof-assistant-menu-update) + (idtable . pg-pgip-update-idtables) ;; TODO: not yet implemented + (addid . pg-pgip-update-idtables) + (delid . pg-pgip-update-idtables)) + "Table of functions to call after processing PGIP commands") + +(defun pg-pgip-post-process (cmdname) + "Perform post-processing for a PGIP command type CMDNAME (a symbol)." + (let ((ppfn (cdr-safe (assoc cmdname pg-pgip-post-process-functions)))) + (if (fboundp ppfn) + (progn + (pg-pgip-debug "Post-processing for PGIP message type `%s' with function `%s'" cmdname ppfn) + (funcall ppfn)) + (pg-pgip-debug "[No post-processing defined for PGIP message type `%s']" cmdname)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; <haspref default="d" kind="k" type="t" -;; description="d" class="c">name</haspref> +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; Proof assistant advises PG that it has a preference value named name, -;; category k, class c, with default value d, type t. -;; - -;; FIXME: PGIP requires prover to support <resetprefs/>, but this -;; could be done from interface, since default values are -;; advertised for preferences. - -(defun pg-pgip-haspref (attrs name) - "Issue a defpacustom from a <haspref> element with attributes ATTRS, name NAME." - (unless (stringp name) - (pg-pgip-error "pg-pgip-haspref: missing NAME in <haspref>NAME</haspref>.")) - (let* - ((type (pg-pgip-get-type attrs)) - (defattr (pg-pgip-get-attr "haspref" 'default attrs t)) - (default (if defattr - (pg-pgip-interpret-value defattr type) - (pg-pgip-default-for type))) - (kind (intern - (or - (pg-pgip-get-attr "haspref" 'kind attrs t) - ;; Default to kind user - "user"))) - (descr (or (pg-pgip-get-attr "haspref" 'descr attrs t) "")) - (subst (pg-pgip-subst-for type)) - (setting - (pg-prover-interpret-pgip-command - (concat "<pgip><setpref name=\"" name "\">" subst "</setpref></pgip>"))) - (symname (intern name))) ;; FIXME: consider Emacs ID normalising - (ignore-errors - ;; FIXME: allow rest of PGIP to be processed, would be better to - ;; accumulate errors somehow. - (proof-debug "haspref calling defpacustom: name:%s default:%s type:%s setting:%s" symname default type setting) - (eval - ;; FIXME: would like unique custom group for settings introduced by haspref. - ;; (preferences or something). - `(defpacustom ,symname ,default - (concat descr (if descr "\n") - "Setting configured by <haspref> PGIP message") - :type (quote ,type) - :setting ,setting))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Message processing: fromprovermsg/kitconfig +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun pg-pgip-process-usespgip (node) + (let ((version (pg-pgip-get-version node))) + (pg-pgip-debug "Received <usespgip> message with version `%s'" version))) + +(defun pg-pgip-process-usespgml (node) + (let ((version (pg-pgip-get-version node))) + (pg-pgip-debug "Received <usespgml> message with version `%s'" version))) + +(defun pg-pgip-process-pgmlconfig (node) + ;; symconfig specify an ascii alternative string for a named symbol; + ;; we process it by storing a property 'pgml-alt on the elisp symbol. + (let ((pgmlconfigures (xml-get-children node))) + (dolist (config pgmlconfigures) + (cond + ((and (not (stringp config)) + (eq (xml-node-name config "symconfig"))) + (let + ((symname (pg-pgip-get-symname node)) + (asciialt (pg-xml-get-attr 'alt node t))) + (put (intern symname) + 'pgml-alt asciialt))) + (t + (pg-pgip-warning "Unexpected child of <pgmlconfig> node: %s" config)))))) + +(defun pg-pgip-process-proverinfo (node) + (let* ((name (pg-pgip-get-name node)) + (descr (pg-pgip-get-descr node t "<no description supplied>")) + (version (pg-pgip-get-version node t "<no version supplied>")) + (url (pg-pgip-get-url node t)) + (welcomeelt (pg-xml-child 'welcomemsg node))) + (welcomemsg (if welcomeelt (pg-pgip-get-text welcomeelt))) + (icon (xml-get-children node 'icon)) + (helpdocs (xml-get-children node 'helpdocs))) + ;; TODO: enter the data into a table of provers (see proof-site.el). + ;; Seems little point doing this while we have the single-prover limitation. + ) + +;; +;; Preferences +;; + +(defun pg-pgip-process-hasprefs (node) + (let* ((prefcat (pg-pgip-get-prefcat node)) + (hasprefnodes (xml-get-children node 'haspref))) + (dolist (node hasprefnodes) + (let* ((name (pg-pgip-get-name node)) + (descr (pg-pgip-get-descr node 'optional)) + (pgiptype (pg-pgip-get-pgiptype (pg-xml-child-elt node))) + (default (pg-pgip-get-default node 'optional)) + (defaultlsp (if default + (pg-pgip-interpret-value default pgiptype) + (pg-pgip-default-for pgiptype))) + (icon (pg-pgip-get-icon node 'optional))) ;; TODO; ignored + (pg-pgip-haspref name pgiptype prefcat descr defaultlsp))))) + +(defun pg-pgip-haspref (name type prefcat descr default) + "Call `defpacustom' with values from a <haspref> element." + (let* ((subst (pg-pgip-subst-for type)) + ;; FIXME: this substitution mechanism is not really good enough, we + ;; want to escape XML characters, etc. Should be possible: turn this + ;; into a function. + (pgipcmd (concat "<setpref name=\"" name "\" prefcategory=\"" prefcat "\">" + subst "</setpref>")) + (symname (intern name))) + (pg-pgip-debug + "haspref calling defpacustom: name:%s default:%s type:%s pgipcmd:%s" symname default type pgipcmd) + (eval + `(defpacustom ,symname ,default + (concat descr (if descr "\n") + "Setting configured by <haspref> PGIP message") + :type (quote ,type) + :pggroup ,prefcat + :pgipcmd ,pgipcmd)))) + +(defun pg-pgip-process-prefval (node) ;; ;; <prefval name="n">value</prefval> ;; @@ -171,28 +180,272 @@ ;(defun pg-pgip-prefval (attrs value) ; "Process a <prefval> element, by setting interface's copy of preference." ; (let* -; ((name (pg-pgip-get-attr "haspref" 'name attrs t)) +; ((name (pg-xml-get-attr 'haspref 'name attrs t)) ; (type ( + ) + + + + +;; +;; guiconfig +;; + + +(defun pg-pgip-process-guiconfig (node) + ) + +;; +;; Identifiers: managing obarrays of symbols used for completion +;; + +(defun pg-pgip-process-ids (node) + "Manipulate identifier tables, according to setids/addids/delids in NODE." + (let ((idtables (pg-xml-child-elts node)) + (opn (xml-node-name node))) + (dolist (idtable idtables) + (let* ((context (pg-xml-get-attr 'context idtable 'optional)) + (objtype (intern (pg-pgip-get-objtype idtable))) + (idents (mapcar 'pg-xml-get-text-content + (xml-get-children idtable 'identifier))) + (obarray (or (and (not (eq opn 'setids)) + (get objtype 'pgsymbols)) + ;; new empty obarray for setids or if unset + (set objtype 'pg-symbols (make-vector 31 0))))) + (cond + ((or (eq opn 'setids) (eq opn 'addids)) + (mapcar (lambda (i) (intern i obarray)) idents)) + ((eq opn 'delids) + (mapcar (lambda (i) (unintern i obarray)) idents)) + (t + (pg-pgip-error "pg-pgip-process-ids: called on wrong node %s" + (xml-node-name node)))))))) + +(defalias 'pg-pgip-process-setids 'pg-pgip-process-ids) +(defalias 'pg-pgip-process-addids 'pg-pgip-process-ids) +(defalias 'pg-pgip-process-delids 'pg-pgip-process-ids) + + +(defun pg-pgip-process-idvalue (node) + (let ((name (pg-pgip-get-name node)) + (objtype (pg-pgip-get-objtype node)) + (text (pg-pgip-get-displaytext node))) + ;; TODO: display and cache the value in a dedicated buffer + ;; FIXME: should idvalue have a context? + (proof-message text))) + +;; +;; Menu configuration [TODO] +;; + +(defun pg-pgip-process-menuadd (node) + ) + +(defun pg-pgip-process-menudel (node) + ) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Message processing: fromprovermsg/proveroutput +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun pg-pgip-process-ready (node) + ) + +(defun pg-pgip-process-cleardisplay (node) + (let ((area (pg-pgip-get-area node))) + (cond + ((equal area "message") + (proof-shell-maybe-erase-response nil t t)) + ((equal area "display") + (proof-clean-buffer proof-goals-buffer)) + ((equal area "all") + (proof-shell-maybe-erase-response nil t t) + (proof-clean-buffer proof-goals-buffer) + ;; TODO: could also erase trace here. + ;; [PGIP: Should trace have a separate cat?] + )))) + +(defun pg-pgip-process-proofstate (node) + ;(let ((pgml (pg-xml-child ) + ) + +(defun pg-pgip-process-normalresponse (node) + (let ((text (pg-pgip-get-displaytext node))) + (proof-message text))) + +(defun pg-pgip-process-errorresponse (node) + (let ((text (pg-pgip-get-displaytext node))) + (proof-warning text))) + +(defun pg-pgip-process-scriptinsert (node) + (let ((text (pg-pgip-get-displaytext node))) + (proof-insert-pbp-command text))) + + +(defun pg-pgip-process-metainforesponse (node) + ) + +(defun pg-pgip-process-parseresult (node) + ) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Message processing: fromprovermsg/fileinfomsg +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun pg-pgip-process-informfileloaded (node) + (let ((thyname (pg-pgip-get-thyname node)) + (url (pg-pgip-get-url node)) + (filename (pg-pgip-get-url-filename url))) ;; FIXME: unimplemented! + (proof-register-possibly-new-processed-file filename))) +(defun pg-pgip-process-informfileretracted (node) + (let ((thyname (pg-pgip-get-thyname node)) + (url (pg-pgip-get-url node)) + (filename (pg-pgip-get-url-filename url))) ;; FIXME: unimplemented! + (proof-unregister-possibly-processed-file filename))) ;; FIXME: unimplemented! + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Message processing: todisplaymsg/brokermsg +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defun pg-pgip-process-brokerstatus (node) + ) + +(defun pg-pgip-process-proveravailmsg (node) + ) + +(defun pg-pgip-process-newprovermsg (node) + ) + +(defun pg-pgip-process-proverstatusmsg (node) + ) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Message processing: todisplaymsg/dispmsg/dispfilemsg +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar pg-pgip-srcids nil + "Association list of srcrds to (buffer file) lists") + +;; FIXME: right action? +(defun pg-pgip-process-newfile (node) + "Process <newfile> message from broker by displaying file. +Also sets local proverid and srcid variables for buffer." + (let* ((srcid (pg-pgip-get-srcid node)) + (proverid (pg-pgip-get-proverid node)) + (url (pg-pgip-get-url node)) + (file (pg-pgip-file-of-url url))) + (find-file file) + (let ((buffer (get-file-buffer file))) + (with-current-buffer buffer + (make-local-variable 'proverid) + (setq proverid proverid)) + (set pg-pgip-srcids (acons srcid (list buffer file) pg-pgip-srcids))))) - + +;; FIXME: right action? +(defun pg-pgip-process-filestatus (node) + "Process <filestatus> flag by adjusting buffer's modified flag." + (let* ((srcid (pg-pgip-get-srcid node)) + (filestat (pg-xml-get-attr 'filestat node)) + (buffer (car-safe (cdr-safe (assoc srcid pg-pgip-srcids))))) + (proof-with-current-buffer-if-exists buffer + (cond + ((equal filestat "changed") + (set-buffer-modified-p t)) + ((equal filestat "saved") + (set-buffer-modified-p nil)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Message processing: todisplaymsg/dispmsg/dispobjmsg ;; -;; Dealing with <pgiptype> +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun pg-pgip-process-newobj (node) + ) + +(defun pg-pgip-process-delobj (node) + ) + +(defun pg-pgip-process-objectstatus (node) + ) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; +;; Message processing: parsescript [incomplete] +;; +;; NB: pgip.rnc v 2.18 only has parsescript sent to prover, +;; so if we get here we have a invalid document. +;; +;; Provide parsing functionality for other interfaces (sacrilege!) +;; + +(defun pg-pgip-process-parsescript (node) + ;; Text ought to be cdata or something. We'll stick it into a buffer + ;; and run the proof-script code on it. + (save-excursion + (let* ((text (pg-pgip-get-text-content node)) + (buf (get-buffer-create " *pgip-parsescript*"))) + (delete-region (point-min) (point-max)) ; wipe previous contents + (insert text) + (let ((semis (proof-segment-up-to (point)))) + ;; semis is now a list of (type, string, int) tuples (in reverse order). + ;; we'll turn it into XML and send it back to the prover + ;; FIXME: todo: make parseresult element according to types, + ;; properscriptcmd = properproofcmd | properfilecmd | bindid + )))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; PGIP types and values <-> Elisp types and values +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +(defun pg-pgip-get-pgiptype (node) + "Return internal (custom format) representation for <pgiptype> element in NODE." + (let ((tyname (and node (xml-node-name node)))) + (cond + ((eq tyname 'pgipbool) 'boolean) + ((eq tyname 'pgipint) 'integer) ;; TODO: implement range limits + ((eq tyname 'pgipstring) 'string) + ((eq tyname 'pgipconst) + (let ((name (pg-pgip-get-name node 'optional)) + (val (pg-pgip-get-value node))) + (if name + (list 'const :tag name val) + (list 'const val)))) + ((eq tyname 'pgipchoice) + (let ((choicesnodes (pg-xml-child-elts node)) + (choices (mapcar 'pg-pgip-get-pgiptype choicesnodes))) + (list 'choice choices))) + (t + (pg-pgip-warning "pg-pgip-get-pgiptype: unrecognized/missing typename \"%s\"" tyname))))) (defun pg-pgip-default-for (type) "Synthesize a default value for type TYPE." (cond - ((eq type 'boolean) nil) - ((eq type 'integer) 0) - ((eq type 'string) "") + ((eq type 'boolean) nil) + ((eq type 'integer) 0) + ((eq type 'string) "") + ((eq (car-safe type) 'const) + (car (last type))) ((eq (car-safe type) 'choice) - (car-safe (cdr-safe type))) + (pg-pgip-default-for (car-safe (cdr-safe type)))) (t (pg-pgip-error "pg-pgip-default-for: unrecognized type passed in")))) @@ -203,160 +456,204 @@ ((eq type 'integer) "%i") (t "%s"))) -(defun pg-pgip-get-type (attrs) - "Extract and check type value from ATTRS. Return type in internal (custom) format." - (let - ((rawtype (pg-pgip-get-attr "haspref" 'type attrs))) - (pg-pgip-pgiptype rawtype))) - - -(defun pg-pgip-pgiptype (rawtype) - "Return internal (custom format) representation for <pgiptype> element." - (cond - ((string-match "choice\(\\(.*\\)\)" rawtype) - (let* ((choiceslist (match-string 1 rawtype)) - ;; FIXME: nested choices not supported yet - (choices (split-string choiceslist "[ \f\t\n\r\v]*,[ \f\t\n\r\v]*"))) - (list 'choice (mapcar 'pg-pgip-pgiptype choices)))) - ((equal rawtype "boolean") - 'boolean) - ((equal rawtype "int") - 'integer) - ((equal rawtype "nat") ;; nat treated as int - 'integer) - ((equal rawtype "string") - 'string) - (t - (error "pg-pgip-pgiptype: unrecognized type %s" rawtype)))) - ;; Converting PGIP representations to elisp representations. This is -;; the inverse of proof-assistant-format translations in proof-menu.el, -;; although we fix PGIP value syntax. - -(defun pg-pgip-interpret-bool (value) - (cond - ((string-equal value "true") t) - ((string-equal value "false") nil) - ;; Non-boolean value: return false, give debug message. - (t (progn - (proof-debug "pg-pgip-interpret-bool: received non-bool value %s" value) - nil)))) - -(defun pg-pgip-interpret-int (value) - ;; FIXME: string-to-int returns zero for non int string, - ;; should have better validation here. - (string-to-int value)) - -(defun pg-pgip-interpret-string (value) - value) - -(defun pg-pgip-interpret-choice (choices value) - ;; Untagged union types: test for each type in turn. - ;; FIXME: nested unions not supported here. - (cond - ((and - (memq 'boolean choices) - (or (string-equal value "true") (string-equal value "false"))) - (pg-pgip-interpret-value value 'boolean)) - ((and - (memq 'integer choices) - (string-match "[0-9]+$" value)) - (pg-pgip-interpret-value value 'integer)) - ((memq 'string choices) - ;; FIXME: No special syntax for string inside PGIP yet, should be? - (pg-pgip-interpret-value value 'string)) - (t - (pg-pgip-error - "pg-pgip-interpret-choice: mismatching value %s for choices %s" - value choices)))) +;; the inverse of proof-assistant-format translations in +;; proof-menu.el, although we fix PGIP value syntax. (defun pg-pgip-interpret-value (value type) + "Interpret the PGIP value VALUE at (lisp-representation for) TYPE." (cond ((eq type 'boolean) - (pg-pgip-interpret-bool value)) - ((eq type 'integer) - (pg-pgip-interpret-int value)) - ((eq type 'string) - (pg-pgip-interpret-string value)) - ((and (consp type) (eq (car type) 'choice)) + (cond + ((or (string-equal value "true") (string-equal value "1")) t) + ((or (string-equal value "false") (string-equal value "0")) nil) + (t (progn + (pg-pgip-warning "pg-pgip-interpret-value: received non-bool value %s" value) + nil)))) + ((eq type 'integer) (string-to-int value)) + ((eq type 'string) value) + ((eq (car-safe type) 'const) value) + ((eq (car-safe type) 'choice) (pg-pgip-interpret-choice (cdr type) value)) (t (pg-pgip-error "pg-pgip-interpret-value: unkown type %s" type)))) - -;;(defun pg-pgip-interpret-choice (value) -;; FIXME: Choice should be tagged. Syntax is <pgiptype>(value) - +(defun pg-pgip-interpret-choice (choices value) + ;; Untagged union types: test for value in each type in turn. + (let (res) + (while (and choices (not res)) + (let ((type (car choices))) + ((eq (car-safe type) 'const) + (string-equal value (cadr type))) + (setq res (pg-pgip-interpret-const value 'const))) + ((and (eq type 'integer) + (string-match "[+-]?[0-9]+$" value)) + (setq res (pg-pgip-interpret-value value 'integer))) + ((and (eq type 'boolean) + (or (string-equal value "true") + (string-equal value "false") + (string-equal value "0") + (string-equal value "1"))) + (setq res (pg-pgip-interpret-value value 'boolean))) + ((eq type 'string) + (setq res (pg-pgip-interpret-value value 'string)))) + (setq choices (cdr choices))) + (or res + (pg-pgip-error + "pg-pgip-interpret-choice: mismatching value %s for choices %s" + value choices))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; Auxiliary functions for parsing +;; Auxiliary functions for parsing common bits of PGIP ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun pg-pgip-get-attr (elt attrnm attrs &optional optional) - (let ((vrs (cdr-safe (assoc attrnm attrs)))) - (if optional - vrs - (or vrs - (error "Didn't find %s attribute in %s element" attrnm elt))))) +(defun pg-pgip-get-icon (node &optional optional defaultval) + "Return the <icon> child of NODE, or nil if none." + (pg-xml-get-child 'icon node)) -(defun pg-pgip-get-version (elt attrs &optional optional) - (pg-pgip-get-attr elt "version" attrs optional)) +(defsubst pg-pgip-get-name (node &optional optional defaultval) + (pg-xml-get-attr 'name node optional defaultval)) +(defsubst pg-pgip-get-version (node &optional optional defaultval) + (pg-xml-get-attr 'version node optional defaultval)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Function to dispatch PGIP commands to prover. -;; -(defun pg-prover-interpret-pgip-command (pgip) - (cond - ((stringp proof-shell-issue-pgip-cmd) - (format proof-shell-issue-pgip-cmd pgip)) - ((functionp proof-shell-issue-pgip-cmd) - (funcall proof-shell-issue-pgip-cmd pgip)) - (t - ;; FIXME: Empty setting: might be better to send a comment - ""))) +(defsubst pg-pgip-get-descr (node &optional optional defaultval) + (pg-xml-get-attr 'descr node optional defaultval)) + +(defsubst pg-pgip-get-thmname (node &optional optional defaultval) + (pg-xml-get-attr 'thmname node optional defaultval)) + +(defsubst pg-pgip-get-thyname (node &optional optional defaultval) + (pg-xml-get-attr 'thmname node optional defaultval)) + +(defsubst pg-pgip-get-url (node &optional optional defaultval) + (pg-xml-get-attr 'url node optional defaultval)) + +(defsubst pg-pgip-get-srcid (node &optional optional defaultval) + (pg-xml-get-attr 'srcid node optional defaultval)) +(defsubst pg-pgip-get-proverid (node &optional optional defaultval) + (pg-xml-get-attr 'proverid node optional defaultval)) +(defsubst pg-pgip-get-symname (node &optional optional defaultval) + (pg-xml-get-attr 'name node optional defaultval)) + +(defsubst pg-pgip-get-prefcat (node &optional optional defaultval) + (pg-xml-get-attr 'prefcategory node optional defaultval)) + +(defsubst pg-pgip-get-default (node &optional optional defaultval) + (pg-xml-get-attr 'default node optional defaultval)) + +(defsubst pg-pgip-get-objtype (node &optional optional defaultval) + (pg-xml-get-attr 'objtype node optional defaultval)) + +(defsubst pg-pgip-get-value (node) + (pg-xml-get-text node)) + +(defalias 'pg-pgip-get-displaytext 'pg-pgip-get-pgmltext) + +(defun pg-pgip-get-pgmltext (node) + ;; TODO: fetch text or markup XML with text properties + (pg-xml-get-text node)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Sending PGIP commands to prover ;; -(defun pg-issue-pgip (pgip &optional block) +(defun pg-pgip-string-of-command (pgip &optional refseq refid otherclass) + "Convert XML node PGIP or string into a command string to send to the prover. +This wraps the node in a <pgip> packet with appropriate attributes. +See `pg-pgip-assemble-packet' " + (let ((wrappedpgip + (pg-xml-string-of + (list (pg-pgip-assemble-packet (list pgip) refseq refid otherclass))))) + (cond + ((stringp proof-shell-issue-pgip-cmd) + (format proof-shell-issue-pgip-cmd wrappedpgip)) + ((functionp proof-shell-issue-pgip-cmd) + (funcall proof-shell-issue-pgip-cmd wrappedpgip)) + (t + ;; FIXME: Empty setting: might be better to send a comment + (pg-pgip-warning "pg-prover-interpret-pgip-command: `proof-shell-issue-pgip' is unset!") + "")))) + +(defconst pg-pgip-id + ;; Identifier based on hostname, user, time, and (FIXME: possible?) ppid + (concat (getenv "HOSTNAME") "/" (getenv "USER") "/" + (let ((tm (current-time))) (format "%d.%d" (car tm) (cadr tm)))) + "PGIP Identifier for this Emacs Proof General component.") + +(defvar pg-pgip-refseq nil + "The sequence number of the received message this reply refers to") +(defvar pg-pgip-refid nil + "The identity of the component this message is being sent to") + +(defvar pg-pgip-seq 0 "Sequence number of messages sent out.") + +(defun pg-pgip-assemble-packet (body &optional refseq refid otherclass) + "Construct a PGIP node with body list BODY. +REFSEQ and REFID are used for the corresponding attributes, if present. +By default, the class of the message is \"pa\" (destined for prover). +OTHERCLASS overrides this." + (let* ((origin (pg-xml-attr origin + (concat "EmacsPG/" + proof-general-short-version + "/" proof-assistant))) + (id (pg-xml-attr id pg-pgip-id)) + (class (pg-xml-attr class (or otherclass "pa"))) + (seq (pg-xml-attr seq (int-to-string (incf pg-pgip-seq)))) + (refseq (if refseq (list (pg-xml-attr refseq refseq)))) + (refid (if refid (list (pg-xml-attr refid refid)))) + (pgip_attrs (append (list origin id class seq) + refseq refid))) + (pg-xml-node pgip pgip_attrs body))) + +(defun pg-pgip-issue (pgip &optional block refseq refid otherclass) + "Issue a PGIP command via `proof-shell-issue-pgip-cmd' and `proof-shell-invisible-command'. +This expects a single XML node/string in PGIP, which will have a PGIP +header attached. If BLOCK is non-nil, we wait for the response from +the prover. For remaining optional args, see doc of +`pgip-assemble-packet'." (proof-shell-invisible-command - (funcall proof-shell-issue-pgip-cmd - ;; FIXME: ought to generate sequence numbers and - ;; engine ids here. - (format "<pgip class=\"pa\">%s</pgip>" pgip)) block)) + (pg-pgip-string-of-command pgip refseq refid otherclass) block)) -;;;###autoload -(defun pg-pgip-askprefs () - (pg-issue-pgip "<askprefs/>" 'block)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; Provide parsing functionality for other interfaces (sacrilege!) +;; Functions to send particular commands ;; - -(defun pg-pgip-parsescript (text) - ;; Text ought to be cdata or something. We'll stick it into a buffer - ;; and run the proof-script code on it. - (save-excursion - (let ((buf (get-buffer-create " *pgip-parsescript*"))) - (delete-region (point-min) (point-max)) ; wipe previous contents - (insert text) - (let ((semis (proof-segment-up-to (point)))) - ;; semis is now a list of (type, string, int) tuples (in reverse order). - ;; we'll turn it into XML and send it back to the prover - ;; FIXME: todo: make parseresult element according to types, - ;; properscriptcmd = properproofcmd | properfilecmd | bindid - )))) - + +;;;###autoload +(defun pg-pgip-askprefs () + "Send an <askprefs> message to the prover." + (pg-pgip-issue "<askprefs/>" 'block)) + +(defun pg-pgip-askids (&optional objtype thyname) + "Send an <askids> message to the prover." + (pg-pgip-issue + (pg-xml-node askids + (append + (if thyname + (list (pg-xml-attr 'thyname objtype))) + (if objtype + (list (pg-xml-attr 'objtype objtype)))) + nil) + 'block)) + + +(defun pg-pgip-reset () + "Reset state of the PGIP module" + (setq pg-pgip-refseq nil + pg-pgip-refid nil + pg-pgip-last-seen-id nil + pg-pgip-last-seen-seq nil + pg-pgip-seq 0 + proof-assistant-settings nil)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; |