emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Visuwesh <visuweshm@gmail.com>
To: Org Mode List <emacs-orgmode@gnu.org>
Subject: Re: [PATCH] Add support for tables in Calc src block :var
Date: Fri, 13 Dec 2024 14:32:11 +0530	[thread overview]
Message-ID: <87jzc42bxo.fsf@gmail.com> (raw)
In-Reply-To: <87pllw2g4l.fsf@gmail.com> (Visuwesh's message of "Fri, 13 Dec 2024 13:01:38 +0530")

[வெள்ளி டிசம்பர் 13, 2024] Visuwesh wrote:

> [...]
> Some caveats:
>
>   1. You need to ensure that all the calc-embedded variables that you
>      use in the formula need to be active and evaluated beforehand.
>   2. The calc-embedded var is considered at last after everything else
>      in org-table-get-constant in the advice.  This would be the best
>      way to go forward.
>
> It would be nice to lift the restrict in (1) but I think it would be
> better to leave it to the user to ensure everything stays updated since
> the user may want to use the old value.  However, ensuring all the vars
> are active and eval-ed would be a royal pain when you're quickly
> evaluating a table formula.
>
> Here's the case I used to test this hack:
>
>     x := 3
>
>     y := 5
>
>     z := 5 x - y => 10
>
>
>     | 1 | 20 |
>
>     #+TBLFM: $2=$z*2

OK, this was not so simple as I thought!  Consider the following
example:

    x := 3

    a := 3 x => 9

    y := 5

    x := 2

    z := 5.2 x - y => 5.4

Update x := 3 and y first.  Then update `a' and `z'.  `z' will be 10.6.
Now update x := 2, and update `z' and `a'.  `z' updates to 5.4 as shown
above but `a' remains 9!  Calc looks for the nearest value of the
variable `x' from the definition of `z'.  The Elisp variable that stores
the var information looks like this:

    (cdar calc-embedded-active)

    ([#<buffer scratch.org> #<buffer *Calculator*> #<marker at 289961 in
           scratch.org> #<marker at 289967 in scratch.org> #<marker at
           289960 in scratch.org> #<marker at 289968 in scratch.org>
           #("x := 3" 0 6 (fontified t)) nil
           (calcFunc-assign (var x var-x) 3) (var x var-x) nil
           (calcFunc-assign (var x var-x) 3) nil nil nil nil]
     [#<buffer scratch.org> #<buffer *Calculator*> #<marker at 289969 in
           scratch.org> #<marker at 289982 in scratch.org> #<marker at
           289968 in scratch.org> #<marker at 289983 in scratch.org>
           #("a := 3 x => 9" 0 13 (fontified t)) nil
           (calcFunc-evalto
            (calcFunc-assign (var a var-a) (* 3 (var x var-x))) 9)
           (var a var-a) (((var x var-x))) 9 nil nil nil nil]
     [#<buffer scratch.org> #<buffer *Calculator*> #<marker at 289984 in
           scratch.org> #<marker at 289990 in scratch.org> #<marker at
           289983 in scratch.org> #<marker at 289991 in scratch.org>
           #("y := 5" 0 6 (fontified t)) nil
           (calcFunc-assign (var y var-y) 5) (var y var-y) nil
           (calcFunc-assign (var y var-y) 5) nil nil nil nil]
     [#<buffer scratch.org> #<buffer *Calculator*> #<marker at 289992 in
           scratch.org> #<marker at 289998 in scratch.org> #<marker at
           289991 in scratch.org> #<marker at 289999 in scratch.org>
           #("x := 2" 0 6 (fontified t)) nil
           (calcFunc-assign (var x var-x) 2) (var x var-x) nil
           (calcFunc-assign (var x var-x) 2) nil nil nil nil]
     [#<buffer scratch.org> #<buffer *Calculator*> #<marker at 290000 in
           scratch.org> #<marker at 290021 in scratch.org> #<marker at
           289999 in scratch.org> #<marker at 290022 in scratch.org>
           "z := 5.2 x - y => 5.4" nil
           (calcFunc-evalto
            (calcFunc-assign (var z var-z)
                     (- (* (float 52 -1) (var x var-x))
                    (var y var-y)))
            (float 54 -1))
           (var z var-z) (((var y var-y)) ((var x var-x)))
           (float 54 -1) nil nil nil nil])

Notice how there are two entries for `x'.  Worse still, change x := 3 to
x := 3.3 and update it.  Evaluating the same expression again now yields
three entries for `x'!

    ([#<buffer scratch.org> #<buffer *Calculator*> #<marker at 289961 in
           scratch.org> #<marker at 289967 in scratch.org> #<marker at
           289960 in scratch.org> #<marker at 289970 in scratch.org>
           #("x := 3" 0 6 (fontified t)) nil
           (calcFunc-assign (var x var-x) 3) (var x var-x) nil
           (calcFunc-assign (var x var-x) 3) nil nil nil nil]
     [#<buffer scratch.org> #<buffer *Calculator*> #<marker at 289961 in
           scratch.org> #<marker at 289969 in scratch.org> #<marker at
           289960 in scratch.org> #<marker at 289970 in scratch.org>
           #("x := 3.3" 0 8 (fontified t)) nil
           (calcFunc-assign (var x var-x) (float 33 -1)) (var x var-x)
           nil (calcFunc-assign (var x var-x) (float 33 -1)) nil nil
           nil nil]
     ...
     ...
     [#<buffer scratch.org> #<buffer *Calculator*> #<marker at 289996 in
           scratch.org> #<marker at 290002 in scratch.org> #<marker at
           289995 in scratch.org> #<marker at 290003 in scratch.org>
           #("x := 2" 0 6 (fontified t)) nil
           (calcFunc-assign (var x var-x) 2) (var x var-x) nil
           (calcFunc-assign (var x var-x) 2) nil nil nil nil]
     ...)

Which begs the question: what should be `x'?

In any case, the code I posted above had a problem with decimals.  The
code still picks the first value of VAR that shows up in the list above.

    (defun vz/calc-embedded-get-var (var)
      "Return the value of active `calc-embedded' VAR in current buffer."
      (let* ((info (cdr (assq (current-buffer) calc-embedded-active)))
             (var-info
              (seq-find
               (lambda (x)
                 ;; 9 is the variable name: (var XXX var-XXX)
                 (eq var (nth 1 (aref x 9))))
               info))
             old-val)
        (when (and info var-info)
          ;; This is called the `old-val' in `calc-embedded-update'.
          ;; This can be nil when the formula isn't evaled I think?
          ;; (aref VAR-INFO 8) is again repeated in 11th slot when the
          ;; variable is simply as assignment.
          (let ((calc-line-numbering))
            ;; For the below trick, see `calc-embedded-update' again.
            (math-format-stack-value
             (list (or (if (and (consp (setq old-val (aref var-info 11)))
                                (eq (car old-val) 'calcFun-assign))
                           (car (last old-val))
                         old-val)
                       ;; 8 is the eval form: (calcFun-evalto ...) or
                       ;; (calcFun-assign ...)
                       (car (last (aref var-info 8))))
                   1 nil))))))

    (define-advice org-table-get-constant (:around (oldfun name) vz/consider-calc-embedded-var)
      "Check if NAME is a `calc-embedded' at last."
      (let ((val (funcall oldfun name)))
        (if (equal val "#UNDEFINED_NAME")
            (or (vz/calc-embedded-get-var (intern name))
                val)
          val)))


  reply	other threads:[~2024-12-13  9:03 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-03-16  3:45 [PATCH] Add support for tables in Calc src block :var Visuwesh
2024-03-16  9:52 ` Ihor Radchenko
2024-03-16 11:37   ` Visuwesh
2024-03-16 12:19     ` Ihor Radchenko
2024-03-16 12:34       ` Visuwesh
2024-03-18 12:48         ` Fraga, Eric
2024-03-18 15:29           ` Visuwesh
2024-03-18 15:45             ` Fraga, Eric
2024-03-30 13:04           ` Visuwesh
2024-04-03 12:43             ` Fraga, Eric
2024-12-13  7:31               ` Visuwesh
2024-12-13  9:02                 ` Visuwesh [this message]
2024-12-13 10:53                   ` Fraga, Eric
2024-12-13 11:06                     ` Visuwesh
2024-12-13 11:48                       ` Fraga, Eric

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87jzc42bxo.fsf@gmail.com \
    --to=visuweshm@gmail.com \
    --cc=emacs-orgmode@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).