aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias EngdegÄrd2021-03-01 20:52:39 +0100
committerMattias EngdegÄrd2021-03-01 20:59:52 +0100
commit08b11a02f49da5ca0e4e58a32fa853df0c5e0214 (patch)
tree2fa214afcabb53abdcb6eee5c7221a45c0358370
parent5f319423c8fdd06b90b076ff6001705884c51f70 (diff)
downloademacs-08b11a02f49da5ca0e4e58a32fa853df0c5e0214.tar.gz
emacs-08b11a02f49da5ca0e4e58a32fa853df0c5e0214.zip
Fix multiple Calc defmath errors (bug#46750)
Fix incorrect variable scoping in `let*`, `for` and `foreach`. Fix loop variable value in `foreach` (should be element, not tail). Fix function quoting, as in ('cons x y) -- didn't work at all. Reported by Stephan Neuhaus. * lisp/calc/calc-prog.el (math-define-exp, math-handle-foreach): * test/lisp/calc/calc-tests.el: (var-g, test1, test2, test3, test4) (test5, test6, test7, calc-defmath): Test various defmath forms.
-rw-r--r--lisp/calc/calc-prog.el65
-rw-r--r--test/lisp/calc/calc-tests.el76
2 files changed, 114 insertions, 27 deletions
diff --git a/lisp/calc/calc-prog.el b/lisp/calc/calc-prog.el
index 3097b09b013..dd221457f83 100644
--- a/lisp/calc/calc-prog.el
+++ b/lisp/calc/calc-prog.el
@@ -1985,22 +1985,37 @@ Redefine the corresponding command."
1985 (cons 'quote 1985 (cons 'quote
1986 (math-define-lambda (nth 1 exp) math-exp-env)) 1986 (math-define-lambda (nth 1 exp) math-exp-env))
1987 exp)) 1987 exp))
1988 ((memq func '(let let* for foreach)) 1988 ((eq func 'let)
1989 (let ((head (nth 1 exp)) 1989 (let ((bindings (nth 1 exp))
1990 (body (cdr (cdr exp)))) 1990 (body (cddr exp)))
1991 (if (memq func '(let let*)) 1991 `(let ,(math-define-let bindings)
1992 () 1992 ,@(math-define-body
1993 (setq func (cdr (assq func '((for . math-for) 1993 body (append (math-define-let-env bindings)
1994 (foreach . math-foreach))))) 1994 math-exp-env)))))
1995 (if (not (listp (car head))) 1995 ((eq func 'let*)
1996 (setq head (list head)))) 1996 ;; Rewrite in terms of `let'.
1997 (macroexpand 1997 (let ((bindings (nth 1 exp))
1998 (cons func 1998 (body (cddr exp)))
1999 (cons (math-define-let head) 1999 (math-define-exp
2000 (math-define-body body 2000 (if (> (length bindings) 1)
2001 (nconc 2001 `(let ,(list (car bindings))
2002 (math-define-let-env head) 2002 (let* ,(cdr bindings) ,@body))
2003 math-exp-env))))))) 2003 `(let ,bindings ,@body)))))
2004 ((memq func '(for foreach))
2005 (let ((bindings (nth 1 exp))
2006 (body (cddr exp)))
2007 (if (> (length bindings) 1)
2008 ;; Rewrite as nested loops.
2009 (math-define-exp
2010 `(,func ,(list (car bindings))
2011 (,func ,(cdr bindings) ,@body)))
2012 (let ((mac (cdr (assq func '((for . math-for)
2013 (foreach . math-foreach))))))
2014 (macroexpand
2015 `(,mac ,(math-define-let bindings)
2016 ,@(math-define-body
2017 body (append (math-define-let-env bindings)
2018 math-exp-env))))))))
2004 ((and (memq func '(setq setf)) 2019 ((and (memq func '(setq setf))
2005 (math-complicated-lhs (cdr exp))) 2020 (math-complicated-lhs (cdr exp)))
2006 (if (> (length exp) 3) 2021 (if (> (length exp) 3)
@@ -2017,7 +2032,7 @@ Redefine the corresponding command."
2017 (math-define-cond (cdr exp)))) 2032 (math-define-cond (cdr exp))))
2018 ((and (consp func) ; ('spam a b) == force use of plain spam 2033 ((and (consp func) ; ('spam a b) == force use of plain spam
2019 (eq (car func) 'quote)) 2034 (eq (car func) 'quote))
2020 (cons func (math-define-list (cdr exp)))) 2035 (cons (cadr func) (math-define-list (cdr exp))))
2021 ((symbolp func) 2036 ((symbolp func)
2022 (let ((args (math-define-list (cdr exp))) 2037 (let ((args (math-define-list (cdr exp)))
2023 (prim (assq func math-prim-funcs))) 2038 (prim (assq func math-prim-funcs)))
@@ -2276,20 +2291,16 @@ Redefine the corresponding command."
2276 2291
2277(defun math-handle-foreach (head body) 2292(defun math-handle-foreach (head body)
2278 (let ((var (nth 0 (car head))) 2293 (let ((var (nth 0 (car head)))
2294 (loop-var (gensym "foreach"))
2279 (data (nth 1 (car head))) 2295 (data (nth 1 (car head)))
2280 (body (if (cdr head) 2296 (body (if (cdr head)
2281 (list (math-handle-foreach (cdr head) body)) 2297 (list (math-handle-foreach (cdr head) body))
2282 body))) 2298 body)))
2283 (cons 'let 2299 `(let ((,loop-var ,data))
2284 (cons (list (list var data)) 2300 (while ,loop-var
2285 (list 2301 (let ((,var (car ,loop-var)))
2286 (cons 'while 2302 ,@(append body
2287 (cons var 2303 `((setq ,loop-var (cdr ,loop-var)))))))))
2288 (append body
2289 (list (list 'setq
2290 var
2291 (list 'cdr var)))))))))))
2292
2293 2304
2294(defun math-body-refers-to (body thing) 2305(defun math-body-refers-to (body thing)
2295 (or (equal body thing) 2306 (or (equal body thing)
diff --git a/test/lisp/calc/calc-tests.el b/test/lisp/calc/calc-tests.el
index bdcf78e020a..c5aa5a31eb2 100644
--- a/test/lisp/calc/calc-tests.el
+++ b/test/lisp/calc/calc-tests.el
@@ -707,6 +707,82 @@ An existing calc stack is reused, otherwise a new one is created."
707 (var c var-c)))))) 707 (var c var-c))))))
708 (calc-set-language nil))) 708 (calc-set-language nil)))
709 709
710(defvar var-g)
711
712;; Test `let'.
713(defmath test1 (x)
714 (let ((x (+ x 1))
715 (y (+ x 3)))
716 (let ((z (+ y 6)))
717 (* x y z g))))
718
719;; Test `let*'.
720(defmath test2 (x)
721 (let* ((y (+ x 1))
722 (z (+ y 3)))
723 (let* ((u (+ z 6)))
724 (* x y z u g))))
725
726;; Test `for'.
727(defmath test3 (x)
728 (let ((s 0))
729 (for ((ii 1 x)
730 (jj 1 ii))
731 (setq s (+ s (* ii jj))))
732 s))
733
734;; Test `for' with non-unit stride.
735(defmath test4 (x)
736 (let ((l nil))
737 (for ((ii 1 x 1)
738 (jj 1 10 ii))
739 (setq l ('cons jj l))) ; Use Lisp `cons', not `calcFunc-cons'.
740 (reverse l)))
741
742;; Test `foreach'.
743(defmath test5 (x)
744 (let ((s 0))
745 (foreach ((a x)
746 (b a))
747 (setq s (+ s b)))
748 s))
749
750;; Test `break'.
751(defmath test6 (x)
752 (let ((a (for ((ii 1 10))
753 (when (= ii x)
754 (break (* ii 2)))))
755 (b (foreach ((e '(9 3 6)))
756 (when (= e x)
757 (break (- e 1))))))
758 (* a b)))
759
760;; Test `return' from `for'.
761(defmath test7 (x)
762 (for ((ii 1 10))
763 (when (= ii x)
764 (return (* ii 2))))
765 5)
766
767(ert-deftest calc-defmath ()
768 (let ((var-g 17))
769 (should (equal (calcFunc-test1 2) (* 3 5 11 17)))
770 (should (equal (calcFunc-test2 2) (* 2 3 6 12 17))))
771 (should (equal (calcFunc-test3 3)
772 (+ (* 1 1)
773 (* 2 1) (* 2 2)
774 (* 3 1) (* 3 2) (* 3 3))))
775 (should (equal (calcFunc-test4 5)
776 '( 1 2 3 4 5 6 7 8 9 10
777 1 3 5 7 9
778 1 4 7 10
779 1 5 9
780 1 6)))
781 (should (equal (calcFunc-test5 '((2 3) (5) (7 11 13)))
782 (+ 2 3 5 7 11 13)))
783 (should (equal (calcFunc-test6 3) (* (* 3 2) (- 3 1))))
784 (should (equal (calcFunc-test7 3) (* 3 2))))
785
710(provide 'calc-tests) 786(provide 'calc-tests)
711;;; calc-tests.el ends here 787;;; calc-tests.el ends here
712 788