1 ;;; ob-oz.el --- Org-babel functions for Oz evaluation
3 ;; Copyright (C) 2009-2013 Torsten Anders and Eric Schulte
5 ;; Author: Torsten Anders and Eric Schulte
6 ;; Keywords: literate programming, reproducible research
7 ;; Homepage: http://orgmode.org
10 ;; This file is not part of GNU Emacs.
12 ;; This program is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 3, or (at your option)
17 ;; This program is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 ;; Boston, MA 02110-1301, USA.
29 ;; Org-Babel support for evaluating Oz source code.
31 ;; Oz code is always send to the Oz Programming Environment (OPI), the
32 ;; Emacs mode and compiler interface for Oz programs. Therefore, only
33 ;; session mode is supported. In practice, non-session code blocks are
34 ;; handled equally well by the session mode. However, only a single
35 ;; session is supported. Consequently, the :session header argument is
38 ;; The Org-babel header argument :results is interpreted as
39 ;; follows. :results output requires the respective code block to be
40 ;; an Oz statement and :results value requires an Oz
41 ;; expression. Currently, results are only supported for expressions
42 ;; (i.e. the result of :results output is always nil).
44 ;; Expression evaluation happens synchronously. Therefore there is an
45 ;; additional header argument :wait-time <number>, which specifies the
46 ;; maximum time to wait for the result of a given expression. nil
47 ;; means to wait as long as it takes to get a result (potentially wait
50 ;; NOTE: Currently the copyright of this file may not be in a state to
51 ;; permit inclusion as core software into Emacs or Org-mode.
55 ;; - Mozart Programming System, the implementation of the Oz
56 ;; programming language (http://www.mozart-oz.org/), which includes
57 ;; the major mode mozart for editing Oz programs.
59 ;; - StartOzServer.oz which is located in the contrib/scripts
60 ;; directory of the Org-mode repository
64 ;; - Decide: set communication to \\switch -threadedqueries?
66 ;; - Only start Oz compiler when required, e.g., load Org-babel only when needed?
68 ;; - Avoid synchronous evaluation to avoid blocking Emacs (complex
69 ;; Strasheela programs can take long to find a result..). In order
70 ;; to cleanly map code blocks to their associated results (which can
71 ;; arrive then in any order) I could use IDs
72 ;; (e.g. integers). However, how do I do concurrency in Emacs Lisp,
73 ;; and how can I define org-babel-execute:oz concurrently.
75 ;; - Expressions are rarely used in Oz at the top-level, and using
76 ;; them in documentation and Literate Programs will cause
77 ;; confusion. Idea: hide expression from reader and instead show
78 ;; them statement (e.g., MIDI output statement) and then include
79 ;; result in Org file. Implementation: for expressions (:results
80 ;; value) support an additional header argument that takes arbitrary
81 ;; Oz code. This code is not seen by the reader, but will be used
82 ;; for the actual expression at the end. Alternative: feed all
83 ;; relevant code as statement (:results output), then add expression
84 ;; as extra code block which outputs, e.g., file name (so the file
85 ;; name must be accessible by global var), but the code of this
86 ;; extra codeblock is not seen. Hm, in that case it might be even
87 ;; more easy to manually add this link to the Org file.
92 ;;; major mode for editing Oz programs
96 ;; Interface to communicate with Oz.
97 ;; (1) For statements without any results: oz-send-string
98 ;; (2) For expressions with a single result: oz-send-string-expression
99 ;; (defined in org-babel-oz-ResultsValue.el)
102 ;; oz-send-string-expression implements an additional very direct
103 ;; communication between Org-babel and the Oz compiler. Communication
104 ;; with the Oz server works already without this code via the function
105 ;; oz-send-string from mozart.el.in, but this function does not get
106 ;; back any results from Oz to Emacs. The following code creates a
107 ;; socket for sending code to the OPI compiler and results are
108 ;; returned by the same socket. On the Oz side, a socket is opened and
109 ;; conected to the compiler of the OPI (via oz-send-string). On the
110 ;; Emacs side, a connection to this socket is created for feeding code
111 ;; and receiving results. This additional communication channel to the
112 ;; OPI compiler ensures that results are returned cleanly (e.g., only
113 ;; the result of the sent code is returned, no parsing or any
114 ;; processing of *Oz Emulator* is required).
116 ;; There is no buffer, nor sentinel involved. Oz code is send
117 ;; directly, and results from Oz are send back, but Emacs Lisp
118 ;; requires a filter function for processing results.
120 (defvar org-babel-oz-server-dir
121 (file-name-as-directory
124 (file-name-as-directory
127 (file-name-directory (or load-file-name buffer-file-name
))))))
128 "Path to the contrib/scripts directory in which
129 StartOzServer.oz is located.")
131 (defvar org-babel-oz-port
6001
132 "Port for communicating with Oz compiler.")
133 (defvar org-babel-oz-OPI-socket nil
134 "Socket for communicating with OPI.")
136 (defvar org-babel-oz-collected-result nil
137 "Aux var to hand result from org-babel-oz-filter to oz-send-string-expression.")
138 (defun org-babel-oz-filter (proc string
)
139 "Processes output from socket org-babel-oz-OPI-socket."
140 ;; (setq org-babel-oz-collected-results (cons string org-babel-oz-collected-results))
141 (setq org-babel-oz-collected-result string
)
145 (defun org-babel-oz-create-socket ()
146 (message "Create OPI socket for evaluating expressions")
149 ;; Create socket on Oz side (after Oz was started).
150 (oz-send-string (concat "\\insert '" org-babel-oz-server-dir
"StartOzServer.oz'"))
151 ;; Wait until socket is created before connecting to it.
152 ;; Quick hack: wait 3 sec
154 ;; extending time to 30 secs does not help when starting Emacs for
155 ;; the first time (and computer does nothing else)
157 ;; connect to OPI socket
158 (setq org-babel-oz-OPI-socket
159 ;; Creates a socket. I/O interface of Emacs sockets as for processes.
160 (open-network-stream "*Org-babel-OPI-socket*" nil
"localhost" org-babel-oz-port
))
162 (set-process-filter org-babel-oz-OPI-socket
#'org-babel-oz-filter
)
165 ;; communication with org-babel-oz-OPI-socket is asynchronous, but
166 ;; oz-send-string-expression turns is into synchronous...
167 (defun oz-send-string-expression (string &optional wait-time
)
168 "Similar to oz-send-string, oz-send-string-expression sends a string to the OPI compiler. However, string must be expression and this function returns the result of the expression (as string). oz-send-string-expression is synchronous, wait-time allows to specify a maximum wait time. After wait-time is over with no result, the function returns nil."
169 (if (not org-babel-oz-OPI-socket
)
170 (org-babel-oz-create-socket))
171 (let ((polling-delay 0.1)
173 (process-send-string org-babel-oz-OPI-socket string
)
180 ;; stop loop if org-babel-oz-collected-result \= nil or waiting time is over
181 (not (or (not (equal org-babel-oz-collected-result nil
))
182 (> waited wait-time
)))
184 (sit-for polling-delay
)
185 ;; (message "org-babel-oz: next polling iteration")
186 (setq waited
(+ waited polling-delay
))))
187 ;; (message "org-babel-oz: waiting over, got result or waiting timed out")
188 ;; (message (format "wait-time: %s, waited: %s" wait-time waited))
189 (setq result org-babel-oz-collected-result
)
190 (setq org-babel-oz-collected-result nil
))))
193 (while (equal org-babel-oz-collected-result nil
)
194 (sit-for polling-delay
))
195 (setq result org-babel-oz-collected-result
)
196 (setq org-babel-oz-collected-result nil
))))
199 (defun org-babel-expand-body:oz
(body params
)
200 (let ((vars (mapcar #'cdr
(org-babel-get-header params
:var
))))
202 ;; prepend code to define all arguments passed to the code block
203 (let ((var-string (mapcar (lambda (pair)
206 (org-babel-oz-var-to-oz (cdr pair
))))
208 ;; only add var declarations if any variables are there
209 (mapconcat #'identity
210 (append (list "local") var-string
(list "in" body
"end"))
214 (defun org-babel-execute:oz
(body params
)
215 "Execute a block of Oz code with org-babel. This function is
216 called by `org-babel-execute-src-block' via multiple-value-bind."
217 (let* ((result-params (cdr (assoc :result-params params
)))
218 (full-body (org-babel-expand-body:oz body params
))
219 (wait-time (plist-get params
:wait-time
)))
220 ;; actually execute the source-code block
221 (org-babel-reassemble-table
223 ((member "output" result-params
)
224 (message "Org-babel: executing Oz statement")
225 (oz-send-string full-body
))
226 ((member "value" result-params
)
227 (message "Org-babel: executing Oz expression")
228 (oz-send-string-expression full-body
(or wait-time
1)))
229 (t (error "either 'output' or 'results' must be members of :results.")))
230 (org-babel-pick-name (cdr (assoc :colname-names params
))
231 (cdr (assoc :colnames params
)))
232 (org-babel-pick-name (cdr (assoc :roname-names params
))
233 (cdr (assoc :rownames params
))))))
235 ;; This function should be used to assign any variables in params in
236 ;; the context of the session environment.
237 (defun org-babel-prep-session:oz
(session params
)
238 "Prepare SESSION according to the header arguments specified in PARAMS."
239 (error "org-babel-prep-session:oz unimplemented"))
240 ;; TODO: testing... (copied from org-babel-haskell.el)
241 ;; (defun org-babel-prep-session:oz (session params)
242 ;; "Prepare SESSION according to the header arguments specified in PARAMS."
243 ;; (save-window-excursion
244 ;; (org-babel-oz-initiate-session session)
245 ;; (let* ((vars (org-babel-ref-variables params))
246 ;; (var-lines (mapconcat ;; define any variables
250 ;; (org-babel-ruby-var-to-ruby (cdr pair))))
252 ;; (vars-file (concat (make-temp-file "org-babel-oz-vars") ".oz")))
255 ;; (insert var-lines) (write-file vars-file)
257 ;; ;; (inferior-oz-load-file) ; ??
259 ;; (current-buffer))))
263 ;; TODO: testing... (simplified version of def in org-babel-prep-session:ocaml)
265 ;; BUG: does not work yet. Error: ad-Orig-error: buffer none doesn't exist or has no process
267 (defun org-babel-oz-initiate-session (&optional session params
)
268 "If there is not a current inferior-process-buffer in SESSION
269 then create. Return the initialized session."
270 (unless (string= session
"none")
271 ;; TODO: make it possible to have multiple sessions
272 (save-window-excursion
274 (get-buffer oz-compiler-buffer
))))
276 (defun org-babel-oz-var-to-oz (var)
277 "Convert an elisp var into a string of Oz source code
278 specifying a var of the same value."
280 ;; (concat "[" (mapconcat #'org-babel-oz-var-to-oz var ", ") "]")
282 (format "%s" var
) ; don't preserve string quotes.
287 (defun org-babel-oz-table-or-string (results)
288 "If the results look like a table, then convert them into an
289 Emacs-lisp table, otherwise return the results as a string."
290 (error "org-babel-oz-table-or-string unimplemented"))
294 ;;; org-babel-oz.el ends here