aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEli Barzilay2014-06-15 00:52:34 -0400
committerEli Barzilay2014-06-15 00:52:34 -0400
commit5335a8ced5a44befa20b759b73c900856defa0d7 (patch)
tree4e306f61e435b5ca97b80da6971076598d1d33b9
parentdf5703a00d610a89fa6bc1da906228907b36b5d8 (diff)
downloademacs-5335a8ced5a44befa20b759b73c900856defa0d7.tar.gz
emacs-5335a8ced5a44befa20b759b73c900856defa0d7.zip
* lisp/calculator.el: Lots of revisions
- Kill the calculator buffer after electric mode too. - Make decimal mode have "," groups, so it's more fitting for use in money calculations. - Factorial works with non-integer inputs. - Swallow less errors. - Lots of other improvements, but no changes to custom variables, or other user visible changes (except the above).
-rw-r--r--lisp/calculator.el996
1 files changed, 423 insertions, 573 deletions
diff --git a/lisp/calculator.el b/lisp/calculator.el
index d6eb892f7fb..52dc8c53661 100644
--- a/lisp/calculator.el
+++ b/lisp/calculator.el
@@ -1,4 +1,4 @@
1;;; calculator.el --- a [not so] simple calculator for Emacs -*- lexical-binding: t -*- 1;;; calculator.el --- a calculator for Emacs -*- lexical-binding: t -*-
2 2
3;; Copyright (C) 1998, 2000-2014 Free Software Foundation, Inc. 3;; Copyright (C) 1998, 2000-2014 Free Software Foundation, Inc.
4 4
@@ -33,15 +33,8 @@
33;; "Run the Emacs calculator." t) 33;; "Run the Emacs calculator." t)
34;; (global-set-key [(control return)] 'calculator) 34;; (global-set-key [(control return)] 'calculator)
35;; 35;;
36;; Written by Eli Barzilay: Maze is Life! eli@barzilay.org 36;; Written by Eli Barzilay, eli@barzilay.org
37;; http://www.barzilay.org/
38;; 37;;
39;; For latest version, check
40;; http://www.barzilay.org/misc/calculator.el
41;;
42
43;;; History:
44;; I hate history.
45 38
46;;;===================================================================== 39;;;=====================================================================
47;;; Customization: 40;;; Customization:
@@ -79,7 +72,7 @@ This determines the default behavior of unary operators."
79 72
80(defcustom calculator-prompt "Calc=%s> " 73(defcustom calculator-prompt "Calc=%s> "
81 "The prompt used by the Emacs calculator. 74 "The prompt used by the Emacs calculator.
82It should contain a \"%s\" somewhere that will indicate the i/o radices; 75It should contain a \"%s\" somewhere that will indicate the i/o radixes;
83this will be a two-character string as described in the documentation 76this will be a two-character string as described in the documentation
84for `calculator-mode'." 77for `calculator-mode'."
85 :type 'string 78 :type 'string
@@ -115,8 +108,8 @@ See `calculator-radix-grouping-mode'."
115 108
116(defcustom calculator-remove-zeros t 109(defcustom calculator-remove-zeros t
117 "Non-nil value means delete all redundant zero decimal digits. 110 "Non-nil value means delete all redundant zero decimal digits.
118If this value is not t, and not nil, redundant zeros are removed except 111If this value is not t and not nil, redundant zeros are removed except
119for one and if it is nil, nothing is removed. 112for one.
120Used by the `calculator-remove-zeros' function." 113Used by the `calculator-remove-zeros' function."
121 :type '(choice (const t) (const leave-decimal) (const nil)) 114 :type '(choice (const t) (const leave-decimal) (const nil))
122 :group 'calculator) 115 :group 'calculator)
@@ -136,23 +129,27 @@ should be able to handle special symbol arguments, currently `left' and
136associated with the displayer function (for example to change the number 129associated with the displayer function (for example to change the number
137of digits displayed). 130of digits displayed).
138 131
139An exception to the above is the case of the list (std C) where C is a 132An exception to the above is the case of the list (std C [G]) where C is
140character, in this case the `calculator-standard-displayer' function 133a character and G is an optional boolean, in this case the
141will be used with this character for a format string." 134`calculator-standard-displayer' function will be used with these as
142 :type '(choice (function) (string) (list (const std) character) (sexp)) 135arguments."
136 :type '(choice (function) (string) (sexp)
137 (list (const std) character)
138 (list (const std) character boolean))
143 :group 'calculator) 139 :group 'calculator)
144 140
145(defcustom calculator-displayers 141(defcustom calculator-displayers
146 '(((std ?n) "Standard display, decimal point or scientific") 142 '(((std ?n) "Standard display, decimal point or scientific")
147 (calculator-eng-display "Eng display") 143 (calculator-eng-display "Eng display")
148 ((std ?f) "Standard display, decimal point") 144 ((std ?f t) "Standard display, decimal point with grouping")
149 ((std ?e) "Standard display, scientific") 145 ((std ?e) "Standard display, scientific")
150 ("%S" "Emacs printer")) 146 ("%S" "Emacs printer"))
151 "A list of displayers. 147 "A list of displayers.
152Each element is a list of a displayer and a description string. The 148Each element is a list of a displayer and a description string. The
153first element is the one which is currently used, this is for the display 149first element is the one which is currently used, this is for the
154of result values not values in expressions. A displayer specification 150display of result values not values in expressions. A displayer
155is the same as the values that can be stored in `calculator-displayer'. 151specification is the same as the values that can be stored in
152`calculator-displayer'.
156 153
157`calculator-rotate-displayer' rotates this list." 154`calculator-rotate-displayer' rotates this list."
158 :type 'sexp 155 :type 'sexp
@@ -182,7 +179,7 @@ Otherwise show as a negative number."
182(defcustom calculator-mode-hook nil 179(defcustom calculator-mode-hook nil
183 "List of hook functions for `calculator-mode' to run. 180 "List of hook functions for `calculator-mode' to run.
184Note: if `calculator-electric-mode' is on, then this hook will get 181Note: if `calculator-electric-mode' is on, then this hook will get
185activated in the minibuffer - in that case it should not do much more 182activated in the minibuffer -- in that case it should not do much more
186than local key settings and other effects that will change things 183than local key settings and other effects that will change things
187outside the scope of calculator related code." 184outside the scope of calculator related code."
188 :type 'hook 185 :type 'hook
@@ -224,15 +221,14 @@ Examples:
224 (\"tF\" mt-to-ft (/ X 0.3048) 1) 221 (\"tF\" mt-to-ft (/ X 0.3048) 1)
225 (\"tM\" ft-to-mt (* X 0.3048) 1))) 222 (\"tM\" ft-to-mt (* X 0.3048) 1)))
226 223
227* Using a function-like form is very simple, X for an argument (Y the 224* Using a function-like form is very simple: use `X' for the argument
228 second in case of a binary operator), TX is a truncated version of X 225 (`Y' for the second in case of a binary operator), `TX' is a truncated
229 and F does a recursive call, Here is a [very inefficient] Fibonacci 226 version of `X' and `F' for a recursive call. Here is a [very
230 number calculation: 227 inefficient] Fibonacci number calculation:
231 228
232 (add-to-list 'calculator-user-operators 229 (add-to-list 'calculator-user-operators
233 '(\"F\" fib (if (<= TX 1) 230 '(\"F\" fib
234 1 231 (if (<= TX 1) 1 (+ (F (- TX 1)) (F (- TX 2))))))
235 (+ (F (- TX 1)) (F (- TX 2)))) 0))
236 232
237 Note that this will be either postfix or prefix, according to 233 Note that this will be either postfix or prefix, according to
238 `calculator-unary-style'." 234 `calculator-unary-style'."
@@ -248,7 +244,7 @@ Examples:
248;;; Variables 244;;; Variables
249 245
250(defvar calculator-initial-operators 246(defvar calculator-initial-operators
251 '(;; "+"/"-" have keybindings of themselves, not calculator-ops 247 '(;; "+"/"-" have keybindings of their own, not calculator-ops
252 ("=" = identity 1 -1) 248 ("=" = identity 1 -1)
253 (nobind "+" + + 2 4) 249 (nobind "+" + + 2 4)
254 (nobind "-" - - 2 4) 250 (nobind "-" - - 2 4)
@@ -303,26 +299,27 @@ user-defined operators, use `calculator-user-operators' instead.")
303 versions), `DX' (converted to radians if degrees mode is on), `D' 299 versions), `DX' (converted to radians if degrees mode is on), `D'
304 (function for converting radians to degrees if deg mode is on), `L' 300 (function for converting radians to degrees if deg mode is on), `L'
305 (list of saved values), `F' (function for recursive iteration calls) 301 (list of saved values), `F' (function for recursive iteration calls)
306 and evaluates to the function value - these variables are capital; 302 and evaluates to the function value -- these variables are capital;
307 303
3084. The function's arity, optional, one of: 2 => binary, -1 => prefix 3044. The function's arity, optional, one of: 2 => binary, -1 => prefix
309 unary, +1 => postfix unary, 0 => a 0-arg operator func, non-number => 305 unary, +1 => postfix unary, 0 => a 0-arg operator func (note that
310 postfix/prefix as determined by `calculator-unary-style' (the 306 using such a function replaces the currently entered number, if any),
311 default); 307 non-number (the default) => postfix or prefix as determined by
308 `calculator-unary-style';
312 309
3135. The function's precedence - should be in the range of 1 (lowest) to 3105. The function's precedence -- should be in the range of 1 (lowest) to
314 9 (highest) (optional, defaults to 1); 311 9 (highest) (optional, defaults to 1);
315 312
316It it possible have a unary prefix version of a binary operator if it 313It it possible have a unary prefix version of a binary operator if it
317comes later in this list. If the list begins with the symbol 'nobind, 314comes later in this list. If the list begins with the symbol 'nobind,
318then no key binding will take place - this is only useful for predefined 315then no key binding will take place -- this is only useful for predefined
319keys. 316keys.
320 317
321Use `calculator-user-operators' to add operators to this list, see its 318Use `calculator-user-operators' to add operators to this list, see its
322documentation for an example.") 319documentation for an example.")
323 320
324(defvar calculator-stack nil 321(defvar calculator-stack nil
325 "Stack contents - operations and operands.") 322 "Stack contents -- operations and operands.")
326 323
327(defvar calculator-curnum nil 324(defvar calculator-curnum nil
328 "Current number being entered (as a string).") 325 "Current number being entered (as a string).")
@@ -427,9 +424,9 @@ Used for repeating operations in calculator-repR/L.")
427 (calculator-backspace [backspace]) 424 (calculator-backspace [backspace])
428 ))) 425 )))
429 (while p 426 (while p
430 ;; reverse the keys so first defs come last - makes the more 427 ;; reverse the keys so earlier definitions come last -- makes
431 ;; sensible bindings visible in the menu 428 ;; the more sensible bindings visible in the menu
432 (let ((func (car (car p))) (keys (reverse (cdr (car p))))) 429 (let ((func (caar p)) (keys (reverse (cdar p))))
433 (while keys 430 (while keys
434 (define-key map (car keys) func) 431 (define-key map (car keys) func)
435 (setq keys (cdr keys)))) 432 (setq keys (cdr keys))))
@@ -441,7 +438,7 @@ Used for repeating operations in calculator-repR/L.")
441 ;; make C-h work in text-mode 438 ;; make C-h work in text-mode
442 (or window-system (define-key map [?\C-h] 'calculator-backspace)) 439 (or window-system (define-key map [?\C-h] 'calculator-backspace))
443 ;; set up a menu 440 ;; set up a menu
444 (if (and calculator-use-menu (not (boundp 'calculator-menu))) 441 (when (and calculator-use-menu (not (boundp 'calculator-menu)))
445 (let ((radix-selectors 442 (let ((radix-selectors
446 (mapcar (lambda (x) 443 (mapcar (lambda (x)
447 `([,(nth 0 x) 444 `([,(nth 0 x)
@@ -580,7 +577,7 @@ Used for repeating operations in calculator-repR/L.")
580 "A [not so] simple calculator for Emacs. 577 "A [not so] simple calculator for Emacs.
581 578
582This calculator is used in the same way as other popular calculators 579This calculator is used in the same way as other popular calculators
583like xcalc or calc.exe - but using an Emacs interface. 580like xcalc or calc.exe -- but using an Emacs interface.
584 581
585Expressions are entered using normal infix notation, parens are used as 582Expressions are entered using normal infix notation, parens are used as
586normal. Unary functions are usually postfix, but some depends on the 583normal. Unary functions are usually postfix, but some depends on the
@@ -589,8 +586,7 @@ specified, then it is fixed, otherwise it depends on this variable).
589`+' and `-' can be used as either binary operators or prefix unary 586`+' and `-' can be used as either binary operators or prefix unary
590operators. Numbers can be entered with exponential notation using `e', 587operators. Numbers can be entered with exponential notation using `e',
591except when using a non-decimal radix mode for input (in this case `e' 588except when using a non-decimal radix mode for input (in this case `e'
592will be the hexadecimal digit). If the result of a calculation is too 589will be the hexadecimal digit).
593large (out of range for Emacs), the value of \"inf\" is returned.
594 590
595Here are the editing keys: 591Here are the editing keys:
596* `RET' `=' evaluate the current expression 592* `RET' `=' evaluate the current expression
@@ -609,8 +605,8 @@ These operators are pre-defined:
609* `_' `;' postfix unary negation and reciprocal 605* `_' `;' postfix unary negation and reciprocal
610* `^' `L' binary operators for x^y and log(x) in base y 606* `^' `L' binary operators for x^y and log(x) in base y
611* `Q' `!' unary square root and factorial 607* `Q' `!' unary square root and factorial
612* `S' `C' `T' unary trigonometric operators - sin, cos and tan 608* `S' `C' `T' unary trigonometric operators: sin, cos and tan
613* `|' `#' `&' `~' bitwise operators - or, xor, and, not 609* `|' `#' `&' `~' bitwise operators: or, xor, and, not
614 610
615The trigonometric functions can be inverted if prefixed with an `I', see 611The trigonometric functions can be inverted if prefixed with an `I', see
616below for the way to use degrees instead of the default radians. 612below for the way to use degrees instead of the default radians.
@@ -636,9 +632,9 @@ The prompt indicates the current modes:
636 632
637Also, the quote key can be used to switch display modes for decimal 633Also, the quote key can be used to switch display modes for decimal
638numbers (double-quote rotates back), and the two brace characters 634numbers (double-quote rotates back), and the two brace characters
639\(\"{\" and \"}\" change display parameters that these displayers use (if 635\(\"{\" and \"}\" change display parameters that these displayers use,
640they handle such). If output is using any radix mode, then these keys 636if they handle such). If output is using any radix mode, then these
641toggle digit grouping mode and the chunk size. 637keys toggle digit grouping mode and the chunk size.
642 638
643Values can be saved for future reference in either a list of saved 639Values can be saved for future reference in either a list of saved
644values, or in registers. 640values, or in registers.
@@ -680,19 +676,21 @@ more information.
680 "Run the Emacs calculator. 676 "Run the Emacs calculator.
681See the documentation for `calculator-mode' for more information." 677See the documentation for `calculator-mode' for more information."
682 (interactive) 678 (interactive)
683 (if calculator-restart-other-mode 679 (when calculator-restart-other-mode
684 (setq calculator-electric-mode (not calculator-electric-mode))) 680 (setq calculator-electric-mode (not calculator-electric-mode)))
685 (if calculator-initial-operators 681 (when calculator-initial-operators
686 (progn (calculator-add-operators calculator-initial-operators) 682 (calculator-add-operators calculator-initial-operators)
687 (setq calculator-initial-operators nil) 683 (setq calculator-initial-operators nil)
688 ;; don't change this since it is a customization variable, 684 ;; don't change this since it is a customization variable,
689 ;; its set function will add any new operators 685 ;; its set function will add any new operators
690 (calculator-add-operators calculator-user-operators))) 686 (calculator-add-operators calculator-user-operators))
691 (setq calculator-buffer (get-buffer-create "*calculator*")) 687 (setq calculator-buffer (get-buffer-create "*calculator*"))
692 (if calculator-electric-mode 688 (if calculator-electric-mode
693 (save-window-excursion 689 (save-window-excursion
694 (progn (require 'electric) (message nil)) ; hide load message 690 (require 'electric) (message nil) ; hide load message
695 (let (old-g-map old-l-map (echo-keystrokes 0) 691 (let (old-g-map old-l-map
692 (old-buf (window-buffer (minibuffer-window)))
693 (echo-keystrokes 0)
696 (garbage-collection-messages nil)) ; no gc msg when electric 694 (garbage-collection-messages nil)) ; no gc msg when electric
697 (set-window-buffer (minibuffer-window) calculator-buffer) 695 (set-window-buffer (minibuffer-window) calculator-buffer)
698 (select-window (minibuffer-window)) 696 (select-window (minibuffer-window))
@@ -712,8 +710,8 @@ See the documentation for `calculator-mode' for more information."
712 (lambda () 'noprompt) 710 (lambda () 'noprompt)
713 nil 711 nil
714 (lambda (_x _y) (calculator-update-display)))) 712 (lambda (_x _y) (calculator-update-display))))
715 (and calculator-buffer 713 (set-window-buffer (minibuffer-window) old-buf)
716 (catch 'calculator-done (calculator-quit))) 714 (kill-buffer calculator-buffer)
717 (use-local-map old-l-map) 715 (use-local-map old-l-map)
718 (use-global-map old-g-map)))) 716 (use-global-map old-g-map))))
719 (progn 717 (progn
@@ -722,45 +720,8 @@ See the documentation for `calculator-mode' for more information."
722 (let ((window-min-height 2)) 720 (let ((window-min-height 2))
723 ;; maybe leave two lines for our window because of the 721 ;; maybe leave two lines for our window because of the
724 ;; normal `raised' mode line 722 ;; normal `raised' mode line
725 (select-window 723 (select-window (split-window-below
726 (split-window-below 724 (if (calculator-need-3-lines) -3 -2)))
727 ;; If the mode line might interfere with the calculator
728 ;; buffer, use 3 lines instead.
729 (if (and (fboundp 'face-attr-construct)
730 (let* ((dh (plist-get (face-attr-construct 'default) :height))
731 (mf (face-attr-construct 'mode-line))
732 (mh (plist-get mf :height)))
733 ;; If the mode line is shorter than the default,
734 ;; stick with 2 lines. (It may be necessary to
735 ;; check how much shorter.)
736 (and
737 (not
738 (or (and (integerp dh)
739 (integerp mh)
740 (< mh dh))
741 (and (numberp mh)
742 (not (integerp mh))
743 (< mh 1))))
744 (or
745 ;; If the mode line is taller than the default,
746 ;; use 3 lines.
747 (and (integerp dh)
748 (integerp mh)
749 (> mh dh))
750 (and (numberp mh)
751 (not (integerp mh))
752 (> mh 1))
753 ;; If the mode line has a box with non-negative line-width,
754 ;; use 3 lines.
755 (let* ((bx (plist-get mf :box))
756 (lh (plist-get bx :line-width)))
757 (and bx
758 (or
759 (not lh)
760 (> lh 0))))
761 ;; If the mode line has an overline, use 3 lines.
762 (plist-get (face-attr-construct 'mode-line) :overline)))))
763 -3 -2)))
764 (switch-to-buffer calculator-buffer))) 725 (switch-to-buffer calculator-buffer)))
765 ((not (eq (current-buffer) calculator-buffer)) 726 ((not (eq (current-buffer) calculator-buffer))
766 (select-window (get-buffer-window calculator-buffer)))) 727 (select-window (get-buffer-window calculator-buffer))))
@@ -768,24 +729,46 @@ See the documentation for `calculator-mode' for more information."
768 (setq buffer-read-only t) 729 (setq buffer-read-only t)
769 (calculator-reset) 730 (calculator-reset)
770 (message "Hit `?' For a quick help screen."))) 731 (message "Hit `?' For a quick help screen.")))
771 (if (and calculator-restart-other-mode calculator-electric-mode) 732 (when (and calculator-restart-other-mode calculator-electric-mode)
772 (calculator))) 733 (calculator)))
773 734
735(defun calculator-need-3-lines ()
736 ;; If the mode line might interfere with the calculator buffer, use 3
737 ;; lines instead.
738 (let* ((dh (face-attribute 'default :height))
739 (mh (face-attribute 'mode-line :height)))
740 ;; if the mode line is shorter than the default, stick with 2 lines
741 ;; (it may be necessary to check how much shorter)
742 (and (not (or (and (integerp dh) (integerp mh) (< mh dh))
743 (and (numberp mh) (not (integerp mh)) (< mh 1))))
744 (or ;; if the mode line is taller than the default, use 3 lines
745 (and (integerp dh) (integerp mh) (> mh dh))
746 (and (numberp mh) (not (integerp mh)) (> mh 1))
747 ;; if the mode line has a box with non-negative line-width,
748 ;; use 3 lines
749 (let* ((bx (face-attribute 'mode-line :box))
750 (lh (plist-get bx :line-width)))
751 (and bx (or (not lh) (> lh 0))))
752 ;; if the mode line has an overline, use 3 lines
753 (not (memq (face-attribute 'mode-line :overline)
754 '(nil unspecified)))))))
755
774(defun calculator-message (string &rest arguments) 756(defun calculator-message (string &rest arguments)
775 "Same as `message', but special handle of electric mode." 757 "Same as `message', but also handle electric mode."
776 (apply 'message string arguments) 758 (apply 'message string arguments)
777 (if calculator-electric-mode 759 (when calculator-electric-mode (sit-for 1) (message nil)))
778 (progn (sit-for 1) (message nil))))
779 760
780;;;--------------------------------------------------------------------- 761;;;---------------------------------------------------------------------
781;;; Operators 762;;; Operators
782 763
783(defun calculator-op-arity (op) 764(defun calculator-op-arity (op)
784 "Return OP's arity, 2, +1 or -1." 765 "Return OP's arity.
785 (let ((arity (or (nth 3 op) 'x))) 766Current results are one of 2 (binary), +1 (postfix), -1 (prefix), or
786 (if (numberp arity) 7670 (nullary)."
787 arity 768 (let ((arity (nth 3 op)))
788 (if (eq calculator-unary-style 'postfix) +1 -1)))) 769 (cond ((numberp arity) arity)
770 ((eq calculator-unary-style 'postfix) +1)
771 (t -1))))
789 772
790(defun calculator-op-prec (op) 773(defun calculator-op-prec (op)
791 "Return OP's precedence for reducing when inserting into the stack. 774 "Return OP's precedence for reducing when inserting into the stack.
@@ -798,8 +781,8 @@ Adds MORE-OPS to `calculator-operator', called initially to handle
798`calculator-initial-operators' and `calculator-user-operators'." 781`calculator-initial-operators' and `calculator-user-operators'."
799 (let ((added-ops nil)) 782 (let ((added-ops nil))
800 (while more-ops 783 (while more-ops
801 (or (eq (car (car more-ops)) 'nobind) 784 (or (eq (caar more-ops) 'nobind)
802 (let ((i -1) (key (car (car more-ops)))) 785 (let ((i -1) (key (caar more-ops)))
803 ;; make sure the key is undefined, so it's easy to define 786 ;; make sure the key is undefined, so it's easy to define
804 ;; prefix keys 787 ;; prefix keys
805 (while (< (setq i (1+ i)) (length key)) 788 (while (< (setq i (1+ i)) (length key))
@@ -811,8 +794,8 @@ Adds MORE-OPS to `calculator-operator', called initially to handle
811 calculator-mode-map (substring key 0 (1+ i)) nil) 794 calculator-mode-map (substring key 0 (1+ i)) nil)
812 (setq i (length key))))) 795 (setq i (length key)))))
813 (define-key calculator-mode-map key 'calculator-op))) 796 (define-key calculator-mode-map key 'calculator-op)))
814 (setq added-ops (cons (if (eq (car (car more-ops)) 'nobind) 797 (setq added-ops (cons (if (eq (caar more-ops) 'nobind)
815 (cdr (car more-ops)) 798 (cdar more-ops)
816 (car more-ops)) 799 (car more-ops))
817 added-ops)) 800 added-ops))
818 (setq more-ops (cdr more-ops))) 801 (setq more-ops (cdr more-ops)))
@@ -833,50 +816,37 @@ Adds MORE-OPS to `calculator-operator', called initially to handle
833 (setq calculator-restart-other-mode nil) 816 (setq calculator-restart-other-mode nil)
834 (calculator-update-display)) 817 (calculator-update-display))
835 818
836(defun calculator-get-prompt () 819(defun calculator-get-display ()
837 "Return a string to display. 820 "Return a string to display.
838The string is set not to exceed the screen width." 821The result should not exceed the screen width."
839 (let* ((calculator-prompt 822 (let* ((in-r (and calculator-input-radix
840 (format calculator-prompt 823 (char-to-string
824 (car (rassq calculator-input-radix
825 calculator-char-radix)))))
826 (out-r (and calculator-output-radix
827 (char-to-string
828 (car (rassq calculator-output-radix
829 calculator-char-radix)))))
830 (prompt (format calculator-prompt
831 (cond ((or in-r out-r)
832 (concat (or in-r "=")
833 (if (equal in-r out-r) "="
834 (or out-r "="))))
835 (calculator-deg "D=")
836 (t "=="))))
837 (expr
838 (concat (cdr calculator-stack-display)
841 (cond 839 (cond
842 ((or calculator-output-radix calculator-input-radix) 840 ;; entering a number
843 (if (eq calculator-output-radix 841 (calculator-curnum (concat calculator-curnum "_"))
844 calculator-input-radix) 842 ;; showing a result
845 (concat 843 ((and (= 1 (length calculator-stack))
846 (char-to-string 844 calculator-display-fragile)
847 (car (rassq calculator-output-radix 845 nil)
848 calculator-char-radix))) 846 ;; waiting for a number or an operator
849 "=") 847 (t "?"))))
850 (concat 848 (trim (+ (length expr) (length prompt) 1 (- (window-width)))))
851 (if calculator-input-radix 849 (concat prompt (if (<= trim 0) expr (substring expr trim)))))
852 (char-to-string
853 (car (rassq calculator-input-radix
854 calculator-char-radix)))
855 "=")
856 (char-to-string
857 (car (rassq calculator-output-radix
858 calculator-char-radix))))))
859 (calculator-deg "D=")
860 (t "=="))))
861 (prompt
862 (concat calculator-prompt
863 (cdr calculator-stack-display)
864 (cond (calculator-curnum
865 ;; number being typed
866 (concat calculator-curnum "_"))
867 ((and (= 1 (length calculator-stack))
868 calculator-display-fragile)
869 ;; only the result is shown, next number will
870 ;; restart
871 nil)
872 (t
873 ;; waiting for a number or an operator
874 "?"))))
875 (trim (- (length prompt) (1- (window-width)))))
876 (if (<= trim 0)
877 prompt
878 (concat calculator-prompt
879 (substring prompt (+ trim (length calculator-prompt)))))))
880 850
881(defun calculator-string-to-number (str) 851(defun calculator-string-to-number (str)
882 "Convert the given STR to a number, according to the value of 852 "Convert the given STR to a number, according to the value of
@@ -902,7 +872,7 @@ The string is set not to exceed the screen width."
902 "Warning: Ignoring bad input character `%c'." ch) 872 "Warning: Ignoring bad input character `%c'." ch)
903 (sit-for 1) 873 (sit-for 1)
904 value)))) 874 value))))
905 (if (if (< new-value 0) (> value 0) (< value 0)) 875 (when (if (< new-value 0) (> value 0) (< value 0))
906 (calculator-message "Warning: Overflow in input.")) 876 (calculator-message "Warning: Overflow in input."))
907 (setq value new-value)) 877 (setq value new-value))
908 value) 878 value)
@@ -916,9 +886,12 @@ The string is set not to exceed the screen width."
916 ((stringp str) (concat str ".0")) 886 ((stringp str) (concat str ".0"))
917 (t "0.0")))))) 887 (t "0.0"))))))
918 888
919(defun calculator-curnum-value () 889(defun calculator-push-curnum ()
920 "Get the numeric value of the displayed number string as a float." 890 "Push the numeric value of the displayed number to the stack."
921 (calculator-string-to-number calculator-curnum)) 891 (when calculator-curnum
892 (push (calculator-string-to-number calculator-curnum)
893 calculator-stack)
894 (setq calculator-curnum nil)))
922 895
923(defun calculator-rotate-displayer (&optional new-disp) 896(defun calculator-rotate-displayer (&optional new-disp)
924 "Switch to the next displayer on the `calculator-displayers' list. 897 "Switch to the next displayer on the `calculator-displayers' list.
@@ -956,7 +929,7 @@ If radix output mode is active, toggle digit grouping."
956 (calculator-rotate-displayer (car (last calculator-displayers)))) 929 (calculator-rotate-displayer (car (last calculator-displayers))))
957 930
958(defun calculator-displayer-prev () 931(defun calculator-displayer-prev ()
959 "Send the current displayer function a 'left argument. 932 "Send the current displayer function a `left' argument.
960This is used to modify display arguments (if the current displayer 933This is used to modify display arguments (if the current displayer
961function supports this). 934function supports this).
962If radix output mode is active, increase the grouping size." 935If radix output mode is active, increase the grouping size."
@@ -967,13 +940,12 @@ If radix output mode is active, increase the grouping size."
967 (calculator-enter)) 940 (calculator-enter))
968 (and (car calculator-displayers) 941 (and (car calculator-displayers)
969 (let ((disp (caar calculator-displayers))) 942 (let ((disp (caar calculator-displayers)))
970 (cond 943 (cond ((symbolp disp) (funcall disp 'left))
971 ((symbolp disp) (funcall disp 'left)) 944 ((and (consp disp) (eq 'std (car disp)))
972 ((and (consp disp) (eq 'std (car disp))) 945 (calculator-standard-displayer 'left)))))))
973 (calculator-standard-displayer 'left (cadr disp))))))))
974 946
975(defun calculator-displayer-next () 947(defun calculator-displayer-next ()
976 "Send the current displayer function a 'right argument. 948 "Send the current displayer function a `right' argument.
977This is used to modify display arguments (if the current displayer 949This is used to modify display arguments (if the current displayer
978function supports this). 950function supports this).
979If radix output mode is active, decrease the grouping size." 951If radix output mode is active, decrease the grouping size."
@@ -984,44 +956,51 @@ If radix output mode is active, decrease the grouping size."
984 (calculator-enter)) 956 (calculator-enter))
985 (and (car calculator-displayers) 957 (and (car calculator-displayers)
986 (let ((disp (caar calculator-displayers))) 958 (let ((disp (caar calculator-displayers)))
987 (cond 959 (cond ((symbolp disp) (funcall disp 'right))
988 ((symbolp disp) (funcall disp 'right)) 960 ((and (consp disp) (eq 'std (car disp)))
989 ((and (consp disp) (eq 'std (car disp))) 961 (calculator-standard-displayer 'right)))))))
990 (calculator-standard-displayer 'right (cadr disp))))))))
991 962
992(defun calculator-remove-zeros (numstr) 963(defun calculator-remove-zeros (numstr)
993 "Get a number string NUMSTR and remove unnecessary zeros. 964 "Get a number string NUMSTR and remove unnecessary zeros.
994The behavior of this function is controlled by 965The behavior of this function is controlled by
995`calculator-remove-zeros'." 966`calculator-remove-zeros'."
996 (cond ((and (eq calculator-remove-zeros t) 967 (let* ((s (if (not (eq calculator-remove-zeros t)) numstr
997 (string-match "\\.0+\\([eE][+-]?[0-9]*\\)?$" numstr)) 968 ;; remove all redundant zeros leaving an integer
998 ;; remove all redundant zeros leaving an integer 969 (replace-regexp-in-string
999 (if (match-beginning 1) 970 "\\.0+\\([eE].*\\)?$" "\\1" numstr)))
1000 (concat (substring numstr 0 (match-beginning 0)) 971 (s (if (not calculator-remove-zeros) s
1001 (match-string 1 numstr)) 972 ;; remove zeros, except for first after the "."
1002 (substring numstr 0 (match-beginning 0)))) 973 (replace-regexp-in-string
1003 ((and calculator-remove-zeros 974 "\\(\\..[0-9]*?\\)0+\\([eE].*\\)?$" "\\1\\2" s))))
1004 (string-match 975 s))
1005 "\\..\\([0-9]*[1-9]\\)?\\(0+\\)\\([eE][+-]?[0-9]*\\)?$" 976
1006 numstr)) 977(defun calculator-groupize-number (str n sep &optional fromleft)
1007 ;; remove zeros, except for first after the "." 978 "Return the input string STR with occurrences of SEP that separate
1008 (if (match-beginning 3) 979every N characters starting from the right, or from the left if
1009 (concat (substring numstr 0 (match-beginning 2)) 980FROMLEFT is true."
1010 (match-string 3 numstr)) 981 (let* ((len (length str)) (i (/ len n)) (j (% len n))
1011 (substring numstr 0 (match-beginning 2)))) 982 (r (if (or (not fromleft) (= j 0)) '()
1012 (t numstr))) 983 (list (substring str (- len j))))))
1013 984 (while (> i 0)
1014(defun calculator-standard-displayer (num char) 985 (let* ((e (* i n)) (e (if fromleft e (+ e j))))
986 (push (substring str (- e n) e) r))
987 (setq i (1- i)))
988 (when (and (not fromleft) (> j 0))
989 (push (substring str 0 j) r))
990 (mapconcat 'identity r sep)))
991
992(defun calculator-standard-displayer (num &optional char group-p)
1015 "Standard display function, used to display NUM. 993 "Standard display function, used to display NUM.
1016Its behavior is determined by `calculator-number-digits' and the given 994Its behavior is determined by `calculator-number-digits' and the given
1017CHAR argument (both will be used to compose a format string). If the 995CHAR argument (both will be used to compose a format string). If the
1018char is \"n\" then this function will choose one between %f or %e, this 996char is \"n\" then this function will choose one between %f or %e, this
1019is a work around %g jumping to exponential notation too fast. 997is a work around %g jumping to exponential notation too fast.
1020 998
1021The special 'left and 'right symbols will make it change the current 999It will also split digit sequences into comma-separated groups
1022number of digits displayed (`calculator-number-digits'). 1000and/or remove redundant zeros.
1023 1001
1024It will also remove redundant zeros from the result." 1002The special `left' and `right' symbols will make it change the current
1003number of digits displayed (`calculator-number-digits')."
1025 (if (symbolp num) 1004 (if (symbolp num)
1026 (cond ((eq num 'left) 1005 (cond ((eq num 'left)
1027 (and (> calculator-number-digits 0) 1006 (and (> calculator-number-digits 0)
@@ -1032,56 +1011,50 @@ It will also remove redundant zeros from the result."
1032 (setq calculator-number-digits 1011 (setq calculator-number-digits
1033 (1+ calculator-number-digits)) 1012 (1+ calculator-number-digits))
1034 (calculator-enter))) 1013 (calculator-enter)))
1035 (let ((str (if (zerop num) 1014 (let* ((s (if (eq char ?n)
1036 "0" 1015 (let ((n (abs num)))
1037 (format 1016 (if (or (and (< 0 n) (< n 0.001)) (< 1e8 n)) ?e ?f))
1038 (concat "%." 1017 char))
1039 (number-to-string calculator-number-digits) 1018 (s (format "%%.%s%c" calculator-number-digits s))
1040 (if (eq char ?n) 1019 (s (calculator-remove-zeros (format s num)))
1041 (let ((n (abs num))) 1020 (s (if (or (not group-p) (string-match-p "[eE]" s)) s
1042 (if (or (< n 0.001) (> n 1e8)) "e" "f")) 1021 (replace-regexp-in-string
1043 (string char))) 1022 "\\([0-9]+\\)\\(?:\\.\\|$\\)"
1044 num)))) 1023 (lambda (s) (calculator-groupize-number s 3 ","))
1045 (calculator-remove-zeros str)))) 1024 s nil nil 1))))
1025 s)))
1046 1026
1047(defun calculator-eng-display (num) 1027(defun calculator-eng-display (num)
1048 "Display NUM in engineering notation. 1028 "Display NUM in engineering notation.
1049The number of decimal digits used is controlled by 1029The number of decimal digits used is controlled by
1050`calculator-number-digits', so to change it at runtime you have to use 1030`calculator-number-digits', so to change it at runtime you have to use
1051the 'left or 'right when one of the standard modes is used." 1031the `left' or `right' when one of the standard modes is used."
1052 (if (symbolp num) 1032 (if (symbolp num)
1053 (cond ((eq num 'left) 1033 (cond ((eq num 'left)
1054 (setq calculator-eng-extra 1034 (setq calculator-eng-extra
1055 (if calculator-eng-extra 1035 (if calculator-eng-extra (1+ calculator-eng-extra) 1))
1056 (1+ calculator-eng-extra)
1057 1))
1058 (let ((calculator-eng-tmp-show t)) (calculator-enter))) 1036 (let ((calculator-eng-tmp-show t)) (calculator-enter)))
1059 ((eq num 'right) 1037 ((eq num 'right)
1060 (setq calculator-eng-extra 1038 (setq calculator-eng-extra
1061 (if calculator-eng-extra 1039 (if calculator-eng-extra (1- calculator-eng-extra) -1))
1062 (1- calculator-eng-extra)
1063 -1))
1064 (let ((calculator-eng-tmp-show t)) (calculator-enter)))) 1040 (let ((calculator-eng-tmp-show t)) (calculator-enter))))
1065 (let ((exp 0)) 1041 (let ((exp 0))
1066 (and (not (= 0 num)) 1042 (unless (= 0 num)
1067 (progn 1043 (while (< (abs num) 1.0)
1068 (while (< (abs num) 1.0) 1044 (setq num (* num 1000.0)) (setq exp (- exp 3)))
1069 (setq num (* num 1000.0)) (setq exp (- exp 3))) 1045 (while (> (abs num) 999.0)
1070 (while (> (abs num) 999.0) 1046 (setq num (/ num 1000.0)) (setq exp (+ exp 3)))
1071 (setq num (/ num 1000.0)) (setq exp (+ exp 3))) 1047 (when (and calculator-eng-tmp-show
1072 (and calculator-eng-tmp-show 1048 (not (= 0 calculator-eng-extra)))
1073 (not (= 0 calculator-eng-extra)) 1049 (let ((i calculator-eng-extra))
1074 (let ((i calculator-eng-extra)) 1050 (while (> i 0)
1075 (while (> i 0) 1051 (setq num (* num 1000.0)) (setq exp (- exp 3))
1076 (setq num (* num 1000.0)) (setq exp (- exp 3)) 1052 (setq i (1- i)))
1077 (setq i (1- i))) 1053 (while (< i 0)
1078 (while (< i 0) 1054 (setq num (/ num 1000.0)) (setq exp (+ exp 3))
1079 (setq num (/ num 1000.0)) (setq exp (+ exp 3)) 1055 (setq i (1+ i))))))
1080 (setq i (1+ i)))))))
1081 (or calculator-eng-tmp-show (setq calculator-eng-extra nil)) 1056 (or calculator-eng-tmp-show (setq calculator-eng-extra nil))
1082 (let ((str (format (concat "%." (number-to-string 1057 (let ((str (format (format "%%.%sf" calculator-number-digits)
1083 calculator-number-digits)
1084 "f")
1085 num))) 1058 num)))
1086 (concat (let ((calculator-remove-zeros 1059 (concat (let ((calculator-remove-zeros
1087 ;; make sure we don't leave integers 1060 ;; make sure we don't leave integers
@@ -1092,56 +1065,48 @@ the 'left or 'right when one of the standard modes is used."
1092(defun calculator-number-to-string (num) 1065(defun calculator-number-to-string (num)
1093 "Convert NUM to a displayable string." 1066 "Convert NUM to a displayable string."
1094 (cond 1067 (cond
1095 ((and (numberp num) calculator-output-radix) 1068 ;; operators are printed here, the rest is for numbers
1096 ;; print with radix - for binary I convert the octal number 1069 ((not (numberp num)) (prin1-to-string (nth 1 num) t))
1097 (let ((str (format (if (eq calculator-output-radix 'hex) "%x" "%o") 1070 ;; %f/%e handle these, but avoid them in radix or in user displayers
1098 (calculator-truncate 1071 ((and (floatp num) (isnan num)) "NaN")
1099 (if calculator-2s-complement num (abs num)))))) 1072 ((<= 1.0e+INF num) "Inf")
1100 (if (eq calculator-output-radix 'bin) 1073 ((<= num -1.0e+INF) "-Inf")
1101 (let ((i -1) (s "")) 1074 (calculator-output-radix
1102 (while (< (setq i (1+ i)) (length str)) 1075 ;; print with radix -- for binary, convert the octal number
1103 (setq s 1076 (let* ((fmt (if (eq calculator-output-radix 'hex) "%x" "%o"))
1104 (concat s 1077 (str (if calculator-2s-complement num (abs num)))
1105 (cdr (assq (aref str i) 1078 (str (format fmt (calculator-truncate str)))
1106 '((?0 . "000") (?1 . "001") 1079 (bins '((?0 "000") (?1 "001") (?2 "010") (?3 "011")
1107 (?2 . "010") (?3 . "011") 1080 (?4 "100") (?5 "101") (?6 "110") (?7 "111")))
1108 (?4 . "100") (?5 . "101") 1081 (str (if (not (eq calculator-output-radix 'bin)) str
1109 (?6 . "110") (?7 . "111"))))))) 1082 (replace-regexp-in-string
1110 (string-match "^0*\\(.+\\)" s) 1083 "^0+\\(.\\)" "\\1"
1111 (setq str (match-string 1 s)))) 1084 (apply 'concat (mapcar (lambda (c)
1112 (if calculator-radix-grouping-mode 1085 (cadr (assq c bins)))
1113 (let ((d (/ (length str) calculator-radix-grouping-digits)) 1086 str)))))
1114 (r (% (length str) calculator-radix-grouping-digits))) 1087 (str (if (not calculator-radix-grouping-mode) str
1115 (while (>= (setq d (1- d)) (if (zerop r) 1 0)) 1088 (calculator-groupize-number
1116 (let ((i (+ r (* d calculator-radix-grouping-digits)))) 1089 str calculator-radix-grouping-digits
1117 (setq str (concat (substring str 0 i) 1090 calculator-radix-grouping-separator))))
1118 calculator-radix-grouping-separator 1091 (upcase (if (or calculator-2s-complement (>= num 0)) str
1119 (substring str i))))))) 1092 (concat "-" str)))))
1120 (upcase 1093 ((stringp calculator-displayer) (format calculator-displayer num))
1121 (if (and (not calculator-2s-complement) (< num 0)) 1094 ((symbolp calculator-displayer) (funcall calculator-displayer num))
1122 (concat "-" str) 1095 ((eq 'std (car-safe calculator-displayer))
1123 str)))) 1096 (apply 'calculator-standard-displayer
1124 ((and (numberp num) calculator-displayer) 1097 num (cdr calculator-displayer)))
1125 (cond 1098 ((listp calculator-displayer)
1126 ((stringp calculator-displayer) 1099 (eval `(let ((num ',num)) ,calculator-displayer) t))
1127 (format calculator-displayer num)) 1100 ;; nil (or bad) displayer
1128 ((symbolp calculator-displayer) 1101 (t (prin1-to-string num t))))
1129 (funcall calculator-displayer num))
1130 ((eq 'std (car-safe calculator-displayer))
1131 (calculator-standard-displayer num (cadr calculator-displayer)))
1132 ((listp calculator-displayer)
1133 (eval calculator-displayer `((num. ,num))))
1134 (t (prin1-to-string num t))))
1135 ;; operators are printed here
1136 (t (prin1-to-string (nth 1 num) t))))
1137 1102
1138(defun calculator-update-display (&optional force) 1103(defun calculator-update-display (&optional force)
1139 "Update the display. 1104 "Update the display.
1140If optional argument FORCE is non-nil, don't use the cached string." 1105If optional argument FORCE is non-nil, don't use the cached string."
1141 (set-buffer calculator-buffer) 1106 (set-buffer calculator-buffer)
1142 ;; update calculator-stack-display 1107 ;; update calculator-stack-display
1143 (if (or force 1108 (when (or force (not (eq (car calculator-stack-display)
1144 (not (eq (car calculator-stack-display) calculator-stack))) 1109 calculator-stack)))
1145 (setq calculator-stack-display 1110 (setq calculator-stack-display
1146 (cons calculator-stack 1111 (cons calculator-stack
1147 (if calculator-stack 1112 (if calculator-stack
@@ -1170,165 +1135,97 @@ If optional argument FORCE is non-nil, don't use the cached string."
1170 "")))) 1135 ""))))
1171 (let ((inhibit-read-only t)) 1136 (let ((inhibit-read-only t))
1172 (erase-buffer) 1137 (erase-buffer)
1173 (insert (calculator-get-prompt))) 1138 (insert (calculator-get-display)))
1174 (set-buffer-modified-p nil) 1139 (set-buffer-modified-p nil)
1175 (if calculator-display-fragile 1140 (goto-char (if calculator-display-fragile
1176 (goto-char (1+ (length calculator-prompt))) 1141 (1+ (length calculator-prompt))
1177 (goto-char (1- (point))))) 1142 (1- (point)))))
1178 1143
1179;;;--------------------------------------------------------------------- 1144;;;---------------------------------------------------------------------
1180;;; Stack computations 1145;;; Stack computations
1181 1146
1147(defun calculator-reduce-stack-once (prec)
1148 "Worker for `calculator-reduce-stack'."
1149 (cl-flet ((check (ar op) (and (listp op)
1150 (<= prec (calculator-op-prec op))
1151 (= ar (calculator-op-arity op))))
1152 (call (op &rest args) (apply 'calculator-funcall
1153 (nth 2 op) args)))
1154 (pcase calculator-stack
1155 ;; reduce "... ( x )" --> "... x"
1156 (`((,_ \) . ,_) ,(and X (pred numberp)) (,_ \( . ,_) . ,rest)
1157 (cons X rest))
1158 ;; reduce "... x op y" --> "... r", r is the result
1159 (`(,(and Y (pred numberp))
1160 ,(and O (pred (check 2)))
1161 ,(and X (pred numberp))
1162 . ,rest)
1163 (cons (call O X Y) rest))
1164 ;; reduce "... op x" --> "... r" for prefix op
1165 (`(,(and X (pred numberp)) ,(and O (pred (check -1))) . ,rest)
1166 (cons (call O X) rest))
1167 ;; reduce "... x op" --> "... r" for postfix op
1168 (`(,(and O (pred (check +1))) ,(and X (pred numberp)) . ,rest)
1169 (cons (call O X) rest))
1170 ;; reduce "... op" --> "... r" for 0-ary op
1171 (`(,(and O (pred (check 0))) . ,rest)
1172 (cons (call O) rest))
1173 ;; reduce "... y x" --> "... x"
1174 ;; (needed for 0-ary ops: replace current number with result)
1175 (`(,(and X (pred numberp)) ,(and _Y (pred numberp)) . ,rest)
1176 (cons X rest))
1177 (_ nil)))) ; nil = done
1178
1182(defun calculator-reduce-stack (prec) 1179(defun calculator-reduce-stack (prec)
1183 "Reduce the stack using top operator. 1180 "Reduce the stack using top operators as long as possible.
1184PREC is a precedence - reduce everything with higher precedence." 1181PREC is a precedence -- reduce everything with higher precedence."
1185 (while 1182 (let ((new nil))
1186 (cond 1183 (while (setq new (calculator-reduce-stack-once prec))
1187 ((and (cdr (cdr calculator-stack)) ; have three values 1184 (setq calculator-stack new))))
1188 (consp (nth 0 calculator-stack)) ; two operators & num
1189 (numberp (nth 1 calculator-stack))
1190 (consp (nth 2 calculator-stack))
1191 (eq '\) (nth 1 (nth 0 calculator-stack)))
1192 (eq '\( (nth 1 (nth 2 calculator-stack))))
1193 ;; reduce "... ( x )" --> "... x"
1194 (setq calculator-stack
1195 (cons (nth 1 calculator-stack)
1196 (nthcdr 3 calculator-stack)))
1197 ;; another iteration
1198 t)
1199 ((and (cdr (cdr calculator-stack)) ; have three values
1200 (numberp (nth 0 calculator-stack)) ; two nums & operator
1201 (consp (nth 1 calculator-stack))
1202 (numberp (nth 2 calculator-stack))
1203 (= 2 (calculator-op-arity ; binary operator
1204 (nth 1 calculator-stack)))
1205 (<= prec ; with higher prec.
1206 (calculator-op-prec (nth 1 calculator-stack))))
1207 ;; reduce "... x op y" --> "... r", r is the result
1208 (setq calculator-stack
1209 (cons (calculator-funcall
1210 (nth 2 (nth 1 calculator-stack))
1211 (nth 2 calculator-stack)
1212 (nth 0 calculator-stack))
1213 (nthcdr 3 calculator-stack)))
1214 ;; another iteration
1215 t)
1216 ((and (>= (length calculator-stack) 2) ; have two values
1217 (numberp (nth 0 calculator-stack)) ; number & operator
1218 (consp (nth 1 calculator-stack))
1219 (= -1 (calculator-op-arity ; prefix-unary op
1220 (nth 1 calculator-stack)))
1221 (<= prec ; with higher prec.
1222 (calculator-op-prec (nth 1 calculator-stack))))
1223 ;; reduce "... op x" --> "... r" for prefix op
1224 (setq calculator-stack
1225 (cons (calculator-funcall
1226 (nth 2 (nth 1 calculator-stack))
1227 (nth 0 calculator-stack))
1228 (nthcdr 2 calculator-stack)))
1229 ;; another iteration
1230 t)
1231 ((and (cdr calculator-stack) ; have two values
1232 (consp (nth 0 calculator-stack)) ; operator & number
1233 (numberp (nth 1 calculator-stack))
1234 (= +1 (calculator-op-arity ; postfix-unary op
1235 (nth 0 calculator-stack)))
1236 (<= prec ; with higher prec.
1237 (calculator-op-prec (nth 0 calculator-stack))))
1238 ;; reduce "... x op" --> "... r" for postfix op
1239 (setq calculator-stack
1240 (cons (calculator-funcall
1241 (nth 2 (nth 0 calculator-stack))
1242 (nth 1 calculator-stack))
1243 (nthcdr 2 calculator-stack)))
1244 ;; another iteration
1245 t)
1246 ((and calculator-stack ; have one value
1247 (consp (nth 0 calculator-stack)) ; an operator
1248 (= 0 (calculator-op-arity ; 0-ary op
1249 (nth 0 calculator-stack))))
1250 ;; reduce "... op" --> "... r" for 0-ary op
1251 (setq calculator-stack
1252 (cons (calculator-funcall
1253 (nth 2 (nth 0 calculator-stack)))
1254 (nthcdr 1 calculator-stack)))
1255 ;; another iteration
1256 t)
1257 ((and (cdr calculator-stack) ; have two values
1258 (numberp (nth 0 calculator-stack)) ; both numbers
1259 (numberp (nth 1 calculator-stack)))
1260 ;; get rid of redundant numbers:
1261 ;; reduce "... y x" --> "... x"
1262 ;; needed for 0-ary ops that puts more values
1263 (setcdr calculator-stack (cdr (cdr calculator-stack))))
1264 (t ;; no more iterations
1265 nil))))
1266 1185
1267(defun calculator-funcall (f &optional X Y) 1186(defun calculator-funcall (f &optional X Y)
1268 "If F is a symbol, evaluate (F X Y). 1187 "If F is a symbol, evaluate (F X Y).
1269Otherwise, it should be a list, evaluate it with X, Y bound to the 1188Otherwise, it should be a list, evaluate it with X, Y bound to the
1270arguments." 1189arguments."
1271 ;; remember binary ops for calculator-repR/L 1190 ;; remember binary ops for calculator-repR/L
1272 (if Y (setq calculator-last-opXY (list f X Y))) 1191 (when Y (setq calculator-last-opXY (list f X Y)))
1273 (condition-case nil 1192 (if (symbolp f)
1274 ;; there used to be code here that returns 0 if the result was 1193 (cond ((and X Y) (funcall f X Y))
1275 ;; smaller than calculator-epsilon (1e-15). I don't think this is 1194 (X (funcall f X))
1276 ;; necessary now. 1195 (t (funcall f)))
1277 (if (symbolp f) 1196 ;; f is an expression
1278 (cond ((and X Y) (funcall f X Y)) 1197 (let ((TX (and X (calculator-truncate X)))
1279 (X (funcall f X)) 1198 (TY (and Y (calculator-truncate Y)))
1280 (t (funcall f))) 1199 (DX (if (and X calculator-deg) (/ (* X pi) 180) X))
1281 ;; f is an expression 1200 (L calculator-saved-list))
1282 (let* ((TX (calculator-truncate X)) 1201 (cl-flet ((F (&optional x y) (calculator-funcall f x y))
1283 (TY (and Y (calculator-truncate Y))) 1202 (D (x) (if calculator-deg (/ (* x 180) float-pi) x)))
1284 (DX (if calculator-deg (/ (* X pi) 180) X)) 1203 (eval `(let ((X ,X) (Y ,Y) (DX ,DX) (TX ,TX) (TY ,TY) (L ',L))
1285 (L calculator-saved-list)) 1204 ,f)
1286 (cl-letf (((symbol-function 'F) 1205 t)))))
1287 (lambda (&optional x y) (calculator-funcall f x y)))
1288 ((symbol-function 'D)
1289 (lambda (x) (if calculator-deg (/ (* x 180) float-pi) x))))
1290 (eval f `((X . ,X)
1291 (Y . ,Y)
1292 (TX . ,TX)
1293 (TY . ,TY)
1294 (DX . ,DX)
1295 (L . ,L))))))
1296 (error 0)))
1297 1206
1298;;;--------------------------------------------------------------------- 1207;;;---------------------------------------------------------------------
1299;;; Input interaction 1208;;; Input interaction
1300 1209
1301(defun calculator-last-input (&optional keys) 1210(defun calculator-last-input (&optional keys)
1302 "Last char (or event or event sequence) that was read. 1211 "Last char (or event or event sequence) that was read.
1303Optional string argument KEYS will force using it as the keys entered." 1212Use KEYS if given, otherwise use `this-command-keys'."
1304 (let ((inp (or keys (this-command-keys)))) 1213 (let ((inp (or keys (this-command-keys))))
1305 (if (or (stringp inp) (not (arrayp inp))) 1214 (if (or (stringp inp) (not (arrayp inp)))
1306 inp 1215 inp
1307 ;; this translates kp-x to x and [tries to] create a string to 1216 ;; translates kp-x to x and [tries to] create a string to lookup
1308 ;; lookup operators 1217 ;; operators; assume all symbols are translatable via
1309 (let* ((i -1) (converted-str (make-string (length inp) ? )) k) 1218 ;; `function-key-map' or with an 'ascii-character property
1310 ;; converts an array to a string the ops lookup with keypad 1219 (concat (mapcar (lambda (k)
1311 ;; input 1220 (if (numberp k) k (or (get k 'ascii-character)
1312 (while (< (setq i (1+ i)) (length inp)) 1221 (error "??bad key??"))))
1313 (setq k (aref inp i)) 1222 (or (lookup-key function-key-map inp) inp))))))
1314 ;; if Emacs will someday have a event-key, then this would
1315 ;; probably be modified anyway
1316 (and (if (fboundp 'key-press-event-p) (key-press-event-p k))
1317 (if (fboundp 'event-key)
1318 (and (event-key k) (setq k (event-key k)))))
1319 ;; assume all symbols are translatable with an ascii-character
1320 (and (symbolp k)
1321 (setq k (or (get k 'ascii-character) ? )))
1322 (aset converted-str i k))
1323 converted-str))))
1324 1223
1325(defun calculator-clear-fragile (&optional op) 1224(defun calculator-clear-fragile (&optional op)
1326 "Clear the fragile flag if it was set, then maybe reset all. 1225 "Clear the fragile flag if it was set, then maybe reset all.
1327OP is the operator (if any) that caused this call." 1226OP is the operator (if any) that caused this call."
1328 (if (and calculator-display-fragile 1227 (when (and calculator-display-fragile
1329 (or (not op) 1228 (or (not op) (memq (calculator-op-arity op) '(-1 0))))
1330 (= -1 (calculator-op-arity op))
1331 (= 0 (calculator-op-arity op))))
1332 ;; reset if last calc finished, and now get a num or prefix or 0-ary 1229 ;; reset if last calc finished, and now get a num or prefix or 0-ary
1333 ;; op 1230 ;; op
1334 (calculator-reset)) 1231 (calculator-reset))
@@ -1338,53 +1235,44 @@ OP is the operator (if any) that caused this call."
1338 "Enter a single digit." 1235 "Enter a single digit."
1339 (interactive) 1236 (interactive)
1340 (let ((inp (aref (calculator-last-input) 0))) 1237 (let ((inp (aref (calculator-last-input) 0)))
1341 (if (and (or calculator-display-fragile 1238 (when (and (or calculator-display-fragile
1342 (not (numberp (car calculator-stack)))) 1239 (not (numberp (car calculator-stack))))
1343 (cond 1240 (<= inp (pcase calculator-input-radix
1344 ((not calculator-input-radix) (<= inp ?9)) 1241 (`nil ?9) (`bin ?1) (`oct ?7) (_ 999))))
1345 ((eq calculator-input-radix 'bin) (<= inp ?1)) 1242 (calculator-clear-fragile)
1346 ((eq calculator-input-radix 'oct) (<= inp ?7)) 1243 (setq calculator-curnum
1347 (t t))) 1244 (concat (if (equal calculator-curnum "0") ""
1348 ;; enter digit if starting a new computation or have an op on the 1245 calculator-curnum)
1349 ;; stack 1246 (list (upcase inp))))
1350 (progn 1247 (calculator-update-display))))
1351 (calculator-clear-fragile)
1352 (let ((digit (upcase (char-to-string inp))))
1353 (if (equal calculator-curnum "0")
1354 (setq calculator-curnum nil))
1355 (setq calculator-curnum
1356 (concat (or calculator-curnum "") digit)))
1357 (calculator-update-display)))))
1358 1248
1359(defun calculator-decimal () 1249(defun calculator-decimal ()
1360 "Enter a decimal period." 1250 "Enter a decimal period."
1361 (interactive) 1251 (interactive)
1362 (if (and (not calculator-input-radix) 1252 (when (and (not calculator-input-radix)
1363 (or calculator-display-fragile 1253 (or calculator-display-fragile
1364 (not (numberp (car calculator-stack)))) 1254 (not (numberp (car calculator-stack))))
1365 (not (and calculator-curnum 1255 (not (and calculator-curnum
1366 (string-match-p "[.eE]" calculator-curnum)))) 1256 (string-match-p "[.eE]" calculator-curnum))))
1367 ;; enter the period on the same condition as a digit, only if no 1257 ;; enter the period on the same condition as a digit, only if no
1368 ;; period or exponent entered yet 1258 ;; period or exponent entered yet
1369 (progn 1259 (calculator-clear-fragile)
1370 (calculator-clear-fragile) 1260 (setq calculator-curnum (concat (or calculator-curnum "0") "."))
1371 (setq calculator-curnum (concat (or calculator-curnum "0") ".")) 1261 (calculator-update-display)))
1372 (calculator-update-display))))
1373 1262
1374(defun calculator-exp () 1263(defun calculator-exp ()
1375 "Enter an `E' exponent character, or a digit in hex input mode." 1264 "Enter an `E' exponent character, or a digit in hex input mode."
1376 (interactive) 1265 (interactive)
1377 (if calculator-input-radix 1266 (cond
1378 (calculator-digit) 1267 (calculator-input-radix (calculator-digit))
1379 (if (and (or calculator-display-fragile 1268 ((and (or calculator-display-fragile
1380 (not (numberp (car calculator-stack)))) 1269 (not (numberp (car calculator-stack))))
1381 (not (and calculator-curnum 1270 (not (and calculator-curnum
1382 (string-match-p "[eE]" calculator-curnum)))) 1271 (string-match-p "[eE]" calculator-curnum))))
1383 ;; same condition as above, also no E so far 1272 ;; same condition as above, also no E so far
1384 (progn 1273 (calculator-clear-fragile)
1385 (calculator-clear-fragile) 1274 (setq calculator-curnum (concat (or calculator-curnum "1") "e"))
1386 (setq calculator-curnum (concat (or calculator-curnum "1") "e")) 1275 (calculator-update-display))))
1387 (calculator-update-display)))))
1388 1276
1389(defun calculator-op (&optional keys) 1277(defun calculator-op (&optional keys)
1390 "Enter an operator on the stack, doing all necessary reductions. 1278 "Enter an operator on the stack, doing all necessary reductions.
@@ -1394,42 +1282,29 @@ Optional string argument KEYS will force using it as the keys entered."
1394 (let* ((last-inp (calculator-last-input keys)) 1282 (let* ((last-inp (calculator-last-input keys))
1395 (op (assoc last-inp calculator-operators))) 1283 (op (assoc last-inp calculator-operators)))
1396 (calculator-clear-fragile op) 1284 (calculator-clear-fragile op)
1397 (if (and calculator-curnum (/= (calculator-op-arity op) 0)) 1285 (calculator-push-curnum)
1398 (setq calculator-stack 1286 (when (and (= 2 (calculator-op-arity op))
1399 (cons (calculator-curnum-value) calculator-stack))) 1287 (not (numberp (car calculator-stack))))
1400 (setq calculator-curnum nil) 1288 ;; we have a binary operator but no number -- search for a
1401 (if (and (= 2 (calculator-op-arity op)) 1289 ;; prefix version
1402 (not (and calculator-stack 1290 (setq op (assoc last-inp (cdr (memq op calculator-operators))))
1403 (numberp (nth 0 calculator-stack))))) 1291 (unless (and op (= -1 (calculator-op-arity op)))
1404 ;; we have a binary operator but no number - search for a prefix 1292 (calculator-message "Binary operator without a first operand")
1405 ;; version 1293 (throw 'op-error nil)))
1406 (let ((rest-ops calculator-operators))
1407 (while (not (equal last-inp (car (car rest-ops))))
1408 (setq rest-ops (cdr rest-ops)))
1409 (setq op (assoc last-inp (cdr rest-ops)))
1410 (if (not (and op (= -1 (calculator-op-arity op))))
1411 ;;(error "Binary operator without a first operand")
1412 (progn
1413 (calculator-message
1414 "Binary operator without a first operand")
1415 (throw 'op-error nil)))))
1416 (calculator-reduce-stack 1294 (calculator-reduce-stack
1417 (cond ((eq (nth 1 op) '\() 10) 1295 (cond ((eq (nth 1 op) '\() 10)
1418 ((eq (nth 1 op) '\)) 0) 1296 ((eq (nth 1 op) '\)) 0)
1419 (t (calculator-op-prec op)))) 1297 (t (calculator-op-prec op))))
1420 (if (or (and (= -1 (calculator-op-arity op)) 1298 (when (let ((hasnum (numberp (car calculator-stack))))
1421 (numberp (car calculator-stack))) 1299 (pcase (calculator-op-arity op)
1422 (and (/= (calculator-op-arity op) -1) 1300 (-1 hasnum)
1423 (/= (calculator-op-arity op) 0) 1301 ((or 1 2) (not hasnum))))
1424 (not (numberp (car calculator-stack))))) 1302 (calculator-message "Incomplete expression")
1425 ;;(error "Unterminated expression") 1303 (throw 'op-error nil))
1426 (progn 1304 (push op calculator-stack)
1427 (calculator-message "Unterminated expression")
1428 (throw 'op-error nil)))
1429 (setq calculator-stack (cons op calculator-stack))
1430 (calculator-reduce-stack (calculator-op-prec op)) 1305 (calculator-reduce-stack (calculator-op-prec op))
1431 (and (= (length calculator-stack) 1) 1306 (and (= (length calculator-stack) 1)
1432 (numberp (nth 0 calculator-stack)) 1307 (numberp (car calculator-stack))
1433 ;; the display is fragile if it contains only one number 1308 ;; the display is fragile if it contains only one number
1434 (setq calculator-display-fragile t) 1309 (setq calculator-display-fragile t)
1435 ;; add number to the saved-list 1310 ;; add number to the saved-list
@@ -1445,7 +1320,8 @@ Optional string argument KEYS will force using it as the keys entered."
1445(defun calculator-op-or-exp () 1320(defun calculator-op-or-exp ()
1446 "Either enter an operator or a digit. 1321 "Either enter an operator or a digit.
1447Used with +/- for entering them as digits in numbers like 1e-3 (there is 1322Used with +/- for entering them as digits in numbers like 1e-3 (there is
1448no need for negative numbers since these are handled by unary operators)." 1323no need for negative numbers since these are handled by unary
1324operators)."
1449 (interactive) 1325 (interactive)
1450 (if (and (not calculator-display-fragile) 1326 (if (and (not calculator-display-fragile)
1451 calculator-curnum 1327 calculator-curnum
@@ -1459,14 +1335,11 @@ no need for negative numbers since these are handled by unary operators)."
1459(defun calculator-dec/deg-mode () 1335(defun calculator-dec/deg-mode ()
1460 "Set decimal mode for display & input, if decimal, toggle deg mode." 1336 "Set decimal mode for display & input, if decimal, toggle deg mode."
1461 (interactive) 1337 (interactive)
1462 (if calculator-curnum 1338 (calculator-push-curnum)
1463 (setq calculator-stack
1464 (cons (calculator-curnum-value) calculator-stack)))
1465 (setq calculator-curnum nil)
1466 (if (or calculator-input-radix calculator-output-radix) 1339 (if (or calculator-input-radix calculator-output-radix)
1467 (progn (setq calculator-input-radix nil) 1340 (progn (setq calculator-input-radix nil)
1468 (setq calculator-output-radix nil)) 1341 (setq calculator-output-radix nil))
1469 ;; already decimal - toggle degrees mode 1342 ;; already decimal -- toggle degrees mode
1470 (setq calculator-deg (not calculator-deg))) 1343 (setq calculator-deg (not calculator-deg)))
1471 (calculator-update-display t)) 1344 (calculator-update-display t))
1472 1345
@@ -1481,10 +1354,7 @@ Optional string argument KEYS will force using it as the keys entered."
1481 "Set input radix modes. 1354 "Set input radix modes.
1482Optional string argument KEYS will force using it as the keys entered." 1355Optional string argument KEYS will force using it as the keys entered."
1483 (interactive) 1356 (interactive)
1484 (if calculator-curnum 1357 (calculator-push-curnum)
1485 (setq calculator-stack
1486 (cons (calculator-curnum-value) calculator-stack)))
1487 (setq calculator-curnum nil)
1488 (setq calculator-input-radix 1358 (setq calculator-input-radix
1489 (let ((inp (calculator-last-input keys))) 1359 (let ((inp (calculator-last-input keys)))
1490 (cdr (assq (upcase (aref inp (1- (length inp)))) 1360 (cdr (assq (upcase (aref inp (1- (length inp))))
@@ -1495,10 +1365,7 @@ Optional string argument KEYS will force using it as the keys entered."
1495 "Set display radix modes. 1365 "Set display radix modes.
1496Optional string argument KEYS will force using it as the keys entered." 1366Optional string argument KEYS will force using it as the keys entered."
1497 (interactive) 1367 (interactive)
1498 (if calculator-curnum 1368 (calculator-push-curnum)
1499 (setq calculator-stack
1500 (cons (calculator-curnum-value) calculator-stack)))
1501 (setq calculator-curnum nil)
1502 (setq calculator-output-radix 1369 (setq calculator-output-radix
1503 (let ((inp (calculator-last-input keys))) 1370 (let ((inp (calculator-last-input keys)))
1504 (cdr (assq (upcase (aref inp (1- (length inp)))) 1371 (cdr (assq (upcase (aref inp (1- (length inp))))
@@ -1524,19 +1391,18 @@ Optional string argument KEYS will force using it as the keys entered."
1524(defun calculator-saved-move (n) 1391(defun calculator-saved-move (n)
1525 "Go N elements up the list of saved values." 1392 "Go N elements up the list of saved values."
1526 (interactive) 1393 (interactive)
1527 (and calculator-saved-list 1394 (when (and calculator-saved-list
1528 (or (null calculator-stack) calculator-display-fragile) 1395 (or (null calculator-stack) calculator-display-fragile))
1529 (progn 1396 (setq calculator-saved-ptr
1530 (setq calculator-saved-ptr 1397 (max (min (+ n calculator-saved-ptr)
1531 (max (min (+ n calculator-saved-ptr) 1398 (length calculator-saved-list))
1532 (length calculator-saved-list)) 1399 0))
1533 0)) 1400 (if (nth calculator-saved-ptr calculator-saved-list)
1534 (if (nth calculator-saved-ptr calculator-saved-list) 1401 (setq calculator-stack (list (nth calculator-saved-ptr
1535 (setq calculator-stack 1402 calculator-saved-list))
1536 (list (nth calculator-saved-ptr calculator-saved-list)) 1403 calculator-display-fragile t)
1537 calculator-display-fragile t) 1404 (calculator-reset))
1538 (calculator-reset)) 1405 (calculator-update-display)))
1539 (calculator-update-display))))
1540 1406
1541(defun calculator-saved-up () 1407(defun calculator-saved-up ()
1542 "Go up the list of saved values." 1408 "Go up the list of saved values."
@@ -1583,7 +1449,7 @@ Optional string argument KEYS will force using it as the keys entered."
1583 (interactive) 1449 (interactive)
1584 (setq calculator-curnum nil) 1450 (setq calculator-curnum nil)
1585 (cond 1451 (cond
1586 ;; if the current number is from the saved-list - remove it 1452 ;; if the current number is from the saved-list remove it
1587 ((and calculator-display-fragile 1453 ((and calculator-display-fragile
1588 calculator-saved-list 1454 calculator-saved-list
1589 (= (car calculator-stack) 1455 (= (car calculator-stack)
@@ -1592,7 +1458,7 @@ Optional string argument KEYS will force using it as the keys entered."
1592 (setq calculator-saved-list (cdr calculator-saved-list)) 1458 (setq calculator-saved-list (cdr calculator-saved-list))
1593 (let ((p (nthcdr (1- calculator-saved-ptr) 1459 (let ((p (nthcdr (1- calculator-saved-ptr)
1594 calculator-saved-list))) 1460 calculator-saved-list)))
1595 (setcdr p (cdr (cdr p))) 1461 (setcdr p (cddr p))
1596 (setq calculator-saved-ptr (1- calculator-saved-ptr)))) 1462 (setq calculator-saved-ptr (1- calculator-saved-ptr))))
1597 (if calculator-saved-list 1463 (if calculator-saved-list
1598 (setq calculator-stack 1464 (setq calculator-stack
@@ -1613,15 +1479,16 @@ Optional string argument KEYS will force using it as the keys entered."
1613 (calculator-enter) 1479 (calculator-enter)
1614 ;; remove trailing spaces and an index 1480 ;; remove trailing spaces and an index
1615 (let ((s (cdr calculator-stack-display))) 1481 (let ((s (cdr calculator-stack-display)))
1616 (and s 1482 (when s
1617 (if (string-match "^\\([^ ]+\\) *\\(\\[[0-9/]+\\]\\)? *$" s) 1483 (kill-new (replace-regexp-in-string
1618 (setq s (match-string 1 s))) 1484 "^\\([^ ]+\\) *\\(\\[[0-9/]+\\]\\)? *$" "\\1" s))))))
1619 (kill-new s)))))
1620 1485
1621;; FIXME this should use register-read-with-preview, but it
1622;; uses calculator-registers rather than register-alist.
1623(defun calculator-set-register (reg) 1486(defun calculator-set-register (reg)
1624 "Set a register value for REG." 1487 "Set a register value for REG."
1488 ;; FIXME: this should use `register-read-with-preview', but it uses
1489 ;; calculator-registers rather than `register-alist'. (Maybe
1490 ;; dynamically rebinding it will get blessed?) Also in to
1491 ;; `calculator-get-register'.
1625 (interactive "cRegister to store into: ") 1492 (interactive "cRegister to store into: ")
1626 (let* ((as (assq reg calculator-registers)) 1493 (let* ((as (assq reg calculator-registers))
1627 (val (progn (calculator-enter) (car calculator-stack)))) 1494 (val (progn (calculator-enter) (car calculator-stack))))
@@ -1634,15 +1501,14 @@ Optional string argument KEYS will force using it as the keys entered."
1634(defun calculator-put-value (val) 1501(defun calculator-put-value (val)
1635 "Paste VAL as if entered. 1502 "Paste VAL as if entered.
1636Used by `calculator-paste' and `get-register'." 1503Used by `calculator-paste' and `get-register'."
1637 (if (and (numberp val) 1504 (when (and (numberp val)
1638 ;; (not calculator-curnum) 1505 ;; (not calculator-curnum)
1639 (or calculator-display-fragile 1506 (or calculator-display-fragile
1640 (not (numberp (car calculator-stack))))) 1507 (not (numberp (car calculator-stack)))))
1641 (progn 1508 (calculator-clear-fragile)
1642 (calculator-clear-fragile) 1509 (setq calculator-curnum (let ((calculator-displayer "%S"))
1643 (setq calculator-curnum (let ((calculator-displayer "%S")) 1510 (calculator-number-to-string val)))
1644 (calculator-number-to-string val))) 1511 (calculator-update-display)))
1645 (calculator-update-display))))
1646 1512
1647(defun calculator-paste () 1513(defun calculator-paste ()
1648 "Paste a value from the `kill-ring'." 1514 "Paste a value from the `kill-ring'."
@@ -1662,8 +1528,6 @@ Used by `calculator-paste' and `get-register'."
1662 (or (match-string 3 str) "")))) 1528 (or (match-string 3 str) ""))))
1663 (ignore-errors (calculator-string-to-number str))))) 1529 (ignore-errors (calculator-string-to-number str)))))
1664 1530
1665;; FIXME this should use register-read-with-preview, but it
1666;; uses calculator-registers rather than register-alist.
1667(defun calculator-get-register (reg) 1531(defun calculator-get-register (reg)
1668 "Get a value from a register REG." 1532 "Get a value from a register REG."
1669 (interactive "cRegister to get value from: ") 1533 (interactive "cRegister to get value from: ")
@@ -1696,16 +1560,13 @@ Used by `calculator-paste' and `get-register'."
1696 (g-map (current-global-map)) 1560 (g-map (current-global-map))
1697 (win (selected-window))) 1561 (win (selected-window)))
1698 (require 'ehelp) 1562 (require 'ehelp)
1699 (if calculator-electric-mode 1563 (when calculator-electric-mode
1700 (use-global-map calculator-saved-global-map)) 1564 (use-global-map calculator-saved-global-map))
1701 (if (or (not calculator-electric-mode)
1702 ;; XEmacs has a problem with electric-describe-mode
1703 (featurep 'xemacs))
1704 (describe-mode)
1705 (electric-describe-mode))
1706 (if calculator-electric-mode 1565 (if calculator-electric-mode
1707 (use-global-map g-map)) 1566 (electric-describe-mode)
1708 (select-window win) ; these are for XEmacs (also below) 1567 (describe-mode))
1568 (when calculator-electric-mode (use-global-map g-map))
1569 (select-window win)
1709 (message nil)) 1570 (message nil))
1710 (let ((one (one-window-p t)) 1571 (let ((one (one-window-p t))
1711 (win (selected-window)) 1572 (win (selected-window))
@@ -1713,12 +1574,11 @@ Used by `calculator-paste' and `get-register'."
1713 (save-window-excursion 1574 (save-window-excursion
1714 (with-output-to-temp-buffer "*Help*" 1575 (with-output-to-temp-buffer "*Help*"
1715 (princ (documentation 'calculator-help))) 1576 (princ (documentation 'calculator-help)))
1716 (if one 1577 (when one (shrink-window-if-larger-than-buffer
1717 (shrink-window-if-larger-than-buffer 1578 (get-buffer-window help-buf)))
1718 (get-buffer-window help-buf))) 1579 (message "`%s' again for more help, %s."
1719 (message 1580 (calculator-last-input)
1720 "`%s' again for more help, any other key continues normally." 1581 "any other key continues normally")
1721 (calculator-last-input))
1722 (select-window win) 1582 (select-window win)
1723 (sit-for 360)) 1583 (sit-for 360))
1724 (select-window win)))) 1584 (select-window win))))
@@ -1731,11 +1591,12 @@ Used by `calculator-paste' and `get-register'."
1731 (unless calculator-electric-mode 1591 (unless calculator-electric-mode
1732 (ignore-errors 1592 (ignore-errors
1733 (while (get-buffer-window calculator-buffer) 1593 (while (get-buffer-window calculator-buffer)
1734 (delete-window (get-buffer-window calculator-buffer)))) 1594 (delete-window (get-buffer-window calculator-buffer)))))
1735 (kill-buffer calculator-buffer)) 1595 (kill-buffer calculator-buffer)
1736 (setq calculator-buffer nil)
1737 (message "Calculator done.") 1596 (message "Calculator done.")
1738 (if calculator-electric-mode (throw 'calculator-done nil))) 1597 (if calculator-electric-mode
1598 (throw 'calculator-done nil) ; will kill the buffer
1599 (setq calculator-buffer nil)))
1739 1600
1740(defun calculator-save-and-quit () 1601(defun calculator-save-and-quit ()
1741 "Quit the calculator, saving the result on the `kill-ring'." 1602 "Quit the calculator, saving the result on the `kill-ring'."
@@ -1764,58 +1625,47 @@ To use this, apply a binary operator (evaluate it), then call this."
1764 (car calculator-last-opXY) (nth 1 calculator-last-opXY) x)) 1625 (car calculator-last-opXY) (nth 1 calculator-last-opXY) x))
1765 x)) 1626 x))
1766 1627
1767(defun calculator-integer-p (x)
1768 "Non-nil if X is equal to an integer."
1769 (ignore-errors (= x (ftruncate x))))
1770
1771(defun calculator-expt (x y) 1628(defun calculator-expt (x y)
1772 "Compute X^Y, dealing with errors appropriately." 1629 "Compute X^Y, dealing with errors appropriately."
1773 (condition-case nil 1630 (condition-case nil
1774 (expt x y) 1631 (expt x y)
1775 (domain-error 0.0e+NaN) 1632 (domain-error 0.0e+NaN)
1776 (range-error 1633 (range-error
1777 (cond 1634 (cond ((and (< x 1.0) (> x -1.0))
1778 ((and (< x 1.0) (> x -1.0)) 1635 ;; For small x, the range error comes from large y.
1779 ;; For small x, the range error comes from large y. 1636 0.0)
1780 0.0) 1637 ((and (> x 0.0) (< y 0.0))
1781 ((and (> x 0.0) (< y 0.0)) 1638 ;; For large positive x and negative y, the range error
1782 ;; For large positive x and negative y, the range error 1639 ;; comes from large negative y.
1783 ;; comes from large negative y. 1640 0.0)
1784 0.0) 1641 ((and (> x 0.0) (> y 0.0))
1785 ((and (> x 0.0) (> y 0.0)) 1642 ;; For large positive x and positive y, the range error
1786 ;; For large positive x and positive y, the range error 1643 ;; comes from large y.
1787 ;; comes from large y. 1644 1.0e+INF)
1788 1.0e+INF) 1645 ;; For the rest, x must be large and negative.
1789 ;; For the rest, x must be large and negative. 1646 ;; The range errors come from large integer y.
1790 ;; The range errors come from large integer y. 1647 ((< y 0.0)
1791 ((< y 0.0) 1648 0.0)
1792 0.0) 1649 ((eq (logand (truncate y) 1) 1) ; expansion of cl `oddp'
1793 ((eq (logand (truncate y) 1) 1) ; expansion of cl `oddp' 1650 ;; If y is odd
1794 ;; If y is odd 1651 -1.0e+INF)
1795 -1.0e+INF) 1652 (t
1796 (t 1653 ;;
1797 ;; 1654 1.0e+INF)))
1798 1.0e+INF)))
1799 (error 0.0e+NaN))) 1655 (error 0.0e+NaN)))
1800 1656
1801(defun calculator-fact (x) 1657(defun calculator-fact (x)
1802 "Simple factorial of X." 1658 "Simple factorial of X."
1803 (if (and (>= x 0) 1659 (cond ((>= x 1.0e+INF) x)
1804 (calculator-integer-p x)) 1660 ((or (and (floatp x) (isnan x)) (< x 0)) 0.0e+NaN)
1805 (if (= (calculator-expt (/ x 3.0) x) 1.0e+INF) 1661 ((>= (calculator-expt (/ x 3.0) x) 1.0e+INF) 1.0e+INF)
1806 1.0e+INF 1662 (t (let ((x (truncate x)) (r 1.0))
1807 (let ((r (if (<= x 10) 1 1.0))) 1663 (while (> x 0) (setq r (* r x) x (1- x)))
1808 (while (> x 0) 1664 r))))
1809 (setq r (* r (truncate x)))
1810 (setq x (1- x)))
1811 (+ 0.0 r)))
1812 (if (= x 1.0e+INF)
1813 x
1814 0.0e+NaN)))
1815 1665
1816(defun calculator-truncate (n) 1666(defun calculator-truncate (n)
1817 "Truncate N, return 0 in case of overflow." 1667 "Truncate N, return 0 in case of overflow."
1818 (condition-case nil (truncate n) (error 0))) 1668 (condition-case nil (truncate n) (range-error 0)))
1819 1669
1820 1670
1821(provide 'calculator) 1671(provide 'calculator)