comparison .elisp/pymacs.el @ 0:c30d68fbd368

Initial import from svn.
author Augie Fackler <durin42@gmail.com>
date Wed, 26 Nov 2008 10:56:09 -0600
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:c30d68fbd368
1 ;;; Interface between Emacs Lisp and Python - Lisp part. -*- emacs-lisp -*-
2 ;;; Copyright © 2001, 2002, 2003 Progiciels Bourbeau-Pinard inc.
3 ;;; François Pinard <pinard@iro.umontreal.ca>, 2001.
4
5 ;;; This program is free software; you can redistribute it and/or modify
6 ;;; it under the terms of the GNU General Public License as published by
7 ;;; the Free Software Foundation; either version 2, or (at your option)
8 ;;; any later version.
9 ;;;
10 ;;; This program is distributed in the hope that it will be useful,
11 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ;;; GNU General Public License for more details.
14 ;;;
15 ;;; You should have received a copy of the GNU General Public License
16 ;;; along with this program; if not, write to the Free Software Foundation,
17 ;;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
18
19 ;;; Portability stunts.
20
21 (defvar pymacs-use-hash-tables
22 (and (fboundp 'make-hash-table) (fboundp 'gethash) (fboundp 'puthash))
23 "Set to t if hash tables are available.")
24
25 (eval-and-compile
26
27 (if (fboundp 'multibyte-string-p)
28 (defalias 'pymacs-multibyte-string-p 'multibyte-string-p)
29 (defun pymacs-multibyte-string-p (string)
30 "Tell XEmacs if STRING should be handled as multibyte."
31 (not (equal (find-charset-string string) '(ascii))))))
32
33 (defalias 'pymacs-report-error (symbol-function 'error))
34
35 ;;; Published variables and functions.
36
37 (defvar pymacs-load-path nil
38 "List of additional directories to search for Python modules.
39 The directories listed will be searched first, in the order given.")
40
41 (defvar pymacs-trace-transit '(5000 . 30000)
42 "Keep the communication buffer growing, for debugging.
43 When this variable is nil, the `*Pymacs*' communication buffer gets erased
44 before each communication round-trip. Setting it to `t' guarantees that
45 the full communication is saved, which is useful for debugging.
46 It could also be given as (KEEP . LIMIT): whenever the buffer exceeds LIMIT
47 bytes, it is reduced to approximately KEEP bytes.")
48
49 (defvar pymacs-forget-mutability nil
50 "Transmit copies to Python instead of Lisp handles, as much as possible.
51 When this variable is nil, most mutable objects are transmitted as handles.
52 This variable is meant to be temporarily rebound to force copies.")
53
54 (defvar pymacs-mutable-strings nil
55 "Prefer transmitting Lisp strings to Python as handles.
56 When this variable is nil, strings are transmitted as copies, and the
57 Python side thus has no way for modifying the original Lisp strings.
58 This variable is ignored whenever `forget-mutability' is set.")
59
60 (defvar pymacs-timeout-at-start 30
61 "Maximum reasonable time, in seconds, for starting the Pymacs helper.
62 A machine should be pretty loaded before one needs to increment this.")
63
64 (defvar pymacs-timeout-at-reply 5
65 "Expected maximum time, in seconds, to get the first line of a reply.
66 The status of the Pymacs helper is checked at every such timeout.")
67
68 (defvar pymacs-timeout-at-line 2
69 "Expected maximum time, in seconds, to get another line of a reply.
70 The status of the Pymacs helper is checked at every such timeout.")
71
72 (defvar pymacs-dreadful-zombies nil
73 "If zombies should trigger hard errors, whenever they get called.
74 If `nil', calling a zombie will merely produce a diagnostic message.")
75
76 (defun pymacs-load (module &optional prefix noerror)
77 "Import the Python module named MODULE into Emacs.
78 Each function in the Python module is made available as an Emacs function.
79 The Lisp name of each function is the concatenation of PREFIX with
80 the Python name, in which underlines are replaced by dashes. If PREFIX is
81 not given, it defaults to MODULE followed by a dash.
82 If NOERROR is not nil, do not raise error when the module is not found."
83 (interactive
84 (let* ((module (read-string "Python module? "))
85 (default (concat (car (last (split-string module "\\."))) "-"))
86 (prefix (read-string (format "Prefix? [%s] " default)
87 nil nil default)))
88 (list module prefix)))
89 (message "Pymacs loading %s..." module)
90 (let ((lisp-code (pymacs-call "pymacs_load_helper" module prefix)))
91 (cond (lisp-code (let ((result (eval lisp-code)))
92 (message "Pymacs loading %s...done" module)
93 result))
94 (noerror (message "Pymacs loading %s...failed" module) nil)
95 (t (pymacs-report-error "Pymacs loading %s...failed" module)))))
96
97 (defun pymacs-eval (text)
98 "Compile TEXT as a Python expression, and return its value."
99 (interactive "sPython expression? ")
100 (let ((value (pymacs-serve-until-reply "eval" `(princ ,text))))
101 (when (interactive-p)
102 (message "%S" value))
103 value))
104
105 (defun pymacs-exec (text)
106 "Compile and execute TEXT as a sequence of Python statements.
107 This functionality is experimental, and does not appear to be useful."
108 (interactive "sPython statements? ")
109 (let ((value (pymacs-serve-until-reply "exec" `(princ ,text))))
110 (when (interactive-p)
111 (message "%S" value))
112 value))
113
114 (defun pymacs-call (function &rest arguments)
115 "Return the result of calling a Python function FUNCTION over ARGUMENTS.
116 FUNCTION is a string denoting the Python function, ARGUMENTS are separate
117 Lisp expressions, one per argument. Immutable Lisp constants are converted
118 to Python equivalents, other structures are converted into Lisp handles."
119 (pymacs-serve-until-reply
120 "eval" `(pymacs-print-for-apply ',function ',arguments)))
121
122 (defun pymacs-apply (function arguments)
123 "Return the result of calling a Python function FUNCTION over ARGUMENTS.
124 FUNCTION is a string denoting the Python function, ARGUMENTS is a list of
125 Lisp expressions. Immutable Lisp constants are converted to Python
126 equivalents, other structures are converted into Lisp handles."
127 (pymacs-serve-until-reply
128 "eval" `(pymacs-print-for-apply ',function ',arguments)))
129
130 ;;; Integration details.
131
132 ;; Python functions and modules should ideally look like Lisp functions and
133 ;; modules. This page tries to increase the integration seamlessness.
134
135 (defadvice documentation (around pymacs-ad-documentation activate)
136 ;; Integration of doc-strings.
137 (let* ((reference (pymacs-python-reference function))
138 (python-doc (when reference
139 (pymacs-eval (format "doc_string(%s)" reference)))))
140 (if (or reference python-doc)
141 (setq ad-return-value
142 (concat
143 "It interfaces to a Python function.\n\n"
144 (when python-doc
145 (if raw python-doc (substitute-command-keys python-doc)))))
146 ad-do-it)))
147
148 (defun pymacs-python-reference (object)
149 ;; Return the text reference of a Python object if possible, else nil.
150 (when (functionp object)
151 (let* ((definition (indirect-function object))
152 (body (and (pymacs-proper-list-p definition)
153 (> (length definition) 2)
154 (eq (car definition) 'lambda)
155 (cddr definition))))
156 (when (and body (listp (car body)) (eq (caar body) 'interactive))
157 ;; Skip the interactive specification of a function.
158 (setq body (cdr body)))
159 (when (and body
160 ;; Advised functions start with a string.
161 (not (stringp (car body)))
162 ;; Python trampolines hold exactly one expression.
163 (= (length body) 1))
164 (let ((expression (car body)))
165 ;; EXPRESSION might now hold something like:
166 ;; (pymacs-apply (quote (pymacs-python . N)) ARGUMENT-LIST)
167 (when (and (pymacs-proper-list-p expression)
168 (= (length expression) 3)
169 (eq (car expression) 'pymacs-apply)
170 (eq (car (cadr expression)) 'quote))
171 (setq object (cadr (cadr expression))))))))
172 (when (eq (car-safe object) 'pymacs-python)
173 (format "python[%d]" (cdr object))))
174
175 ;; The following functions are experimental -- they are not satisfactory yet.
176
177 (defun pymacs-file-handler (operation &rest arguments)
178 ;; Integration of load-file, autoload, etc.
179 ;; Emacs might want the contents of some `MODULE.el' which does not exist,
180 ;; while there is a `MODULE.py' or `MODULE.pyc' file in the same directory.
181 ;; The goal is to generate a virtual contents for this `MODULE.el' file, as
182 ;; a set of Lisp trampoline functions to the Python module functions.
183 ;; Python modules can then be loaded or autoloaded as if they were Lisp.
184 (cond ((and (eq operation 'file-readable-p)
185 (let ((module (substring (car arguments) 0 -3)))
186 (or (pymacs-file-force operation arguments)
187 (file-readable-p (concat module ".py"))
188 (file-readable-p (concat module ".pyc"))))))
189 ((and (eq operation 'load)
190 (not (pymacs-file-force
191 'file-readable-p (list (car arguments))))
192 (file-readable-p (car arguments)))
193 (let ((lisp-code (pymacs-call "pymacs_load_helper"
194 (substring (car arguments) 0 -3)
195 nil)))
196 (unless lisp-code
197 (pymacs-report-error "Python import error"))
198 (eval lisp-code)))
199 ((and (eq operation 'insert-file-contents)
200 (not (pymacs-file-force
201 'file-readable-p (list (car arguments))))
202 (file-readable-p (car arguments)))
203 (let ((lisp-code (pymacs-call "pymacs_load_helper"
204 (substring (car arguments) 0 -3)
205 nil)))
206 (unless lisp-code
207 (pymacs-report-error "Python import error"))
208 (insert (prin1-to-string lisp-code))))
209 (t (pymacs-file-force operation arguments))))
210
211 (defun pymacs-file-force (operation arguments)
212 ;; Bypass the file handler.
213 (let ((inhibit-file-name-handlers
214 (cons 'pymacs-file-handler
215 (and (eq inhibit-file-name-operation operation)
216 inhibit-file-name-handlers)))
217 (inhibit-file-name-operation operation))
218 (apply operation arguments)))
219
220 ;(add-to-list 'file-name-handler-alist '("\\.el\\'" . pymacs-file-handler))
221
222 ;;; Gargabe collection of Python IDs.
223
224 ;; Python objects which have no Lisp representation are allocated on the
225 ;; Python side as `python[INDEX]', and INDEX is transmitted to Emacs, with
226 ;; the value to use on the Lisp side for it. Whenever Lisp does not need a
227 ;; Python object anymore, it should be freed on the Python side. The
228 ;; following variables and functions are meant to fill this duty.
229
230 (defvar pymacs-used-ids nil
231 "List of received IDs, currently allocated on the Python side.")
232
233 (defvar pymacs-weak-hash nil
234 "Weak hash table, meant to find out which IDs are still needed.")
235
236 (defvar pymacs-gc-wanted nil
237 "Flag if it is time to clean up unused IDs on the Python side.")
238
239 (defvar pymacs-gc-running nil
240 "Flag telling that a Pymacs garbage collection is in progress.")
241
242 (defvar pymacs-gc-timer nil
243 "Timer to trigger Pymacs garbage collection at regular time intervals.
244 The timer is used only if `post-gc-hook' is not available.")
245
246 (defun pymacs-schedule-gc (&optional xemacs-list)
247 (unless pymacs-gc-running
248 (setq pymacs-gc-wanted t)))
249
250 (defun pymacs-garbage-collect ()
251 ;; Clean up unused IDs on the Python side.
252 (when pymacs-use-hash-tables
253 (let ((pymacs-gc-running t)
254 (pymacs-forget-mutability t)
255 (ids pymacs-used-ids)
256 used-ids unused-ids)
257 (while ids
258 (let ((id (car ids)))
259 (setq ids (cdr ids))
260 (if (gethash id pymacs-weak-hash)
261 (setq used-ids (cons id used-ids))
262 (setq unused-ids (cons id unused-ids)))))
263 (setq pymacs-used-ids used-ids
264 pymacs-gc-wanted nil)
265 (when unused-ids
266 (pymacs-apply "free_python" unused-ids)))))
267
268 (defun pymacs-defuns (arguments)
269 ;; Take one argument, a list holding a number of items divisible by 3. The
270 ;; first argument is an INDEX, the second is a NAME, the third is the
271 ;; INTERACTION specification, and so forth. Register Python INDEX with a
272 ;; function with that NAME and INTERACTION on the Lisp side. The strange
273 ;; calling convention is to minimise quoting at call time.
274 (while (>= (length arguments) 3)
275 (let ((index (nth 0 arguments))
276 (name (nth 1 arguments))
277 (interaction (nth 2 arguments)))
278 (fset name (pymacs-defun index interaction))
279 (setq arguments (nthcdr 3 arguments)))))
280
281 (defun pymacs-defun (index interaction)
282 ;; Register INDEX on the Lisp side with a Python object that is a function,
283 ;; and return a lambda form calling that function. If the INTERACTION
284 ;; specification is nil, the function is not interactive. Otherwise, the
285 ;; function is interactive, INTERACTION is then either a string, or the
286 ;; index of an argument-less Python function returning the argument list.
287 (let ((object (pymacs-python index)))
288 (cond ((null interaction)
289 `(lambda (&rest arguments)
290 (pymacs-apply ',object arguments)))
291 ((stringp interaction)
292 `(lambda (&rest arguments)
293 (interactive ,interaction)
294 (pymacs-apply ',object arguments)))
295 (t `(lambda (&rest arguments)
296 (interactive (pymacs-call ',(pymacs-python interaction)))
297 (pymacs-apply ',object arguments))))))
298
299 (defun pymacs-python (index)
300 ;; Register on the Lisp side a Python object having INDEX, and return it.
301 ;; The result is meant to be recognised specially by `print-for-eval', and
302 ;; in the function position by `print-for-apply'.
303 (let ((object (cons 'pymacs-python index)))
304 (when pymacs-use-hash-tables
305 (puthash index object pymacs-weak-hash)
306 (setq pymacs-used-ids (cons index pymacs-used-ids)))
307 object))
308
309 ;;; Generating Python code.
310
311 ;; Many Lisp expressions cannot fully be represented in Python, at least
312 ;; because the object is mutable on the Lisp side. Such objects are allocated
313 ;; somewhere into a vector of handles, and the handle index is used for
314 ;; communication instead of the expression itself.
315
316 (defvar pymacs-lisp nil
317 "Vector of handles to hold transmitted expressions.")
318
319 (defvar pymacs-freed-list nil
320 "List of unallocated indices in Lisp.")
321
322 ;; When the Python GC is done with a Lisp object, a communication occurs so to
323 ;; free the object on the Lisp side as well.
324
325 (defun pymacs-allocate-lisp (expression)
326 ;; This function allocates some handle for an EXPRESSION, and return its
327 ;; index.
328 (unless pymacs-freed-list
329 (let* ((previous pymacs-lisp)
330 (old-size (length previous))
331 (new-size (if (zerop old-size) 100 (+ old-size (/ old-size 2))))
332 (counter new-size))
333 (setq pymacs-lisp (make-vector new-size nil))
334 (while (> counter 0)
335 (setq counter (1- counter))
336 (if (< counter old-size)
337 (aset pymacs-lisp counter (aref previous counter))
338 (setq pymacs-freed-list (cons counter pymacs-freed-list))))))
339 (let ((index (car pymacs-freed-list)))
340 (setq pymacs-freed-list (cdr pymacs-freed-list))
341 (aset pymacs-lisp index expression)
342 index))
343
344 (defun pymacs-free-lisp (indices)
345 ;; This function is triggered from Python side for Lisp handles which lost
346 ;; their last reference. These references should be cut on the Lisp side as
347 ;; well, or else, the objects will never be garbage-collected.
348 (while indices
349 (let ((index (car indices)))
350 (aset pymacs-lisp index nil)
351 (setq pymacs-freed-list (cons index pymacs-freed-list)
352 indices (cdr indices)))))
353
354 (defun pymacs-print-for-apply (function arguments)
355 ;; This function prints a Python expression calling FUNCTION, which is a
356 ;; string naming a Python function, or a Python reference, over all its
357 ;; ARGUMENTS, which are Lisp expressions.
358 (let ((separator "")
359 argument)
360 (if (eq (car-safe function) 'pymacs-python)
361 (princ (format "python[%d]" (cdr function)))
362 (princ function))
363 (princ "(")
364 (while arguments
365 (setq argument (car arguments)
366 arguments (cdr arguments))
367 (princ separator)
368 (setq separator ", ")
369 (pymacs-print-for-eval argument))
370 (princ ")")))
371
372 (defun pymacs-print-for-eval (expression)
373 ;; This function prints a Python expression out of a Lisp EXPRESSION.
374 (let (done)
375 (cond ((not expression)
376 (princ "None")
377 (setq done t))
378 ((eq expression t)
379 (princ "True")
380 (setq done t))
381 ((numberp expression)
382 (princ expression)
383 (setq done t))
384 ((stringp expression)
385 (when (or pymacs-forget-mutability
386 (not pymacs-mutable-strings))
387 (let* ((multibyte (pymacs-multibyte-string-p expression))
388 (text (if multibyte
389 (encode-coding-string expression 'utf-8)
390 (copy-sequence expression))))
391 (set-text-properties 0 (length text) nil text)
392 (princ (mapconcat 'identity
393 (split-string (prin1-to-string text) "\n")
394 "\\n"))
395 (when (and multibyte
396 (not (equal (find-charset-string text) '(ascii))))
397 (princ ".decode('UTF-8')")))
398 (setq done t)))
399 ((symbolp expression)
400 (let ((name (symbol-name expression)))
401 ;; The symbol can only be transmitted when in the main oblist.
402 (when (eq expression (intern-soft name))
403 (princ "lisp[")
404 (prin1 name)
405 (princ "]")
406 (setq done t))))
407 ((vectorp expression)
408 (when pymacs-forget-mutability
409 (let ((limit (length expression))
410 (counter 0))
411 (princ "(")
412 (while (< counter limit)
413 (unless (zerop counter)
414 (princ ", "))
415 (pymacs-print-for-eval (aref expression counter))
416 (setq counter (1+ counter)))
417 (when (= limit 1)
418 (princ ","))
419 (princ ")")
420 (setq done t))))
421 ((eq (car-safe expression) 'pymacs-python)
422 (princ "python[")
423 (princ (cdr expression))
424 (princ "]")
425 (setq done t))
426 ((pymacs-proper-list-p expression)
427 (when pymacs-forget-mutability
428 (princ "[")
429 (pymacs-print-for-eval (car expression))
430 (while (setq expression (cdr expression))
431 (princ ", ")
432 (pymacs-print-for-eval (car expression)))
433 (princ "]")
434 (setq done t))))
435 (unless done
436 (let ((class (cond ((vectorp expression) "Vector")
437 ((and pymacs-use-hash-tables
438 (hash-table-p expression))
439 "Table")
440 ((bufferp expression) "Buffer")
441 ((pymacs-proper-list-p expression) "List")
442 (t "Lisp"))))
443 (princ class)
444 (princ "(")
445 (princ (pymacs-allocate-lisp expression))
446 (princ ")")))))
447
448 ;;; Communication protocol.
449
450 (defvar pymacs-transit-buffer nil
451 "Communication buffer between Emacs and Python.")
452
453 ;; The principle behind the communication protocol is that it is easier to
454 ;; generate than parse, and that each language already has its own parser.
455 ;; So, the Emacs side generates Python text for the Python side to interpret,
456 ;; while the Python side generates Lisp text for the Lisp side to interpret.
457 ;; About nothing but expressions are transmitted, which are evaluated on
458 ;; arrival. The pseudo `reply' function is meant to signal the final result
459 ;; of a series of exchanges following a request, while the pseudo `error'
460 ;; function is meant to explain why an exchange could not have been completed.
461
462 ;; The protocol itself is rather simple, and contains human readable text
463 ;; only. A message starts at the beginning of a line in the communication
464 ;; buffer, either with `>' for the Lisp to Python direction, or `<' for the
465 ;; Python to Lisp direction. This is followed by a decimal number giving the
466 ;; length of the message text, a TAB character, and the message text itself.
467 ;; Message direction alternates systematically between messages, it never
468 ;; occurs that two successive messages are sent in the same direction. The
469 ;; first message is received from the Python side, it is `(version VERSION)'.
470
471 (defun pymacs-start-services ()
472 ;; This function gets called automatically, as needed.
473 (let ((buffer (get-buffer-create "*Pymacs*")))
474 (with-current-buffer buffer
475 (buffer-disable-undo)
476 (set-buffer-multibyte nil)
477 (set-buffer-file-coding-system 'raw-text)
478 (save-match-data
479 ;; Launch the Pymacs helper.
480 (let ((process
481 (apply 'start-process "pymacs" buffer
482 (let ((python (getenv "PYMACS_PYTHON")))
483 (if (or (null python) (equal python ""))
484 "python"
485 python))
486 "-c" (concat "import sys;"
487 " from Pymacs.pymacs import main;"
488 " main(*sys.argv[1:])")
489 (mapcar 'expand-file-name pymacs-load-path))))
490 (cond ((fboundp 'set-process-query-on-exit-flag)
491 (set-process-query-on-exit-flag process nil))
492 ((fboundp 'process-kill-without-query-process)
493 (process-kill-without-query process)))
494 ;; Receive the synchronising reply.
495 (while (progn
496 (goto-char (point-min))
497 (not (re-search-forward "<\\([0-9]+\\)\t" nil t)))
498 (unless (accept-process-output process pymacs-timeout-at-start)
499 (pymacs-report-error
500 "Pymacs helper did not start within %d seconds"
501 pymacs-timeout-at-start)))
502 (let ((marker (process-mark process))
503 (limit-position (+ (match-end 0)
504 (string-to-number (match-string 1)))))
505 (while (< (marker-position marker) limit-position)
506 (unless (accept-process-output process pymacs-timeout-at-start)
507 (pymacs-report-error
508 "Pymacs helper probably was interrupted at start")))))
509 ;; Check that synchronisation occurred.
510 (goto-char (match-end 0))
511 (let ((reply (read (current-buffer))))
512 (if (and (pymacs-proper-list-p reply)
513 (= (length reply) 2)
514 (eq (car reply) 'version))
515 (unless (string-equal (cadr reply) "0.23")
516 (pymacs-report-error
517 "Pymacs Lisp version is 0.23, Python is %s"
518 (cadr reply)))
519 (pymacs-report-error "Pymacs got an invalid initial reply")))))
520 (when pymacs-use-hash-tables
521 (if pymacs-weak-hash
522 ;; A previous Pymacs session occurred in *this* Emacs session. Some
523 ;; IDs may hang around, which do not correspond to anything on the
524 ;; Python side. Python should not recycle such IDs for new objects.
525 (when pymacs-used-ids
526 (let ((pymacs-transit-buffer buffer)
527 (pymacs-forget-mutability t))
528 (pymacs-apply "zombie_python" pymacs-used-ids)))
529 (setq pymacs-weak-hash (make-hash-table :weakness 'value)))
530 (if (boundp 'post-gc-hook)
531 (add-hook 'post-gc-hook 'pymacs-schedule-gc)
532 (setq pymacs-gc-timer (run-at-time 20 20 'pymacs-schedule-gc))))
533 ;; If nothing failed, only then declare that Pymacs has started!
534 (setq pymacs-transit-buffer buffer)))
535
536 (defun pymacs-terminate-services ()
537 ;; This function is mainly provided for documentation purposes.
538 (interactive)
539 (garbage-collect)
540 (pymacs-garbage-collect)
541 (when (or (not pymacs-used-ids)
542 (yes-or-no-p "\
543 Killing the Pymacs helper might create zombie objects. Kill? "))
544 (cond ((boundp 'post-gc-hook)
545 (remove-hook 'post-gc-hook 'pymacs-schedule-gc))
546 ((timerp pymacs-gc-timer)
547 (cancel-timer pymacs-gc-timer)))
548 (when pymacs-transit-buffer
549 (kill-buffer pymacs-transit-buffer))
550 (setq pymacs-gc-running nil
551 pymacs-gc-timer nil
552 pymacs-transit-buffer nil
553 pymacs-lisp nil
554 pymacs-freed-list nil)))
555
556 (defun pymacs-serve-until-reply (action inserter)
557 ;; This function builds a Python request by printing ACTION and
558 ;; evaluating INSERTER, which itself prints an argument. It then
559 ;; sends the request to the Pymacs helper, and serves all
560 ;; sub-requests coming from the Python side, until either a reply or
561 ;; an error is finally received.
562 (unless (and pymacs-transit-buffer
563 (buffer-name pymacs-transit-buffer)
564 (get-buffer-process pymacs-transit-buffer))
565 (pymacs-start-services))
566 (when pymacs-gc-wanted
567 (pymacs-garbage-collect))
568 (let ((inhibit-quit t)
569 done value)
570 (while (not done)
571 (let ((form (pymacs-round-trip action inserter)))
572 (setq action (car form))
573 (when (eq action 'free)
574 (pymacs-free-lisp (cadr form))
575 (setq form (cddr form)
576 action (car form)))
577 (let* ((pair (pymacs-interruptible-eval (cadr form)))
578 (success (cdr pair)))
579 (setq value (car pair))
580 (cond ((eq action 'eval)
581 (if success
582 (setq action "return"
583 inserter `(pymacs-print-for-eval ',value))
584 (setq action "raise"
585 inserter `(let ((pymacs-forget-mutability t))
586 (pymacs-print-for-eval ,value)))))
587 ((eq action 'expand)
588 (if success
589 (setq action "return"
590 inserter `(let ((pymacs-forget-mutability t))
591 (pymacs-print-for-eval ,value)))
592 (setq action "raise"
593 inserter `(let ((pymacs-forget-mutability t))
594 (pymacs-print-for-eval ,value)))))
595 ((eq action 'return)
596 (if success
597 (setq done t)
598 (pymacs-report-error "%s" value)))
599 ((eq action 'raise)
600 (if success
601 (pymacs-report-error "Python: %s" value)
602 (pymacs-report-error "%s" value)))
603 (t (pymacs-report-error "Protocol error: %s" form))))))
604 value))
605
606 (defun pymacs-round-trip (action inserter)
607 ;; This function produces a Python request by printing and
608 ;; evaluating INSERTER, which itself prints an argument. It sends
609 ;; the request to the Pymacs helper, awaits for any kind of reply,
610 ;; and returns it.
611 (with-current-buffer pymacs-transit-buffer
612 ;; Possibly trim the beginning of the transit buffer.
613 (cond ((not pymacs-trace-transit)
614 (erase-buffer))
615 ((consp pymacs-trace-transit)
616 (when (> (buffer-size) (cdr pymacs-trace-transit))
617 (let ((cut (- (buffer-size) (car pymacs-trace-transit))))
618 (when (> cut 0)
619 (save-excursion
620 (goto-char cut)
621 (unless (memq (preceding-char) '(0 ?\n))
622 (forward-line 1))
623 (delete-region (point-min) (point))))))))
624 ;; Send the request, wait for a reply, and process it.
625 (let* ((process (get-buffer-process pymacs-transit-buffer))
626 (status (process-status process))
627 (marker (process-mark process))
628 (moving (= (point) marker))
629 send-position reply-position reply)
630 (save-excursion
631 (save-match-data
632 ;; Encode request.
633 (setq send-position (marker-position marker))
634 (let ((standard-output marker))
635 (princ action)
636 (princ " ")
637 (eval inserter))
638 (goto-char marker)
639 (unless (= (preceding-char) ?\n)
640 (princ "\n" marker))
641 ;; Send request text.
642 (goto-char send-position)
643 (insert (format ">%d\t" (- marker send-position)))
644 (setq reply-position (marker-position marker))
645 (process-send-region process send-position marker)
646 ;; Receive reply text.
647 (while (and (eq status 'run)
648 (progn
649 (goto-char reply-position)
650 (not (re-search-forward "<\\([0-9]+\\)\t" nil t))))
651 (unless (accept-process-output process pymacs-timeout-at-reply)
652 (setq status (process-status process))))
653 (when (eq status 'run)
654 (let ((limit-position (+ (match-end 0)
655 (string-to-number (match-string 1)))))
656 (while (and (eq status 'run)
657 (< (marker-position marker) limit-position))
658 (unless (accept-process-output process pymacs-timeout-at-line)
659 (setq status (process-status process))))))
660 ;; Decode reply.
661 (if (not (eq status 'run))
662 (pymacs-report-error "Pymacs helper status is `%S'" status)
663 (goto-char (match-end 0))
664 (setq reply (read (current-buffer))))))
665 (when (and moving (not pymacs-trace-transit))
666 (goto-char marker))
667 reply)))
668
669 (defun pymacs-interruptible-eval (expression)
670 ;; This function produces a pair (VALUE . SUCCESS) for EXPRESSION.
671 ;; A cautious evaluation of EXPRESSION is attempted, and any
672 ;; error while evaluating is caught, including Emacs quit (C-g).
673 ;; Any Emacs quit also gets forward as a SIGINT to the Pymacs handler.
674 ;; With SUCCESS being true, VALUE is the expression value.
675 ;; With SUCCESS being false, VALUE is an interruption diagnostic.
676 (condition-case info
677 (cons (let ((inhibit-quit nil)) (eval expression)) t)
678 (quit (setq quit-flag t)
679 (interrupt-process pymacs-transit-buffer)
680 (cons "*Interrupted!*" nil))
681 (error (cons (prin1-to-string info) nil))))
682
683 (defun pymacs-proper-list-p (expression)
684 ;; Tell if a list is proper, id est, that it is `nil' or ends with `nil'.
685 (cond ((not expression))
686 ((consp expression) (not (cdr (last expression))))))
687
688 (provide 'pymacs)