comparison lisp/calculator.el @ 33491:23166da66d5f

New maintainer version.
author Dave Love <fx@gnu.org>
date Tue, 14 Nov 2000 18:51:34 +0000
parents 5f9c434a6e88
children dd42fdd51753
comparison
equal deleted inserted replaced
33490:b714a06b99ec 33491:23166da66d5f
1 ;;; calculator.el --- A simple pocket calculator. 1 ;;; calculator.el --- A [not so] simple calculator for Emacs.
2 2
3 ;; Copyright (C) 1998 by Free Software Foundation, Inc. 3 ;; Copyright (C) 1998, 2000 by Free Software Foundation, Inc.
4 4
5 ;; Author: Eli Barzilay <eli@lambda.cs.cornell.edu> 5 ;; Author: Eli Barzilay <eli@www.barzilay.org>
6 ;; Keywords: tools, convenience 6 ;; Keywords: tools, convenience
7 ;; Time-stamp: <2000-02-16 21:07:54 eli> 7 ;; Time-stamp: <2000-11-07 15:04:06 eli>
8 8
9 ;; This file is part of GNU Emacs. 9 ;; This file is part of GNU Emacs.
10 10
11 ;; GNU Emacs is free software; you can redistribute it and/or modify it 11 ;; GNU Emacs is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by the 12 ;; under the terms of the GNU General Public License as published by the
21 ;; You should have received a copy of the GNU General Public License 21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the 22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
24 ;; MA 02111-1307, USA. 24 ;; MA 02111-1307, USA.
25 25
26 ;;;============================================================================
26 ;;; Commentary: 27 ;;; Commentary:
27 ;; 28 ;;
28 ;; A simple pocket calculator for Emacs. 29 ;; A calculator for Emacs.
29 ;; Why touch your mouse to get xcalc (or calc.exe), when you have Emacs? 30 ;; Why should you each for your mouse to get xcalc (calc.exe, gcalc or
31 ;; whatever), when you have Emacs running already?
30 ;; 32 ;;
31 ;; If this is not part of your Emacs distribution, then simply bind 33 ;; If this is not part of your Emacs distribution, then simply bind
32 ;; `calculator' to a key and make it an autoloaded function, e.g.: 34 ;; `calculator' to a key and make it an autoloaded function, e.g.:
33 ;; (autoload 'calculator "calculator" 35 ;; (autoload 'calculator "calculator"
34 ;; "Run the pocket calculator." t) 36 ;; "Run the Emacs calculator." t)
35 ;; (global-set-key [(control return)] 'calculator) 37 ;; (global-set-key [(control return)] 'calculator)
36 ;; 38 ;;
37 ;; Written by Eli Barzilay: Maze is Life! eli@cs.cornell.edu 39 ;; Written by Eli Barzilay: Maze is Life! eli@barzilay.org
38 ;; http://www.cs.cornell.edu/eli 40 ;; http://www.barzilay.org/
39 ;; 41 ;;
40 ;; For latest version, check 42 ;; For latest version, check
41 ;; http://www.cs.cornell.edu/eli/misc/calculator.el 43 ;; http://www.barzilay.org/misc/calculator.el
42
43 44
44 (eval-and-compile 45 (eval-and-compile
45 (if (fboundp 'defgroup) nil 46 (if (fboundp 'defgroup) nil
46 (defmacro defgroup (&rest forms) nil) 47 (defmacro defgroup (&rest forms) nil)
47 (defmacro defcustom (s v d &rest r) (list 'defvar s v d)))) 48 (defmacro defcustom (s v d &rest r) (list 'defvar s v d))))
48 49
50 ;;;============================================================================
49 ;;; Customization: 51 ;;; Customization:
50 52
51 (defgroup calculator nil 53 (defgroup calculator nil
52 "Simple pocket calculator." 54 "Simple Emacs calculator."
53 :prefix "calculator" 55 :prefix "calculator"
54 :version "21.1" 56 :version "21.1"
55 :group 'tools 57 :group 'tools
56 :group 'convenience) 58 :group 'convenience)
57 59
58 (defcustom calculator-electric-mode nil 60 (defcustom calculator-electric-mode nil
59 "*Run `calculator' electrically, in the echo area. 61 "*Run `calculator' electrically, in the echo area.
60 Note that if you use electric-mode, you wouldn't be able to use 62 Electric mode saves some place but changes the way you interact with the
61 conventional help keys." 63 calculator."
62 :type 'boolean 64 :type 'boolean
63 :group 'calculator) 65 :group 'calculator)
64 66
65 (defcustom calculator-use-menu t 67 (defcustom calculator-use-menu t
66 "*Make `calculator' create a menu. 68 "*Make `calculator' create a menu.
77 "*Value is either 'prefix or 'postfix. 79 "*Value is either 'prefix or 'postfix.
78 This determines the default behavior of unary operators." 80 This determines the default behavior of unary operators."
79 :type '(choice (const prefix) (const postfix)) 81 :type '(choice (const prefix) (const postfix))
80 :group 'calculator) 82 :group 'calculator)
81 83
82 (defcustom calculator-prompt "Calculator=%s> " 84 (defcustom calculator-prompt "Calc=%s> "
83 "*The prompt used by the pocket calculator. 85 "*The prompt used by the Emacs calculator.
84 It should contain a \"%s\" somewhere that will indicate the i/o radixes, 86 It should contain a \"%s\" somewhere that will indicate the i/o radixes,
85 this string will be a two-character string as described in the 87 this string will be a two-character string as described in the
86 documentation for `calculator-mode'." 88 documentation for `calculator-mode'."
87 :type 'string 89 :type 'string
88 :group 'calculator) 90 :group 'calculator)
89 91
90 (defcustom calculator-epsilon 1e-15 92 (defcustom calculator-number-digits 3
91 "*A threshold for results. 93 "*The calculator's number of digits used for standard display.
92 If any result computed in `calculator-funcall' is smaller than this in 94 Used by the `calculator-standard-display' function - it will use the
93 its absolute value, then zero will be returned." 95 format string \"%.NC\" where this number is N and C is a character given
94 :type 'number 96 at runtime."
95 :group 'calculator)
96
97 (defcustom calculator-number-format "%1.3f"
98 "*The calculator's string used to display normal numbers."
99 :type 'string 97 :type 'string
100 :group 'calculator) 98 :group 'calculator)
101 99
102 (defcustom calculator-number-exp-ulimit 1e16 100 (defcustom calculator-remove-zeros t
103 "*The calculator's upper limit for normal numbers." 101 "*Non-nil value means delete all redundant zero decimal digits.
104 :type 'number 102 If this value is not t, and not nil, redundant zeros are removed except
103 for one and if it is nil, nothing is removed.
104 Used by the `calculator-remove-zeros' function."
105 :type '(choice (const t) (const leave-decimal) (const nil))
105 :group 'calculator) 106 :group 'calculator)
106 107
107 (defcustom calculator-number-exp-llimit 0.001 108 (defcustom calculator-displayer '(std ?n)
108 "*The calculator's lower limit for normal numbers." 109 "*A displayer specification for numerical values.
109 :type 'number 110 This is the displayer used to show all numbers in an expression. Result
111 values will be displayed according to the first element of
112 `calculator-displayers'.
113
114 The displayer is a symbol, a string or an expression. A symbol should
115 be the name of a one-argument function, a string is used with a single
116 argument and an expression will be evaluated with the variable `num'
117 bound to whatever should be displayed. If it is a function symbol, it
118 should be able to handle special symbol arguments, currently 'left and
119 'right which will be sent by special keys to modify display parameters
120 associated with the displayer function (for example to change the number
121 of digits displayed).
122
123 An exception to the above is the case of the list (std C) where C is a
124 character, in this case the `calculator-standard-displayer' function
125 will be used with this character for a format string.")
126
127 (defcustom calculator-displayers
128 '(((std ?n) "Standard dislpay, decimal point or scientific")
129 (calculator-eng-display "Eng display")
130 ((std ?f) "Standard display, decimal point")
131 ((std ?e) "Standard dislpay, scientific")
132 ("%S" "Emacs printer"))
133 "*A list of displayers.
134 Each element is a list of a displayer and a description string. The
135 first element is the one which is curently used, this is for the display
136 of result values not values in expressions. A displayer specification
137 is the same as the values that can be stored in `calculator-displayer'.
138
139 `calculator-rotate-displayer' rotates this list."
140 :type 'sexp
110 :group 'calculator) 141 :group 'calculator)
111 142
112 (defcustom calculator-number-exp-format "%g" 143 (defcustom calculator-paste-decimals t
113 "*The calculator's string used to display exponential numbers." 144 "*If non-nil, convert pasted integers so they have a decimal point.
114 :type 'string 145 This makes it possible to paste big integers since they will be read as
115 :group 'calculator) 146 floats, otherwise the Emacs reader will fail on them."
116
117 (defcustom calculator-show-integers t
118 "*Non-nil value means delete all zero digits after the decimal point."
119 :type 'boolean 147 :type 'boolean
120 :group 'calculator) 148 :group 'calculator)
121 149
122 (defcustom calculator-2s-complement nil 150 (defcustom calculator-2s-complement nil
123 "*If non-nil, show negative numbers in 2s complement in radix modes. 151 "*If non-nil, show negative numbers in 2s complement in radix modes.
124 Otherwise show as a negative number." 152 Otherwise show as a negative number."
125 :type 'boolean 153 :type 'boolean
126 :group 'calculator) 154 :group 'calculator)
127 155
128 (defcustom calculator-mode-hook nil 156 (defcustom calculator-mode-hook nil
129 "*List of hook functions run by `calculator-mode'." 157 "*List of hook functions for `calculator-mode' to run."
130 :type 'hook 158 :type 'hook
131 :group 'calculator) 159 :group 'calculator)
132 160
133 (defcustom calculator-user-registers nil 161 (defcustom calculator-user-registers nil
134 "*An association list of user-defined register bindings. 162 "*An association list of user-defined register bindings.
135
136 Each element in this list is a list of a character and a number that 163 Each element in this list is a list of a character and a number that
137 will be stored in that character's register. 164 will be stored in that character's register.
138 165
139 For example, use this to define the golden ratio number: 166 For example, use this to define the golden ratio number:
140 (setq calculator-user-registers '((?g . 1.61803398875)))" 167 (setq calculator-user-registers '((?g . 1.61803398875)))
168 before you load calculator."
141 :type '(repeat (cons character number)) 169 :type '(repeat (cons character number))
142 :set '(lambda (_ val) 170 :set '(lambda (_ val)
143 (and (boundp 'calculator-registers) 171 (and (boundp 'calculator-registers)
144 (setq calculator-registers 172 (setq calculator-registers
145 (append val calculator-registers))) 173 (append val calculator-registers)))
146 (setq calculator-user-registers val)) 174 (setq calculator-user-registers val))
147 :group 'calculator) 175 :group 'calculator)
148 176
149 (defcustom calculator-user-operators nil 177 (defcustom calculator-user-operators nil
150 "*A list of additional operators. 178 "*A list of additional operators.
151
152 This is a list in the same format as specified in the documentation for 179 This is a list in the same format as specified in the documentation for
153 `calculator-operators', that you can use to bind additional calculator 180 `calculator-operators', that you can use to bind additional calculator
154 operators. It is probably not a good idea to modify this value with 181 operators. It is probably not a good idea to modify this value with
155 `customize' since it is too complex... 182 `customize' since it is too complex...
156 183
172 and F does a recursive call, Here is a [very inefficient] Fibonacci 199 and F does a recursive call, Here is a [very inefficient] Fibonacci
173 number calculation: 200 number calculation:
174 201
175 (add-to-list 'calculator-user-operators 202 (add-to-list 'calculator-user-operators
176 '(\"F\" fib (if (<= TX 1) 203 '(\"F\" fib (if (<= TX 1)
177 1 204 1
178 (+ (F (- TX 1)) (F (- TX 2)))) 0)) 205 (+ (F (- TX 1)) (F (- TX 2)))) 0))
179 206
180 Note that this will be either postfix or prefix, according to 207 Note that this will be either postfix or prefix, according to
181 `calculator-unary-style'." 208 `calculator-unary-style'."
182 :type '(repeat (list string symbol sexp integer integer)) 209 :type '(repeat (list string symbol sexp integer integer))
183 :group 'calculator) 210 :group 'calculator)
184 211
212 ;;;============================================================================
185 ;;; Code: 213 ;;; Code:
214
215 ;;;----------------------------------------------------------------------------
216 ;;; Variables
186 217
187 (defvar calculator-initial-operators 218 (defvar calculator-initial-operators
188 '(;; "+"/"-" have keybindings of themselves, not calculator-ops 219 '(;; "+"/"-" have keybindings of themselves, not calculator-ops
189 ("=" = identity 1 -1) 220 ("=" = identity 1 -1)
190 (nobind "+" + + 2 4) 221 (nobind "+" + + 2 4)
191 (nobind "-" - - 2 4) 222 (nobind "-" - - 2 4)
192 (nobind "+" + + -1 9) 223 (nobind "+" + + -1 9)
193 (nobind "-" - - -1 9) 224 (nobind "-" - - -1 9)
194 ("(" \( identity -1 -1) 225 ("(" \( identity -1 -1)
195 (")" \) identity +1 10) 226 (")" \) identity +1 10)
196 ;; normal keys 227 ;; normal keys
197 ("|" or (logior TX TY) 2 2) 228 ("|" or (logior TX TY) 2 2)
198 ("#" xor (logxor TX TY) 2 2) 229 ("#" xor (logxor TX TY) 2 2)
218 ("<" repL calculator-repL 1 8) 249 ("<" repL calculator-repL 1 8)
219 ("v" avg (/ (apply '+ L) (length L)) 0 8) 250 ("v" avg (/ (apply '+ L) (length L)) 0 8)
220 ("l" tot (apply '+ L) 0 8) 251 ("l" tot (apply '+ L) 0 8)
221 ) 252 )
222 "A list of initial operators. 253 "A list of initial operators.
223
224 This is a list in the same format as `calculator-operators'. Whenever 254 This is a list in the same format as `calculator-operators'. Whenever
225 `calculator' starts, it looks at the value of this variable, and if it 255 `calculator' starts, it looks at the value of this variable, and if it
226 is not empty, its contents is prepended to `calculator-operators' and 256 is not empty, its contents is prepended to `calculator-operators' and
227 the appropriate key bindings are made. 257 the appropriate key bindings are made.
228 258
241 versions), `DX' (converted to radians if degrees mode is on), `D' 271 versions), `DX' (converted to radians if degrees mode is on), `D'
242 (function for converting radians to degrees if deg mode is on), `L' 272 (function for converting radians to degrees if deg mode is on), `L'
243 (list of saved values), `F' (function for recursive iteration calls) 273 (list of saved values), `F' (function for recursive iteration calls)
244 and evaluates to the function value - these variables are capital; 274 and evaluates to the function value - these variables are capital;
245 275
246 4. The function's arity, optional, one of: 2=binary, -1=prefix unary, 276 4. The function's arity, optional, one of: 2 => binary, -1 => prefix
247 +1=postfix unary, 0=a 0-arg operator func, non-number=postfix/prefix 277 unary, +1 => postfix unary, 0 => a 0-arg operator func, non-number =>
248 as determined by `calculator-unary-style' (the default); 278 postfix/prefix as determined by `calculator-unary-style' (the
249 279 default);
250 5. The function's precedence - should be in the range of 1=lowest to 280
251 9=highest (optional, defaults to 1); 281 5. The function's precedence - should be in the range of 1 (lowest) to
282 9 (highest) (optional, defaults to 1);
252 283
253 It it possible have a unary prefix version of a binary operator if it 284 It it possible have a unary prefix version of a binary operator if it
254 comes later in this list. If the list begins with the symbol 'nobind, 285 comes later in this list. If the list begins with the symbol 'nobind,
255 then no key binding will take place - this is only useful for predefined 286 then no key binding will take place - this is only useful for predefined
256 keys. 287 keys.
293 "When non-nil, we see something that the next digit should replace.") 324 "When non-nil, we see something that the next digit should replace.")
294 325
295 (defvar calculator-buffer nil 326 (defvar calculator-buffer nil
296 "The current calculator buffer.") 327 "The current calculator buffer.")
297 328
329 (defvar calculator-eng-extra nil
330 "Internal value used by `calculator-eng-display'.")
331
332 (defvar calculator-eng-tmp-show nil
333 "Internal value used by `calculator-eng-display'.")
334
298 (defvar calculator-last-opXY nil 335 (defvar calculator-last-opXY nil
299 "The last binary operation and its arguments. 336 "The last binary operation and its arguments.
300 Used for repeating operations in calculator-repR/L.") 337 Used for repeating operations in calculator-repR/L.")
301 338
302 (defvar calculator-registers ; use user-bindings first 339 (defvar calculator-registers ; use user-bindings first
305 342
306 (defvar calculator-saved-global-map nil 343 (defvar calculator-saved-global-map nil
307 "Saved global key map.") 344 "Saved global key map.")
308 345
309 (defvar calculator-restart-other-mode nil 346 (defvar calculator-restart-other-mode nil
310 "Used to hack restarting with the mode electric mode changed.") 347 "Used to hack restarting with the electric mode changed.")
348
349 ;;;----------------------------------------------------------------------------
350 ;;; Key bindings
311 351
312 (defvar calculator-mode-map nil 352 (defvar calculator-mode-map nil
313 "The calculator key map.") 353 "The calculator key map.")
314 354
315 (or calculator-mode-map 355 (or calculator-mode-map
316 (let ((map (make-sparse-keymap))) 356 (let ((map (make-sparse-keymap)))
317 (suppress-keymap map t) 357 (suppress-keymap map t)
318 (define-key map "i" nil) 358 (define-key map "i" nil)
319 (define-key map "o" nil) 359 (define-key map "o" nil)
320 (let ((p 360 (let ((p
321 '(("(" "[" "{") 361 '((calculator-open-paren "[")
322 (")" "]" "}") 362 (calculator-close-paren "]")
323 (calculator-op-or-exp "+" "-" [kp-add] [kp-subtract]) 363 (calculator-op-or-exp "+" "-" [kp-add] [kp-subtract])
324 (calculator-digit "0" "1" "2" "3" "4" "5" "6" "7" "8" 364 (calculator-digit "0" "1" "2" "3" "4" "5" "6" "7" "8"
325 "9" "a" "b" "c" "d" "f" 365 "9" "a" "b" "c" "d" "f"
326 [kp-0] [kp-1] [kp-2] [kp-3] [kp-4] 366 [kp-0] [kp-1] [kp-2] [kp-3] [kp-4]
327 [kp-5] [kp-6] [kp-7] [kp-8] [kp-9]) 367 [kp-5] [kp-6] [kp-7] [kp-8] [kp-9])
328 (calculator-op [kp-divide] [kp-multiply]) 368 (calculator-op [kp-divide] [kp-multiply])
329 (calculator-decimal "." [kp-decimal]) 369 (calculator-decimal "." [kp-decimal])
330 (calculator-exp "e") 370 (calculator-exp "e")
331 (calculator-dec/deg-mode "D") 371 (calculator-dec/deg-mode "D")
332 (calculator-set-register "s") 372 (calculator-set-register "s")
333 (calculator-get-register "g") 373 (calculator-get-register "g")
334 (calculator-radix-mode "H" "X" "O" "B") 374 (calculator-radix-mode "H" "X" "O" "B")
335 (calculator-radix-input-mode "id" "ih" "ix" "io" "ib" 375 (calculator-radix-input-mode "id" "ih" "ix" "io" "ib"
336 "iD" "iH" "iX" "iO" "iB") 376 "iD" "iH" "iX" "iO" "iB")
337 (calculator-radix-output-mode "od" "oh" "ox" "oo" "ob" 377 (calculator-radix-output-mode "od" "oh" "ox" "oo" "ob"
338 "oD" "oH" "oX" "oO" "oB") 378 "oD" "oH" "oX" "oO" "oB")
379 (calculator-rotate-displayer "'")
380 (calculator-rotate-displayer-back "\"")
381 (calculator-displayer-left "{")
382 (calculator-displayer-right "}")
339 (calculator-saved-up [up] [?\C-p]) 383 (calculator-saved-up [up] [?\C-p])
340 (calculator-saved-down [down] [?\C-n]) 384 (calculator-saved-down [down] [?\C-n])
341 (calculator-quit "q" [?\C-g]) 385 (calculator-quit "q" [?\C-g])
342 ("=" [enter] [linefeed] [kp-enter] 386 (calculator-enter [enter] [linefeed] [kp-enter]
343 [?\r] [?\n]) 387 [return] [?\r] [?\n])
344 (calculator-save-on-list " " [space]) 388 (calculator-save-on-list " " [space])
345 (calculator-clear-saved [?\C-c] [(control delete)]) 389 (calculator-clear-saved [?\C-c] [(control delete)])
346 (calculator-save-and-quit [(control return)] 390 (calculator-save-and-quit [(control return)]
347 [(control kp-enter)]) 391 [(control kp-enter)])
348 (calculator-paste [insert] [(shift insert)]) 392 (calculator-paste [insert] [(shift insert)] [mouse-2])
349 (calculator-clear [delete] [?\C-?] [?\C-d]) 393 (calculator-clear [delete] [?\C-?] [?\C-d])
350 (calculator-help [?h] [??] [f1] [help]) 394 (calculator-help [?h] [??] [f1] [help])
351 (calculator-copy [(control insert)]) 395 (calculator-copy [(control insert)])
352 (calculator-backspace [backspace]) 396 (calculator-backspace [backspace])
353 ))) 397 )))
480 ,@(mapcar 'car radix-selectors) 524 ,@(mapcar 'car radix-selectors)
481 ("Seperate I/O" 525 ("Seperate I/O"
482 ,@(mapcar (lambda (x) (nth 1 x)) radix-selectors) 526 ,@(mapcar (lambda (x) (nth 1 x)) radix-selectors)
483 "---" 527 "---"
484 ,@(mapcar (lambda (x) (nth 2 x)) radix-selectors))) 528 ,@(mapcar (lambda (x) (nth 2 x)) radix-selectors)))
529 ("Decimal Dislpay"
530 ,@(mapcar (lambda (d)
531 (vector (cadr d)
532 ;; Note: inserts actual object here
533 `(calculator-rotate-displayer ',d)))
534 calculator-displayers)
535 "---"
536 ["Change Display Left" calculator-displayer-left]
537 ["Change Display Right" calculator-displayer-right])
485 "---" 538 "---"
486 ["Copy+Quit" calculator-save-and-quit] 539 ["Copy+Quit" calculator-save-and-quit]
487 ["Quit" calculator-quit])))) 540 ["Quit" calculator-quit]))))
488 (setq calculator-mode-map map))) 541 (setq calculator-mode-map map)))
489 542
543 ;;;----------------------------------------------------------------------------
544 ;;; Startup and mode stuff
545
490 (defun calculator-mode () 546 (defun calculator-mode ()
491 "A simple pocket calculator in Emacs. 547 ;; this help is also used as the major help screen
548 "A [not so] simple calculator for Emacs.
492 549
493 This calculator is used in the same way as other popular calculators 550 This calculator is used in the same way as other popular calculators
494 like xcalc or calc.exe - but using an Emacs interface. 551 like xcalc or calc.exe - but using an Emacs interface.
495 552
496 Expressions are entered using normal infix notation, parens are used as 553 Expressions are entered using normal infix notation, parens are used as
542 * \"D=\": degrees mode; 599 * \"D=\": degrees mode;
543 * \"?=\": (? is B/O/H) this is the radix for both input and output; 600 * \"?=\": (? is B/O/H) this is the radix for both input and output;
544 * \"=?\": (? is B/O/H) the display radix (when input is decimal); 601 * \"=?\": (? is B/O/H) the display radix (when input is decimal);
545 * \"??\": (? is D/B/O/H) 1st char for input radix, 2nd for display. 602 * \"??\": (? is D/B/O/H) 1st char for input radix, 2nd for display.
546 603
604 Also, the quote character can be used to switch display modes for
605 decimal numbers (double-quote rotates back), and the two brace
606 characters (\"{\" and \"}\" change display parameters that these
607 displayers use (if they handle such).
608
547 Values can be saved for future reference in either a list of saved 609 Values can be saved for future reference in either a list of saved
548 values, or in registers. 610 values, or in registers.
549 611
550 The list of saved values is useful for statistics operations on some 612 The list of saved values is useful for statistics operations on some
551 collected data. It is possible to navigate in this list, and if the 613 collected data. It is possible to navigate in this list, and if the
579 (setq major-mode 'calculator-mode) 641 (setq major-mode 'calculator-mode)
580 (setq mode-name "Calculator") 642 (setq mode-name "Calculator")
581 (use-local-map calculator-mode-map) 643 (use-local-map calculator-mode-map)
582 (run-hooks 'calculator-mode-hook)) 644 (run-hooks 'calculator-mode-hook))
583 645
646 (eval-when-compile (require 'electric) (require 'ehelp))
647
584 ;;;###autoload 648 ;;;###autoload
585 (defun calculator () 649 (defun calculator ()
586 "Run the pocket calculator. 650 "Run the Emacs calculator.
587 See the documentation for `calculator-mode' for more information." 651 See the documentation for `calculator-mode' for more information."
588 (interactive) 652 (interactive)
589 (if calculator-restart-other-mode 653 (if calculator-restart-other-mode
590 (setq calculator-electric-mode (not calculator-electric-mode))) 654 (setq calculator-electric-mode (not calculator-electric-mode)))
591 (if calculator-initial-operators 655 (if calculator-initial-operators
592 (progn (calculator-add-operators calculator-initial-operators) 656 (progn (calculator-add-operators calculator-initial-operators)
593 (setq calculator-initial-operators nil) 657 (setq calculator-initial-operators nil)
594 ;; don't change this since it is a customization variable, 658 ;; don't change this since it is a customization variable,
595 ;; its set function will add any new operators. 659 ;; its set function will add any new operators
596 (calculator-add-operators calculator-user-operators))) 660 (calculator-add-operators calculator-user-operators)))
597 (if calculator-electric-mode 661 (if calculator-electric-mode
598 (save-window-excursion 662 (save-window-excursion
599 (progn (require 'electric) (message nil)) ; hide load message 663 (progn (require 'electric) (message nil)) ; hide load message
600 (let (old-g-map old-l-map (echo-keystrokes 0) 664 (let (old-g-map old-l-map (echo-keystrokes 0)
630 (if calculator-electric-mode 694 (if calculator-electric-mode
631 (get-buffer-create "*calculator*") 695 (get-buffer-create "*calculator*")
632 (let ((split-window-keep-point nil) 696 (let ((split-window-keep-point nil)
633 (window-min-height 2)) 697 (window-min-height 2))
634 (select-window 698 (select-window
635 ;; Maybe leave two lines for our window because 699 ;; maybe leave two lines for our window because
636 ;; of the normal `raised' modeline in Emacs 21. 700 ;; of the normal `raised' modeline in Emacs 21
637 (split-window-vertically 701 (split-window-vertically
638 (- (window-height) 702 (- (window-height)
639 (if (plist-get (face-attr-construct 'modeline) 703 (if (and
640 :box) 704 (fboundp 'face-attr-construct)
641 3 705 (plist-get (face-attr-construct 'modeline)
642 2)))) 706 :box))
707 3
708 2))))
643 (switch-to-buffer 709 (switch-to-buffer
644 (get-buffer-create "*calculator*")))))) 710 (get-buffer-create "*calculator*"))))))
645 (set-buffer calculator-buffer) 711 (set-buffer calculator-buffer)
646 (calculator-mode) 712 (calculator-mode)
647 (setq buffer-read-only t) 713 (setq buffer-read-only t)
648 (calculator-reset) 714 (calculator-reset)
649 (message "Hit `?' For a quick help screen."))) 715 (message "Hit `?' For a quick help screen.")))
650 (if (and calculator-restart-other-mode calculator-electric-mode) 716 (if (and calculator-restart-other-mode calculator-electric-mode)
651 (calculator))) 717 (calculator)))
718
719 ;;;----------------------------------------------------------------------------
720 ;;; Operatos
652 721
653 (defun calculator-op-arity (op) 722 (defun calculator-op-arity (op)
654 "Return OP's arity, 2, +1 or -1." 723 "Return OP's arity, 2, +1 or -1."
655 (let ((arity (or (nth 3 op) 'x))) 724 (let ((arity (or (nth 3 op) 'x)))
656 (if (numberp arity) 725 (if (numberp arity)
687 added-ops)) 756 added-ops))
688 (setq more-ops (cdr more-ops))) 757 (setq more-ops (cdr more-ops)))
689 ;; added-ops come first, but in correct order 758 ;; added-ops come first, but in correct order
690 (setq calculator-operators 759 (setq calculator-operators
691 (append (nreverse added-ops) calculator-operators)))) 760 (append (nreverse added-ops) calculator-operators))))
761
762 ;;;----------------------------------------------------------------------------
763 ;;; Display stuff
692 764
693 (defun calculator-reset () 765 (defun calculator-reset ()
694 "Reset calculator variables." 766 "Reset calculator variables."
695 (or calculator-restart-other-mode 767 (or calculator-restart-other-mode
696 (setq calculator-stack nil 768 (setq calculator-stack nil
767 ((string-match "[eE][+-]?$" calculator-curnum) 839 ((string-match "[eE][+-]?$" calculator-curnum)
768 (concat calculator-curnum "0")) 840 (concat calculator-curnum "0"))
769 ((string-match "\\.[0-9]\\|[eE]" calculator-curnum) 841 ((string-match "\\.[0-9]\\|[eE]" calculator-curnum)
770 calculator-curnum) 842 calculator-curnum)
771 ((string-match "\\." calculator-curnum) 843 ((string-match "\\." calculator-curnum)
772 ;; do this because Emacs reads "23." as an integer. 844 ;; do this because Emacs reads "23." as an integer
773 (concat calculator-curnum "0")) 845 (concat calculator-curnum "0"))
774 ((stringp calculator-curnum) 846 ((stringp calculator-curnum)
775 (concat calculator-curnum ".0")) 847 (concat calculator-curnum ".0"))
776 (t "0.0")))))) 848 (t "0.0"))))))
849
850 (defun calculator-rotate-displayer (&optional new-disp)
851 "Switch to the next displayer on the `calculator-displayers' list.
852 Can be called with an optional argument NEW-DISP to force rotation to
853 that argument."
854 (interactive)
855 (setq calculator-displayers
856 (if (and new-disp (memq new-disp calculator-displayers))
857 (let ((tmp nil))
858 (while (not (eq (car calculator-displayers) new-disp))
859 (setq tmp (cons (car calculator-displayers) tmp))
860 (setq calculator-displayers (cdr calculator-displayers)))
861 (setq calculator-displayers
862 (nconc calculator-displayers (nreverse tmp))))
863 (nconc (cdr calculator-displayers)
864 (list (car calculator-displayers)))))
865 (message "Using %s." (cadr (car calculator-displayers)))
866 (if calculator-electric-mode
867 (progn (sit-for 1) (message nil)))
868 (calculator-enter))
869
870 (defun calculator-rotate-displayer-back ()
871 "Like `calculator-rotate-displayer', but rotates modes back."
872 (interactive)
873 (calculator-rotate-displayer (car (last calculator-displayers))))
874
875 (defun calculator-displayer-left ()
876 "Send the current displayer function a 'left argument.
877 This is used to modify display arguments (if the current displayer
878 function supports this)."
879 (interactive)
880 (and (car calculator-displayers)
881 (let ((disp (caar calculator-displayers)))
882 (cond ((symbolp disp) (funcall disp 'left))
883 ((and (consp disp) (eq 'std (car disp)))
884 (calculator-standard-displayer 'left (cadr disp)))))))
885
886 (defun calculator-displayer-right ()
887 "Send the current displayer function a 'right argument.
888 This is used to modify display arguments (if the current displayer
889 function supports this)."
890 (interactive)
891 (and (car calculator-displayers)
892 (let ((disp (caar calculator-displayers)))
893 (cond ((symbolp disp) (funcall disp 'right))
894 ((and (consp disp) (eq 'std (car disp)))
895 (calculator-standard-displayer 'right (cadr disp)))))))
896
897 (defun calculator-remove-zeros (numstr)
898 "Get a number string NUMSTR and remove unnecessary zeroes.
899 the behavior of this function is controlled by
900 `calculator-remove-zeros'."
901 (cond ((and (eq calculator-remove-zeros t)
902 (string-match "\\.0+\\([eE][+-]?[0-9]*\\)?$" numstr))
903 ;; remove all redundant zeros leaving an integer
904 (if (match-beginning 1)
905 (concat (substring numstr 0 (match-beginning 0))
906 (match-string 1 numstr))
907 (substring numstr 0 (match-beginning 0))))
908 ((and calculator-remove-zeros
909 (string-match
910 "\\..\\([0-9]*[1-9]\\)?\\(0+\\)\\([eE][+-]?[0-9]*\\)?$"
911 numstr))
912 ;; remove zeros, except for first after the "."
913 (if (match-beginning 3)
914 (concat (substring numstr 0 (match-beginning 2))
915 (match-string 3 numstr))
916 (substring numstr 0 (match-beginning 2))))
917 (t numstr)))
918
919 (defun calculator-standard-displayer (num char)
920 "Standard display function, used to display NUM.
921 Its behavior is determined by `calculator-number-digits' and the given
922 CHAR argument (both will be used to compose a format string). If the
923 char is \"n\" then this function will choose one between %f or %e, this
924 is a work around %g jumping to exponential notation too fast.
925
926 The special 'left and 'right symbols will make it change the current
927 number of digits displayed (`calculator-number-digits').
928
929 It will also remove redundant zeros from the result."
930 (if (symbolp num)
931 (cond ((eq num 'left)
932 (and (> calculator-number-digits 0)
933 (setq calculator-number-digits
934 (1- calculator-number-digits))
935 (calculator-enter)))
936 ((eq num 'right)
937 (setq calculator-number-digits
938 (1+ calculator-number-digits))
939 (calculator-enter)))
940 (let ((str (format
941 (concat "%."
942 (number-to-string calculator-number-digits)
943 (if (eq char ?n)
944 (let ((n (abs num)))
945 (if (or (< n 0.001) (> n 1e8)) "e" "f"))
946 (string char)))
947 num)))
948 (calculator-remove-zeros str))))
949
950 (defun calculator-eng-display (num)
951 "Display NUM in engineering notation.
952 The number of decimal digits used is controlled by
953 `calculator-number-digits', so to change it at runtime you have to use
954 the 'left or 'right when one of the standard modes is used."
955 (if (symbolp num)
956 (cond ((eq num 'left)
957 (setq calculator-eng-extra
958 (if calculator-eng-extra
959 (1+ calculator-eng-extra)
960 1))
961 (let ((calculator-eng-tmp-show t)) (calculator-enter)))
962 ((eq num 'right)
963 (setq calculator-eng-extra
964 (if calculator-eng-extra
965 (1- calculator-eng-extra)
966 -1))
967 (let ((calculator-eng-tmp-show t)) (calculator-enter))))
968 (let ((exp 0))
969 (and (not (= 0 num))
970 (progn
971 (while (< (abs num) 1.0)
972 (setq num (* num 1000.0)) (setq exp (- exp 3)))
973 (while (> (abs num) 999.0)
974 (setq num (/ num 1000.0)) (setq exp (+ exp 3)))
975 (and calculator-eng-tmp-show
976 (not (= 0 calculator-eng-extra))
977 (let ((i calculator-eng-extra))
978 (while (> i 0)
979 (setq num (* num 1000.0)) (setq exp (- exp 3))
980 (setq i (1- i)))
981 (while (< i 0)
982 (setq num (/ num 1000.0)) (setq exp (+ exp 3))
983 (setq i (1+ i)))))))
984 (or calculator-eng-tmp-show (setq calculator-eng-extra nil))
985 (let ((str (format (concat "%." calculator-number-digits "f")
986 num)))
987 (concat (let ((calculator-remove-zeros
988 ;; make sure we don't leave integers
989 (and calculator-remove-zeros 'x)))
990 (calculator-remove-zeros str))
991 "e" (number-to-string exp))))))
777 992
778 (defun calculator-num-to-string (num) 993 (defun calculator-num-to-string (num)
779 "Convert NUM to a displayable string." 994 "Convert NUM to a displayable string."
780 (cond 995 (cond
781 ((and (numberp num) calculator-output-radix) 996 ((and (numberp num) calculator-output-radix)
797 (setq str (match-string 1 s)))) 1012 (setq str (match-string 1 s))))
798 (upcase 1013 (upcase
799 (if (and (not calculator-2s-complement) (< num 0)) 1014 (if (and (not calculator-2s-complement) (< num 0))
800 (concat "-" str) 1015 (concat "-" str)
801 str)))) 1016 str))))
802 ((and (numberp num) 1017 ((and (numberp num) (car calculator-displayers))
803 ;; is this a normal-range number? 1018 (let ((disp (if (= 1 (length calculator-stack))
804 (>= (abs num) calculator-number-exp-llimit) 1019 ;; customizable display for a single value
805 (< (abs num) calculator-number-exp-ulimit)) 1020 (caar calculator-displayers)
806 (let ((str (format calculator-number-format num))) 1021 calculator-displayer)))
807 (cond 1022 (cond ((stringp disp) (format disp num))
808 ((and calculator-show-integers (string-match "\\.?0+$" str)) 1023 ((symbolp disp) (funcall disp num))
809 ;; remove all redundant zeros 1024 ((and (consp disp)
810 (substring str 0 (match-beginning 0))) 1025 (eq 'std (car disp)))
811 ((and (not calculator-show-integers) 1026 (calculator-standard-displayer
812 (string-match "\\..\\(.*[^0]\\)?\\(0+\\)$" str)) 1027 num (cadr disp)))
813 ;; remove zeros, except for first after the "." 1028 ((listp disp) (eval disp))
814 (substring str 0 (match-beginning 2))) 1029 (t (prin1-to-string num t)))))
815 (t str)))) 1030 ;; operators are printed here
816 ((numberp num) (format calculator-number-exp-format num))
817 (t (prin1-to-string (nth 1 num) t)))) 1031 (t (prin1-to-string (nth 1 num) t))))
818 1032
819 (defun calculator-update-display (&optional force) 1033 (defun calculator-update-display (&optional force)
820 "Update the display. 1034 "Update the display.
821 If optional argument FORCE is non-nil, don't use the cached string." 1035 If optional argument FORCE is non-nil, don't use the cached string."
848 (insert (calculator-get-prompt))) 1062 (insert (calculator-get-prompt)))
849 (set-buffer-modified-p nil) 1063 (set-buffer-modified-p nil)
850 (if calculator-display-fragile 1064 (if calculator-display-fragile
851 (goto-char (1+ (length calculator-prompt))) 1065 (goto-char (1+ (length calculator-prompt)))
852 (goto-char (1- (point))))) 1066 (goto-char (1- (point)))))
1067
1068 ;;;----------------------------------------------------------------------------
1069 ;;; Stack computations
853 1070
854 (defun calculator-reduce-stack (prec) 1071 (defun calculator-reduce-stack (prec)
855 "Reduce the stack using top operator. 1072 "Reduce the stack using top operator.
856 PREC is a precedence - reduce everything with higher precedence." 1073 PREC is a precedence - reduce everything with higher precedence."
857 (while 1074 (while
934 ;; needed for 0-ary ops that puts more values 1151 ;; needed for 0-ary ops that puts more values
935 (setcdr calculator-stack (cdr (cdr calculator-stack)))) 1152 (setcdr calculator-stack (cdr (cdr calculator-stack))))
936 (t ;; no more iterations 1153 (t ;; no more iterations
937 nil)))) 1154 nil))))
938 1155
1156 (defun calculator-funcall (f &optional X Y)
1157 "If F is a symbol, evaluate (F X Y).
1158 Otherwise, it should be a list, evaluate it with X, Y bound to the
1159 arguments."
1160 ;; remember binary ops for calculator-repR/L
1161 (if Y (setq calculator-last-opXY (list f X Y)))
1162 (condition-case nil
1163 ;; there used to be code here that returns 0 if the result was
1164 ;; smaller than calculator-epsilon (1e-15). I don't think this is
1165 ;; necessary now.
1166 (if (symbolp f)
1167 (cond ((and X Y) (funcall f X Y))
1168 (X (funcall f X))
1169 (t (funcall f)))
1170 ;; f is an expression
1171 (let* ((__f__ f) ; so we can get this value below...
1172 (TX (calculator-truncate X))
1173 (TY (and Y (calculator-truncate Y)))
1174 (DX (if calculator-deg (/ (* X pi) 180) X))
1175 (L calculator-saved-list)
1176 (Fbound (fboundp 'F))
1177 (Fsave (and Fbound (symbol-function 'F)))
1178 (Dbound (fboundp 'D))
1179 (Dsave (and Dbound (symbol-function 'D))))
1180 ;; a shortened version of flet
1181 (fset 'F (function
1182 (lambda (&optional x y)
1183 (calculator-funcall __f__ x y))))
1184 (fset 'D (function
1185 (lambda (x)
1186 (if calculator-deg (/ (* x 180) pi) x))))
1187 (unwind-protect (eval f)
1188 (if Fbound (fset 'F Fsave) (fmakunbound 'F))
1189 (if Dbound (fset 'D Dsave) (fmakunbound 'D)))))
1190 (error 0)))
1191
939 (eval-when-compile ; silence the compiler 1192 (eval-when-compile ; silence the compiler
940 (or (fboundp 'event-key) 1193 (or (fboundp 'event-key)
941 (defun event-key (&rest _) nil)) 1194 (defun event-key (&rest _) nil))
942 (or (fboundp 'key-press-event-p) 1195 (or (fboundp 'key-press-event-p)
943 (defun key-press-event-p (&rest _) nil))) 1196 (defun key-press-event-p (&rest _) nil)))
1197
1198 ;;;----------------------------------------------------------------------------
1199 ;;; Input interaction
944 1200
945 (defun calculator-last-input (&optional keys) 1201 (defun calculator-last-input (&optional keys)
946 "Last char (or event or event sequence) that was read. 1202 "Last char (or event or event sequence) that was read.
947 Optional string argument KEYS will force using it as the keys entered." 1203 Optional string argument KEYS will force using it as the keys entered."
948 (let ((inp (or keys (this-command-keys)))) 1204 (let ((inp (or keys (this-command-keys))))
956 (while (< (setq i (1+ i)) (length inp)) 1212 (while (< (setq i (1+ i)) (length inp))
957 (setq k (aref inp i)) 1213 (setq k (aref inp i))
958 ;; if Emacs will someday have a event-key, then this would 1214 ;; if Emacs will someday have a event-key, then this would
959 ;; probably be modified anyway 1215 ;; probably be modified anyway
960 (and (fboundp 'event-key) (key-press-event-p k) 1216 (and (fboundp 'event-key) (key-press-event-p k)
961 (setq k (event-key k))) 1217 (event-key k) (setq k (event-key k)))
962 ;; assume all symbols are translatable with an ascii-character 1218 ;; assume all symbols are translatable with an ascii-character
963 (and (symbolp k) 1219 (and (symbolp k)
964 (setq k (or (get k 'ascii-character) ? ))) 1220 (setq k (or (get k 'ascii-character) ? )))
965 (aset converted-str i k)) 1221 (aset converted-str i k))
966 converted-str)))) 1222 converted-str))))
971 (if (and calculator-display-fragile 1227 (if (and calculator-display-fragile
972 (or (not op) 1228 (or (not op)
973 (= -1 (calculator-op-arity op)) 1229 (= -1 (calculator-op-arity op))
974 (= 0 (calculator-op-arity op)))) 1230 (= 0 (calculator-op-arity op))))
975 ;; reset if last calc finished, and now get a num or prefix or 0-ary 1231 ;; reset if last calc finished, and now get a num or prefix or 0-ary
976 ;; op. 1232 ;; op
977 (calculator-reset)) 1233 (calculator-reset))
978 (setq calculator-display-fragile nil)) 1234 (setq calculator-display-fragile nil))
979 1235
980 (defun calculator-digit () 1236 (defun calculator-digit ()
981 "Enter a single digit." 1237 "Enter a single digit."
987 ((not calculator-input-radix) (<= inp ?9)) 1243 ((not calculator-input-radix) (<= inp ?9))
988 ((eq calculator-input-radix 'bin) (<= inp ?1)) 1244 ((eq calculator-input-radix 'bin) (<= inp ?1))
989 ((eq calculator-input-radix 'oct) (<= inp ?7)) 1245 ((eq calculator-input-radix 'oct) (<= inp ?7))
990 (t t))) 1246 (t t)))
991 ;; enter digit if starting a new computation or have an op on the 1247 ;; enter digit if starting a new computation or have an op on the
992 ;; stack. 1248 ;; stack
993 (progn 1249 (progn
994 (calculator-clear-fragile) 1250 (calculator-clear-fragile)
995 (let ((digit (upcase (char-to-string inp)))) 1251 (let ((digit (upcase (char-to-string inp))))
996 (if (equal calculator-curnum "0") 1252 (if (equal calculator-curnum "0")
997 (setq calculator-curnum nil)) 1253 (setq calculator-curnum nil))
1006 (or calculator-display-fragile 1262 (or calculator-display-fragile
1007 (not (numberp (car calculator-stack)))) 1263 (not (numberp (car calculator-stack))))
1008 (not (and calculator-curnum 1264 (not (and calculator-curnum
1009 (string-match "[.eE]" calculator-curnum)))) 1265 (string-match "[.eE]" calculator-curnum))))
1010 ;; enter the period on the same condition as a digit, only if no 1266 ;; enter the period on the same condition as a digit, only if no
1011 ;; period or exponent entered yet. 1267 ;; period or exponent entered yet
1012 (progn 1268 (progn
1013 (calculator-clear-fragile) 1269 (calculator-clear-fragile)
1014 (setq calculator-curnum (concat (or calculator-curnum "0") ".")) 1270 (setq calculator-curnum (concat (or calculator-curnum "0") "."))
1015 (calculator-update-display)))) 1271 (calculator-update-display))))
1016 1272
1021 (calculator-digit) 1277 (calculator-digit)
1022 (if (and (or calculator-display-fragile 1278 (if (and (or calculator-display-fragile
1023 (not (numberp (car calculator-stack)))) 1279 (not (numberp (car calculator-stack))))
1024 (not (and calculator-curnum 1280 (not (and calculator-curnum
1025 (string-match "[eE]" calculator-curnum)))) 1281 (string-match "[eE]" calculator-curnum))))
1026 ;; same condition as above, also no E so far. 1282 ;; same condition as above, also no E so far
1027 (progn 1283 (progn
1028 (calculator-clear-fragile) 1284 (calculator-clear-fragile)
1029 (setq calculator-curnum (concat (or calculator-curnum "1") "e")) 1285 (setq calculator-curnum (concat (or calculator-curnum "1") "e"))
1030 (calculator-update-display))))) 1286 (calculator-update-display)))))
1031 1287
1032 (defun calculator-op (&optional keys) 1288 (defun calculator-op (&optional keys)
1033 "Enter an operator on the stack, doing all necessary reductions. 1289 "Enter an operator on the stack, doing all necessary reductions.
1034 Optional string argument KEYS will force using it as the keys entered." 1290 Optional string argument KEYS will force using it as the keys entered."
1035 (interactive) 1291 (interactive)
1036 (let* ((last-inp (calculator-last-input keys)) 1292 (catch 'op-error
1037 (op (assoc last-inp calculator-operators))) 1293 (let* ((last-inp (calculator-last-input keys))
1038 (calculator-clear-fragile op) 1294 (op (assoc last-inp calculator-operators)))
1039 (if (and calculator-curnum (/= (calculator-op-arity op) 0)) 1295 (calculator-clear-fragile op)
1040 (setq calculator-stack 1296 (if (and calculator-curnum (/= (calculator-op-arity op) 0))
1041 (cons (calculator-curnum-value) calculator-stack))) 1297 (setq calculator-stack
1042 (setq calculator-curnum nil) 1298 (cons (calculator-curnum-value) calculator-stack)))
1043 (if (and (= 2 (calculator-op-arity op)) 1299 (setq calculator-curnum nil)
1044 (not (and calculator-stack 1300 (if (and (= 2 (calculator-op-arity op))
1045 (numberp (nth 0 calculator-stack))))) 1301 (not (and calculator-stack
1046 ;; we have a binary operator but no number - search for a prefix 1302 (numberp (nth 0 calculator-stack)))))
1047 ;; version 1303 ;; we have a binary operator but no number - search for a prefix
1048 (let ((rest-ops calculator-operators)) 1304 ;; version
1049 (while (not (equal last-inp (car (car rest-ops)))) 1305 (let ((rest-ops calculator-operators))
1050 (setq rest-ops (cdr rest-ops))) 1306 (while (not (equal last-inp (car (car rest-ops))))
1051 (setq op (assoc last-inp (cdr rest-ops))) 1307 (setq rest-ops (cdr rest-ops)))
1052 (if (not (and op (= -1 (calculator-op-arity op)))) 1308 (setq op (assoc last-inp (cdr rest-ops)))
1053 (error "Binary operator without a first operand")))) 1309 (if (not (and op (= -1 (calculator-op-arity op))))
1054 (calculator-reduce-stack 1310 ;;(error "Binary operator without a first operand")
1055 (cond ((eq (nth 1 op) '\() 10) 1311 (progn
1056 ((eq (nth 1 op) '\)) 0) 1312 (message "Binary operator without a first operand")
1057 (t (calculator-op-prec op)))) 1313 (if calculator-electric-mode
1058 (if (or (and (= -1 (calculator-op-arity op)) 1314 (progn (sit-for 1) (message nil)))
1059 (numberp (car calculator-stack))) 1315 (throw 'op-error nil)))))
1060 (and (/= (calculator-op-arity op) -1) 1316 (calculator-reduce-stack
1061 (/= (calculator-op-arity op) 0) 1317 (cond ((eq (nth 1 op) '\() 10)
1062 (not (numberp (car calculator-stack))))) 1318 ((eq (nth 1 op) '\)) 0)
1063 (error "Unterminated expression")) 1319 (t (calculator-op-prec op))))
1064 (setq calculator-stack (cons op calculator-stack)) 1320 (if (or (and (= -1 (calculator-op-arity op))
1065 (calculator-reduce-stack (calculator-op-prec op)) 1321 (numberp (car calculator-stack)))
1066 (and (= (length calculator-stack) 1) 1322 (and (/= (calculator-op-arity op) -1)
1067 (numberp (nth 0 calculator-stack)) 1323 (/= (calculator-op-arity op) 0)
1068 ;; the display is fragile if it contains only one number 1324 (not (numberp (car calculator-stack)))))
1069 (setq calculator-display-fragile t) 1325 ;;(error "Unterminated expression")
1070 ;; add number to the saved-list 1326 (progn
1071 calculator-add-saved 1327 (message "Unterminated expression")
1072 (if (= 0 calculator-saved-ptr) 1328 (if calculator-electric-mode
1073 (setq calculator-saved-list 1329 (progn (sit-for 1) (message nil)))
1074 (cons (car calculator-stack) calculator-saved-list)) 1330 (throw 'op-error nil)))
1075 (let ((p (nthcdr (1- calculator-saved-ptr) 1331 (setq calculator-stack (cons op calculator-stack))
1076 calculator-saved-list))) 1332 (calculator-reduce-stack (calculator-op-prec op))
1077 (setcdr p (cons (car calculator-stack) (cdr p)))))) 1333 (and (= (length calculator-stack) 1)
1078 (calculator-update-display))) 1334 (numberp (nth 0 calculator-stack))
1335 ;; the display is fragile if it contains only one number
1336 (setq calculator-display-fragile t)
1337 ;; add number to the saved-list
1338 calculator-add-saved
1339 (if (= 0 calculator-saved-ptr)
1340 (setq calculator-saved-list
1341 (cons (car calculator-stack) calculator-saved-list))
1342 (let ((p (nthcdr (1- calculator-saved-ptr)
1343 calculator-saved-list)))
1344 (setcdr p (cons (car calculator-stack) (cdr p))))))
1345 (calculator-update-display))))
1079 1346
1080 (defun calculator-op-or-exp () 1347 (defun calculator-op-or-exp ()
1081 "Either enter an operator or a digit. 1348 "Either enter an operator or a digit.
1082 Used with +/- for entering them as digits in numbers like 1e-3." 1349 Used with +/- for entering them as digits in numbers like 1e-3 (there is
1350 no need for negative numbers since these are handled by unary
1351 operators)."
1083 (interactive) 1352 (interactive)
1084 (if (and (not calculator-display-fragile) 1353 (if (and (not calculator-display-fragile)
1085 calculator-curnum 1354 calculator-curnum
1086 (string-match "[eE]$" calculator-curnum)) 1355 (string-match "[eE]$" calculator-curnum))
1087 (calculator-digit) 1356 (calculator-digit)
1088 (calculator-op))) 1357 (calculator-op)))
1358
1359 ;;;----------------------------------------------------------------------------
1360 ;;; Input/output modes (not display)
1089 1361
1090 (defun calculator-dec/deg-mode () 1362 (defun calculator-dec/deg-mode ()
1091 "Set decimal mode for display & input, if decimal, toggle deg mode." 1363 "Set decimal mode for display & input, if decimal, toggle deg mode."
1092 (interactive) 1364 (interactive)
1093 (if calculator-curnum 1365 (if calculator-curnum
1134 (let ((inp (calculator-last-input keys))) 1406 (let ((inp (calculator-last-input keys)))
1135 (cdr (assq (upcase (aref inp (1- (length inp)))) 1407 (cdr (assq (upcase (aref inp (1- (length inp))))
1136 calculator-char-radix)))) 1408 calculator-char-radix))))
1137 (calculator-update-display t)) 1409 (calculator-update-display t))
1138 1410
1411 ;;;----------------------------------------------------------------------------
1412 ;;; Saved values list
1413
1139 (defun calculator-save-on-list () 1414 (defun calculator-save-on-list ()
1140 "Evaluate current expression, put result on the saved values list." 1415 "Evaluate current expression, put result on the saved values list."
1141 (interactive) 1416 (interactive)
1142 (let ((calculator-add-saved t)) ; marks the result to be added 1417 (let ((calculator-add-saved t)) ; marks the result to be added
1143 (calculator-enter))) 1418 (calculator-enter)))
1144 1419
1145 (defun calculator-clear-saved () 1420 (defun calculator-clear-saved ()
1146 "Clear the list of saved values in `calculator-saved-list'." 1421 "Clear the list of saved values in `calculator-saved-list'."
1147 (interactive) 1422 (interactive)
1148 (setq calculator-saved-list nil) 1423 (setq calculator-saved-list nil)
1424 (setq calculator-saved-ptr 0)
1149 (calculator-update-display t)) 1425 (calculator-update-display t))
1150 1426
1151 (defun calculator-saved-move (n) 1427 (defun calculator-saved-move (n)
1152 "Go N elements up the list of saved values." 1428 "Go N elements up the list of saved values."
1153 (interactive) 1429 (interactive)
1172 1448
1173 (defun calculator-saved-down () 1449 (defun calculator-saved-down ()
1174 "Go down the list of saved values." 1450 "Go down the list of saved values."
1175 (interactive) 1451 (interactive)
1176 (calculator-saved-move -1)) 1452 (calculator-saved-move -1))
1453
1454 ;;;----------------------------------------------------------------------------
1455 ;;; Misc functions
1177 1456
1178 (defun calculator-open-paren () 1457 (defun calculator-open-paren ()
1179 "Equivalents of `(' use this." 1458 "Equivalents of `(' use this."
1180 (interactive) 1459 (interactive)
1181 (calculator-op "(")) 1460 (calculator-op "("))
1229 1508
1230 (defun calculator-copy () 1509 (defun calculator-copy ()
1231 "Copy current number to the `kill-ring'." 1510 "Copy current number to the `kill-ring'."
1232 (interactive) 1511 (interactive)
1233 (calculator-enter) 1512 (calculator-enter)
1234 ;; remove trailing .0 and spaces .0 1513 ;; remove trailing spaces and and an index
1235 (let ((s (cdr calculator-stack-display))) 1514 (let ((s (cdr calculator-stack-display)))
1236 (if (string-match "^\\(.*[^ ]\\) *$" s) 1515 (if (string-match "^\\([^ ]+\\) *\\(\\[[0-9/]+\\]\\)? *$" s)
1237 (setq s (match-string 1 s))) 1516 (setq s (match-string 1 s)))
1238 (kill-new s))) 1517 (kill-new s)))
1239 1518
1240 (defun calculator-set-register (reg) 1519 (defun calculator-set-register (reg)
1241 "Set a register value for REG." 1520 "Set a register value for REG."
1262 1541
1263 (defun calculator-paste () 1542 (defun calculator-paste ()
1264 "Paste a value from the `kill-ring'." 1543 "Paste a value from the `kill-ring'."
1265 (interactive) 1544 (interactive)
1266 (calculator-put-value 1545 (calculator-put-value
1267 (condition-case nil (car (read-from-string (current-kill 0))) 1546 (let ((str (current-kill 0)))
1268 (error nil)))) 1547 (if calculator-paste-decimals
1548 (progn
1549 (string-match "\\([0-9]+\\)\\(\\.[0-9]+\\)?\\(e[0-9]+\\)?" str)
1550 (if (or (match-string 1 str)
1551 (match-string 2 str)
1552 (match-string 3 str))
1553 (setq str (concat (match-string 1 str)
1554 (or (match-string 2 str) ".0")
1555 (match-string 3 str))))))
1556 (condition-case nil (car (read-from-string str))
1557 (error nil)))))
1269 1558
1270 (defun calculator-get-register (reg) 1559 (defun calculator-get-register (reg)
1271 "Get a value from a register REG." 1560 "Get a value from a register REG."
1272 (interactive "cRegister to get value from: ") 1561 (interactive "cRegister to get value from: ")
1273 (calculator-put-value (cdr (assq reg calculator-registers)))) 1562 (calculator-put-value (cdr (assq reg calculator-registers))))
1277 "Quick reference: 1566 "Quick reference:
1278 * numbers/operators/parens/./e - enter expressions 1567 * numbers/operators/parens/./e - enter expressions
1279 + - * / \\(div) %(rem) _(-X,postfix) ;(1/X,postfix) ^(exp) L(og) 1568 + - * / \\(div) %(rem) _(-X,postfix) ;(1/X,postfix) ^(exp) L(og)
1280 Q(sqrt) !(fact) S(in) C(os) T(an) |(or) #(xor) &(and) ~(not) 1569 Q(sqrt) !(fact) S(in) C(os) T(an) |(or) #(xor) &(and) ~(not)
1281 * >/< repeats last binary operation with its 2nd (1st) arg as postfix op 1570 * >/< repeats last binary operation with its 2nd (1st) arg as postfix op
1282 * I inverses next trig function 1571 * I inverses next trig function * '/\"/{} - display/display args
1283 * D - switch to all-decimal mode, or toggles deg/rad mode 1572 * D - switch to all-decimal, or toggle deg/rad mode
1284 * B/O/H/X - binary/octal/hex mode for i/o (X is a shortcut for H) 1573 * B/O/H/X - binary/octal/hex mode for i/o (X is a shortcut for H)
1285 * i/o - prefix for d/b/o/x - set only input/output modes 1574 * i/o - prefix for d/b/o/x - set only input/output modes
1286 * enter/= - evaluate current expr. * s/g - set/get a register 1575 * enter/= - evaluate current expr. * s/g - set/get a register
1287 * space - evaluate & save on list * l/v - list total/average 1576 * space - evaluate & save on list * l/v - list total/average
1288 * up/down/C-p/C-n - browse saved * C-delete - clear all saved 1577 * up/down/C-p/C-n - browse saved * C-delete - clear all saved
1341 (interactive) 1630 (interactive)
1342 (calculator-enter) 1631 (calculator-enter)
1343 (calculator-copy) 1632 (calculator-copy)
1344 (calculator-quit)) 1633 (calculator-quit))
1345 1634
1346 (defun calculator-funcall (f &optional X Y)
1347 "If F is a symbol, evaluate (F X Y).
1348 Otherwise, it should be a list, evaluate it with X, Y bound to the
1349 arguments."
1350 ;; remember binary ops for calculator-repR/L
1351 (if Y (setq calculator-last-opXY (list f X Y)))
1352 (condition-case nil
1353 (let ((result
1354 (if (symbolp f)
1355 (cond ((and X Y) (funcall f X Y))
1356 (X (funcall f X))
1357 (t (funcall f)))
1358 ;; f is an expression
1359 (let* ((__f__ f) ; so we can get this value below...
1360 (TX (calculator-truncate X))
1361 (TY (and Y (calculator-truncate Y)))
1362 (DX (if calculator-deg (/ (* X pi) 180) X))
1363 (L calculator-saved-list)
1364 (Fbound (fboundp 'F))
1365 (Fsave (and Fbound (symbol-function 'F)))
1366 (Dbound (fboundp 'D))
1367 (Dsave (and Dbound (symbol-function 'D))))
1368 ;; a shortened version of flet
1369 (fset 'F (function
1370 (lambda (&optional x y)
1371 (calculator-funcall __f__ x y))))
1372 (fset 'D (function
1373 (lambda (x)
1374 (if calculator-deg (/ (* x 180) pi) x))))
1375 (unwind-protect (eval f)
1376 (if Fbound (fset 'F Fsave) (fmakunbound 'F))
1377 (if Dbound (fset 'D Dsave) (fmakunbound 'D)))))))
1378 (if (< (abs result) calculator-epsilon)
1379 0
1380 result))
1381 (error 0)))
1382
1383 (defun calculator-repR (x) 1635 (defun calculator-repR (x)
1384 "Repeats the last binary operation with its second argument and X. 1636 "Repeats the last binary operation with its second argument and X.
1385 To use this, apply a binary operator (evaluate it), then call this." 1637 To use this, apply a binary operator (evaluate it), then call this."
1386 (if calculator-last-opXY 1638 (if calculator-last-opXY
1387 ;; avoid rebinding calculator-last-opXY 1639 ;; avoid rebinding calculator-last-opXY