*** calculator-at-emacs.el	Sat Jul  6 03:24:21 2002
--- calculator.el	Tue Jul  9 14:18:52 2002
***************
*** 4,9 ****
--- 4,10 ----
  
  ;; Author: Eli Barzilay <eli@barzilay.org>
  ;; Keywords: tools, convenience
+ ;; Time-stamp: <2002-07-05 11:43:49 eli>
  
  ;; This file is part of GNU Emacs.
  
***************
*** 100,105 ****
--- 101,120 ----
    :type  'integer
    :group 'calculator)
  
+ (defcustom calculator-radix-grouping-mode t
+   "*Use digit grouping in radix output mode.
+ If this is set, chunks of `calculator-radix-grouping-digits' characters
+ will be separated by `calculator-radix-grouping-separator' when in radix
+ output mode is active (determined by `calculator-output-radix').")
+ 
+ (defcustom calculator-radix-grouping-digits 4
+   "*The number of digits used for grouping display in radix modes.
+ See `calculator-radix-grouping-mode'.")
+ 
+ (defcustom calculator-radix-grouping-separator "'"
+   "*The separator used in radix grouping display.
+ See `calculator-radix-grouping-mode'.")
+ 
  (defcustom calculator-remove-zeros t
    "*Non-nil value means delete all redundant zero decimal digits.
  If this value is not t, and not nil, redundant zeros are removed except
***************
*** 387,393 ****
                                             "oD" "oH" "oX" "oO" "oB")
               (calculator-rotate-displayer      "'")
               (calculator-rotate-displayer-back "\"")
!              (calculator-displayer-pref        "{")
               (calculator-displayer-next        "}")
               (calculator-saved-up      [up] [?\C-p])
               (calculator-saved-down    [down] [?\C-n])
--- 402,408 ----
                                             "oD" "oH" "oX" "oO" "oB")
               (calculator-rotate-displayer      "'")
               (calculator-rotate-displayer-back "\"")
!              (calculator-displayer-prev        "{")
               (calculator-displayer-next        "}")
               (calculator-saved-up      [up] [?\C-p])
               (calculator-saved-down    [down] [?\C-n])
***************
*** 399,408 ****
               (calculator-save-and-quit [(control return)]
                                         [(control kp-enter)])
               (calculator-paste         [insert] [(shift insert)]
!                                        [mouse-2])
               (calculator-clear         [delete] [?\C-?] [?\C-d])
               (calculator-help          [?h] [??] [f1] [help])
!              (calculator-copy          [(control insert)])
               (calculator-backspace     [backspace])
               )))
        (while p
--- 414,423 ----
               (calculator-save-and-quit [(control return)]
                                         [(control kp-enter)])
               (calculator-paste         [insert] [(shift insert)]
!                                        [paste] [mouse-2] [?\C-y])
               (calculator-clear         [delete] [?\C-?] [?\C-d])
               (calculator-help          [?h] [??] [f1] [help])
!              (calculator-copy          [(control insert)] [copy])
               (calculator-backspace     [backspace])
               )))
        (while p
***************
*** 536,542 ****
               ,@(mapcar (lambda (x) (nth 1 x)) radix-selectors)
               "---"
               ,@(mapcar (lambda (x) (nth 2 x)) radix-selectors)))
!            ("Decimal Dislpay"
              ,@(mapcar (lambda (d)
                          (vector (cadr d)
                                  ;; Note: inserts actual object here
--- 551,557 ----
               ,@(mapcar (lambda (x) (nth 1 x)) radix-selectors)
               "---"
               ,@(mapcar (lambda (x) (nth 2 x)) radix-selectors)))
!            ("Decimal Display"
              ,@(mapcar (lambda (d)
                          (vector (cadr d)
                                  ;; Note: inserts actual object here
***************
*** 611,620 ****
  * \"=?\": (? is B/O/H) the display radix (when input is decimal);
  * \"??\": (? is D/B/O/H) 1st char for input radix, 2nd for display.
  
! Also, the quote character can be used to switch display modes for
! decimal numbers (double-quote rotates back), and the two brace
! characters (\"{\" and \"}\" change display parameters that these
! displayers use (if they handle such).
  
  Values can be saved for future reference in either a list of saved
  values, or in registers.
--- 626,636 ----
  * \"=?\": (? is B/O/H) the display radix (when input is decimal);
  * \"??\": (? is D/B/O/H) 1st char for input radix, 2nd for display.
  
! Also, the quote key can be used to switch display modes for decimal
! numbers (double-quote rotates back), and the two brace characters
! \(\"{\" and \"}\" change display parameters that these displayers use (if
! they handle such).  If output is using any radix mode, then these keys
! toggle digit grouping mode and the chunk size.
  
  Values can be saved for future reference in either a list of saved
  values, or in registers.
***************
*** 719,724 ****
--- 735,746 ----
    (if (and calculator-restart-other-mode calculator-electric-mode)
      (calculator)))
  
+ (defun calculator-message (string &rest arguments)
+   "Same as `message', but special handle of electric mode."
+   (apply 'message string arguments)
+   (if calculator-electric-mode
+     (progn (sit-for 1) (message nil))))
+ 
  ;;;---------------------------------------------------------------------
  ;;; Operators
  
***************
*** 820,901 ****
        (concat calculator-prompt
                (substring prompt (+ trim (length calculator-prompt)))))))
  
