Use geiser for babel scheme evaluation.

This commit is contained in:
Michael Gauland 2013-01-09 12:41:13 +13:00 committed by Eric Schulte
parent 5c7e28a1bc
commit 4c13e8a827
1 changed files with 117 additions and 75 deletions

View File

@ -2,7 +2,7 @@
;; Copyright (C) 2010-2013 Free Software Foundation, Inc.
;; Author: Eric Schulte
;; Authors: Eric Schulte, Michael Gauland
;; Keywords: literate programming, reproducible research, scheme
;; Homepage: http://orgmode.org
@ -33,27 +33,16 @@
;; - a working scheme implementation
;; (e.g. guile http://www.gnu.org/software/guile/guile.html)
;;
;; - for session based evaluation cmuscheme.el is required which is
;; included in Emacs
;; - for session based evaluation geiser is required, which is available from
;; ELPA.
;;; Code:
(require 'ob)
(eval-when-compile (require 'cl))
(declare-function run-scheme "ext:cmuscheme" (cmd))
(load-library "geiser-impl")
(defvar org-babel-default-header-args:scheme '()
"Default header arguments for scheme code blocks.")
(defvar org-babel-scheme-eoe "org-babel-scheme-eoe"
"String to indicate that evaluation has completed.")
(defcustom org-babel-scheme-cmd "guile"
"Name of command used to evaluate scheme blocks."
:group 'org-babel
:version "24.1"
:type 'string)
(defun org-babel-expand-body:scheme (body params)
"Expand BODY according to PARAMS, return the expanded body."
(let ((vars (mapcar #'cdr (org-babel-get-header params :var))))
@ -65,70 +54,123 @@
")\n" body ")")
body)))
(defvar scheme-program-name)
(defvar org-babel-scheme-repl-map (make-hash-table :test 'equal)
"Map of scheme sessions to session names.")
(defun org-babel-scheme-cleanse-repl-map ()
"Remove dead buffers from the REPL map."
(maphash
(lambda (x y)
(when (not (buffer-name y))
(remhash x org-babel-scheme-repl-map)))
org-babel-scheme-repl-map))
(defun org-babel-scheme-get-session-buffer (session-name)
"Look up the scheme buffer for a session; return nil if it doesn't exist."
(org-babel-scheme-cleanse-repl-map) ; Prune dead sessions
(gethash session-name org-babel-scheme-repl-map))
(defun org-babel-scheme-set-session-buffer (session-name buffer)
"Record the scheme buffer used for a given session."
(puthash session-name buffer org-babel-scheme-repl-map))
(defun org-babel-scheme-get-buffer-impl (buffer)
"Returns the scheme implementation geiser associates with the buffer."
(with-current-buffer (set-buffer buffer)
geiser-impl--implementation))
(defun org-babel-scheme-get-repl (impl name)
"Switch to a scheme REPL, creating it if it doesn't exist:"
(let ((buffer (org-babel-scheme-get-session-buffer name)))
(or buffer
(progn
(run-geiser impl)
(if name
(progn
(rename-buffer name t)
(org-babel-scheme-set-session-buffer name (current-buffer))))
(current-buffer)))))
(defun org-babel-scheme-make-session-name (buffer name impl)
"Generate a name for the session buffer.
For a named session, the buffer name will be the session name.
If the session is unnamed (nil), generate a name.
If the session is 'none', use nil for the session name, and
org-babel-scheme-execute-with-geiser will use a temporary session."
(let ((result
(cond ((not name)
(concat buffer " " (symbol-name impl) " REPL"))
((string= name "none") nil)
(name))))
result))
(defun org-babel-scheme-execute-with-geiser (code output impl repl)
"Execute code in specified REPL. If the REPL doesn't exist, create it
using the given scheme implementation.
Returns the output of executing the code if the output parameter
is true; otherwise returns the last value."
(let ((result nil))
(with-temp-buffer
(insert (format ";; -*- geiser-scheme-implementation: %s -*-" impl))
(newline)
(insert (if output
(format "(with-output-to-string (lambda () %s))" code)
code))
(geiser-mode)
(let ((repl-buffer (save-current-buffer (org-babel-scheme-get-repl impl repl))))
(when (not (eq impl (org-babel-scheme-get-buffer-impl (current-buffer))))
(message "Implementation mismatch: %s (%s) %s (s)" impl (symbolp impl)
(org-babel-scheme-get-buffer-impl (current-buffer))
(symbolp (org-babel-scheme-get-buffer-impl (current-buffer)))))
(setq geiser-repl--repl repl-buffer)
(setq geiser-impl--implementation nil)
(geiser-eval-region (point-min) (point-max))
(setq result
(if (equal (substring (current-message) 0 3) "=> ")
(replace-regexp-in-string "^=> " "" (current-message))
"\"An error occurred.\""))
(when (not repl)
(save-current-buffer (set-buffer repl-buffer)
(geiser-repl-exit))
(set-process-query-on-exit-flag (get-buffer-process repl-buffer) nil)
(kill-buffer repl-buffer))
(setq result (if (or (string= result "#<void>")
(string= result "#<unspecified>"))
nil
(read result)))))
result))
(defun org-babel-execute:scheme (body params)
"Execute a block of Scheme code with org-babel.
This function is called by `org-babel-execute-src-block'"
(let* ((result-type (cdr (assoc :result-type params)))
(org-babel-scheme-cmd (or (cdr (assoc :scheme params))
org-babel-scheme-cmd))
(full-body (org-babel-expand-body:scheme body params)))
(read
(if (not (string= (cdr (assoc :session params)) "none"))
;; session evaluation
(let ((session (org-babel-prep-session:scheme
(cdr (assoc :session params)) params)))
(org-babel-comint-with-output
(session (format "%S" org-babel-scheme-eoe) t body)
(mapc
(lambda (line)
(insert (org-babel-chomp line)) (comint-send-input nil t))
(list body (format "%S" org-babel-scheme-eoe)))))
;; external evaluation
(let ((script-file (org-babel-temp-file "scheme-script-")))
(with-temp-file script-file
(insert
;; return the value or the output
(if (string= result-type "value")
(format "(display %s)" full-body)
full-body)))
(org-babel-eval
(format "%s %s" org-babel-scheme-cmd
(org-babel-process-file-name script-file)) ""))))))
(defun org-babel-prep-session:scheme (session params)
"Prepare SESSION according to the header arguments specified in PARAMS."
(let* ((session (org-babel-scheme-initiate-session session))
(vars (mapcar #'cdr (org-babel-get-header params :var)))
(var-lines
(mapcar
(lambda (var) (format "%S" (print `(define ,(car var) ',(cdr var)))))
vars)))
(when session
(org-babel-comint-in-buffer session
(sit-for .5) (goto-char (point-max))
(mapc (lambda (var)
(insert var) (comint-send-input nil t)
(org-babel-comint-wait-for-output session)
(sit-for .1) (goto-char (point-max))) var-lines)))
session))
(defun org-babel-scheme-initiate-session (&optional session)
"If there is not a current inferior-process-buffer in SESSION
then create. Return the initialized session."
(require 'cmuscheme)
(unless (string= session "none")
(let ((session-buffer (save-window-excursion
(run-scheme org-babel-scheme-cmd)
(rename-buffer session)
(current-buffer))))
(if (org-babel-comint-buffer-livep session-buffer)
(progn (sit-for .25) session-buffer)
(sit-for .5)
(org-babel-scheme-initiate-session session)))))
(let* ((source-buffer (current-buffer))
(source-buffer-name (replace-regexp-in-string ;; zap surrounding *
"^ ?\\*\\([^*]+\\)\\*" "\\1" (buffer-name source-buffer))))
(save-excursion
(org-babel-reassemble-table
(let* ((result-type (cdr (assoc :result-type params)))
(impl (or (when (cdr (assoc :scheme params))
(intern (cdr (assoc :scheme params))))
geiser-default-implementation
(car geiser-active-implementations)))
(session (org-babel-scheme-make-session-name source-buffer-name (cdr (assoc :session params)) impl))
(full-body (org-babel-expand-body:scheme body params)))
(org-babel-scheme-execute-with-geiser full-body ; code
(string= result-type "output") ; output?
impl ; implementation
(and (not (string= session "none")) session)); session
)
(org-babel-pick-name (cdr (assoc :colname-names params))
(cdr (assoc :colnames params)))
(org-babel-pick-name (cdr (assoc :rowname-names params))
(cdr (assoc :rownames params)))))))
(provide 'ob-scheme)
;;; ob-scheme.el ends here