Mercurial > emacs
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 |