Org Babel Scheme
Copyright Statement (expand to view)
This is an example of both using org-babel to tangle output, and how to make a new org-babel mode for a new language. The target language we are going to work with is Gambit Scheme. Theoretically, it should be relatively trivial to make it work with most other scheme implementations, and with a little more work, any other language that provides a REPL.
Any Scheme code in here should be strictly R5RS, so porting it to other implementations and testing can be as painless as possible.
Overall Structure of an org-babel-langauge.el file:
;;; org-babel-scheme.el --- org babel functions for scheme evaluation. ;;<<header-info>> ;;<<license>> ;;<<commentary>> ;;; Code: (require 'org-babel) ;;<<tell-org-babel-about-scheme>> ;;<<allow-scheme-execution>> (provide 'org-babel-scheme)
Telling Org-babel about scheme
We need to tell org babel that we have a language called scheme that we want to allow tangling.
We do this by using the org-babel-add-interpreter function which updates the internal regexp engine responsible for handling the tangling of code (org-babel-src-block-regexp).
Next we add to the org-babel-tangle-langs list, which is an associative list laid out like this:
- A string indicating the source block language
- the extension of the file name
- the shebang line of the file (if applicable); can be null.
- a flag to indicate that the language is not commentable; default null. (i.e. the default is that the langauge is commentable)
(org-babel-add-interpreter "scheme") (add-to-list 'org-babel-tangle-langs '("scheme" "scm"))
Executing Scheme Code
org-babel executes sourcecode through the function org-babel-execute-src-block, which in turn calls org-babel-execute:<lang-name>. This function takes 2 arguments, the source-code body, and any incoming parameters.
Additionally, the following variables are defined with multiple-value-bind:
- the currently active session, if any.
- Any incoming variables
- A symbol, either ‘output or ‘value.
- The entire standard out of the code is the result.
- only the final return value of the code is the result.
Setting Variables Properly
Babel has the ability to call source-code blocks with variables, so when we execute our source code, we will need to make sure those variables are properly defined before executing the code block. This is fairly easily done with a let expression:
(let ((var value) (var2 value2) ...) ...code...)
If our builder function takes in 2 variables, called body and form, then building an s-expression like that is fairly painless:
`(let ,(mapcar (lambda (v) (list (car v) (cadr v))) vars) ,@body)
Now we just wrap that in a call to prin1-to-string and a function. Note that we have to re-define print-length and print-level, so that we do not get an elipsis (…) in our output code.
(defun org-babel-scheme-build-full-form (body vars) (let ((print-level nil) (print-length nil)) (prin1-to-string ;;<<build-let-expression>> t)))
Getting the active scheme session
This is probably going to be the most difficult and implementation specific part of the whole thing. We can probably assume that our buffer is going to be the scheme buffer. Also we will need to install some kind of “listener” to capture output from that buffer, so it can be sent to org-babel. IN the process of this capturing we will need to strip out any non-value related output from the interpreter (such as a prompt, for instance).
(defconst org-babel-scheme-buffer-name "*scheme* "Name of the scheme interaction buffer")
Capturing output from the scheme buffer
Hooking our capture function up properly
Dealing with already existing buffers
Putting it all together
(deffun 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'. For more information, see org-babel-scheme.el." (let* ((body (org-babel-scheme-build-full-form body vars)) (session (org-babel-scheme-get-session session))) (org-babel-scheme-eval session body result-type))) ;;<<org-babel-scheme-build-full-form>> ;;<<org-babel-scheme-get-session>> ;;<<org-babel-scheme-eval-results>>
Setting Variables Properly
Wrap the form inside of a let and exception handler
Handle different result types: ‘output and ‘value
Figure out the return code jazz.
In Org Babel. Tangle to output.
Look at the elisp, ruby and python modes too
Testing with org-babel
Evaluate all the cells in this table for a comprehensive test of the org-babel functionality.
Note: if you have customized
org-babel-default-header-args then some
of these tests may fail.
|clojure||hello-scheme||hello scheme||hello clojure||pass|
|source block references||pass|
|all languages||scheme-chained-ref-last||class clojure.lang.PersistentList||class clojure.lang.PersistentList||pass|
|source block functions||pass|
|set ruby session||set-python-session-var||set||set||pass|
|get from ruby session||get-python-session-var||4||4||pass|
|set clojure session||set-clojure-session-var||:set||:set||pass|
|get from clojure session||get-clojure-session-var||3||3||pass|
(+ 1 4)
'(1 2 3 4 5)
(display "hello output-no-session scheme")
hello output-no-session clojure
(pp '(6 7 8 9 ))
(6 7 8 9)
(length (first table))
(apply string-append (map number->string (car table)))
(list '(A B C) (reverse (car table)))
Lets pass a references through all of our languages…
Lets start by reversing the table from the previous examples
Take the first part of the list
Turn the numbers into string
(map #(format "%S" %) table)
and Check that it is still a list
source blocks as functions
(define (fib n) (if (<= n 1) n (+ (fib (- n 1)) (fib (- n 2)))))
(string-append "I'm going to format the number:" (number->string n))
I'm going to format the number: 7
(define *var* '(1 2 3)) :set
(display "hello clojure") (newline)
Template for a new Langauge:
;;; org-babel-template.el --- org-babel functions for template evaluation ;; Copyright (C) your name here ;; Author: your name here ;; Keywords: literate programming, reproducible research ;; Homepage: http://orgmode.org ;; Version: 0.01 ;;; License: ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; ;; This program 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 General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; This file is not intended to ever be loaded by org-babel, rather it ;; is a template for use in adding new language support to Org-babel. ;; Good first steps are to copy this file to a file named by the ;; language you are adding, and then use `query-replace' to replace ;; all strings of "template" in this file with the name of your new ;; language. ;; ;; If you have questions as to any of the portions of the file defined ;; below please look to existing language support for guidance. ;; ;; If you are planning on adding a language to org-babel we would ask ;; that if possible you fill out the FSF copyright assignment form ;; available at http://orgmode.org/request-assign-future.txt as this ;; will simplify the eventual inclusion of your addition into ;; org-babel and possibly at some point into org-mode and Emacs ;; proper. ;;; Requirements: ;; Use this section to list the requirements of this language. Most ;; languages will require that at least the language be installed on ;; the user's system, and the Emacs major mode relevant to the ;; language be installed as well. ;;; Code: (require 'org-babel) ;; possibly require modes required for your language ;; Add this language to the list of supported languages. Org-babel ;; will match the string below against the declared language of the ;; source-code block. (org-babel-add-interpreter "template") ;; specify the name, file extension, and shebang line for this language (add-to-list 'org-babel-tangle-langs '("template" "template-extension" "#!/usr/bin/env template")) ;; This is the main function which is called to evaluate a code ;; block. It should setup the source code block according to all of ;; the header arguments packaged into params, including... ;; - defining variables ;; - optionally starting up a session (depending on the value of the ;; :session) header argument ;; ;; This function will then evaluate the body of the source code and ;; return the results as emacs-lisp depending on the value of the ;; :results header argument ;; - output means that the output to STDOUT will be captured and ;; returned ;; - value means that the value of the last statement in the ;; source code block will be returned ;; ;; The most common first step in this function is the expansion of the ;; PARAMS argument using `org-babel-process-params'. ;; ;; Please feel free to not implement options which aren't appropriate ;; for your language (e.g. not all languages support interactive ;; "session" evaluation). Also you are free to define any new header ;; arguments which you feel may be useful -- all header arguments ;; specified by the user will be available in the PARAMS variable. (defun org-babel-execute:template (body params) "Execute a block of Template code with org-babel. This function is called by `org-babel-execute-src-block' via multiple-value-bind." (message "executing Template source code block") (let* ((processed-params (org-babel-process-params params)) ;; set the session if the session variable is non-nil (session (org-babel-template-initiate-session (first processed-params))) ;; variables assigned for use in the block (vars (second processed-params)) (result-params (third processed-params)) ;; either OUTPUT or VALUE which should behave as described above (result-type (fourth processed-params)) (full-body (concat ;; prepend code to define all arguments passed to the code block ;; (may not be appropriate for all languages) (mapconcat (lambda (pair) (format "%s=%s" (car pair) (org-babel-template-var-to-template (cdr pair)))) vars "\n") "\n" body "\n"))) ;; actually execute the source-code block either in a session or ;; possibly by dropping it to a temporary file and evaluating the ;; file. ;; ;; for session based evaluation the helpers defined in ;; `org-babel-comint' will probably be helpful. )) ;; This function should be used to assign any variables in params in ;; the context of the session environment. (defun org-babel-prep-session:template (session params) "Prepare SESSION according to the header arguments specified in PARAMS." ) (defun org-babel-template-var-to-template (var) "Convert an elisp var into a string of template source code specifying a var of the same value." ) (defun org-babel-template-table-or-string (results) "If the results look like a table, then convert them into an Emacs-lisp table, otherwise return the results as a string." ) (defun org-babel-template-initiate-session (&optional session) "If there is not a current inferior-process-buffer in SESSION then create. Return the initialized session." (unless (string= session "none") )) (provide 'org-babel-template) ;;; org-babel-template.el ends here