2001-04-25 12:15:24 +00:00
|
|
|
|
;;; guile.el --- Emacs Guile interface
|
|
|
|
|
|
|
|
|
|
|
|
;; Copyright (C) 2001 Keisuke Nishida <kxn30@po.cwru.edu>
|
|
|
|
|
|
|
2009-06-17 00:22:09 +01:00
|
|
|
|
;;;; This library is free software; you can redistribute it and/or
|
|
|
|
|
|
;;;; modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
|
;;;; License as published by the Free Software Foundation; either
|
|
|
|
|
|
;;;; version 3 of the License, or (at your option) any later version.
|
|
|
|
|
|
;;;;
|
|
|
|
|
|
;;;; This library is distributed in the hope that it will be useful,
|
|
|
|
|
|
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
|
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
|
;;;; Lesser General Public License for more details.
|
|
|
|
|
|
;;;;
|
|
|
|
|
|
;;;; You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
|
;;;; License along with this library; if not, write to the Free
|
|
|
|
|
|
;;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
|
|
|
|
;;;; 02111-1307 USA
|
2001-04-25 12:15:24 +00:00
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
2001-05-04 20:59:16 +00:00
|
|
|
|
(require 'cl)
|
|
|
|
|
|
|
2001-04-25 12:15:24 +00:00
|
|
|
|
;;;
|
|
|
|
|
|
;;; Low level interface
|
|
|
|
|
|
;;;
|
|
|
|
|
|
|
2001-06-21 19:39:03 +00:00
|
|
|
|
(defvar guile-emacs-file
|
2001-04-25 12:15:24 +00:00
|
|
|
|
(catch 'return
|
|
|
|
|
|
(mapc (lambda (dir)
|
|
|
|
|
|
(let ((file (expand-file-name "guile-emacs.scm" dir)))
|
|
|
|
|
|
(if (file-exists-p file) (throw 'return file))))
|
|
|
|
|
|
load-path)
|
|
|
|
|
|
(error "Cannot find guile-emacs.scm")))
|
|
|
|
|
|
|
2001-06-21 19:39:03 +00:00
|
|
|
|
(defvar guile-channel-file
|
2001-04-26 04:40:02 +00:00
|
|
|
|
(catch 'return
|
|
|
|
|
|
(mapc (lambda (dir)
|
|
|
|
|
|
(let ((file (expand-file-name "channel.scm" dir)))
|
|
|
|
|
|
(if (file-exists-p file) (throw 'return file))))
|
2001-06-21 19:39:03 +00:00
|
|
|
|
load-path)
|
|
|
|
|
|
(error "Cannot find channel.scm")))
|
2001-04-26 04:40:02 +00:00
|
|
|
|
|
|
|
|
|
|
(defvar guile-libs
|
2001-06-21 19:39:03 +00:00
|
|
|
|
(nconc (if guile-channel-file (list "-l" guile-channel-file) '())
|
|
|
|
|
|
(list "-l" guile-emacs-file)))
|
2001-04-26 04:40:02 +00:00
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
2001-04-25 12:15:24 +00:00
|
|
|
|
(defun guile:make-adapter (command channel)
|
|
|
|
|
|
(let* ((buff (generate-new-buffer " *guile object channel*"))
|
2001-06-21 19:39:03 +00:00
|
|
|
|
(libs (if guile-channel-file (list "-l" guile-channel-file) nil))
|
2001-04-26 04:40:02 +00:00
|
|
|
|
(proc (apply 'start-process "guile-oa" buff command "-q" guile-libs)))
|
2001-04-25 12:15:24 +00:00
|
|
|
|
(process-kill-without-query proc)
|
|
|
|
|
|
(accept-process-output proc)
|
|
|
|
|
|
(guile-process-require proc (format "(%s)\n" channel) "channel> ")
|
|
|
|
|
|
proc))
|
|
|
|
|
|
|
|
|
|
|
|
(put 'guile-error 'error-conditions '(guile-error error))
|
|
|
|
|
|
(put 'guile-error 'error-message "Guile error")
|
|
|
|
|
|
|
2001-04-25 13:24:45 +00:00
|
|
|
|
(defvar guile-token-tag "<guile>")
|
|
|
|
|
|
|
|
|
|
|
|
(defun guile-tokenp (x) (and (consp x) (eq (car x) guile-token-tag)))
|
|
|
|
|
|
|
2001-04-26 04:40:02 +00:00
|
|
|
|
;;;###autoload
|
2001-04-25 12:15:24 +00:00
|
|
|
|
(defun guile:eval (string adapter)
|
2001-05-06 21:35:14 +00:00
|
|
|
|
(condition-case error
|
|
|
|
|
|
(let ((output (guile-process-require adapter (concat "eval " string "\n")
|
|
|
|
|
|
"channel> ")))
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((string= output "") nil)
|
|
|
|
|
|
((string-match "^\\(\\(value\\)\\|\\(token\\)\\|\\(exception\\)\\) = "
|
|
|
|
|
|
output)
|
|
|
|
|
|
(cond
|
|
|
|
|
|
;; value
|
|
|
|
|
|
((match-beginning 2)
|
|
|
|
|
|
(car (read-from-string (substring output (match-end 0)))))
|
|
|
|
|
|
;; token
|
|
|
|
|
|
((match-beginning 3)
|
|
|
|
|
|
(cons guile-token-tag
|
|
|
|
|
|
(car (read-from-string (substring output (match-end 0))))))
|
|
|
|
|
|
;; exception
|
|
|
|
|
|
((match-beginning 4)
|
|
|
|
|
|
(signal 'guile-error
|
|
|
|
|
|
(car (read-from-string (substring output (match-end 0))))))))
|
|
|
|
|
|
(t
|
|
|
|
|
|
(error "Unsupported result" output))))
|
|
|
|
|
|
(quit
|
|
|
|
|
|
(signal-process (process-id adapter) 'SIGINT)
|
|
|
|
|
|
(signal 'quit nil))))
|
2001-04-25 12:15:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; Guile Lisp adapter
|
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
|
|
(defvar guile-lisp-command "guile")
|
|
|
|
|
|
(defvar guile-lisp-adapter nil)
|
|
|
|
|
|
|
|
|
|
|
|
(defvar true "#t")
|
|
|
|
|
|
(defvar false "#f")
|
|
|
|
|
|
|
2001-05-06 21:35:14 +00:00
|
|
|
|
(unless (boundp 'keywordp)
|
|
|
|
|
|
(defun keywordp (x) (and (symbolp x) (eq (aref (symbol-name x) 0) ?:))))
|
|
|
|
|
|
|
2001-04-25 12:15:24 +00:00
|
|
|
|
(defun guile-lisp-adapter ()
|
|
|
|
|
|
(if (and (processp guile-lisp-adapter)
|
|
|
|
|
|
(eq (process-status guile-lisp-adapter) 'run))
|
|
|
|
|
|
guile-lisp-adapter
|
|
|
|
|
|
(setq guile-lisp-adapter
|
|
|
|
|
|
(guile:make-adapter guile-lisp-command 'emacs-lisp-channel))))
|
|
|
|
|
|
|
|
|
|
|
|
(defun guile-lisp-convert (x)
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((or (eq x true) (eq x false)) x)
|
2001-04-25 13:24:45 +00:00
|
|
|
|
((null x) "'()")
|
2001-04-26 04:40:02 +00:00
|
|
|
|
((keywordp x) (concat "#" (prin1-to-string x)))
|
2001-04-25 12:15:24 +00:00
|
|
|
|
((stringp x) (prin1-to-string x))
|
2001-04-25 13:24:45 +00:00
|
|
|
|
((guile-tokenp x) (cadr x))
|
2001-04-25 12:15:24 +00:00
|
|
|
|
((consp x)
|
2001-04-25 13:24:45 +00:00
|
|
|
|
(if (null (cdr x))
|
|
|
|
|
|
(list (guile-lisp-convert (car x)))
|
2001-04-25 12:15:24 +00:00
|
|
|
|
(cons (guile-lisp-convert (car x)) (guile-lisp-convert (cdr x)))))
|
|
|
|
|
|
(t x)))
|
|
|
|
|
|
|
2001-04-26 04:40:02 +00:00
|
|
|
|
;;;###autoload
|
|
|
|
|
|
(defun guile-lisp-eval (form)
|
|
|
|
|
|
(guile:eval (format "%s" (guile-lisp-convert form)) (guile-lisp-adapter)))
|
|
|
|
|
|
|
|
|
|
|
|
(defun guile-lisp-flat-eval (&rest form)
|
|
|
|
|
|
(let ((args (mapcar (lambda (x)
|
|
|
|
|
|
(if (guile-tokenp x) (cadr x) (list 'quote x)))
|
|
|
|
|
|
(cdr form))))
|
|
|
|
|
|
(guile-lisp-eval (cons (car form) args))))
|
2001-04-25 12:15:24 +00:00
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
2001-04-26 04:40:02 +00:00
|
|
|
|
(defmacro guile-import (name &optional new-name &rest opts)
|
|
|
|
|
|
`(guile-process-import ',name ',new-name ',opts))
|
2001-04-25 12:15:24 +00:00
|
|
|
|
|
2001-04-26 04:40:02 +00:00
|
|
|
|
(defun guile-process-import (name new-name opts)
|
|
|
|
|
|
(let ((real (or new-name name))
|
|
|
|
|
|
(docs (if (memq :with-docs opts) true false)))
|
|
|
|
|
|
(eval (guile-lisp-eval `(guile-emacs-export ',name ',real ,docs)))))
|
2001-04-25 12:15:24 +00:00
|
|
|
|
|
2001-05-06 21:35:14 +00:00
|
|
|
|
;;;###autoload
|
|
|
|
|
|
(defmacro guile-use-module (name)
|
|
|
|
|
|
`(guile-lisp-eval '(use-modules ,name)))
|
|
|
|
|
|
|
2001-04-25 12:15:24 +00:00
|
|
|
|
;;;###autoload
|
2001-04-26 04:40:02 +00:00
|
|
|
|
(defmacro guile-import-module (name &rest opts)
|
2001-05-06 21:35:14 +00:00
|
|
|
|
`(guile-process-import-module ',name ',opts))
|
2001-04-25 12:15:24 +00:00
|
|
|
|
|
2001-05-06 21:35:14 +00:00
|
|
|
|
(defun guile-process-import-module (name opts)
|
2001-04-25 12:15:24 +00:00
|
|
|
|
(unless (boundp 'guile-emacs-export-procedures)
|
|
|
|
|
|
(guile-import guile-emacs-export-procedures))
|
2001-04-26 04:40:02 +00:00
|
|
|
|
(let ((docs (if (memq :with-docs opts) true false)))
|
|
|
|
|
|
(guile-lisp-eval `(use-modules ,name))
|
|
|
|
|
|
(eval (guile-emacs-export-procedures name docs))
|
|
|
|
|
|
name))
|
2001-04-25 12:15:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; Process handling
|
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
|
|
(defvar guile-process-output-start nil)
|
|
|
|
|
|
(defvar guile-process-output-value nil)
|
|
|
|
|
|
(defvar guile-process-output-finished nil)
|
|
|
|
|
|
(defvar guile-process-output-separator nil)
|
|
|
|
|
|
|
|
|
|
|
|
(defun guile-process-require (process string separator)
|
|
|
|
|
|
(setq guile-process-output-value nil)
|
|
|
|
|
|
(setq guile-process-output-finished nil)
|
|
|
|
|
|
(setq guile-process-output-separator separator)
|
|
|
|
|
|
(let (temp-buffer)
|
|
|
|
|
|
(unless (process-buffer process)
|
|
|
|
|
|
(setq temp-buffer (guile-temp-buffer))
|
|
|
|
|
|
(set-process-buffer process temp-buffer))
|
|
|
|
|
|
(with-current-buffer (process-buffer process)
|
|
|
|
|
|
(goto-char (point-max))
|
|
|
|
|
|
(insert string)
|
|
|
|
|
|
(setq guile-process-output-start (point))
|
|
|
|
|
|
(set-process-filter process 'guile-process-filter)
|
|
|
|
|
|
(process-send-string process string)
|
|
|
|
|
|
(while (not guile-process-output-finished)
|
|
|
|
|
|
(unless (accept-process-output process 3)
|
|
|
|
|
|
(when (> (point) guile-process-output-start)
|
|
|
|
|
|
(display-buffer (current-buffer))
|
|
|
|
|
|
(error "BUG in Guile object channel!!")))))
|
|
|
|
|
|
(when temp-buffer
|
|
|
|
|
|
(set-process-buffer process nil)
|
|
|
|
|
|
(kill-buffer temp-buffer)))
|
|
|
|
|
|
guile-process-output-value)
|
|
|
|
|
|
|
|
|
|
|
|
(defun guile-process-filter (process string)
|
|
|
|
|
|
(with-current-buffer (process-buffer process)
|
|
|
|
|
|
(insert string)
|
|
|
|
|
|
(forward-line -1)
|
|
|
|
|
|
(if (< (point) guile-process-output-start)
|
|
|
|
|
|
(goto-char guile-process-output-start))
|
|
|
|
|
|
(when (re-search-forward guile-process-output-separator nil 0)
|
|
|
|
|
|
(goto-char (match-beginning 0))
|
|
|
|
|
|
(setq guile-process-output-value
|
|
|
|
|
|
(buffer-substring guile-process-output-start (point)))
|
|
|
|
|
|
(setq guile-process-output-finished t))))
|
|
|
|
|
|
|
|
|
|
|
|
(defun guile-process-kill (process)
|
|
|
|
|
|
(set-process-filter process nil)
|
|
|
|
|
|
(delete-process process)
|
|
|
|
|
|
(if (process-buffer process)
|
|
|
|
|
|
(kill-buffer (process-buffer process))))
|
|
|
|
|
|
|
|
|
|
|
|
(provide 'guile)
|
|
|
|
|
|
|
|
|
|
|
|
;;; guile.el ends here
|