emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* How is org-sbe supposed to work?
@ 2020-11-13 14:39 Daniele Nicolodi
  2020-11-13 21:56 ` Daniele Nicolodi
  0 siblings, 1 reply; 2+ messages in thread
From: Daniele Nicolodi @ 2020-11-13 14:39 UTC (permalink / raw)
  To: Org Mode List

Hello,

I am trying to use the org-sbe macro to execute a code block with input
from an org table, however it does not work as I would expect it to work
and the code is cryptic enough for me to not being able to understand
what the intent was when it was written. I searched for tests in the
codebase that exercise this functionality, but I haven't found any:

daniele@black:~/src/org-mode$ grep org-sbe * -r
lisp/ob-table.el:;; `org-sbe' as so...
lisp/ob-table.el:;; #+TBLFM: $2='(org-sbe "fibbd" (n $1))
lisp/ob-table.el:(defmacro org-sbe (source-block &rest variables)
lisp/ob-table.el:So this `org-sbe' construct
lisp/ob-table.el: (org-sbe \"source-block\" (n $2) (m 3))
lisp/ob-table.el:#+TBLFM: @1$4=\\='(org-sbe test-sbe $3 (x $1) (y $2))"

How are variables passed to the code block supposed to be handled? The
macro docstring does not mention anything particular, thus I would
imagine they are handled just like in any other lisp code. However, this
does not seem to be the case.

In the case that the values are integers, all works as expected:

#+name: example
#+begin_src emacs-lisp :var x=1
(format "x:%S" x)
#+end_src

#+call: example(x=2)

#+RESULTS:
: x:2

| 1 | x:2 |
#+TBLFM: $2='(org-sbe "example" (x 2))

However, strings are not handled correctly:

#+call: example(x="baz")

#+RESULTS:
: x:"baz"

| 1 | #ERROR |
#+TBLFM: $2='(org-sbe "example" (x "baz"))

unless "double quoted":

| 1 | x:"baz" |
#+TBLFM: $2='(org-sbe "example" (x "\"baz\""))

and becomes an exercise in code injection if the string needs to come
from a table cell:

| baz | x:"baz" | x:"baz" |
#+TBLFM: $2='(org-sbe "example" (x \"$1\"));L
#+TBLFM: $3='(format "x:%S" $1)

Things get even more fun if the string value is the name of a code block:

#+name: foo
#+begin_src emacs-lisp :var x=1
nil
#+end_src

#+call: example(x="foo")

#+RESULTS:
: x:"foo"

| 1 |  |
#+TBLFM: $2='(org-sbe "example" (x "foo"))

as in this case, org-sbe actually tries to execute that code block
instead than using the literal string.

Should we come up with some tests highlighting the expected behavior of
org-sbe and make the implementation work accordingly?

Thank you.

Cheers,
Dan


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: How is org-sbe supposed to work?
  2020-11-13 14:39 How is org-sbe supposed to work? Daniele Nicolodi
@ 2020-11-13 21:56 ` Daniele Nicolodi
  0 siblings, 0 replies; 2+ messages in thread
From: Daniele Nicolodi @ 2020-11-13 21:56 UTC (permalink / raw)
  To: emacs-orgmode

On 13/11/2020 15:39, Daniele Nicolodi wrote:
> How are variables passed to the code block supposed to be handled? The
> macro docstring does not mention anything particular, thus I would
> imagine they are handled just like in any other lisp code. However, this
> does not seem to be the case.

After some more testing and starring at the code some more, it seems
that the odd behavior comes from the attempt of org-sbe to be clever and
do not require to use the N flag in the org table formula definition to
parse numbers as such, maybe.

Anyway, I came up with an alternative macro that does what I would
expect org-sbe to do, acting as a transparent interface to execute org
code blocks, without surprises:

(defun org-sbx1 (name header args)
  (let* ((args (mapconcat
                (lambda (x)
                  (format "%s=%S" (symbol-name (car x)) (cadr x)))
                args ", "))
         (ctx (list 'babel-call (list :call name
                                      :name name
                                      :inside-header header
                                      :arguments args
                                      :end-header ":results silent")))
         (info (org-babel-lob-get-info ctx)))
    (when info (org-babel-execute-src-block nil info))))

(defmacro org-sbx (name &rest args)
  (let* ((header (if (stringp (car args)) (car args) nil))
	 (args (if (stringp (car args)) (cdr args) args)))
    (unless (stringp name)
      (setq name (symbol-name name)))
    (let ((result (org-sbx1 name header args)))
      (org-trim (if (stringp result) result (format "%S" result))))))

which I find significantly simpler than the implementation of org-sbe.
It works emulating what a #+call: directive does constructing by hand
the parsed version returned by org-element-parse. Demo:

#+name: sum
#+begin_src elisp :var x='(2 3)
  (apply '+ x)
#+end_src

#+name: concat
#+begin_src elisp :var x='()
  (apply 'concat x)
#+end_src

| 1 | 2 | 3 | 4 | 5 |    15 |    15 |
| a | b | c | d | e | abcde | abcde |
#+TBLFM: @1$6='(org-sbx sum (x '($1..$5)));N
#+TBLFM: @1$7='(apply '+ '($1..$5));N
#+TBLFM: @2$6='(org-sbx concat (x '($1..$5)))
#+TBLFM: @2$7='(apply 'concat '($1..$5))


The only problem I have with it is that the point jumps around during
evaluation and that while the macro is evaluated the formula definition
line is duplicated. I haven't yet investigated why (however the same
happens with org-sbe).

It may be that it is not too difficult to avoid having to convert the
arguments to a string representation and back, but this would require
digging a bit deeper into ob-core.el.

Cheers,
Dan


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2020-11-13 21:57 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-13 14:39 How is org-sbe supposed to work? Daniele Nicolodi
2020-11-13 21:56 ` Daniele Nicolodi

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).