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