! (defun calculator-curnum-value ()
!   "Get the numeric value of the displayed number string as a float."
    (if calculator-input-radix
      (let ((radix
             (cdr (assq calculator-input-radix
                        '((bin . 2) (oct . 8) (hex . 16)))))
!           (i -1) (value 0))
!       ;; assume valid input (upcased & characters in range)
!       (while (< (setq i (1+ i)) (length calculator-curnum))
!         (setq value
!               (+ (let ((ch (aref calculator-curnum i)))
!                    (- ch (if (<= ch ?9) ?0 (- ?A 10))))
!                  (* radix value))))
        value)
!     (car
!      (read-from-string
!       (cond
!         ((equal "." calculator-curnum)
!          "0.0")
!         ((string-match "[eE][+-]?$" calculator-curnum)
!          (concat calculator-curnum "0"))
!         ((string-match "\\.[0-9]\\|[eE]" calculator-curnum)
!          calculator-curnum)
!         ((string-match "\\." calculator-curnum)
!          ;; do this because Emacs reads "23." as an integer
!          (concat calculator-curnum "0"))
!         ((stringp calculator-curnum)
!          (concat calculator-curnum ".0"))
!         (t "0.0"))))))
  
  (defun calculator-rotate-displayer (&optional new-disp)
    "Switch to the next displayer on the `calculator-displayers' list.
  Can be called with an optional argument NEW-DISP to force rotation to
! that argument."
    (interactive)
!   (setq calculator-displayers
!         (if (and new-disp (memq new-disp calculator-displayers))
!           (let ((tmp nil))
!             (while (not (eq (car calculator-displayers) new-disp))
!               (setq tmp (cons (car calculator-displayers) tmp))
!               (setq calculator-displayers (cdr calculator-displayers)))
!             (setq calculator-displayers
!                   (nconc calculator-displayers (nreverse tmp))))
!           (nconc (cdr calculator-displayers)
!                  (list (car calculator-displayers)))))
!   (message "Using %s." (cadr (car calculator-displayers)))
!   (if calculator-electric-mode
!     (progn (sit-for 1) (message nil)))
    (calculator-enter))
  
  (defun calculator-rotate-displayer-back ()
!   "Like `calculator-rotate-displayer', but rotates modes back."
    (interactive)
    (calculator-rotate-displayer (car (last calculator-displayers))))
  
  (defun calculator-displayer-prev ()
    "Send the current displayer function a 'left argument.
  This is used to modify display arguments (if the current displayer
! function supports this)."
    (interactive)
!   (and (car calculator-displayers)
!        (let ((disp (caar calculator-displayers)))
!          (cond ((symbolp disp) (funcall disp 'left))
!                ((and (consp disp) (eq 'std (car disp)))
!                 (calculator-standard-displayer 'left (cadr disp)))))))
  
  (defun calculator-displayer-next ()
    "Send the current displayer function a 'right argument.
  This is used to modify display arguments (if the current displayer
! function supports this)."
    (interactive)
!   (and (car calculator-displayers)
!        (let ((disp (caar calculator-displayers)))
!          (cond ((symbolp disp) (funcall disp 'right))
!                ((and (consp disp) (eq 'std (car disp)))
!                 (calculator-standard-displayer 'right (cadr disp)))))))
  
  (defun calculator-remove-zeros (numstr)
    "Get a number string NUMSTR and remove unnecessary zeroes.
--- 842,957 ----
        (concat calculator-prompt
                (substring prompt (+ trim (length calculator-prompt)))))))
  
! (defun calculator-string-to-number (str)
!   "Convert the given STR to a number, according to the value of
! `calculator-input-radix'."
    (if calculator-input-radix
      (let ((radix
             (cdr (assq calculator-input-radix
                        '((bin . 2) (oct . 8) (hex . 16)))))
!           (i -1) (value 0) (new-value 0))
!       ;; assume mostly valid input (e.g., characters in range)
!       (while (< (setq i (1+ i)) (length str))
!         (setq new-value
!               (let* ((ch (upcase (aref str i)))
!                      (n (cond ((< ch ?0)  nil)
!                               ((<= ch ?9) (- ch ?0))
!                               ((< ch ?A)  nil)
!                               ((<= ch ?Z) (- ch (- ?A 10)))
!                               (t          nil))))
!                 (if (and n (<= 0 n) (< n radix))
!                   (+ n (* radix value))
!                   (progn
!                     (calculator-message
!                      "Warning: Ignoring bad input character `%c'." ch)
!                     (sit-for 1)
!                     value))))
!         (if (if (< new-value 0) (> value 0) (< value 0))
!           (calculator-message "Warning: Overflow in input."))
!         (setq value new-value))
        value)
!     (car (read-from-string
!           (cond ((equal "." str) "0.0")
!                 ((string-match "[eE][+-]?$" str) (concat str "0"))
!                 ((string-match "\\.[0-9]\\|[eE]" str) str)
!                 ((string-match "\\." str)
!                  ;; do this because Emacs reads "23." as an integer
!                  (concat str "0"))
!                 ((stringp str) (concat str ".0"))
!                 (t "0.0"))))))
! 
! (defun calculator-curnum-value ()
!   "Get the numeric value of the displayed number string as a float."
!   (calculator-string-to-number calculator-curnum))
  
  (defun calculator-rotate-displayer (&optional new-disp)
    "Switch to the next displayer on the `calculator-displayers' list.
  Can be called with an optional argument NEW-DISP to force rotation to
! that argument.
! If radix output mode is active, toggle digit grouping."
    (interactive)
!   (cond
!     (calculator-output-radix
!      (setq calculator-radix-grouping-mode
!            (not calculator-radix-grouping-mode))
!      (calculator-message
!       "Digit grouping mode %s."
!       (if calculator-radix-grouping-mode "ON" "OFF")))
!     (t
!      (setq calculator-displayers
!            (if (and new-disp (memq new-disp calculator-displayers))
!              (let ((tmp nil))
!                (while (not (eq (car calculator-displayers) new-disp))
!                  (setq tmp (cons (car calculator-displayers) tmp))
!                  (setq calculator-displayers
!                        (cdr calculator-displayers)))
!                (setq calculator-displayers
!                      (nconc calculator-displayers (nreverse tmp))))
!              (nconc (cdr calculator-displayers)
!                     (list (car calculator-displayers)))))
!      (calculator-message
!       "Using %s." (cadr (car calculator-displayers)))))
    (calculator-enter))
  
  (defun calculator-rotate-displayer-back ()
!   "Like `calculator-rotate-displayer', but rotates modes back.
! If radix output mode is active, toggle digit grouping."
    (interactive)
    (calculator-rotate-displayer (car (last calculator-displayers))))
  
  (defun calculator-displayer-prev ()
    "Send the current displayer function a 'left argument.
  This is used to modify display arguments (if the current displayer
! function supports this).
! If radix output mode is active, increase the grouping size."
    (interactive)
!   (if calculator-output-radix
!     (progn (setq calculator-radix-grouping-digits
!                  (1+ calculator-radix-grouping-digits))
!            (calculator-enter))
!     (and (car calculator-displayers)
!          (let ((disp (caar calculator-displayers)))
!            (cond
!              ((symbolp disp) (funcall disp 'left))
!              ((and (consp disp) (eq 'std (car disp)))
!               (calculator-standard-displayer 'left (cadr disp))))))))
  
  (defun calculator-displayer-next ()
    "Send the current displayer function a 'right argument.
  This is used to modify display arguments (if the current displayer
! function supports this).
! If radix output mode is active, decrease the grouping size."
    (interactive)
!   (if calculator-output-radix
!     (progn (setq calculator-radix-grouping-digits
!                  (max 2 (1- calculator-radix-grouping-digits)))
!            (calculator-enter))
!     (and (car calculator-displayers)
!          (let ((disp (caar calculator-displayers)))
!            (cond
!              ((symbolp disp) (funcall disp 'right))
!              ((and (consp disp) (eq 'std (car disp)))
!               (calculator-standard-displayer 'right (cadr disp))))))))
  
  (defun calculator-remove-zeros (numstr)
    "Get a number string NUMSTR and remove unnecessary zeroes.
***************
*** 997,1003 ****
                    (calculator-remove-zeros str))
                  "e" (number-to-string exp))))))
  
! (defun calculator-num-to-string (num)
    "Convert NUM to a displayable string."
    (cond
      ((and (numberp num) calculator-output-radix)
--- 1053,1059 ----
                    (calculator-remove-zeros str))
                  "e" (number-to-string exp))))))
  
! (defun calculator-number-to-string (num)
    "Convert NUM to a displayable string."
    (cond
      ((and (numberp num) calculator-output-radix)
***************
*** 1017,1022 ****
--- 1073,1086 ----
                                          (?6 . "110") (?7 . "111")))))))
             (string-match "^0*\\(.+\\)" s)
             (setq str (match-string 1 s))))
+        (if calculator-radix-grouping-mode
+          (let ((d (/ (length str) calculator-radix-grouping-digits))
+                (r (% (length str) calculator-radix-grouping-digits)))
+            (while (>= (setq d (1- d)) (if (zerop r) 1 0))
+              (let ((i (+ r (* d calculator-radix-grouping-digits))))
+                (setq str (concat (substring str 0 i)
+                                  calculator-radix-grouping-separator
+                                  (substring str i)))))))
         (upcase
          (if (and (not calculator-2s-complement) (< num 0))
            (concat "-" str)
***************
*** 1053,1059 ****
                              ;; customizable display for a single value
                              (caar calculator-displayers)
                              calculator-displayer)))
!                      (mapconcat 'calculator-num-to-string
                                  (reverse calculator-stack)
                                  " "))
                     " "
--- 1117,1123 ----
                              ;; customizable display for a single value
                              (caar calculator-displayers)
                              calculator-displayer)))
!                      (mapconcat 'calculator-number-to-string
                                  (reverse calculator-stack)
                                  " "))
                     " "
***************
*** 1321,1329 ****
            (if (not (and op (= -1 (calculator-op-arity op))))
              ;;(error "Binary operator without a first operand")
              (progn
!               (message "Binary operator without a first operand")
!               (if calculator-electric-mode
!                 (progn (sit-for 1) (message nil)))
                (throw 'op-error nil)))))
        (calculator-reduce-stack
         (cond ((eq (nth 1 op) '\() 10)
--- 1385,1392 ----
            (if (not (and op (= -1 (calculator-op-arity op))))
              ;;(error "Binary operator without a first operand")
              (progn
!               (calculator-message
!                "Binary operator without a first operand")
                (throw 'op-error nil)))))
        (calculator-reduce-stack
         (cond ((eq (nth 1 op) '\() 10)
***************
*** 1336,1344 ****
                     (not (numberp (car calculator-stack)))))
          ;;(error "Unterminated expression")
          (progn
!           (message "Unterminated expression")
!           (if calculator-electric-mode
!             (progn (sit-for 1) (message nil)))
            (throw 'op-error nil)))
        (setq calculator-stack (cons op calculator-stack))
        (calculator-reduce-stack (calculator-op-prec op))
--- 1399,1405 ----
                     (not (numberp (car calculator-stack)))))
          ;;(error "Unterminated expression")
          (progn
!           (calculator-message "Unterminated expression")
            (throw 'op-error nil)))
        (setq calculator-stack (cons op calculator-stack))
        (calculator-reduce-stack (calculator-op-prec op))
***************
*** 1542,1548 ****
        (setcdr as val)
        (setq calculator-registers
              (cons (cons reg val) calculator-registers)))
!     (message (format "[%c] := %S" reg val))))
  
  (defun calculator-put-value (val)
    "Paste VAL as if entered.
--- 1603,1609 ----
        (setcdr as val)
        (setq calculator-registers
              (cons (cons reg val) calculator-registers)))
!     (calculator-message "[%c] := %S" reg val)))
  
  (defun calculator-put-value (val)
    "Paste VAL as if entered.
***************
*** 1554,1577 ****
      (progn
        (calculator-clear-fragile)
        (setq calculator-curnum (let ((calculator-displayer "%S"))
!                                 (calculator-num-to-string val)))
        (calculator-update-display))))
  
  (defun calculator-paste ()
    "Paste a value from the `kill-ring'."
    (interactive)
    (calculator-put-value
!    (let ((str (current-kill 0)))
!      (and calculator-paste-decimals
            (string-match "\\([0-9]+\\)\\(\\.[0-9]+\\)?\\(e[0-9]+\\)?"
                          str)
            (or (match-string 1 str)
                (match-string 2 str)
                (match-string 3 str))
!           (setq str (concat (match-string 1 str)
                              (or (match-string 2 str) ".0")
!                             (match-string 3 str))))
!      (condition-case nil (car (read-from-string str))
         (error nil)))))
  
  (defun calculator-get-register (reg)
--- 1615,1640 ----
      (progn
        (calculator-clear-fragile)
        (setq calculator-curnum (let ((calculator-displayer "%S"))
!                                 (calculator-number-to-string val)))
        (calculator-update-display))))
  
  (defun calculator-paste ()
    "Paste a value from the `kill-ring'."
    (interactive)
    (calculator-put-value
!    (let ((str (replace-regexp-in-string
!                "^ *\\(.+[^ ]\\) *$" "\\1" (current-kill 0))))
!      (and (not calculator-input-radix)
!           calculator-paste-decimals
            (string-match "\\([0-9]+\\)\\(\\.[0-9]+\\)?\\(e[0-9]+\\)?"
                          str)
            (or (match-string 1 str)
                (match-string 2 str)
                (match-string 3 str))
!           (setq str (concat (or (match-string 1 str) "0")
                              (or (match-string 2 str) ".0")
!                             (or (match-string 3 str) ""))))
!      (condition-case nil (calculator-string-to-number str)
         (error nil)))))
  
  (defun calculator-get-register (reg)
