1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-15 10:30:25 -08:00

Make ses-range environment agnostic.

* lisp/ses.el (ses-sym-rowcol): Do not make border effect on interned
symbols.
(ses-formula-references): Do not use ses-range implementation any
longer, as the ses-range is now environnement agnostic, and as such do
not expand any longer to a list of symbols.
(ses-range): Do not make border effect on interned symbols, and be
environment agnostic. As such ses-range macro now expand to a sexp the
evaluation of which yields a list of symbol, and no longer to a list
of symbol with some function call to make it evaluate to the
list of values.

* test/lisp/ses-tests.el (ses-expand-range): New test.
This commit is contained in:
Vincent Belaïche 2023-11-06 17:19:22 +01:00 committed by Vincent Belaïche
parent 5a8c993eb3
commit ab7d6fbe88
2 changed files with 137 additions and 76 deletions

View file

@ -463,10 +463,11 @@ functions refer to its value."
"From a cell-symbol SYM, gets the cons (row . col). A1 => (0 . 0).
Result is nil if SYM is not a symbol that names a cell."
(declare (debug t))
`(let ((rc (and (symbolp ,sym) (get ,sym 'ses-cell))))
(if (eq rc :ses-named)
(and ses--named-cell-hashmap (gethash ,sym ses--named-cell-hashmap))
rc)))
(let ((rc (make-symbol "rc")))
`(let ((,rc (and (symbolp ,sym) (get ,sym 'ses-cell))))
(if (eq ,rc :ses-named)
(and ses--named-cell-hashmap (gethash ,sym ses--named-cell-hashmap))
,rc))))
(defun ses-cell-p (cell)
"Return non-nil if CELL is a cell of current buffer."
@ -1531,11 +1532,11 @@ first reference is found."
(if (consp formula)
(cond
((eq (car formula) 'ses-range)
(dolist (cur
(cdr (funcall 'macroexpand
(list 'ses-range (nth 1 formula)
(nth 2 formula)))))
(cl-pushnew cur result-so-far :test #'equal)))
;; simplified implementation of ses-range where we output
;; symbols rather than values, and when all the flags
;; handling is skipped
(ses-dorange (cons (nth 1 formula) (nth 2 formula))
(cl-pushnew (ses-cell-symbol row col) result-so-far :test #'equal)))
((null (eq (car formula) 'quote))
;;Recursive call for subformulas
(dolist (cur formula)
@ -3986,75 +3987,90 @@ matrix whatever the number of rows.
Warning: interaction with Calc is experimental and may produce
confusing results if you are not aware of Calc data format.
Use `math-format-value' as a printer for Calc objects."
(let (result-row
result
(prev-row -1)
(reorient-x nil)
(reorient-y nil)
transpose vectorize
(clean 'list))
(ses-dorange (cons from to)
(when (/= prev-row row)
(push result-row result)
(setq result-row nil))
(push (ses-cell-symbol row col) result-row)
(setq prev-row row))
(push result-row result)
(while rest
(let ((x (pop rest)))
(pcase x
('>v (setq transpose nil reorient-x nil reorient-y nil))
('>^ (setq transpose nil reorient-x nil reorient-y t))
('<^ (setq transpose nil reorient-x t reorient-y t))
('<v (setq transpose nil reorient-x t reorient-y nil))
('v> (setq transpose t reorient-x nil reorient-y t))
('^> (setq transpose t reorient-x nil reorient-y nil))
('^< (setq transpose t reorient-x t reorient-y nil))
('v< (setq transpose t reorient-x t reorient-y t))
((or '* '*2 '*1) (setq vectorize x))
('! (setq clean 'ses--clean-!))
('_ (setq clean `(lambda (&rest x)
(ses--clean-_ x ,(if rest (pop rest) 0)))))
(_
(cond
;; Note ses-formula-references contains some simplified code for ses-range.
(let ((result-row (make-symbol "result-row"))
(result (make-symbol "result"))
(prev-row (make-symbol "prev-row"))
(reorient-x (make-symbol "reorient-x"))
(reorient-y (make-symbol "reorient-y"))
(transpose (make-symbol "transpose"))
(vectorize (make-symbol "vectorize"))
(clean (make-symbol "clean"))
(elt (make-symbol "elt"))
(x (make-symbol "x"))
(iter (make-symbol "iter"))
(rest-arg (make-symbol "rest-arg"))
(ret (make-symbol "ret")))
`(let (,result-row
,result
(,prev-row -1)
,reorient-x
,reorient-y
,transpose ,vectorize
(,clean #'list)
(,rest-arg (quote ,rest)))
(ses-dorange (cons (quote ,from) (quote ,to))
(when (/= ,prev-row row)
(push ,result-row ,result)
(setq ,result-row nil))
(push (ses-cell-value row col) ,result-row)
(setq ,prev-row row))
(push ,result-row ,result)
(while ,rest-arg
(let ((,x (pop ,rest-arg)))
(pcase ,x
('>v (setq ,transpose nil ,reorient-x nil ,reorient-y nil))
('>^ (setq ,transpose nil ,reorient-x nil ,reorient-y t))
('<^ (setq ,transpose nil ,reorient-x t ,reorient-y t))
('<v (setq ,transpose nil ,reorient-x t ,reorient-y nil))
('v> (setq ,transpose t ,reorient-x nil ,reorient-y t))
('^> (setq ,transpose t ,reorient-x nil ,reorient-y nil))
('^< (setq ,transpose t ,reorient-x t ,reorient-y nil))
('v< (setq ,transpose t ,reorient-x t ,reorient-y t))
((or '* '*2 '*1) (setq ,vectorize ,x))
('! (setq ,clean #'ses--clean-!))
('_ (setq ,clean `(lambda (&rest x)
(ses--clean-_ x ,(if ,rest-arg (pop ,rest-arg) 0)))))
(_
(cond
; shorthands one row
((and (null (cdar ,result)) (memq x '(> <)))
(push (intern (concat (symbol-name x) "v")) rest))
((and (null (cdar ,result)) (memq ,x '(> <)))
(push (intern (concat (symbol-name ,x) "v")) ,rest-arg))
; shorthands one col
((and (null (cdar result)) (memq x '(v ^)))
(push (intern (concat (symbol-name x) ">")) rest))
(t (error "Unexpected flag `%S' in ses-range" x)))))))
(if reorient-y
(setcdr (last result 2) nil)
(setq result (cdr (nreverse result))))
(unless reorient-x
(setq result (mapcar #'nreverse result)))
(when transpose
(let ((ret (mapcar #'list (pop result))) iter)
(while result
(setq iter ret)
(dolist (elt (pop result))
(setcar iter (cons elt (car iter)))
(setq iter (cdr iter))))
(setq result ret)))
((and (null (cdar ,result)) (memq ,x '(v ^)))
(push (intern (concat (symbol-name ,x) ">")) ,rest-arg))
(t (error "Unexpected flag `%S' in ses-range" ,x)))))))
(if ,reorient-y
(setcdr (last ,result 2) nil)
(setq ,result (cdr (nreverse ,result))))
(unless ,reorient-x
(setq ,result (mapcar #'nreverse ,result)))
(when ,transpose
(let ((,ret (mapcar #'list (pop ,result))) ,iter)
(while ,result
(setq ,iter ,ret)
(dolist (,elt (pop ,result))
(setcar ,iter (cons ,elt (car ,iter)))
(setq ,iter (cdr ,iter))))
(setq ,result ,ret)))
(cl-flet ((vectorize-*1
(clean result)
(cons clean (cons (quote 'vec) (apply #'append result))))
(vectorize-*2
(clean result)
(cons clean (cons (quote 'vec)
(mapcar (lambda (x)
(cons clean (cons (quote 'vec) x)))
result)))))
(pcase vectorize
('nil (cons clean (apply #'append result)))
('*1 (vectorize-*1 clean result))
('*2 (vectorize-*2 clean result))
('* (funcall (if (cdr result)
#'vectorize-*2
#'vectorize-*1)
clean result))))))
(cl-flet ((vectorize-*1
(clean result)
(apply clean (cons 'vec (apply #'append result))))
(vectorize-*2
(clean result)
(apply clean (cons 'vec
(mapcar (lambda (x)
(apply clean (cons 'vec x)))
result)))))
(pcase ,vectorize
('nil (apply ,clean (apply #'append ,result)))
('*1 (vectorize-*1 ,clean ,result))
('*2 (vectorize-*2 ,clean ,result))
('* (funcall (if (cdr ,result)
#'vectorize-*2
#'vectorize-*1)
,clean ,result)))))))
(defun ses-delete-blanks (&rest args)
"Return ARGS reversed, with the blank elements (nil and *skip*) removed."

View file

@ -109,7 +109,7 @@ to (1+ ses--foo), makes A2 value equal to 2."
(ert-deftest ses-tests-renamed-cell-after-setting ()
"Check that setting A1 to 1 and A2 to (1+ A1), and then
renaming A1 to `ses--foo' makes `ses--foo' value equal to 2."
renaming A2 to `ses--foo' makes `ses--foo' value equal to 2."
(let ((ses-initial-size '(2 . 1)))
(with-temp-buffer
(ses-mode)
@ -241,6 +241,51 @@ to `ses--bar' and inserting a row, makes A2 value empty, and `ses--bar' equal to
(ses-command-hook)
(should (eq (ses--cell-at-pos (point)) 'ses--toto)))))
(ert-deftest ses-expand-range ()
"Test `ses-range' expansion works well even if the current buffer is not a SES buffer during expansion."
(let ((ses-initial-size '(4 . 3))
ses-after-entry-functions
(ses-buffer (generate-new-buffer "*SES tests*")))
(unwind-protect
(progn
(with-current-buffer ses-buffer
(ses-mode)
(dolist (c '([0 0 1] [1 0 2] [2 0 3]))
(ses-set-cell (aref c 0) (aref c 1) 'value (aref c 2)))
(dolist (c '((0 1 (* 2 A1)) (1 1 (* 2 A2)) (2 1 (* 2 A3))))
(apply 'ses-cell-set-formula c)
(apply 'ses-calculate-cell (list (car c) (cadr c) nil))))
(should (equal (with-current-buffer ses-buffer (ses-range A1 A3 v))
'(1 2 3)))
(should (equal (with-current-buffer ses-buffer (ses-range A1 A3 ^))
'(3 2 1)))
(should (equal (with-current-buffer ses-buffer (ses-range A1 B3 >v))
'(1 2 2 4 3 6)))
(should (equal (with-current-buffer ses-buffer (ses-range A1 B3 >^))
'(3 6 2 4 1 2)))
(should (equal (with-current-buffer ses-buffer (ses-range A1 B3 <v))
'(2 1 4 2 6 3)))
(should (equal (with-current-buffer ses-buffer (ses-range A1 B3 <^))
'(6 3 4 2 2 1)))
(should (equal (with-current-buffer ses-buffer (ses-range A1 A3 v !))
'(1 2 3)))
(should (equal (with-current-buffer ses-buffer (ses-range A1 B4 >v !))
'(1 2 2 4 3 6)))
(should (equal (with-current-buffer ses-buffer (ses-range A1 A4 v _ 10))
'(1 2 3 10)))
(should (equal (with-current-buffer ses-buffer (ses-range A1 B4 >v _ 10))
'(1 2 2 4 3 6 10 10)))
(should (equal (with-current-buffer ses-buffer (ses-range A1 A3 v *))
'(vec 1 2 3)))
(should (equal (with-current-buffer ses-buffer (ses-range A1 A3 v *1))
'(vec 1 2 3)))
(should (equal (with-current-buffer ses-buffer (ses-range A1 A3 v *2))
'(vec (vec 1 2 3))))
(should (equal (with-current-buffer ses-buffer (ses-range A1 A3 > *2))
'(vec (vec 1) (vec 2) (vec 3)))))
(kill-buffer ses-buffer))))
(ert-deftest ses-set-formula-write-cells-with-changed-references ()
"Test fix of bug#5852.
When setting a formula has some cell with changed references, this