calculator.el 67 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664
  1. ;;; calculator.el --- a calculator for Emacs -*- lexical-binding: t -*-
  2. ;; Copyright (C) 1998, 2000-2017 Free Software Foundation, Inc.
  3. ;; Author: Eli Barzilay <eli@barzilay.org>
  4. ;; Keywords: tools, convenience
  5. ;; This file is part of GNU Emacs.
  6. ;; GNU Emacs is free software: you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation, either version 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; GNU Emacs is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  16. ;;;=====================================================================
  17. ;;; Commentary:
  18. ;;
  19. ;; A calculator for Emacs.
  20. ;; Why should you reach for your mouse to get xcalc (calc.exe, gcalc or
  21. ;; whatever), when you have Emacs running already?
  22. ;;
  23. ;; If this is not part of your Emacs distribution, then simply bind
  24. ;; `calculator' to a key and make it an autoloaded function, e.g.:
  25. ;; (autoload 'calculator "calculator"
  26. ;; "Run the Emacs calculator." t)
  27. ;; (global-set-key [(control return)] 'calculator)
  28. ;;
  29. ;; Written by Eli Barzilay, eli@barzilay.org
  30. ;;
  31. ;;;=====================================================================
  32. ;;; Customization:
  33. (defgroup calculator nil
  34. "Simple Emacs calculator."
  35. :prefix "calculator"
  36. :version "21.1"
  37. :group 'tools
  38. :group 'applications)
  39. (defcustom calculator-electric-mode nil
  40. "Run `calculator' electrically, in the echo area.
  41. Electric mode saves some place but changes the way you interact with the
  42. calculator."
  43. :type 'boolean
  44. :group 'calculator)
  45. (defcustom calculator-use-menu t
  46. "Make `calculator' create a menu.
  47. Note that this requires easymenu. Must be set before loading."
  48. :type 'boolean
  49. :group 'calculator)
  50. (defcustom calculator-bind-escape nil
  51. "If non-nil, set escape to exit the calculator."
  52. :type 'boolean
  53. :group 'calculator)
  54. (defcustom calculator-unary-style 'postfix
  55. "Value is either `prefix' or `postfix'.
  56. This determines the default behavior of unary operators."
  57. :type '(choice (const prefix) (const postfix))
  58. :group 'calculator)
  59. (defcustom calculator-prompt "Calc=%s> "
  60. "The prompt used by the Emacs calculator.
  61. It should contain a \"%s\" somewhere that will indicate the i/o radixes;
  62. this will be a two-character string as described in the documentation
  63. for `calculator-mode'."
  64. :type 'string
  65. :group 'calculator)
  66. (defcustom calculator-number-digits 3
  67. "The calculator's number of digits used for standard display.
  68. Used by the `calculator-standard-display' function - it will use the
  69. format string \"%.NC\" where this number is N and C is a character given
  70. at runtime."
  71. :type 'integer
  72. :group 'calculator)
  73. (defcustom calculator-radix-grouping-mode t
  74. "Use digit grouping in radix output mode.
  75. If this is set, chunks of `calculator-radix-grouping-digits' characters
  76. will be separated by `calculator-radix-grouping-separator' when in radix
  77. output mode is active (determined by `calculator-output-radix')."
  78. :type 'boolean
  79. :group 'calculator)
  80. (defcustom calculator-radix-grouping-digits 4
  81. "The number of digits used for grouping display in radix modes.
  82. See `calculator-radix-grouping-mode'."
  83. :type 'integer
  84. :group 'calculator)
  85. (defcustom calculator-radix-grouping-separator "'"
  86. "The separator used in radix grouping display.
  87. See `calculator-radix-grouping-mode'."
  88. :type 'string
  89. :group 'calculator)
  90. (defcustom calculator-remove-zeros t
  91. "Non-nil value means delete all redundant zero decimal digits.
  92. If this value is not t and not nil, redundant zeros are removed except
  93. for one.
  94. Used by the `calculator-remove-zeros' function."
  95. :type '(choice (const t) (const leave-decimal) (const nil))
  96. :group 'calculator)
  97. (defcustom calculator-displayer '(std ?n)
  98. "A displayer specification for numerical values.
  99. This is the displayer used to show all numbers in an expression. Result
  100. values will be displayed according to the first element of
  101. `calculator-displayers'.
  102. The displayer is a symbol, a string or an expression. A symbol should
  103. be the name of a one-argument function, a string is used with a single
  104. argument and an expression will be evaluated with the variable `num'
  105. bound to whatever should be displayed. If it is a function symbol, it
  106. should be able to handle special symbol arguments, currently `left' and
  107. `right' which will be sent by special keys to modify display parameters
  108. associated with the displayer function (for example to change the number
  109. of digits displayed).
  110. An exception to the above is the case of the list (std C [G]) where C is
  111. a character and G is an optional boolean, in this case the
  112. `calculator-standard-displayer' function will be used with these as
  113. arguments."
  114. :type '(choice (function) (string) (sexp)
  115. (list (const std) character)
  116. (list (const std) character boolean))
  117. :group 'calculator)
  118. (defcustom calculator-displayers
  119. '(((std ?n) "Standard display, decimal point or scientific")
  120. (calculator-eng-display "Eng display")
  121. ((std ?f t) "Standard display, decimal point with grouping")
  122. ((std ?e) "Standard display, scientific")
  123. ("%S" "Emacs printer"))
  124. "A list of displayers.
  125. Each element is a list of a displayer and a description string. The
  126. first element is the one which is currently used, this is for the
  127. display of result values not values in expressions. A displayer
  128. specification is the same as the values that can be stored in
  129. `calculator-displayer'.
  130. `calculator-rotate-displayer' rotates this list."
  131. :type 'sexp
  132. :group 'calculator)
  133. (defcustom calculator-paste-decimals t
  134. "If non-nil, convert pasted integers so they have a decimal point.
  135. This makes it possible to paste big integers since they will be read as
  136. floats, otherwise the Emacs reader will fail on them."
  137. :type 'boolean
  138. :group 'calculator)
  139. (make-obsolete-variable 'calculator-paste-decimals
  140. "it is no longer used." nil)
  141. (defcustom calculator-copy-displayer nil
  142. "If non-nil, this is any value that can be used for
  143. `calculator-displayer', to format a string before copying it with
  144. `calculator-copy'. If nil, then `calculator-displayer's normal value is
  145. used."
  146. :type 'boolean
  147. :group 'calculator)
  148. (defcustom calculator-2s-complement nil
  149. "If non-nil, show negative numbers in 2s complement in radix modes.
  150. Otherwise show as a negative number."
  151. :type 'boolean
  152. :group 'calculator)
  153. (defcustom calculator-mode-hook nil
  154. "List of hook functions for `calculator-mode' to run.
  155. Note: if `calculator-electric-mode' is on, then this hook will get
  156. activated in the minibuffer -- in that case it should not do much more
  157. than local key settings and other effects that will change things
  158. outside the scope of calculator related code."
  159. :type 'hook
  160. :group 'calculator)
  161. (defcustom calculator-user-registers nil
  162. "An association list of user-defined register bindings.
  163. Each element in this list is a list of a character and a number that
  164. will be stored in that character's register.
  165. For example, use this to define the golden ratio number:
  166. (setq calculator-user-registers \\='((?g . 1.61803398875)))
  167. before you load calculator."
  168. :type '(repeat (cons character number))
  169. :set (lambda (_ val)
  170. (when (boundp 'calculator-registers)
  171. (setq calculator-registers
  172. (append val calculator-registers)))
  173. (setq calculator-user-registers val))
  174. :group 'calculator)
  175. (defcustom calculator-user-operators nil
  176. "A list of additional operators.
  177. This is a list in the same format as specified in the documentation for
  178. `calculator-operators', that you can use to bind additional calculator
  179. operators. It is probably not a good idea to modify this value with
  180. `customize' since it is too complex...
  181. Examples:
  182. * A very simple one, adding a postfix \"x-to-y\" conversion keys, using
  183. t as a prefix key:
  184. (setq calculator-user-operators
  185. \\='((\"tf\" cl-to-fr (+ 32 (/ (* X 9) 5)) 1)
  186. (\"tc\" fr-to-cl (/ (* (- X 32) 5) 9) 1)
  187. (\"tp\" kg-to-lb (/ X 0.453592) 1)
  188. (\"tk\" lb-to-kg (* X 0.453592) 1)
  189. (\"tF\" mt-to-ft (/ X 0.3048) 1)
  190. (\"tM\" ft-to-mt (* X 0.3048) 1)))
  191. * Using a function-like form is simple: use `X' for the argument (`Y'
  192. for a second one in case of a binary operator), `TX' is a truncated
  193. version of `X' and `F' for a recursive call. Here is a [very
  194. inefficient] Fibonacci number operator:
  195. (add-to-list \\='calculator-user-operators
  196. \\='(\"F\" fib
  197. (if (<= TX 1) 1 (+ (F (- TX 1)) (F (- TX 2))))))
  198. Note that this will be either postfix or prefix, according to
  199. `calculator-unary-style'."
  200. :type '(repeat (list string symbol sexp integer integer))
  201. :group 'calculator)
  202. ;;;=====================================================================
  203. ;;; Code:
  204. (eval-when-compile (require 'cl-lib))
  205. ;;;---------------------------------------------------------------------
  206. ;;; Variables
  207. (defvar calculator-initial-operators
  208. '(;; "+"/"-" have keybindings of their own, not calculator-ops
  209. ("=" = identity 1 -1)
  210. (nobind "+" + + 2 4)
  211. (nobind "-" - - 2 4)
  212. (nobind "+" + + -1 9)
  213. (nobind "-" - - -1 9)
  214. ("(" \( identity -1 -1)
  215. (")" \) identity +1 10)
  216. ;; normal keys
  217. ("|" or (logior TX TY) 2 2)
  218. ("#" xor (logxor TX TY) 2 2)
  219. ("&" and (logand TX TY) 2 3)
  220. ("*" * * 2 5)
  221. ("/" / / 2 5)
  222. ("\\" div (/ TX TY) 2 5)
  223. ("%" rem (% TX TY) 2 5)
  224. ("L" log log 2 6)
  225. ("S" sin (sin DX) x 6)
  226. ("C" cos (cos DX) x 6)
  227. ("T" tan (tan DX) x 6)
  228. ("IS" asin (D (asin X)) x 6)
  229. ("IC" acos (D (acos X)) x 6)
  230. ("IT" atan (D (atan X)) x 6)
  231. ("Q" sqrt sqrt x 7)
  232. ("^" ^ calculator-expt 2 7)
  233. ("!" ! calculator-fact x 7)
  234. (";" 1/ (/ 1 X) 1 7)
  235. ("_" - - 1 8)
  236. ("~" ~ (lognot TX) x 8)
  237. (">" repR calculator-repR 1 8)
  238. ("<" repL calculator-repL 1 8)
  239. ("v" avg (/ (apply '+ L) (length L)) 0 8)
  240. ("l" tot (apply '+ L) 0 8)
  241. )
  242. "A list of initial operators.
  243. This is a list in the same format as `calculator-operators'. Whenever
  244. `calculator' starts, it looks at the value of this variable, and if it
  245. is not empty, its contents is prepended to `calculator-operators' and
  246. the appropriate key bindings are made.
  247. This variable is then reset to nil. Don't use this if you want to add
  248. user-defined operators, use `calculator-user-operators' instead.")
  249. (defvar calculator-operators nil
  250. "The calculator operators, each a list with:
  251. 1. The key(s) that is bound to for this operation, a string that is
  252. used with `kbd';
  253. 2. The displayed symbol for this function;
  254. 3. The function symbol, or a form that uses the variables `X' and `Y',
  255. (if it is a binary operator), `TX' and `TY' (truncated integer
  256. versions), `DX' (converted to radians if degrees mode is on), `D'
  257. (function for converting radians to degrees if deg mode is on), `L'
  258. (list of saved values), `F' (function for recursive iteration calls)
  259. and evaluates to the function value -- these variables are capital;
  260. 4. The function's arity, optional, one of: 2 => binary, -1 => prefix
  261. unary, +1 => postfix unary, 0 => a 0-arg operator func (note that
  262. using such a function replaces the currently entered number, if any),
  263. non-number (the default) => postfix or prefix as determined by
  264. `calculator-unary-style';
  265. 5. The function's precedence -- should be in the range of 1 (lowest) to
  266. 9 (highest) (optional, defaults to 1);
  267. It it possible have a unary prefix version of a binary operator if it
  268. comes later in this list. If the list begins with the symbol `nobind',
  269. then no key binding will take place -- this is only used for predefined
  270. keys.
  271. Use `calculator-user-operators' to add operators to this list, see its
  272. documentation for an example.")
  273. (defvar calculator-stack nil
  274. "Stack contents -- operations and operands.")
  275. (defvar calculator-curnum nil
  276. "Current number being entered (as a string).")
  277. (defvar calculator-stack-display nil
  278. "Cons of the stack and its string representation.")
  279. (defvar calculator-char-radix
  280. '((?D . nil) (?B . bin) (?O . oct) (?H . hex) (?X . hex))
  281. "A table to convert input characters to corresponding radix symbols.")
  282. (defvar calculator-output-radix nil
  283. "The mode for display, one of: nil (decimal), `bin', `oct' or `hex'.")
  284. (defvar calculator-input-radix nil
  285. "The mode for input, one of: nil (decimal), `bin', `oct' or `hex'.")
  286. (defvar calculator-deg nil
  287. "Non-nil if trig functions operate on degrees instead of radians.")
  288. (defvar calculator-saved-list nil
  289. "A list of saved values collected.")
  290. (defvar calculator-saved-ptr 0
  291. "The pointer to the current saved number.")
  292. (defvar calculator-add-saved nil
  293. "Bound to t when a value should be added to the saved-list.")
  294. (defvar calculator-display-fragile nil
  295. "When non-nil, we see something that the next digit should replace.")
  296. (defvar calculator-buffer nil
  297. "The current calculator buffer.")
  298. (defvar calculator-eng-extra nil
  299. "Internal value used by `calculator-eng-display'.")
  300. (defvar calculator-eng-tmp-show nil
  301. "Internal value used by `calculator-eng-display'.")
  302. (defvar calculator-last-opXY nil
  303. "The last binary operation and its arguments.
  304. Used for repeating operations in calculator-repR/L.")
  305. (defvar calculator-registers ; use user-bindings first
  306. (append calculator-user-registers
  307. (list (cons ?e float-e) (cons ?p float-pi)))
  308. "The association list of calculator register values.")
  309. (defvar calculator-restart-other-mode nil
  310. "Used to hack restarting with the electric mode changed.")
  311. ;;;---------------------------------------------------------------------
  312. ;;; Key bindings
  313. (defun calculator-define-key (key cmd map)
  314. ;; Arranges for unbound alphabetic keys to be used as their un/shifted
  315. ;; versions if those are bound (mimics the usual Emacs global bindings).
  316. ;; FIXME: We should adjust Emacs's native "fallback to unshifted binding"
  317. ;; such that it can also be used here, rather than having to use a hack like
  318. ;; this one.
  319. (let* ((key (if (stringp key) (kbd key) key))
  320. (omap (keymap-parent map)))
  321. (define-key map key cmd)
  322. ;; "other" map, used for case-flipped bindings
  323. (unless omap
  324. (setq omap (make-sparse-keymap))
  325. (suppress-keymap omap t)
  326. (set-keymap-parent map omap))
  327. (let ((m omap))
  328. ;; Bind all case-flipped versions.
  329. (dotimes (i (length key))
  330. (let* ((c (aref key i))
  331. (k (vector c))
  332. (b (lookup-key m k))
  333. (defkey (lambda (x)
  334. (define-key m k x)
  335. (when (and (characterp c)
  336. (or (<= ?A c ?Z) (<= ?a c ?z)))
  337. (define-key m (vector (logxor 32 c)) x)))))
  338. (cond ((= i (1- (length key)))
  339. ;; Prefer longer sequences.
  340. (unless (keymapp b) (funcall defkey cmd)))
  341. ((keymapp b) (setq m b))
  342. (t (let ((sub (make-sparse-keymap)))
  343. (funcall defkey sub)
  344. (setq m sub)))))))))
  345. (defvar calculator-mode-map
  346. (let ((map (make-sparse-keymap)))
  347. (suppress-keymap map t)
  348. (dolist (x '((calculator-digit
  349. "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "a" "b" "c"
  350. "d" "f" "<kp-0>" "<kp-1>" "<kp-2>" "<kp-3>" "<kp-4>"
  351. "<kp-5>" "<kp-6>" "<kp-7>" "<kp-8>" "<kp-9>")
  352. (calculator-open-paren "[")
  353. (calculator-close-paren "]")
  354. (calculator-op-or-exp "+" "-"
  355. "<kp-add>" "<kp-subtract>")
  356. (calculator-op "<kp-divide>" "<kp-multiply>")
  357. (calculator-decimal "." "<kp-decimal>")
  358. (calculator-exp "e")
  359. (calculator-dec/deg-mode "D")
  360. (calculator-set-register "s")
  361. (calculator-get-register "g")
  362. (calculator-radix-mode "H" "X" "O" "B")
  363. (calculator-radix-input-mode "iD" "iH" "iX" "iO" "iB")
  364. (calculator-radix-output-mode "oD" "oH" "oX" "oO" "oB")
  365. (calculator-rotate-displayer "'")
  366. (calculator-rotate-displayer-back "\"")
  367. (calculator-displayer-prev "{")
  368. (calculator-displayer-next "}")
  369. (calculator-saved-up "<up>" "C-p")
  370. (calculator-saved-down "<down>" "C-n")
  371. (calculator-quit "q" "C-g")
  372. (calculator-enter "<enter>" "<linefeed>"
  373. "<kp-enter>" "<return>"
  374. "RET" "LFD")
  375. (calculator-save-on-list "SPC" "<space>")
  376. (calculator-clear-saved "C-c" "<C-delete>")
  377. (calculator-save-and-quit "<C-return>" "<C-kp-enter>")
  378. (calculator-paste "<insert>" "<S-insert>"
  379. "<paste>" "<mouse-2>" "C-y")
  380. (calculator-clear "<delete>" "DEL" "C-d")
  381. (calculator-help "h" "?" "<f1>" "<help>")
  382. (calculator-copy "<C-insert>" "<copy>")
  383. (calculator-backspace "<backspace>")
  384. ))
  385. ;; reverse the keys so earlier definitions come last -- makes the
  386. ;; more sensible bindings visible in the menu
  387. (dolist (k (reverse (cdr x)))
  388. (calculator-define-key k (car x) map)))
  389. (if calculator-bind-escape
  390. (progn (calculator-define-key "ESC" 'calculator-quit map)
  391. (calculator-define-key "<escape>" 'calculator-quit map))
  392. (calculator-define-key "ESC ESC ESC" 'calculator-quit map))
  393. ;; make C-h work in text-mode
  394. (unless window-system
  395. (calculator-define-key "C-h" 'calculator-backspace map))
  396. ;; set up a menu
  397. (when (and calculator-use-menu (not (boundp 'calculator-menu)))
  398. (let ((radix-selectors
  399. (mapcar (lambda (x)
  400. `([,(nth 0 x)
  401. (calculator-radix-mode ,(nth 2 x))
  402. :style radio
  403. :keys ,(nth 2 x)
  404. :selected
  405. (and
  406. (eq calculator-input-radix ',(nth 1 x))
  407. (eq calculator-output-radix ',(nth 1 x)))]
  408. [,(concat (nth 0 x) " Input")
  409. (calculator-radix-input-mode ,(nth 2 x))
  410. :keys ,(concat "i" (downcase (nth 2 x)))
  411. :style radio
  412. :selected
  413. (eq calculator-input-radix ',(nth 1 x))]
  414. [,(concat (nth 0 x) " Output")
  415. (calculator-radix-output-mode ,(nth 2 x))
  416. :keys ,(concat "o" (downcase (nth 2 x)))
  417. :style radio
  418. :selected
  419. (eq calculator-output-radix ',(nth 1 x))]))
  420. '(("Decimal" nil "D")
  421. ("Binary" bin "B")
  422. ("Octal" oct "O")
  423. ("Hexadecimal" hex "H"))))
  424. (op (lambda (name key)
  425. `[,name (calculator-op ,key) :keys ,key])))
  426. (easy-menu-define
  427. calculator-menu map "Calculator menu."
  428. `("Calculator"
  429. ["Help"
  430. (let ((last-command 'calculator-help)) (calculator-help))
  431. :keys "?"]
  432. "---"
  433. ["Copy" calculator-copy]
  434. ["Paste" calculator-paste]
  435. "---"
  436. ["Electric mode"
  437. (progn (calculator-quit)
  438. (setq calculator-restart-other-mode t)
  439. (run-with-timer 0.1 nil (lambda () (message nil)))
  440. ;; the message from the menu will be visible,
  441. ;; couldn't make it go away...
  442. (calculator))
  443. :active (not calculator-electric-mode)]
  444. ["Normal mode"
  445. (progn (setq calculator-restart-other-mode t)
  446. (calculator-quit))
  447. :active calculator-electric-mode]
  448. "---"
  449. ("Functions"
  450. ,(funcall op "Repeat-right" ">")
  451. ,(funcall op "Repeat-left" "<")
  452. "------General------"
  453. ,(funcall op "Reciprocal" ";")
  454. ,(funcall op "Log" "L")
  455. ,(funcall op "Square-root" "Q")
  456. ,(funcall op "Factorial" "!")
  457. "------Trigonometric------"
  458. ,(funcall op "Sinus" "S")
  459. ,(funcall op "Cosine" "C")
  460. ,(funcall op "Tangent" "T")
  461. ,(funcall op "Inv-Sinus" "IS")
  462. ,(funcall op "Inv-Cosine" "IC")
  463. ,(funcall op "Inv-Tangent" "IT")
  464. "------Bitwise------"
  465. ,(funcall op "Or" "|")
  466. ,(funcall op "Xor" "#")
  467. ,(funcall op "And" "&")
  468. ,(funcall op "Not" "~"))
  469. ("Saved List"
  470. ["Eval+Save" calculator-save-on-list]
  471. ["Prev number" calculator-saved-up]
  472. ["Next number" calculator-saved-down]
  473. ["Delete current" calculator-clear
  474. :active (and calculator-display-fragile
  475. calculator-saved-list
  476. (= (car calculator-stack)
  477. (nth calculator-saved-ptr
  478. calculator-saved-list)))]
  479. ["Delete all" calculator-clear-saved]
  480. "---"
  481. ,(funcall op "List-total" "l")
  482. ,(funcall op "List-average" "v"))
  483. ("Registers"
  484. ["Get register" calculator-get-register]
  485. ["Set register" calculator-set-register])
  486. ("Modes"
  487. ["Radians"
  488. (progn
  489. (when (or calculator-input-radix calculator-output-radix)
  490. (calculator-radix-mode "D"))
  491. (when calculator-deg (calculator-dec/deg-mode)))
  492. :keys "D"
  493. :style radio
  494. :selected (not (or calculator-input-radix
  495. calculator-output-radix
  496. calculator-deg))]
  497. ["Degrees"
  498. (progn
  499. (when (or calculator-input-radix calculator-output-radix)
  500. (calculator-radix-mode "D"))
  501. (unless calculator-deg (calculator-dec/deg-mode)))
  502. :keys "D"
  503. :style radio
  504. :selected (and calculator-deg
  505. (not (or calculator-input-radix
  506. calculator-output-radix)))]
  507. "---"
  508. ,@(mapcar 'car radix-selectors)
  509. ("Separate I/O"
  510. ,@(mapcar (lambda (x) (nth 1 x)) radix-selectors)
  511. "---"
  512. ,@(mapcar (lambda (x) (nth 2 x)) radix-selectors)))
  513. ("Decimal Display"
  514. ,@(mapcar (lambda (d)
  515. (vector (cadr d)
  516. ;; Note: inserts actual object here
  517. `(calculator-rotate-displayer ',d)))
  518. calculator-displayers)
  519. "---"
  520. ["Change Prev Display" calculator-displayer-prev]
  521. ["Change Next Display" calculator-displayer-next])
  522. "---"
  523. ["Copy+Quit" calculator-save-and-quit]
  524. ["Quit" calculator-quit]))))
  525. map)
  526. "The calculator key map.")
  527. ;;;---------------------------------------------------------------------
  528. ;;; Startup and mode stuff
  529. (define-derived-mode calculator-mode fundamental-mode "Calculator"
  530. ;; this help is also used as the major help screen
  531. "A [not so] simple calculator for Emacs.
  532. This calculator is used in the same way as other popular calculators
  533. like xcalc or calc.exe -- but using an Emacs interface.
  534. Expressions are entered using normal infix notation, parens are used as
  535. normal. Unary functions are usually postfix, but some depends on the
  536. value of `calculator-unary-style' (if the style for an operator below is
  537. specified, then it is fixed, otherwise it depends on this variable).
  538. `+' and `-' can be used as either binary operators or prefix unary
  539. operators. Numbers can be entered with exponential notation using `e',
  540. except when using a non-decimal radix mode for input (in this case `e'
  541. will be the hexadecimal digit).
  542. Here are the editing keys:
  543. * `RET' `=' evaluate the current expression
  544. * `C-insert' copy the whole current expression to the `kill-ring'
  545. * `C-return' evaluate, save result the `kill-ring' and exit
  546. * `insert' paste a number if the one was copied (normally)
  547. * `delete' `C-d' clear last argument or whole expression (hit twice)
  548. * `backspace' delete a digit or a previous expression element
  549. * `h' `?' pop-up a quick reference help
  550. * `ESC' `q' exit (`ESC' can be used if `calculator-bind-escape' is
  551. non-nil, otherwise use three consecutive `ESC's)
  552. These operators are pre-defined:
  553. * `+' `-' `*' `/' the common binary operators
  554. * `\\' `%' integer division and reminder
  555. * `_' `;' postfix unary negation and reciprocal
  556. * `^' `L' binary operators for x^y and log(x) in base y
  557. * `Q' `!' unary square root and factorial
  558. * `S' `C' `T' unary trigonometric operators: sin, cos and tan
  559. * `|' `#' `&' `~' bitwise operators: or, xor, and, not
  560. The trigonometric functions can be inverted if prefixed with an `I', see
  561. below for the way to use degrees instead of the default radians.
  562. Two special postfix unary operators are `>' and `<': whenever a binary
  563. operator is performed, it is remembered along with its arguments; then
  564. `>' (`<') will apply the same operator with the same right (left)
  565. argument.
  566. hex/oct/bin modes can be set for input and for display separately.
  567. Another toggle-able mode is for using degrees instead of radians for
  568. trigonometric functions.
  569. The keys to switch modes are (both `H' and `X' are for hex):
  570. * `D' switch to all-decimal mode, or toggle degrees/radians
  571. * `B' `O' `H' `X' binary/octal/hexadecimal modes for input & display
  572. * `i' `o' followed by one of `D' `B' `O' `H' `X' (case
  573. insensitive) sets only the input or display radix mode
  574. The prompt indicates the current modes:
  575. * \"==\": decimal mode (using radians);
  576. * \"D=\": decimal mode using degrees;
  577. * \"?=\": ? is B/O/H, the radix for both input and output;
  578. * \"=?\": ? is B/O/H, the display radix (with decimal input);
  579. * \"??\": ? is D/B/O/H, 1st char for input radix, 2nd for display.
  580. Also, the quote key can be used to switch display modes for decimal
  581. numbers (double-quote rotates back), and the two brace characters
  582. \(\"{\" and \"}\" change display parameters that these displayers use,
  583. if they handle such). If output is using any radix mode, then these
  584. keys toggle digit grouping mode and the chunk size.
  585. Values can be saved for future reference in either a list of saved
  586. values, or in registers.
  587. The list of saved values is useful for statistics operations on some
  588. collected data. It is possible to navigate in this list, and if the
  589. value shown is the current one on the list, an indication is displayed
  590. as \"[N]\" if this is the last number and there are N numbers, or
  591. \"[M/N]\" if the M-th value is shown.
  592. * `SPC' evaluate the current value as usual, but also adds
  593. the result to the list of saved values
  594. * `l' `v' computes total / average of saved values
  595. * `up' `C-p' browse to the previous value in the list
  596. * `down' `C-n' browse to the next value in the list
  597. * `delete' `C-d' remove current value from the list (if it is on it)
  598. * `C-delete' `C-c' delete the whole list
  599. Registers are variable-like place-holders for values:
  600. * `s' followed by a character attach the current value to that character
  601. * `g' followed by a character fetches the attached value
  602. There are many variables that can be used to customize the calculator.
  603. Some interesting customization variables are:
  604. * `calculator-electric-mode' use only the echo-area electrically.
  605. * `calculator-unary-style' set most unary ops to pre/postfix style.
  606. * `calculator-user-registers' to define user-preset registers.
  607. * `calculator-user-operators' to add user-defined operators.
  608. See the documentation for these variables, and \"calculator.el\" for
  609. more information.
  610. \\{calculator-mode-map}")
  611. (declare-function Electric-command-loop "electric"
  612. (return-tag &optional prompt inhibit-quitting
  613. loop-function loop-state))
  614. ;;;###autoload
  615. (defun calculator ()
  616. "Run the Emacs calculator.
  617. See the documentation for `calculator-mode' for more information."
  618. (interactive)
  619. (when calculator-restart-other-mode
  620. (setq calculator-electric-mode (not calculator-electric-mode)))
  621. (when calculator-initial-operators
  622. (calculator-add-operators calculator-initial-operators)
  623. (setq calculator-initial-operators nil)
  624. ;; don't change this since it is a customization variable,
  625. ;; its set function will add any new operators
  626. (calculator-add-operators calculator-user-operators))
  627. (setq calculator-buffer (get-buffer-create "*calculator*"))
  628. (if calculator-electric-mode
  629. (save-window-excursion
  630. (require 'electric) (message nil) ; hide load message
  631. (let ((old-buf (window-buffer (minibuffer-window)))
  632. (echo-keystrokes 0)
  633. (garbage-collection-messages nil)) ; no gc msg when electric
  634. (set-window-buffer (minibuffer-window) calculator-buffer)
  635. (select-window (minibuffer-window))
  636. (calculator-reset)
  637. (calculator-update-display)
  638. (use-local-map calculator-mode-map)
  639. (run-hooks 'calculator-mode-hook)
  640. (unwind-protect
  641. (catch 'calculator-done
  642. (Electric-command-loop
  643. 'calculator-done
  644. ;; can't use 'noprompt, bug in electric.el
  645. (lambda () 'noprompt)
  646. nil
  647. (lambda (_x _y) (calculator-update-display))))
  648. (set-window-buffer (minibuffer-window) old-buf)
  649. (kill-buffer calculator-buffer))))
  650. (progn
  651. (cond
  652. ((not (get-buffer-window calculator-buffer))
  653. (let ((window-min-height 2))
  654. ;; maybe leave two lines for our window because of the
  655. ;; normal `raised' mode line
  656. (select-window (split-window-below
  657. (if (calculator-need-3-lines) -3 -2)))
  658. (switch-to-buffer calculator-buffer)))
  659. ((not (eq (current-buffer) calculator-buffer))
  660. (select-window (get-buffer-window calculator-buffer))))
  661. (calculator-mode)
  662. (setq buffer-read-only t)
  663. (calculator-reset)
  664. (message "Hit `?' For a quick help screen.")))
  665. (when (and calculator-restart-other-mode calculator-electric-mode)
  666. (calculator)))
  667. (defun calculator-need-3-lines ()
  668. ;; If the mode line might interfere with the calculator buffer, use 3
  669. ;; lines instead.
  670. (let* ((dh (face-attribute 'default :height))
  671. (mh (face-attribute 'mode-line :height)))
  672. ;; if the mode line is shorter than the default, stick with 2 lines
  673. ;; (it may be necessary to check how much shorter)
  674. (and (not (or (and (integerp dh) (integerp mh) (< mh dh))
  675. (and (numberp mh) (not (integerp mh)) (< mh 1))))
  676. (or ;; if the mode line is taller than the default, use 3 lines
  677. (and (integerp dh) (integerp mh) (> mh dh))
  678. (and (numberp mh) (not (integerp mh)) (> mh 1))
  679. ;; if the mode line has a box with non-negative line-width,
  680. ;; use 3 lines
  681. (let* ((bx (face-attribute 'mode-line :box))
  682. (lh (plist-get bx :line-width)))
  683. (and bx (or (not lh) (> lh 0))))
  684. ;; if the mode line has an overline, use 3 lines
  685. (not (memq (face-attribute 'mode-line :overline)
  686. '(nil unspecified)))))))
  687. (defun calculator-message (string &rest arguments)
  688. "Same as `message', but also handle electric mode."
  689. (apply 'message string arguments)
  690. (when calculator-electric-mode (sit-for 1) (message nil)))
  691. ;;;---------------------------------------------------------------------
  692. ;;; Operators
  693. (defun calculator-op-arity (op)
  694. "Return OP's arity.
  695. Current results are one of 2 (binary), +1 (postfix), -1 (prefix), or
  696. 0 (nullary)."
  697. (let ((arity (nth 3 op)))
  698. (cond ((numberp arity) arity)
  699. ((eq calculator-unary-style 'postfix) +1)
  700. (t -1))))
  701. (defun calculator-op-prec (op)
  702. "Return OP's precedence for reducing when inserting into the stack.
  703. Defaults to 1."
  704. (or (nth 4 op) 1))
  705. (defun calculator-add-operators (more-ops)
  706. "This function handles operator addition.
  707. Adds MORE-OPS to `calculator-operator', called initially to handle
  708. `calculator-initial-operators' and `calculator-user-operators'."
  709. (let ((added-ops nil))
  710. (dolist (op more-ops)
  711. (unless (eq (car op) 'nobind)
  712. (calculator-define-key (car op) 'calculator-op calculator-mode-map))
  713. (push (if (eq (car op) 'nobind) (cdr op) op)
  714. added-ops))
  715. ;; added-ops come first, but in correct order
  716. (setq calculator-operators
  717. (append (nreverse added-ops) calculator-operators))))
  718. ;;;---------------------------------------------------------------------
  719. ;;; Display stuff
  720. (defun calculator-reset ()
  721. "Reset calculator variables."
  722. (unless calculator-restart-other-mode
  723. (setq calculator-stack nil
  724. calculator-curnum nil
  725. calculator-stack-display nil
  726. calculator-display-fragile nil))
  727. (setq calculator-restart-other-mode nil)
  728. (calculator-update-display))
  729. (defun calculator-get-display ()
  730. "Return a string to display.
  731. The result should not exceed the screen width."
  732. (let* ((in-r (and calculator-input-radix
  733. (char-to-string
  734. (car (rassq calculator-input-radix
  735. calculator-char-radix)))))
  736. (out-r (and calculator-output-radix
  737. (char-to-string
  738. (car (rassq calculator-output-radix
  739. calculator-char-radix)))))
  740. (prompt (format calculator-prompt
  741. (cond ((or in-r out-r)
  742. (concat (or in-r "=")
  743. (if (equal in-r out-r) "="
  744. (or out-r "D"))))
  745. (calculator-deg "D=")
  746. (t "=="))))
  747. (expr
  748. (concat (cdr calculator-stack-display)
  749. (cond
  750. ;; entering a number
  751. (calculator-curnum (concat calculator-curnum "_"))
  752. ;; showing a result
  753. ((and (= 1 (length calculator-stack))
  754. calculator-display-fragile)
  755. nil)
  756. ;; waiting for a number or an operator
  757. (t "?"))))
  758. (trim (+ (length expr) (length prompt) 1 (- (window-width)))))
  759. (concat prompt (if (<= trim 0) expr (substring expr trim)))))
  760. (defun calculator-string-to-number (str)
  761. "Convert the given STR to a number, according to the value of
  762. `calculator-input-radix'."
  763. (if calculator-input-radix
  764. (string-to-number str (cadr (assq calculator-input-radix
  765. '((bin 2) (oct 8) (hex 16)))))
  766. (let* ((str (replace-regexp-in-string
  767. "\\.\\([^0-9].*\\)?$" ".0\\1" str))
  768. (str (replace-regexp-in-string
  769. "[eE][+-]?\\([^0-9].*\\)?$" "e0\\1" str)))
  770. (string-to-number str))))
  771. (defun calculator-push-curnum ()
  772. "Push the numeric value of the displayed number to the stack."
  773. (when calculator-curnum
  774. (push (calculator-string-to-number calculator-curnum)
  775. calculator-stack)
  776. (setq calculator-curnum nil)))
  777. (defun calculator-rotate-displayer (&optional new-disp)
  778. "Switch to the next displayer on the `calculator-displayers' list.
  779. Can be called with an optional argument NEW-DISP to force rotation to
  780. that argument.
  781. If radix output mode is active, toggle digit grouping."
  782. (interactive)
  783. (cond
  784. (calculator-output-radix
  785. (setq calculator-radix-grouping-mode
  786. (not calculator-radix-grouping-mode))
  787. (calculator-message
  788. "Digit grouping mode %s."
  789. (if calculator-radix-grouping-mode "ON" "OFF")))
  790. (t
  791. (setq calculator-displayers
  792. (if (and new-disp (memq new-disp calculator-displayers))
  793. (let ((tmp nil))
  794. (while (not (eq (car calculator-displayers) new-disp))
  795. (push (pop calculator-displayers) tmp))
  796. (setq calculator-displayers
  797. (nconc calculator-displayers (nreverse tmp))))
  798. (nconc (cdr calculator-displayers)
  799. (list (car calculator-displayers)))))
  800. (calculator-message
  801. "Using %s." (cadr (car calculator-displayers)))))
  802. (calculator-enter))
  803. (defun calculator-rotate-displayer-back ()
  804. "Like `calculator-rotate-displayer', but rotates modes back.
  805. If radix output mode is active, toggle digit grouping."
  806. (interactive)
  807. (calculator-rotate-displayer (car (last calculator-displayers))))
  808. (defun calculator-displayer-prev ()
  809. "Send the current displayer function a `left' argument.
  810. This is used to modify display arguments (if the current displayer
  811. function supports this).
  812. If radix output mode is active, increase the grouping size."
  813. (interactive)
  814. (if calculator-output-radix
  815. (progn (setq calculator-radix-grouping-digits
  816. (1+ calculator-radix-grouping-digits))
  817. (calculator-enter))
  818. (when (car calculator-displayers)
  819. (let ((disp (caar calculator-displayers)))
  820. (cond ((symbolp disp) (funcall disp 'left))
  821. ((and (consp disp) (eq 'std (car disp)))
  822. (calculator-standard-displayer 'left)))))))
  823. (defun calculator-displayer-next ()
  824. "Send the current displayer function a `right' argument.
  825. This is used to modify display arguments (if the current displayer
  826. function supports this).
  827. If radix output mode is active, decrease the grouping size."
  828. (interactive)
  829. (if calculator-output-radix
  830. (progn (setq calculator-radix-grouping-digits
  831. (max 2 (1- calculator-radix-grouping-digits)))
  832. (calculator-enter))
  833. (when (car calculator-displayers)
  834. (let ((disp (caar calculator-displayers)))
  835. (cond ((symbolp disp) (funcall disp 'right))
  836. ((and (consp disp) (eq 'std (car disp)))
  837. (calculator-standard-displayer 'right)))))))
  838. (defun calculator-remove-zeros (numstr)
  839. "Get a number string NUMSTR and remove unnecessary zeros.
  840. The behavior of this function is controlled by
  841. `calculator-remove-zeros'."
  842. (let* ((s (if (not (eq calculator-remove-zeros t)) numstr
  843. ;; remove all redundant zeros leaving an integer
  844. (replace-regexp-in-string
  845. "\\.0+\\([eE].*\\)?$" "\\1" numstr)))
  846. (s (if (not calculator-remove-zeros) s
  847. ;; remove zeros, except for first after the "."
  848. (replace-regexp-in-string
  849. "\\(\\..[0-9]*?\\)0+\\([eE].*\\)?$" "\\1\\2" s))))
  850. s))
  851. (defun calculator-groupize-number (str n sep &optional fromleft)
  852. "Return the input string STR with occurrences of SEP that separate
  853. every N characters starting from the right, or from the left if
  854. FROMLEFT is true."
  855. (let* ((len (length str)) (i (/ len n)) (j (% len n))
  856. (r (if (or (not fromleft) (= j 0)) '()
  857. (list (substring str (- len j))))))
  858. (while (> i 0)
  859. (let* ((e (* i n)) (e (if fromleft e (+ e j))))
  860. (push (substring str (- e n) e) r))
  861. (setq i (1- i)))
  862. (when (and (not fromleft) (> j 0))
  863. (push (substring str 0 j) r))
  864. (mapconcat 'identity r sep)))
  865. (defun calculator-standard-displayer (num &optional char group-p)
  866. "Standard display function, used to display NUM.
  867. Its behavior is determined by `calculator-number-digits' and the given
  868. CHAR argument (both will be used to compose a format string). If the
  869. char is \"n\" then this function will choose one between %f or %e, this
  870. is a work around %g jumping to exponential notation too fast.
  871. It will also split digit sequences into comma-separated groups
  872. and/or remove redundant zeros.
  873. The special `left' and `right' symbols will make it change the current
  874. number of digits displayed (`calculator-number-digits')."
  875. (if (symbolp num)
  876. (cond ((eq num 'left)
  877. (when (> calculator-number-digits 0)
  878. (setq calculator-number-digits
  879. (1- calculator-number-digits))
  880. (calculator-enter)))
  881. ((eq num 'right)
  882. (setq calculator-number-digits
  883. (1+ calculator-number-digits))
  884. (calculator-enter)))
  885. (let* ((s (if (eq char ?n)
  886. (let ((n (abs num)))
  887. (if (or (and (< 0 n) (< n 0.001)) (< 1e8 n)) ?e ?f))
  888. char))
  889. (s (format "%%.%s%c" calculator-number-digits s))
  890. (s (calculator-remove-zeros (format s num)))
  891. (s (if (or (not group-p) (string-match-p "[eE]" s)) s
  892. (replace-regexp-in-string
  893. "\\([0-9]+\\)\\(?:\\..*\\|$\\)"
  894. (lambda (_) (calculator-groupize-number
  895. (match-string 1 s) 3 ","))
  896. s nil nil 1))))
  897. s)))
  898. (defun calculator-eng-display (num)
  899. "Display NUM in engineering notation.
  900. The number of decimal digits used is controlled by
  901. `calculator-number-digits', so to change it at runtime you have to use
  902. the `left' or `right' when one of the standard modes is used."
  903. (if (symbolp num)
  904. (cond ((eq num 'left)
  905. (setq calculator-eng-extra
  906. (if calculator-eng-extra (1+ calculator-eng-extra) 1))
  907. (let ((calculator-eng-tmp-show t)) (calculator-enter)))
  908. ((eq num 'right)
  909. (setq calculator-eng-extra
  910. (if calculator-eng-extra (1- calculator-eng-extra) -1))
  911. (let ((calculator-eng-tmp-show t)) (calculator-enter))))
  912. (let ((exp 0))
  913. (unless (= 0 num)
  914. (while (< (abs num) 1.0)
  915. (setq num (* num 1000.0)) (setq exp (- exp 3)))
  916. (while (> (abs num) 999.0)
  917. (setq num (/ num 1000.0)) (setq exp (+ exp 3)))
  918. (when (and calculator-eng-tmp-show
  919. (not (= 0 calculator-eng-extra)))
  920. (let ((i calculator-eng-extra))
  921. (while (> i 0)
  922. (setq num (* num 1000.0)) (setq exp (- exp 3))
  923. (setq i (1- i)))
  924. (while (< i 0)
  925. (setq num (/ num 1000.0)) (setq exp (+ exp 3))
  926. (setq i (1+ i))))))
  927. (unless calculator-eng-tmp-show (setq calculator-eng-extra nil))
  928. (let ((str (format (format "%%.%sf" calculator-number-digits)
  929. num)))
  930. (concat (let ((calculator-remove-zeros
  931. ;; make sure we don't leave integers
  932. (and calculator-remove-zeros 'x)))
  933. (calculator-remove-zeros str))
  934. "e" (number-to-string exp))))))
  935. (defun calculator-number-to-string (num)
  936. "Convert NUM to a displayable string."
  937. (cond
  938. ;; operators are printed here, the rest is for numbers
  939. ((not (numberp num)) (prin1-to-string (nth 1 num) t))
  940. ;; %f/%e handle these, but avoid them in radix or in user displayers
  941. ((and (floatp num) (isnan num)) "NaN")
  942. ((<= 1.0e+INF num) "Inf")
  943. ((<= num -1.0e+INF) "-Inf")
  944. (calculator-output-radix
  945. ;; print with radix -- for binary, convert the octal number
  946. (let* ((fmt (if (eq calculator-output-radix 'hex) "%x" "%o"))
  947. (str (if calculator-2s-complement num (abs num)))
  948. (str (format fmt (calculator-truncate str)))
  949. (bins '((?0 "000") (?1 "001") (?2 "010") (?3 "011")
  950. (?4 "100") (?5 "101") (?6 "110") (?7 "111")))
  951. (str (if (not (eq calculator-output-radix 'bin)) str
  952. (replace-regexp-in-string
  953. "^0+\\(.\\)" "\\1"
  954. (apply 'concat (mapcar (lambda (c)
  955. (cadr (assq c bins)))
  956. str)))))
  957. (str (if (not calculator-radix-grouping-mode) str
  958. (calculator-groupize-number
  959. str calculator-radix-grouping-digits
  960. calculator-radix-grouping-separator))))
  961. (upcase (if (or calculator-2s-complement (>= num 0)) str
  962. (concat "-" str)))))
  963. ((stringp calculator-displayer) (format calculator-displayer num))
  964. ((symbolp calculator-displayer) (funcall calculator-displayer num))
  965. ((eq 'std (car-safe calculator-displayer))
  966. (apply 'calculator-standard-displayer
  967. num (cdr calculator-displayer)))
  968. ((listp calculator-displayer)
  969. (eval `(let ((num ',num)) ,calculator-displayer) t))
  970. ;; nil (or bad) displayer
  971. (t (prin1-to-string num t))))
  972. (defun calculator-update-display (&optional force)
  973. "Update the display.
  974. If optional argument FORCE is non-nil, don't use the cached string."
  975. (set-buffer calculator-buffer)
  976. ;; update calculator-stack-display
  977. (when (or force (not (eq (car calculator-stack-display)
  978. calculator-stack)))
  979. (setq calculator-stack-display
  980. (cons calculator-stack
  981. (if calculator-stack
  982. (concat
  983. (let ((calculator-displayer
  984. (if (and calculator-displayers
  985. (= 1 (length calculator-stack)))
  986. ;; customizable display for a single value
  987. (caar calculator-displayers)
  988. calculator-displayer)))
  989. (mapconcat 'calculator-number-to-string
  990. (reverse calculator-stack)
  991. " "))
  992. " "
  993. (and calculator-display-fragile
  994. calculator-saved-list
  995. ;; Hack: use `eq' to compare the number: it's a
  996. ;; flonum, so `eq' means that its the actual
  997. ;; number rather than a computation that had an
  998. ;; equal result (eg, enter 1,3,2, use "v" to see
  999. ;; the average -- it now shows "2" instead of
  1000. ;; "2 [3]").
  1001. (eq (car calculator-stack)
  1002. (nth calculator-saved-ptr
  1003. calculator-saved-list))
  1004. (if (= 0 calculator-saved-ptr)
  1005. (format "[%s]" (length calculator-saved-list))
  1006. (format "[%s/%s]"
  1007. (- (length calculator-saved-list)
  1008. calculator-saved-ptr)
  1009. (length calculator-saved-list)))))
  1010. ""))))
  1011. (let ((inhibit-read-only t))
  1012. (erase-buffer)
  1013. (insert (calculator-get-display)))
  1014. (set-buffer-modified-p nil)
  1015. (goto-char (if calculator-display-fragile
  1016. (1+ (length calculator-prompt))
  1017. (1- (point)))))
  1018. ;;;---------------------------------------------------------------------
  1019. ;;; Stack computations
  1020. (defun calculator-reduce-stack-once (prec)
  1021. "Worker for `calculator-reduce-stack'."
  1022. (cl-flet ((check (ar op) (and (listp op)
  1023. (<= prec (calculator-op-prec op))
  1024. (= ar (calculator-op-arity op))))
  1025. (call (op &rest args) (apply 'calculator-funcall
  1026. (nth 2 op) args)))
  1027. (pcase calculator-stack
  1028. ;; reduce "... ( x )" --> "... x"
  1029. (`((,_ \) . ,_) ,(and X (pred numberp)) (,_ \( . ,_) . ,rest)
  1030. (cons X rest))
  1031. ;; reduce "... x op y" --> "... r", r is the result
  1032. (`(,(and Y (pred numberp))
  1033. ,(and O (pred (check 2)))
  1034. ,(and X (pred numberp))
  1035. . ,rest)
  1036. (cons (call O X Y) rest))
  1037. ;; reduce "... op x" --> "... r" for prefix op
  1038. (`(,(and X (pred numberp)) ,(and O (pred (check -1))) . ,rest)
  1039. (cons (call O X) rest))
  1040. ;; reduce "... x op" --> "... r" for postfix op
  1041. (`(,(and O (pred (check +1))) ,(and X (pred numberp)) . ,rest)
  1042. (cons (call O X) rest))
  1043. ;; reduce "... op" --> "... r" for 0-ary op
  1044. (`(,(and O (pred (check 0))) . ,rest)
  1045. (cons (call O) rest))
  1046. ;; reduce "... y x" --> "... x"
  1047. ;; (needed for 0-ary ops: replace current number with result)
  1048. (`(,(and X (pred numberp)) ,(and _Y (pred numberp)) . ,rest)
  1049. (cons X rest))
  1050. (_ nil)))) ; nil = done
  1051. (defun calculator-reduce-stack (prec)
  1052. "Reduce the stack using top operators as long as possible.
  1053. PREC is a precedence -- reduce everything with higher precedence."
  1054. (let ((new nil))
  1055. (while (setq new (calculator-reduce-stack-once prec))
  1056. (setq calculator-stack new))))
  1057. (defun calculator-funcall (f &optional X Y)
  1058. "If F is a symbol, evaluate (F X Y).
  1059. Otherwise, it should be a list, evaluate it with X, Y bound to the
  1060. arguments."
  1061. ;; remember binary ops for calculator-repR/L
  1062. (when Y (setq calculator-last-opXY (list f X Y)))
  1063. (if (symbolp f)
  1064. (cond ((and X Y) (funcall f X Y))
  1065. (X (funcall f X))
  1066. (t (funcall f)))
  1067. ;; f is an expression
  1068. (let ((TX (and X (calculator-truncate X)))
  1069. (TY (and Y (calculator-truncate Y)))
  1070. (DX (if (and X calculator-deg) (degrees-to-radians X) X))
  1071. (L calculator-saved-list)
  1072. (fF `(calculator-funcall ',f x y))
  1073. (fD `(if calculator-deg (radians-to-degrees x) x)))
  1074. (eval `(cl-flet ((F (&optional x y) ,fF) (D (x) ,fD))
  1075. (let ((X ,X) (Y ,Y) (DX ,DX) (TX ,TX) (TY ,TY) (L ',L))
  1076. ,f))
  1077. t))))
  1078. ;;;---------------------------------------------------------------------
  1079. ;;; Input interaction
  1080. (defun calculator-last-input (&optional keys)
  1081. "Return the last key sequence that was used to invoke this command, or
  1082. the input KEYS. Uses the `function-key-map' translate keypad numbers to
  1083. plain ones."
  1084. (let* ((inp (or keys (this-command-keys)))
  1085. (inp (or (and (arrayp inp) (not (stringp inp))
  1086. (lookup-key function-key-map inp))
  1087. inp)))
  1088. (if (or (not inp) (stringp inp) (not (arrayp inp))
  1089. (catch 'done ; any non-chars?
  1090. (dotimes (i (length inp))
  1091. (unless (characterp (aref inp i)) (throw 'done t)))
  1092. nil))
  1093. inp
  1094. (concat inp))))
  1095. (defun calculator-clear-fragile (&optional op)
  1096. "Clear the fragile flag if it was set, then maybe reset all.
  1097. OP is the operator (if any) that caused this call."
  1098. (when (and calculator-display-fragile
  1099. (or (not op) (memq (calculator-op-arity op) '(-1 0))))
  1100. ;; reset if last calc finished, and now get a num or prefix or 0-ary
  1101. ;; op
  1102. (calculator-reset))
  1103. (setq calculator-display-fragile nil))
  1104. (defun calculator-digit ()
  1105. "Enter a single digit."
  1106. (interactive)
  1107. (let ((inp (aref (calculator-last-input) 0)))
  1108. (when (and (or calculator-display-fragile
  1109. (not (numberp (car calculator-stack))))
  1110. (<= inp (pcase calculator-input-radix
  1111. (`nil ?9) (`bin ?1) (`oct ?7) (_ 999))))
  1112. (calculator-clear-fragile)
  1113. (setq calculator-curnum
  1114. (concat (if (equal calculator-curnum "0") ""
  1115. calculator-curnum)
  1116. (list (upcase inp))))
  1117. (calculator-update-display))))
  1118. (defun calculator-decimal ()
  1119. "Enter a decimal period."
  1120. (interactive)
  1121. (when (and (not calculator-input-radix)
  1122. (or calculator-display-fragile
  1123. (not (numberp (car calculator-stack))))
  1124. (not (and calculator-curnum
  1125. (string-match-p "[.eE]" calculator-curnum))))
  1126. ;; enter the period on the same condition as a digit, only if no
  1127. ;; period or exponent entered yet
  1128. (calculator-clear-fragile)
  1129. (setq calculator-curnum (concat (or calculator-curnum "0") "."))
  1130. (calculator-update-display)))
  1131. (defun calculator-exp ()
  1132. "Enter an exponent, or an \"E\" digit in hex input mode."
  1133. (interactive)
  1134. (cond
  1135. (calculator-input-radix (calculator-digit))
  1136. ((and (or calculator-display-fragile
  1137. (not (numberp (car calculator-stack))))
  1138. (not (and calculator-curnum
  1139. (string-match-p "[eE]" calculator-curnum))))
  1140. ;; same condition as above, also no E so far
  1141. (calculator-clear-fragile)
  1142. (setq calculator-curnum (concat (or calculator-curnum "1") "e"))
  1143. (calculator-update-display))))
  1144. (defun calculator-op (&optional keys)
  1145. "Enter an operator on the stack, doing all necessary reductions.
  1146. Optional string argument KEYS will force using it as the keys entered."
  1147. (interactive)
  1148. (catch 'op-error
  1149. (let* ((last-inp (calculator-last-input keys))
  1150. (op (assoc last-inp calculator-operators)))
  1151. (calculator-clear-fragile op)
  1152. (calculator-push-curnum)
  1153. (when (and (= 2 (calculator-op-arity op))
  1154. (not (numberp (car calculator-stack))))
  1155. ;; we have a binary operator but no number -- search for a
  1156. ;; prefix version
  1157. (setq op (assoc last-inp (cdr (memq op calculator-operators))))
  1158. (unless (and op (= -1 (calculator-op-arity op)))
  1159. (calculator-message "Binary operator without a first operand")
  1160. (throw 'op-error nil)))
  1161. (calculator-reduce-stack
  1162. (cond ((eq (nth 1 op) '\() 10)
  1163. ((eq (nth 1 op) '\)) 0)
  1164. (t (calculator-op-prec op))))
  1165. (when (let ((hasnum (numberp (car calculator-stack))))
  1166. (pcase (calculator-op-arity op)
  1167. (-1 hasnum)
  1168. ((or 1 2) (not hasnum))))
  1169. (calculator-message "Incomplete expression")
  1170. (throw 'op-error nil))
  1171. (push op calculator-stack)
  1172. (calculator-reduce-stack (calculator-op-prec op))
  1173. (when (and (= (length calculator-stack) 1)
  1174. (numberp (car calculator-stack)))
  1175. ;; the display is fragile if it contains only one number
  1176. (setq calculator-display-fragile t)
  1177. (when calculator-add-saved ; add number to the saved-list
  1178. (push (car calculator-stack)
  1179. (nthcdr calculator-saved-ptr calculator-saved-list))))
  1180. (calculator-update-display))))
  1181. (defun calculator-op-or-exp ()
  1182. "Either enter an operator or a digit.
  1183. Used with +/- for entering them as digits in numbers like 1e-3 (there is
  1184. no need for negative numbers since these are handled by unary
  1185. operators)."
  1186. (interactive)
  1187. (if (and (not calculator-input-radix)
  1188. (not calculator-display-fragile)
  1189. calculator-curnum
  1190. (string-match-p "[eE]$" calculator-curnum))
  1191. (calculator-digit)
  1192. (calculator-op)))
  1193. ;;;---------------------------------------------------------------------
  1194. ;;; Input/output modes (not display)
  1195. (defun calculator-dec/deg-mode ()
  1196. "Set decimal mode for display & input, if decimal, toggle deg mode."
  1197. (interactive)
  1198. (calculator-push-curnum)
  1199. (if (or calculator-input-radix calculator-output-radix)
  1200. (setq calculator-input-radix nil
  1201. calculator-output-radix nil)
  1202. ;; already decimal -- toggle degrees mode
  1203. (setq calculator-deg (not calculator-deg)))
  1204. (calculator-update-display t))
  1205. (defun calculator-radix-mode (&optional keys)
  1206. "Set input and display radix modes.
  1207. Optional string argument KEYS will force using it as the keys entered."
  1208. (interactive)
  1209. (calculator-radix-input-mode keys)
  1210. (calculator-radix-output-mode keys))
  1211. (defun calculator-radix-input-mode (&optional keys)
  1212. "Set input radix modes.
  1213. Optional string argument KEYS will force using it as the keys entered."
  1214. (interactive)
  1215. (calculator-push-curnum)
  1216. (setq calculator-input-radix
  1217. (let ((inp (calculator-last-input keys)))
  1218. (cdr (assq (upcase (aref inp (1- (length inp))))
  1219. calculator-char-radix))))
  1220. (calculator-update-display))
  1221. (defun calculator-radix-output-mode (&optional keys)
  1222. "Set display radix modes.
  1223. Optional string argument KEYS will force using it as the keys entered."
  1224. (interactive)
  1225. (calculator-push-curnum)
  1226. (setq calculator-output-radix
  1227. (let ((inp (calculator-last-input keys)))
  1228. (cdr (assq (upcase (aref inp (1- (length inp))))
  1229. calculator-char-radix))))
  1230. (calculator-update-display t))
  1231. ;;;---------------------------------------------------------------------
  1232. ;;; Saved values list
  1233. (defun calculator-save-on-list ()
  1234. "Evaluate current expression, put result on the saved values list."
  1235. (interactive)
  1236. (let ((calculator-add-saved t)) ; marks the result to be added
  1237. (calculator-enter)))
  1238. (defun calculator-clear-saved ()
  1239. "Clear the list of saved values in `calculator-saved-list'."
  1240. (interactive)
  1241. (setq calculator-saved-list nil
  1242. calculator-saved-ptr 0)
  1243. (calculator-update-display t))
  1244. (defun calculator-saved-move (n)
  1245. "Go N elements up the list of saved values."
  1246. (interactive)
  1247. (when (and calculator-saved-list
  1248. (or (null calculator-stack) calculator-display-fragile))
  1249. (setq calculator-saved-ptr
  1250. (max (min (+ n calculator-saved-ptr)
  1251. (length calculator-saved-list))
  1252. 0))
  1253. (if (nth calculator-saved-ptr calculator-saved-list)
  1254. (setq calculator-stack (list (nth calculator-saved-ptr
  1255. calculator-saved-list))
  1256. calculator-display-fragile t)
  1257. (calculator-reset))
  1258. (calculator-update-display)))
  1259. (defun calculator-saved-up ()
  1260. "Go up the list of saved values."
  1261. (interactive)
  1262. (calculator-saved-move +1))
  1263. (defun calculator-saved-down ()
  1264. "Go down the list of saved values."
  1265. (interactive)
  1266. (calculator-saved-move -1))
  1267. ;;;---------------------------------------------------------------------
  1268. ;;; Misc functions
  1269. (defun calculator-open-paren ()
  1270. "Equivalents of `(' use this."
  1271. (interactive)
  1272. (calculator-op "("))
  1273. (defun calculator-close-paren ()
  1274. "Equivalents of `)' use this."
  1275. (interactive)
  1276. (calculator-op ")"))
  1277. (defun calculator-enter ()
  1278. "Evaluate current expression."
  1279. (interactive)
  1280. (calculator-op "="))
  1281. (defun calculator-backspace ()
  1282. "Backward delete a single digit or a stack element."
  1283. (interactive)
  1284. (if calculator-curnum
  1285. (setq calculator-curnum
  1286. (if (> (length calculator-curnum) 1)
  1287. (substring calculator-curnum
  1288. 0 (1- (length calculator-curnum)))
  1289. nil))
  1290. (setq calculator-stack (cdr calculator-stack)))
  1291. (calculator-update-display))
  1292. (defun calculator-clear ()
  1293. "Clear current number."
  1294. (interactive)
  1295. (setq calculator-curnum nil)
  1296. (cond
  1297. ;; if the current number is from the saved-list remove it
  1298. ((and calculator-display-fragile
  1299. calculator-saved-list
  1300. (= (car calculator-stack)
  1301. (nth calculator-saved-ptr calculator-saved-list)))
  1302. (if (= 0 calculator-saved-ptr)
  1303. (setq calculator-saved-list (cdr calculator-saved-list))
  1304. (let ((p (nthcdr (1- calculator-saved-ptr)
  1305. calculator-saved-list)))
  1306. (setcdr p (cddr p))
  1307. (setq calculator-saved-ptr (1- calculator-saved-ptr))))
  1308. (if calculator-saved-list
  1309. (setq calculator-stack
  1310. (list (nth calculator-saved-ptr calculator-saved-list)))
  1311. (calculator-reset)))
  1312. ;; reset if fragile or double clear
  1313. ((or calculator-display-fragile (eq last-command this-command))
  1314. (calculator-reset)))
  1315. (calculator-update-display))
  1316. (defun calculator-copy ()
  1317. "Copy current number to the `kill-ring'."
  1318. (interactive)
  1319. (let ((calculator-displayer
  1320. (or calculator-copy-displayer calculator-displayer))
  1321. (calculator-displayers
  1322. (if calculator-copy-displayer nil calculator-displayers)))
  1323. (calculator-enter)
  1324. ;; remove trailing spaces and an index
  1325. (let ((s (cdr calculator-stack-display)))
  1326. (when s
  1327. (kill-new (replace-regexp-in-string
  1328. "^\\([^ ]+\\) *\\(\\[[0-9/]+\\]\\)? *$" "\\1" s))))))
  1329. (defun calculator-put-value (val)
  1330. "Paste VAL as if entered.
  1331. Used by `calculator-paste' and `get-register'."
  1332. (when (and (numberp val)
  1333. ;; (not calculator-curnum)
  1334. (or calculator-display-fragile
  1335. (not (numberp (car calculator-stack)))))
  1336. (calculator-clear-fragile)
  1337. (setq calculator-curnum
  1338. (let ((calculator-displayer "%S")
  1339. (calculator-radix-grouping-mode nil)
  1340. (calculator-output-radix calculator-input-radix))
  1341. (calculator-number-to-string val)))
  1342. (calculator-update-display)))
  1343. (defun calculator-paste (arg)
  1344. "Paste a value from the `kill-ring'.
  1345. With a prefix argument, paste the raw string as a sequence of key
  1346. presses, which can be used to paste expressions. Note that this
  1347. is literal; examples: spaces will store values, pasting \"1+2\"
  1348. will not produce 3 if it's done you're entering a number or after
  1349. a multiplication."
  1350. (interactive "P")
  1351. (let ((str (current-kill 0)))
  1352. (if arg
  1353. (setq unread-command-events
  1354. `(,@(listify-key-sequence str) ,@unread-command-events))
  1355. (calculator-put-value (calculator-string-to-number str)))))
  1356. (defun calculator-register-read-with-preview (prompt)
  1357. "Similar to `register-read-with-preview' but for calculator
  1358. registers."
  1359. (let ((register-alist calculator-registers)
  1360. (register-preview-delay 1)
  1361. (register-preview-function
  1362. (lambda (r)
  1363. (format "%s: %s\n"
  1364. (single-key-description (car r))
  1365. (calculator-number-to-string (cdr r))))))
  1366. (register-read-with-preview prompt)))
  1367. (defun calculator-set-register (reg)
  1368. "Set a register value for REG."
  1369. (interactive (list (calculator-register-read-with-preview
  1370. "Register to store value into: ")))
  1371. (let* ((as (assq reg calculator-registers))
  1372. (val (progn (calculator-enter) (car calculator-stack))))
  1373. (if as
  1374. (setcdr as val)
  1375. (push (cons reg val) calculator-registers))
  1376. (calculator-message "[%c] := %S" reg val)))
  1377. (defun calculator-get-register (reg)
  1378. "Get a value from a register REG."
  1379. (interactive (list (calculator-register-read-with-preview
  1380. "Register to get value from: ")))
  1381. (calculator-put-value (cdr (assq reg calculator-registers))))
  1382. (declare-function electric-describe-mode "ehelp" ())
  1383. (defun calculator-help ()
  1384. ;; this is used as the quick reference screen you get with `h'
  1385. "Quick reference:
  1386. * numbers/operators/parens/./e - enter expressions
  1387. + - * / \\(div) %(rem) _(-X,postfix) ;(1/X,postfix) ^(exp) L(og)
  1388. Q(sqrt) !(fact) S(in) C(os) T(an) |(or) #(xor) &(and) ~(not)
  1389. * >/< repeats last binary operation with its 2nd (1st) arg as postfix op
  1390. * I inverse the next trig function \
  1391. * \\='/\"/{/} - display/display args
  1392. * D - switch to all-decimal, or toggle deg/rad mode
  1393. * B/O/H/X - binary/octal/hex mode for i/o (both H and X are for hex)
  1394. * i/o - prefix for D/B/O/X - set only input/output modes
  1395. * enter/= - evaluate current expr. * s/g - set/get a register
  1396. * space - evaluate & save on list * l/v - list total/average
  1397. * up/down/C-p/C-n - browse saved * C-delete - clear all saved
  1398. * C-insert - copy whole expr. * C-return - evaluate, copy, exit
  1399. * insert - paste a number * backspace- delete backwards
  1400. * delete - clear argument or list value or whole expression (twice)
  1401. * escape/q - exit."
  1402. (interactive)
  1403. (if (eq last-command 'calculator-help)
  1404. (let ((mode-name "Calculator")
  1405. (major-mode 'calculator-mode)
  1406. (win (selected-window)))
  1407. (require 'ehelp)
  1408. (if (not calculator-electric-mode)
  1409. (describe-mode)
  1410. (electric-describe-mode))
  1411. (select-window win)
  1412. (message nil))
  1413. (let ((one (one-window-p t))
  1414. (win (selected-window))
  1415. (help-buf (get-buffer-create "*Help*")))
  1416. (save-window-excursion
  1417. (with-output-to-temp-buffer "*Help*"
  1418. (princ (documentation 'calculator-help)))
  1419. (when one (shrink-window-if-larger-than-buffer
  1420. (get-buffer-window help-buf)))
  1421. (message "`%s' again for more help, %s."
  1422. (calculator-last-input)
  1423. "any other key continues normally")
  1424. (select-window win)
  1425. (sit-for 360))
  1426. (select-window win))))
  1427. (defun calculator-quit ()
  1428. "Quit calculator."
  1429. (interactive)
  1430. (set-buffer calculator-buffer)
  1431. (let ((inhibit-read-only t)) (erase-buffer))
  1432. (unless calculator-electric-mode
  1433. (ignore-errors
  1434. (while (get-buffer-window calculator-buffer)
  1435. (delete-window (get-buffer-window calculator-buffer)))))
  1436. (kill-buffer calculator-buffer)
  1437. (message "Calculator done.")
  1438. (if calculator-electric-mode
  1439. (throw 'calculator-done nil) ; will kill the buffer
  1440. (setq calculator-buffer nil)))
  1441. (defun calculator-save-and-quit ()
  1442. "Quit the calculator, saving the result on the `kill-ring'."
  1443. (interactive)
  1444. (calculator-enter)
  1445. (calculator-copy)
  1446. (calculator-quit))
  1447. (defun calculator-repR (x)
  1448. "Repeat the last binary operation with its second argument and X.
  1449. To use this, apply a binary operator (evaluate it), then call this."
  1450. (if calculator-last-opXY
  1451. ;; avoid rebinding calculator-last-opXY
  1452. (let ((calculator-last-opXY calculator-last-opXY))
  1453. (calculator-funcall
  1454. (car calculator-last-opXY) x (nth 2 calculator-last-opXY)))
  1455. x))
  1456. (defun calculator-repL (x)
  1457. "Repeat the last binary operation with its first argument and X.
  1458. To use this, apply a binary operator (evaluate it), then call this."
  1459. (if calculator-last-opXY
  1460. ;; avoid rebinding calculator-last-opXY
  1461. (let ((calculator-last-opXY calculator-last-opXY))
  1462. (calculator-funcall
  1463. (car calculator-last-opXY) (nth 1 calculator-last-opXY) x))
  1464. x))
  1465. (defun calculator-expt (x y)
  1466. "Compute X^Y, dealing with errors appropriately."
  1467. (condition-case nil
  1468. (expt x y)
  1469. (domain-error 0.0e+NaN)
  1470. (range-error
  1471. (cond ((and (< x 1.0) (> x -1.0))
  1472. ;; For small x, the range error comes from large y.
  1473. 0.0)
  1474. ((and (> x 0.0) (< y 0.0))
  1475. ;; For large positive x and negative y, the range error
  1476. ;; comes from large negative y.
  1477. 0.0)
  1478. ((and (> x 0.0) (> y 0.0))
  1479. ;; For large positive x and positive y, the range error
  1480. ;; comes from large y.
  1481. 1.0e+INF)
  1482. ;; For the rest, x must be large and negative.
  1483. ;; The range errors come from large integer y.
  1484. ((< y 0.0)
  1485. 0.0)
  1486. ((eq (logand (truncate y) 1) 1) ; expansion of cl `oddp'
  1487. ;; If y is odd
  1488. -1.0e+INF)
  1489. (t
  1490. ;;
  1491. 1.0e+INF)))
  1492. (error 0.0e+NaN)))
  1493. (defun calculator-fact (x)
  1494. "Simple factorial of X."
  1495. (cond ((>= x 1.0e+INF) x)
  1496. ((or (and (floatp x) (isnan x)) (< x 0)) 0.0e+NaN)
  1497. ((>= (calculator-expt (/ x 3.0) x) 1.0e+INF) 1.0e+INF)
  1498. (t (let ((x (truncate x)) (r 1.0))
  1499. (while (> x 0) (setq r (* r x) x (1- x)))
  1500. r))))
  1501. (defun calculator-truncate (n)
  1502. "Truncate N, return 0 in case of overflow."
  1503. (condition-case nil (truncate n) (range-error 0)))
  1504. (provide 'calculator)
  1505. ;;; calculator.el ends here