Some years ago, I was working on a personal project that seemed to work best with Emacs as the user interface, and Common Lisp (SBCL) as the back-end. The accepted answer on How to use SLIME as a proxy between Common Lisp program and Emacs Lisp? talks about how to move code from Common Lisp to Emacs, but I needed to work in the other direction, which the macro below accomplishes. At the time when I wrote this, clsql seemed more mature and usable than any existing Emacs database system. I was also interested in experimenting with elephant. If I was to rewrite the code these days I might use EmacSQL directly within Emacs to connect to the database. Or if I felt like being fancy I might try to bridge to Clojure rather than Common Lisp, and make use of the interesting ideas of software transactional memory, and then try to persist that.
These comments serve to underscore the point that it is potentially useful to bridge to different programming languages for different purposes.
The question I'd like to ask is: Are other people currently doing this sort of bridging, getting Emacs to interoperate programmatically at run time with other platforms (not just over a web API, comint buffer, or shell-command
) and if so, how are you going about it? What kind of Emacs-centric applications are you building?
I include the outline of my CL bridge for context.
(defmacro Defun (name arglist &rest body)
(declare (indent defun))
`(defun ,name ,arglist
(let* ((outbound-string
(translate-emacs-syntax-to-common-syntax
(format "%S"
(append
(list
(append (list 'lambda ',arglist)
',body))
(mapcar
(lambda (arg) `',arg)
(list
,@(remove-if
(lambda (testelt)
(eq testelt
'&optional))
arglist)))))))
(returned-string
(second
;; we now specify the right package!
(slime-eval
(list 'swank:eval-and-grab-output
outbound-string)
:arxana))))
(process-slime-output returned-string))))
With these helper functions:
(defun translate-emacs-syntax-to-common-syntax (str)
(with-temp-buffer
(insert str)
(dolist (swap '(("(\\` " "`")
("(\\\, " ",")))
(goto-char (point-min))
(while (search-forward (first swap) nil t)
(goto-char (match-beginning 0))
(forward-sexp)
(delete-char -1)
(goto-char (match-beginning 0))
(delete-region (match-beginning 0)
(match-end 0))
(insert (second swap))))
(buffer-substring-no-properties (point-min)
(point-max))))
(defun process-slime-output (str)
(condition-case nil
(let ((read-value (read str)))
(if (symbolp read-value)
(read (downcase str)))
(nsubst nil 'NIL read-value))
(error str)))
In the end, what is achieved is that I can write functions like this, defined in Emacs, but whose bodies are evaluated on Common Lisp:
(Defun article (name contents &optional heading)
(let ((coordinates (add-triple name
"has content"
contents)))
(when theory (add-triple coordinates "in" heading))
(when place (if (numberp place)
(put-in-place coordinates place)
(put-in-place coordinates)))
coordinates))