diff options
| author | Mattias EngdegÄrd | 2021-03-01 20:52:39 +0100 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2021-03-01 20:59:52 +0100 |
| commit | 08b11a02f49da5ca0e4e58a32fa853df0c5e0214 (patch) | |
| tree | 2fa214afcabb53abdcb6eee5c7221a45c0358370 | |
| parent | 5f319423c8fdd06b90b076ff6001705884c51f70 (diff) | |
| download | emacs-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.el | 65 | ||||
| -rw-r--r-- | test/lisp/calc/calc-tests.el | 76 |
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 | ||