commit | author | age
|
76bbd0
|
1 |
;;; ob-octave.el --- Babel Functions for Octave and Matlab -*- lexical-binding: t; -*- |
C |
2 |
|
|
3 |
;; Copyright (C) 2010-2018 Free Software Foundation, Inc. |
|
4 |
|
|
5 |
;; Author: Dan Davison |
|
6 |
;; Keywords: literate programming, reproducible research |
|
7 |
;; Homepage: https://orgmode.org |
|
8 |
|
|
9 |
;; This file is part of GNU Emacs. |
|
10 |
|
|
11 |
;; GNU Emacs 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 of the License, or |
|
14 |
;; (at your option) any later version. |
|
15 |
|
|
16 |
;; GNU Emacs 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. |
|
20 |
|
|
21 |
;; You should have received a copy of the GNU General Public License |
|
22 |
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. |
|
23 |
|
|
24 |
;;; Commentary: |
|
25 |
|
|
26 |
;;; Requirements: |
|
27 |
|
|
28 |
;; octave |
|
29 |
;; octave-mode.el and octave-inf.el come with GNU emacs |
|
30 |
|
|
31 |
;;; Code: |
|
32 |
(require 'ob) |
|
33 |
|
|
34 |
(declare-function matlab-shell "ext:matlab-mode") |
|
35 |
(declare-function matlab-shell-run-region "ext:matlab-mode") |
|
36 |
(declare-function org-trim "org" (s &optional keep-lead)) |
|
37 |
|
|
38 |
(defvar org-babel-default-header-args:matlab '()) |
|
39 |
(defvar org-babel-default-header-args:octave '()) |
|
40 |
|
|
41 |
(defvar org-babel-matlab-shell-command "matlab -nosplash" |
|
42 |
"Shell command to run matlab as an external process.") |
|
43 |
(defvar org-babel-octave-shell-command "octave -q" |
|
44 |
"Shell command to run octave as an external process.") |
|
45 |
|
|
46 |
(defvar org-babel-matlab-with-emacs-link nil |
|
47 |
"If non-nil use matlab-shell-run-region for session evaluation. |
|
48 |
This will use EmacsLink if (matlab-with-emacs-link) evaluates |
|
49 |
to a non-nil value.") |
|
50 |
|
|
51 |
(defvar org-babel-matlab-emacs-link-wrapper-method |
|
52 |
"%s |
|
53 |
if ischar(ans), fid = fopen('%s', 'w'); fprintf(fid, '%%s\\n', ans); fclose(fid); |
|
54 |
else, save -ascii %s ans |
|
55 |
end |
|
56 |
delete('%s') |
|
57 |
") |
|
58 |
(defvar org-babel-octave-wrapper-method |
|
59 |
"%s |
|
60 |
if ischar(ans), fid = fopen('%s', 'w'); fprintf(fid, '%%s\\n', ans); fclose(fid); |
|
61 |
else, dlmwrite('%s', ans, '\\t') |
|
62 |
end") |
|
63 |
|
|
64 |
(defvar org-babel-octave-eoe-indicator "'org_babel_eoe'") |
|
65 |
|
|
66 |
(defvar org-babel-octave-eoe-output "ans = org_babel_eoe") |
|
67 |
|
|
68 |
(defun org-babel-execute:matlab (body params) |
|
69 |
"Execute a block of matlab code with Babel." |
|
70 |
(org-babel-execute:octave body params 'matlab)) |
|
71 |
|
|
72 |
(defun org-babel-execute:octave (body params &optional matlabp) |
|
73 |
"Execute a block of octave code with Babel." |
|
74 |
(let* ((session |
|
75 |
(funcall (intern (format "org-babel-%s-initiate-session" |
|
76 |
(if matlabp "matlab" "octave"))) |
|
77 |
(cdr (assq :session params)) params)) |
|
78 |
(result-type (cdr (assq :result-type params))) |
|
79 |
(full-body |
|
80 |
(org-babel-expand-body:generic |
|
81 |
body params (org-babel-variable-assignments:octave params))) |
|
82 |
(gfx-file (ignore-errors (org-babel-graphical-output-file params))) |
|
83 |
(result (org-babel-octave-evaluate |
|
84 |
session |
|
85 |
(if gfx-file |
|
86 |
(mapconcat 'identity |
|
87 |
(list |
|
88 |
"set (0, \"defaultfigurevisible\", \"off\");" |
|
89 |
full-body |
|
90 |
(format "print -dpng %s" gfx-file)) |
|
91 |
"\n") |
|
92 |
full-body) |
|
93 |
result-type matlabp))) |
|
94 |
(if gfx-file |
|
95 |
nil |
|
96 |
(org-babel-reassemble-table |
|
97 |
result |
|
98 |
(org-babel-pick-name |
|
99 |
(cdr (assq :colname-names params)) (cdr (assq :colnames params))) |
|
100 |
(org-babel-pick-name |
|
101 |
(cdr (assq :rowname-names params)) (cdr (assq :rownames params))))))) |
|
102 |
|
|
103 |
(defun org-babel-prep-session:matlab (session params) |
|
104 |
"Prepare SESSION according to PARAMS." |
|
105 |
(org-babel-prep-session:octave session params 'matlab)) |
|
106 |
|
|
107 |
(defun org-babel-variable-assignments:octave (params) |
|
108 |
"Return list of octave statements assigning the block's variables." |
|
109 |
(mapcar |
|
110 |
(lambda (pair) |
|
111 |
(format "%s=%s;" |
|
112 |
(car pair) |
|
113 |
(org-babel-octave-var-to-octave (cdr pair)))) |
|
114 |
(org-babel--get-vars params))) |
|
115 |
|
|
116 |
(defalias 'org-babel-variable-assignments:matlab |
|
117 |
'org-babel-variable-assignments:octave) |
|
118 |
|
|
119 |
(defun org-babel-octave-var-to-octave (var) |
|
120 |
"Convert an emacs-lisp value into an octave variable. |
|
121 |
Converts an emacs-lisp variable into a string of octave code |
|
122 |
specifying a variable of the same value." |
|
123 |
(if (listp var) |
|
124 |
(concat "[" (mapconcat #'org-babel-octave-var-to-octave var |
|
125 |
(if (listp (car var)) "; " ",")) "]") |
|
126 |
(cond |
|
127 |
((stringp var) |
|
128 |
(format "'%s'" var)) |
|
129 |
(t |
|
130 |
(format "%s" var))))) |
|
131 |
|
|
132 |
(defun org-babel-prep-session:octave (session params &optional matlabp) |
|
133 |
"Prepare SESSION according to the header arguments specified in PARAMS." |
|
134 |
(let* ((session (org-babel-octave-initiate-session session params matlabp)) |
|
135 |
(var-lines (org-babel-variable-assignments:octave params))) |
|
136 |
(org-babel-comint-in-buffer session |
|
137 |
(mapc (lambda (var) |
|
138 |
(end-of-line 1) (insert var) (comint-send-input nil t) |
|
139 |
(org-babel-comint-wait-for-output session)) var-lines)) |
|
140 |
session)) |
|
141 |
|
|
142 |
(defun org-babel-matlab-initiate-session (&optional session params) |
|
143 |
"Create a matlab inferior process buffer. |
|
144 |
If there is not a current inferior-process-buffer in SESSION then |
|
145 |
create. Return the initialized session." |
|
146 |
(org-babel-octave-initiate-session session params 'matlab)) |
|
147 |
|
|
148 |
(defun org-babel-octave-initiate-session (&optional session _params matlabp) |
|
149 |
"Create an octave inferior process buffer. |
|
150 |
If there is not a current inferior-process-buffer in SESSION then |
|
151 |
create. Return the initialized session." |
|
152 |
(if matlabp (require 'matlab) (or (require 'octave-inf nil 'noerror) |
|
153 |
(require 'octave))) |
|
154 |
(unless (string= session "none") |
|
155 |
(let ((session (or session |
|
156 |
(if matlabp "*Inferior Matlab*" "*Inferior Octave*")))) |
|
157 |
(if (org-babel-comint-buffer-livep session) session |
|
158 |
(save-window-excursion |
|
159 |
(if matlabp (unless org-babel-matlab-with-emacs-link (matlab-shell)) |
|
160 |
(run-octave)) |
|
161 |
(rename-buffer (if (bufferp session) (buffer-name session) |
|
162 |
(if (stringp session) session (buffer-name)))) |
|
163 |
(current-buffer)))))) |
|
164 |
|
|
165 |
(defun org-babel-octave-evaluate |
|
166 |
(session body result-type &optional matlabp) |
|
167 |
"Pass BODY to the octave process in SESSION. |
|
168 |
If RESULT-TYPE equals `output' then return the outputs of the |
|
169 |
statements in BODY, if RESULT-TYPE equals `value' then return the |
|
170 |
value of the last statement in BODY, as elisp." |
|
171 |
(if session |
|
172 |
(org-babel-octave-evaluate-session session body result-type matlabp) |
|
173 |
(org-babel-octave-evaluate-external-process body result-type matlabp))) |
|
174 |
|
|
175 |
(defun org-babel-octave-evaluate-external-process (body result-type matlabp) |
|
176 |
"Evaluate BODY in an external octave process." |
|
177 |
(let ((cmd (if matlabp |
|
178 |
org-babel-matlab-shell-command |
|
179 |
org-babel-octave-shell-command))) |
|
180 |
(pcase result-type |
|
181 |
(`output (org-babel-eval cmd body)) |
|
182 |
(`value (let ((tmp-file (org-babel-temp-file "octave-"))) |
|
183 |
(org-babel-eval |
|
184 |
cmd |
|
185 |
(format org-babel-octave-wrapper-method body |
|
186 |
(org-babel-process-file-name tmp-file 'noquote) |
|
187 |
(org-babel-process-file-name tmp-file 'noquote))) |
|
188 |
(org-babel-octave-import-elisp-from-file tmp-file)))))) |
|
189 |
|
|
190 |
(defun org-babel-octave-evaluate-session |
|
191 |
(session body result-type &optional matlabp) |
|
192 |
"Evaluate BODY in SESSION." |
|
193 |
(let* ((tmp-file (org-babel-temp-file (if matlabp "matlab-" "octave-"))) |
|
194 |
(wait-file (org-babel-temp-file "matlab-emacs-link-wait-signal-")) |
|
195 |
(full-body |
|
196 |
(pcase result-type |
|
197 |
(`output |
|
198 |
(mapconcat |
|
199 |
#'org-babel-chomp |
|
200 |
(list body org-babel-octave-eoe-indicator) "\n")) |
|
201 |
(`value |
|
202 |
(if (and matlabp org-babel-matlab-with-emacs-link) |
|
203 |
(concat |
|
204 |
(format org-babel-matlab-emacs-link-wrapper-method |
|
205 |
body |
|
206 |
(org-babel-process-file-name tmp-file 'noquote) |
|
207 |
(org-babel-process-file-name tmp-file 'noquote) wait-file) "\n") |
|
208 |
(mapconcat |
|
209 |
#'org-babel-chomp |
|
210 |
(list (format org-babel-octave-wrapper-method |
|
211 |
body |
|
212 |
(org-babel-process-file-name tmp-file 'noquote) |
|
213 |
(org-babel-process-file-name tmp-file 'noquote)) |
|
214 |
org-babel-octave-eoe-indicator) "\n"))))) |
|
215 |
(raw (if (and matlabp org-babel-matlab-with-emacs-link) |
|
216 |
(save-window-excursion |
|
217 |
(with-temp-buffer |
|
218 |
(insert full-body) |
|
219 |
(write-region "" 'ignored wait-file nil nil nil 'excl) |
|
220 |
(matlab-shell-run-region (point-min) (point-max)) |
|
221 |
(message "Waiting for Matlab Emacs Link") |
|
222 |
(while (file-exists-p wait-file) (sit-for 0.01)) |
|
223 |
"")) ;; matlab-shell-run-region doesn't seem to |
|
224 |
;; make *matlab* buffer contents easily |
|
225 |
;; available, so :results output currently |
|
226 |
;; won't work |
|
227 |
(org-babel-comint-with-output |
|
228 |
(session |
|
229 |
(if matlabp |
|
230 |
org-babel-octave-eoe-indicator |
|
231 |
org-babel-octave-eoe-output) |
|
232 |
t full-body) |
|
233 |
(insert full-body) (comint-send-input nil t)))) results) |
|
234 |
(pcase result-type |
|
235 |
(`value |
|
236 |
(org-babel-octave-import-elisp-from-file tmp-file)) |
|
237 |
(`output |
|
238 |
(setq results |
|
239 |
(if matlabp |
|
240 |
(cdr (reverse (delq "" (mapcar |
|
241 |
#'org-babel-strip-quotes |
|
242 |
(mapcar #'org-trim raw))))) |
|
243 |
(cdr (member org-babel-octave-eoe-output |
|
244 |
(reverse (mapcar |
|
245 |
#'org-babel-strip-quotes |
|
246 |
(mapcar #'org-trim raw))))))) |
|
247 |
(mapconcat #'identity (reverse results) "\n"))))) |
|
248 |
|
|
249 |
(defun org-babel-octave-import-elisp-from-file (file-name) |
|
250 |
"Import data from FILE-NAME. |
|
251 |
This removes initial blank and comment lines and then calls |
|
252 |
`org-babel-import-elisp-from-file'." |
|
253 |
(let ((temp-file (org-babel-temp-file "octave-matlab-")) beg end) |
|
254 |
(with-temp-file temp-file |
|
255 |
(insert-file-contents file-name) |
|
256 |
(re-search-forward "^[ \t]*[^# \t]" nil t) |
|
257 |
(if (< (setq beg (point-min)) |
|
258 |
(setq end (point-at-bol))) |
|
259 |
(delete-region beg end))) |
|
260 |
(org-babel-import-elisp-from-file temp-file '(16)))) |
|
261 |
|
|
262 |
(provide 'ob-octave) |
|
263 |
|
|
264 |
|
|
265 |
|
|
266 |
;;; ob-octave.el ends here |