2

Calc supports displaying results as percentages with c %. How can I make babel return this? I tried:

#+BEGIN_SRC calc
1
2
'/
'c%
#+END_SRC

But this just gives an error. I also tried with a space separating c and % as well several other variations. How can I input this command? These kinds of commands are fairly common in calc, so surely there is a way to input them from babel.

1 Answer 1

3

How can I input this command?

I believe you cannot.

I am convinced that this is a bug in org-babel-execute:calc: see ob-calc.el, line 67.

org-babel-execute:calc expects every Calc "stack operation" to be a function taking a prefix argument which specifies the number of times it is applied. calc-divide, for example, takes a prefix argument, so line 3 of the script in the question works fine. The "c%" command corresponds to calc-convert-percent, which takes no arguments, and this results in a "Wrong number of arguments" error (it is called as (calc-convert-percent nil).)

Consider submitting a bug report.

Update (November 6, 2014)

An answer stating that this is a bug is not complete unless it contains a bug fix. :-)

There are two problems here:

  1. As stated above, org-babel-execute:calc expects stack operations to take one argument.

  2. Some Calc operations (c% is one of them) do not change the value but change the way it is printed. So, even after fixing the bug evaluating the source code block given in the question produces 0.5, not 50%.

I extended org-babel-execute:calc (see code below) so that

# this is equivalent to using ":results value"
#+BEGIN_SRC calc
1
2
'/
'c%
#+END_SRC

gives

#+RESULTS:
: 0.5

and

#+BEGIN_SRC calc :results output
1
2
'/
'c%
#+END_SRC

gives

#+RESULTS:
: 50%

(The reasoning is that c% changes the output of Calc.)

Here's the code:

(defun org-babel-execute:calc (body params)
  "Execute a block of calc code with Babel."
  (unless (get-buffer "*Calculator*")
    (save-window-excursion (calc) (calc-quit)))
  (let* ((vars (mapcar #'cdr (org-babel-get-header params :var)))
         (org--var-syms (mapcar #'car vars))
         (var-names (mapcar #'symbol-name org--var-syms)))
    (mapc
     (lambda (pair)
       (calc-push-list (list (cdr pair)))
       (calc-store-into (car pair)))
     vars)
    (mapc
     (lambda (line)
       (when (> (length line) 0)
         (cond
          ;; simple variable name
          ((member line var-names) (calc-recall (intern line)))
          ;; stack operation
          ((string= "'" (substring line 0 1))
           (let ((f (lookup-key calc-mode-map (substring line 1))))
             (condition-case nil
                 (funcall f nil)        ;; try calling with one arg
               (error (funcall f)))))   ;; if failed, call without args
          ;; complex expression
          (t
           (calc-push-list
            (list (let ((res (calc-eval line)))
                    (cond
                     ((numberp res) res)
                     ((math-read-number res) (math-read-number res))
                     ((listp res) (error "Calc error \"%s\" on input \"%s\""
                                         (cadr res) line))
                     (t (replace-regexp-in-string
                         "'" ""
                         (calc-eval
                          (math-evaluate-expr
                           ;; resolve user variables, calc built in
                           ;; variables are handled automatically
                           ;; upstream by calc
                           (mapcar #'org-babel-calc-maybe-resolve-var
                                   ;; parse line into calc objects
                                   (car (math-read-exprs line)))))))))
                  ))))))
     (mapcar #'org-babel-trim
             (split-string (org-babel-expand-body:calc body params) "[\n\r]"))))
  (save-excursion
    (with-current-buffer (get-buffer "*Calculator*")
      (if (equal (cdr (assoc :result-type params)) 'output)
          (math-format-value (calc-top 1)) ; return formatted value if asked for the output
        (calc-eval (calc-top 1))))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.