diff --git a/lisp/langs/org-babel-R.el b/lisp/langs/org-babel-R.el index 86548192e..3b9958874 100644 --- a/lisp/langs/org-babel-R.el +++ b/lisp/langs/org-babel-R.el @@ -96,43 +96,62 @@ R process in `org-babel-R-buffer'." (defun org-babel-R-initiate-session (session) "If there is not a current R process then create one." - (setq session (or session "*R*")) - (if (org-babel-comint-buffer-livep session) - session - (save-window-excursion (R) (rename-buffer (if (bufferp session) (buffer-name session) - (if (stringp session) session (buffer-name)))) (current-buffer)))) + (unless (string= session "none") + (setq session (or session "*R*")) + (if (org-babel-comint-buffer-livep session) + session + (save-window-excursion (R) (rename-buffer (if (bufferp session) (buffer-name session) + (if (stringp session) session (buffer-name)))) (current-buffer))))) (defvar org-babel-R-eoe-indicator "'org_babel_R_eoe'") (defvar org-babel-R-eoe-output "[1] \"org_babel_R_eoe\"") +(defvar org-babel-R-wrapper-method "main <- function ()\n{\n%s\n} +write.table(main(), file=\"%s\", sep=\"\\t\", na=\"nil\",row.names=FALSE, col.names=FALSE, quote=FALSE)") (defun org-babel-R-evaluate (buffer body result-type) "Pass BODY to the R process in BUFFER. If RESULT-TYPE equals 'output then return a list of the outputs of the statements in BODY, if RESULT-TYPE equals 'value then return the value of the last statement in BODY." - (org-babel-comint-in-buffer buffer - (let* ((tmp-file (make-temp-file "org-babel-R")) - (last-value-eval - (format "write.table(.Last.value, file=\"%s\", sep=\"\\t\", na=\"nil\",row.names=FALSE, col.names=FALSE, quote=FALSE)" - tmp-file)) - (full-body (mapconcat #'org-babel-chomp (list body last-value-eval org-babel-R-eoe-indicator) "\n")) - (raw (org-babel-comint-with-output buffer org-babel-R-eoe-output nil - (insert full-body) (inferior-ess-send-input))) - (results (let ((broke nil)) - (delete nil (mapcar (lambda (el) - (if (or broke - (and (string-match (regexp-quote org-babel-R-eoe-output) el) (setq broke t))) - nil - (if (= (length el) 0) + (if (not session) + ;; external process evaluation + (let ((in-tmp-file (make-temp-file "R-in-functional-results")) + (out-tmp-file (make-temp-file "R-out-functional-results"))) + (case result-type + (output + (with-temp-file in-tmp-file (insert body)) + (message "R --slave --no-save < '%s' > '%s'" in-tmp-file out-tmp-file) + (shell-command-to-string (format "R --slave --no-save < '%s' > '%s'" in-tmp-file out-tmp-file))) + (value + (with-temp-file in-tmp-file + (insert (format org-babel-R-wrapper-method body out-tmp-file))) + (message "R --no-save < '%s'" in-tmp-file) + (shell-command (format "R --no-save < '%s'" in-tmp-file)))) + (with-temp-buffer (insert-file-contents out-tmp-file) (buffer-string))) + ;; comint session evaluation + (org-babel-comint-in-buffer buffer + (let* ((tmp-file (make-temp-file "org-babel-R")) + (last-value-eval + (format "write.table(.Last.value, file=\"%s\", sep=\"\\t\", na=\"nil\",row.names=FALSE, col.names=FALSE, quote=FALSE)" + tmp-file)) + (full-body (mapconcat #'org-babel-chomp (list body last-value-eval org-babel-R-eoe-indicator) "\n")) + (raw (org-babel-comint-with-output buffer org-babel-R-eoe-output nil + (insert full-body) (inferior-ess-send-input))) + (results (let ((broke nil)) + (delete nil (mapcar (lambda (el) + (if (or broke + (and (string-match (regexp-quote org-babel-R-eoe-output) el) (setq broke t))) nil - (if (string-match comint-prompt-regexp el) - (substring el (match-end 0)) - el)))) - (mapcar #'org-babel-trim raw)))))) - (case result-type - (output (org-babel-trim (mapconcat #'identity results "\n"))) - (value (org-babel-trim (with-temp-buffer (insert-file-contents tmp-file) (buffer-string)))) - (t (reverse results)))))) + (if (= (length el) 0) + nil + (if (string-match comint-prompt-regexp el) + (substring el (match-end 0)) + el)))) + (mapcar #'org-babel-trim raw)))))) + (case result-type + (output (org-babel-trim (mapconcat #'identity results "\n"))) + (value (org-babel-trim (with-temp-buffer (insert-file-contents tmp-file) (buffer-string)))) + (t (reverse results))))))) (provide 'org-babel-R) ;;; org-babel-R.el ends here diff --git a/org-babel.org b/org-babel.org index ded408cb9..1947cb4de 100644 --- a/org-babel.org +++ b/org-babel.org @@ -114,7 +114,7 @@ table, allowing the test suite to be run be evaluation of the table and the results to be collected in the same table. -* Tasks [25/42] +* Tasks [26/42] ** TODO add a function to jump to a source-block by name I've had an initial stab at that in org-babel-find-named-block (library-of-babel branch). @@ -497,65 +497,6 @@ tabel Another example is in the [[*operations%20in%20on%20tables][grades example]]. -** PROPOSED add =:none= session argument (for purely functional execution) [3/4] -This would allow source blocks to be run in their own new process - -- These blocks could then also be run in the background (since we can - detach and just wait for the process to signal that it has terminated) -- We wouldn't be drowning in session buffers after running the tests -- we can re-use much of the session code to run in a more /functional/ - mode - -While session provide a lot of cool features, like persistent -environments, [[* DONE function to bring up inferior-process buffer][pop-to-session]], and hints at exportation for -org-babel-tangle, they also have some down sides and I'm thinking that -session-based execution maybe shouldn't be the default behavior. - -Down-sides to sessions -- *much* more complicated than functional evaluation - - maintaining the state of the session has weird issues - - waiting for evaluation to finish - - prompt issues like [[* TODO weird escaped characters in shell prompt break shell evaluation][shell-prompt-escapes-bug]] -- can't run in background -- litter emacs with session buffers - -*** DONE ruby - -#+srcname: ruby-task-no-session -#+begin_src ruby :results replace output -puts :eric -puts :schulte -[1, 2, 3] -#+end_src - -#+resname: ruby-task-no-session -| "eric" | -| "schulte" | -*** DONE python - -#+srcname: task-python-none-session -#+begin_src python :session none :results replace value -print 'something' -print 'output' -[1, 2, 3] -#+end_src - -#+resname: task-python-none-session -| 1 | 2 | 3 | - -*** DONE sh - -#+srcname: task-session-none-sh -#+begin_src sh :results replace -echo "first" -echo "second" -#+end_src - -#+resname: task-session-none-sh -| "first" | -| "second" | - -*** TODO R ** PROPOSED Are we happy with current behaviour regarding vector/scalar output? This simple example of multilingual chaining produces vector output if there are spaces in the message and scalar otherwise. @@ -806,6 +747,78 @@ $0 waiting for guidance from those more familiar with yasnippets +** DONE add =:none= session argument (for purely functional execution) [4/4] +This would allow source blocks to be run in their own new process + +- These blocks could then also be run in the background (since we can + detach and just wait for the process to signal that it has terminated) +- We wouldn't be drowning in session buffers after running the tests +- we can re-use much of the session code to run in a more /functional/ + mode + +While session provide a lot of cool features, like persistent +environments, [[* DONE function to bring up inferior-process buffer][pop-to-session]], and hints at exportation for +org-babel-tangle, they also have some down sides and I'm thinking that +session-based execution maybe shouldn't be the default behavior. + +Down-sides to sessions +- *much* more complicated than functional evaluation + - maintaining the state of the session has weird issues + - waiting for evaluation to finish + - prompt issues like [[* TODO weird escaped characters in shell prompt break shell evaluation][shell-prompt-escapes-bug]] +- can't run in background +- litter emacs with session buffers + +*** DONE ruby + +#+srcname: ruby-task-no-session +#+begin_src ruby :results replace output +puts :eric +puts :schulte +[1, 2, 3] +#+end_src + +#+resname: ruby-task-no-session +| "eric" | +| "schulte" | +*** DONE python + +#+srcname: task-python-none-session +#+begin_src python :session none :results replace value +print 'something' +print 'output' +[1, 2, 3] +#+end_src + +#+resname: task-python-none-session +| 1 | 2 | 3 | + +*** DONE sh + +#+srcname: task-session-none-sh +#+begin_src sh :results replace +echo "first" +echo "second" +#+end_src + +#+resname: task-session-none-sh +| "first" | +| "second" | + +*** DONE R + +#+srcname: task-no-session-R +#+begin_src R :results replace output +a <- 8 +b <- 9 +a + b +b - a +#+end_src + +#+resname: task-no-session-R +| "[1]" | 17 | +| "[1]" | 1 | + ** DONE fully purge org-babel-R of direct comint interaction try to remove all code under the [[file:lisp/org-babel-R.el::functions%20for%20evaluation%20of%20R%20code][;; functions for evaluation of R code]] line