;;; calc-prog.el --- user programmability functions for Calc
-;; Copyright (C) 1990, 1991, 1992, 1993, 2001 Free Software Foundation, Inc.
+;; Copyright (C) 1990, 1991, 1992, 1993, 2001, 2002, 2003, 2004,
+;; 2005 Free Software Foundation, Inc.
;; Author: David Gillespie <daveg@synaptics.com>
;; Maintainer: Jay Belanger <belanger@truman.edu>
(let ((lang calc-language))
(calc-edit-mode (list 'calc-finish-user-syntax-edit (list 'quote lang))
t
- (format "Editing %s-Mode Syntax Table"
+ (format "Editing %s-Mode Syntax Table. "
(cond ((null lang) "Normal")
((eq lang 'tex) "TeX")
+ ((eq lang 'latex) "LaTeX")
(t (capitalize (symbol-name lang))))))
(calc-write-parse-table (cdr (assq lang calc-user-parse-tables))
lang)))
(cond ((stringp (car p))
(let ((s (car p)))
(if (and (string-match "\\`\\\\dots\\>" s)
- (not (eq calc-lang 'tex)))
+ (not (memq calc-lang '(tex latex))))
(setq s (concat ".." (substring s 5))))
(if (or (and (string-match
"[a-zA-Z0-9\"{}]\\|\\`:=\\'\\|\\`#\\|\\`%%" s)
(defun calc-fix-token-name (name &optional unquoted)
(cond ((string-match "\\`\\.\\." name)
(concat "\\dots" (substring name 2)))
- ((and (equal name "{") (memq calc-lang '(tex eqn)))
+ ((and (equal name "{") (memq calc-lang '(tex latex eqn)))
"(")
- ((and (equal name "}") (memq calc-lang '(tex eqn)))
+ ((and (equal name "}") (memq calc-lang '(tex latex eqn)))
")")
- ((and (equal name "&") (eq calc-lang 'tex))
+ ((and (equal name "&") (memq calc-lang '(tex latex)))
",")
((equal name "#")
(search-backward "#")
(setq part (nconc part (list (if (= (match-beginning 1)
(match-end 1))
0
- (string-to-int
+ (string-to-number
(buffer-substring
(1+ (match-beginning 1))
(match-end 1)))))))
(list '\? (list (car last)) '("$$"))))))))
part))
-
(defun calc-user-define-invocation ()
(interactive)
(or last-kbd-macro
(setq calc-invocation-macro last-kbd-macro)
(message "Use `M-# Z' to invoke this macro"))
-
-(defun calc-user-define-edit (prefix)
- (interactive "P") ; but no calc-wrapper!
+(defun calc-user-define-edit ()
+ (interactive) ; but no calc-wrapper!
(message "Edit definition of command: z-")
- (let* ((key (read-char))
+ (let* (cmdname
+ (key (read-char))
(def (or (assq key (calc-user-key-map))
(assq (upcase key) (calc-user-key-map))
(assq (downcase key) (calc-user-key-map))
(error "No command defined for that key")))
(cmd (cdr def)))
- (if (symbolp cmd)
- (setq cmd (symbol-function cmd)))
+ (when (symbolp cmd)
+ (setq cmdname (symbol-name cmd))
+ (setq cmd (symbol-function cmd)))
(cond ((or (stringp cmd)
(and (consp cmd)
(eq (car-safe (nth 3 cmd)) 'calc-execute-kbd-macro)))
- (if (and (>= (prefix-numeric-value prefix) 0)
- (fboundp 'edit-kbd-macro)
- (symbolp (cdr def))
- (eq major-mode 'calc-mode))
- (progn
- (if (and (< (window-width) (frame-width))
- calc-display-trail)
- (let ((win (get-buffer-window (calc-trail-buffer))))
- (if win
- (delete-window win))))
- (edit-kbd-macro (cdr def) prefix nil
- (function
- (lambda (x)
- (and calc-display-trail
- (calc-wrapper
- (calc-trail-display 1 t)))))
- (function
- (lambda (cmd)
- (if (stringp (symbol-function cmd))
- (symbol-function cmd)
- (let ((mac (nth 1 (nth 3 (symbol-function
- cmd)))))
- (if (vectorp mac)
- (aref mac 1)
- mac)))))
- (function
- (lambda (new cmd)
- (if (stringp (symbol-function cmd))
- (fset cmd new)
- (let ((mac (cdr (nth 3 (symbol-function
- cmd)))))
- (if (vectorp (car mac))
- (progn
- (aset (car mac) 0
- (key-description new))
- (aset (car mac) 1 new))
- (setcar mac new))))))))
- (let ((keys (progn (and (fboundp 'edit-kbd-macro)
- (edit-kbd-macro nil))
- (fboundp 'MacEdit-parse-keys))))
- (calc-wrapper
- (calc-edit-mode (list 'calc-finish-macro-edit
- (list 'quote def)
- keys)
- t)
- (if keys
- (let (top
- (fill-column 70)
- (fill-prefix nil))
- (insert "Notations: RET, SPC, TAB, DEL, LFD, NUL"
- ", C-xxx, M-xxx.\n\n")
- (setq top (point))
- (insert (if (stringp cmd)
- (key-description cmd)
- (if (vectorp (nth 1 (nth 3 cmd)))
- (aref (nth 1 (nth 3 cmd)) 0)
- (key-description (nth 1 (nth 3 cmd)))))
- "\n")
- (if (>= (prog2 (forward-char -1)
- (current-column)
- (forward-char 1))
- (frame-width))
- (fill-region top (point))))
- (insert "Press C-q to quote control characters like RET"
- " and TAB.\n"
- (if (stringp cmd)
- cmd
- (if (vectorp (nth 1 (nth 3 cmd)))
- (aref (nth 1 (nth 3 cmd)) 1)
- (nth 1 (nth 3 cmd)))))))
- (calc-show-edit-buffer)
- (forward-line (if keys 2 1)))))
+ (let* ((mac (elt (nth 1 (nth 3 cmd)) 1))
+ (str (edmacro-format-keys mac t))
+ (kys (nth 3 (nth 3 cmd))))
+ (calc-edit-mode
+ (list 'calc-edit-macro-finish-edit cmdname kys)
+ t (format (concat
+ "Editing keyboard macro (%s, bound to %s).\n"
+ "Original keys: %s \n")
+ cmdname kys (elt (nth 1 (nth 3 cmd)) 0)))
+ (insert str "\n")
+ (calc-edit-format-macro-buffer)
+ (calc-show-edit-buffer)))
(t (let* ((func (calc-stack-command-p cmd))
(defn (and func
(symbolp func)
- (get func 'calc-user-defn))))
+ (get func 'calc-user-defn)))
+ (kys (concat "z" (char-to-string (car def))))
+ (intcmd (symbol-name (cdr def)))
+ (algcmd (if func (substring (symbol-name func) 9) "")))
(if (and defn (calc-valid-formula-func func))
- (progn
+ (let ((niceexpr (math-format-nice-expr defn (frame-width))))
(calc-wrapper
- (calc-edit-mode (list 'calc-finish-formula-edit
- (list 'quote func)))
- (insert (math-showing-full-precision
- (math-format-nice-expr defn (frame-width)))
- "\n"))
+ (calc-edit-mode
+ (list 'calc-finish-formula-edit (list 'quote func))
+ nil
+ (format (concat
+ "Editing formula (%s, %s, bound to %s).\n"
+ "Original formula: %s\n")
+ intcmd algcmd kys niceexpr))
+ (insert (math-showing-full-precision
+ niceexpr)
+ "\n"))
(calc-show-edit-buffer))
(error "That command's definition cannot be edited")))))))
-(defun calc-finish-macro-edit (def keys)
- (forward-line 1)
- (if (and keys (looking-at "\n")) (forward-line 1))
- (let* ((true-str (buffer-substring (point) (point-max)))
- (str true-str))
- (if keys (setq str (MacEdit-parse-keys str)))
- (if (symbolp (cdr def))
- (if (stringp (symbol-function (cdr def)))
- (fset (cdr def) str)
- (let ((mac (cdr (nth 3 (symbol-function (cdr def))))))
- (if (vectorp (car mac))
- (progn
- (aset (car mac) 0 (if keys true-str (key-description str)))
- (aset (car mac) 1 str))
- (setcar mac str))))
- (setcdr def str))))
-
-;;; The following are hooks into the MacEdit package from macedit.el.
-(put 'calc-execute-extended-command 'MacEdit-print
- (function (lambda ()
- (setq macro-str (concat "\excalc-" macro-str)))))
-
-(put 'calcDigit-start 'MacEdit-print
- (function (lambda ()
- (if calc-algebraic-mode
- (calc-macro-edit-algebraic)
- (MacEdit-unread-chars key-last)
- (let ((str "")
- (min-bsp 0)
- ch last)
- (while (and (setq ch (MacEdit-read-char))
- (or (and (>= ch ?0) (<= ch ?9))
- (memq ch '(?\. ?e ?\_ ?n ?\: ?\# ?M
- ?o ?h ?\@ ?\"))
- (and (memq ch '(?\' ?m ?s))
- (string-match "[@oh]" str))
- (and (or (and (>= ch ?a) (<= ch ?z))
- (and (>= ch ?A) (<= ch ?Z)))
- (string-match
- "^[-+]?\\(1[1-9]\\|[2-9][0-9]\\)#"
- str))
- (and (memq ch '(?\177 ?\C-h))
- (> (length str) 0))
- (and (memq ch '(?+ ?-))
- (> (length str) 0)
- (eq (aref str (1- (length str)))
- ?e))))
- (if (or (and (>= ch ?0) (<= ch ?9))
- (and (or (not (memq ch '(?\177 ?\C-h)))
- (<= (length str) min-bsp))
- (setq min-bsp (1+ (length str)))))
- (setq str (concat str (char-to-string ch)))
- (setq str (substring str 0 -1))))
- (if (memq ch '(32 10 13))
- (setq str (concat str (char-to-string ch)))
- (MacEdit-unread-chars ch))
- (insert "type \"")
- (MacEdit-insert-string str)
- (insert "\"\n"))))))
-
-(defun calc-macro-edit-algebraic ()
- (MacEdit-unread-chars key-last)
- (let ((str "")
- (min-bsp 0))
- (while (progn
- (MacEdit-lookup-key calc-alg-ent-map)
- (or (and (memq key-symbol '(self-insert-command
- calcAlg-previous))
- (< (length str) 60))
- (memq key-symbol
- '(backward-delete-char
- delete-backward-char
- backward-delete-char-untabify))
- (eq key-last 9)))
- (setq macro-str (substring macro-str (length key-str)))
- (if (or (eq key-symbol 'self-insert-command)
- (and (or (not (memq key-symbol '(backward-delete-char
- delete-backward-char
- backward-delete-char-untabify)))
- (<= (length str) min-bsp))
- (setq min-bsp (+ (length str) (length key-str)))))
- (setq str (concat str key-str))
- (setq str (substring str 0 -1))))
- (if (memq key-last '(10 13))
- (setq str (concat str key-str)
- macro-str (substring macro-str (length key-str))))
- (if (> (length str) 0)
- (progn
- (insert "type \"")
- (MacEdit-insert-string str)
- (insert "\"\n")))))
-(put 'calc-algebraic-entry 'MacEdit-print 'calc-macro-edit-algebraic)
-(put 'calc-auto-algebraic-entry 'MacEdit-print 'calc-macro-edit-algebraic)
-
-(defun calc-macro-edit-variable (&optional no-cmd)
- (let ((str "") ch)
- (or no-cmd (insert (symbol-name key-symbol) "\n"))
- (if (memq (MacEdit-peek-char) '(?\+ ?\- ?\* ?\/ ?^ ?\|))
- (setq str (char-to-string (MacEdit-read-char))))
- (if (and (setq ch (MacEdit-peek-char))
- (>= ch ?0) (<= ch ?9))
- (insert "type \"" str
- (char-to-string (MacEdit-read-char)) "\"\n")
- (if (> (length str) 0)
- (insert "type \"" str "\"\n"))
- (MacEdit-read-argument))))
-(put 'calc-store 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-store-into 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-store-neg 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-store-plus 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-store-minus 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-store-times 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-store-div 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-store-power 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-store-concat 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-store-inv 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-store-decr 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-store-incr 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-store-exchange 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-unstore 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-recall 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-let 'MacEdit-print 'calc-macro-edit-variable)
-(put 'calc-permanent-variable 'MacEdit-print 'calc-macro-edit-variable)
-
-(defun calc-macro-edit-variable-2 ()
- (calc-macro-edit-variable)
- (calc-macro-edit-variable t))
-(put 'calc-copy-variable 'MacEdit-print 'calc-macro-edit-variable-2)
-(put 'calc-declare-variable 'MacEdit-print 'calc-macro-edit-variable-2)
-
-(defun calc-macro-edit-quick-digit ()
- (insert "type \"" key-str "\" # " (symbol-name key-symbol) "\n"))
-(put 'calc-store-quick 'MacEdit-print 'calc-macro-edit-quick-digit)
-(put 'calc-store-into-quick 'MacEdit-print 'calc-macro-edit-quick-digit)
-(put 'calc-recall-quick 'MacEdit-print 'calc-macro-edit-quick-digit)
-(put 'calc-select-part 'MacEdit-print 'calc-macro-edit-quick-digit)
-(put 'calc-clean-num 'MacEdit-print 'calc-macro-edit-quick-digit)
-
+;; Formatting the macro buffer
+
+(defvar calc-edit-top)
+
+(defun calc-edit-macro-repeats ()
+ (goto-char calc-edit-top)
+ (while
+ (re-search-forward "^\\([0-9]+\\)\\*" nil t)
+ (let ((num (string-to-number (match-string 1)))
+ (line (buffer-substring (point) (line-end-position))))
+ (goto-char (line-beginning-position))
+ (kill-line 1)
+ (while (> num 0)
+ (insert line "\n")
+ (setq num (1- num))))))
+
+(defun calc-edit-macro-adjust-buffer ()
+ (calc-edit-macro-repeats)
+ (goto-char calc-edit-top)
+ (while (re-search-forward "^RET$" nil t)
+ (delete-char 1))
+ (goto-char calc-edit-top)
+ (while (and (re-search-forward "^$" nil t)
+ (not (= (point) (point-max))))
+ (delete-char 1)))
+
+(defun calc-edit-macro-command ()
+ "Return the command on the current line in a Calc macro editing buffer."
+ (let ((beg (line-beginning-position))
+ (end (save-excursion
+ (if (search-forward ";;" (line-end-position) 1)
+ (forward-char -2))
+ (skip-chars-backward " \t")
+ (point))))
+ (buffer-substring beg end)))
+
+(defun calc-edit-macro-command-type ()
+ "Return the type of command on the current line in a Calc macro editing buffer."
+ (let ((beg (save-excursion
+ (if (search-forward ";;" (line-end-position) t)
+ (progn
+ (skip-chars-forward " \t")
+ (point)))))
+ (end (save-excursion
+ (goto-char (line-end-position))
+ (skip-chars-backward " \t")
+ (point))))
+ (if beg
+ (buffer-substring beg end)
+ "")))
+
+(defun calc-edit-macro-combine-alg-ent ()
+ "Put an entire algebraic entry on a single line."
+ (let ((line (calc-edit-macro-command))
+ (type (calc-edit-macro-command-type))
+ curline
+ match)
+ (goto-char (line-beginning-position))
+ (kill-line 1)
+ (setq curline (calc-edit-macro-command))
+ (while (and curline
+ (not (string-equal "RET" curline))
+ (not (setq match (string-match "<return>" curline))))
+ (setq line (concat line curline))
+ (kill-line 1)
+ (setq curline (calc-edit-macro-command)))
+ (when match
+ (kill-line 1)
+ (setq line (concat line (substring curline 0 match))))
+ (setq line (replace-regexp-in-string "SPC" " SPC "
+ (replace-regexp-in-string " " "" line)))
+ (insert line "\t\t\t")
+ (if (> (current-column) 24)
+ (delete-char -1))
+ (insert ";; " type "\n")
+ (if match
+ (insert "RET\t\t\t;; calc-enter\n"))))
+
+(defun calc-edit-macro-combine-ext-command ()
+ "Put an entire extended command on a single line."
+ (let ((cmdbeg (calc-edit-macro-command))
+ (line "")
+ (type (calc-edit-macro-command-type))
+ curline
+ match)
+ (goto-char (line-beginning-position))
+ (kill-line 1)
+ (setq curline (calc-edit-macro-command))
+ (while (and curline
+ (not (string-equal "RET" curline))
+ (not (setq match (string-match "<return>" curline))))
+ (setq line (concat line curline))
+ (kill-line 1)
+ (setq curline (calc-edit-macro-command)))
+ (when match
+ (kill-line 1)
+ (setq line (concat line (substring curline 0 match))))
+ (setq line (replace-regexp-in-string " " "" line))
+ (insert cmdbeg " " line "\t\t\t")
+ (if (> (current-column) 24)
+ (delete-char -1))
+ (insert ";; " type "\n")
+ (if match
+ (insert "RET\t\t\t;; calc-enter\n"))))
+
+(defun calc-edit-macro-combine-var-name ()
+ "Put an entire variable name on a single line."
+ (let ((line (calc-edit-macro-command))
+ curline
+ match)
+ (goto-char (line-beginning-position))
+ (kill-line 1)
+ (if (member line '("0" "1" "2" "3" "4" "5" "6" "7" "8" "9"))
+ (insert line "\t\t\t;; calc quick variable\n")
+ (setq curline (calc-edit-macro-command))
+ (while (and curline
+ (not (string-equal "RET" curline))
+ (not (setq match (string-match "<return>" curline))))
+ (setq line (concat line curline))
+ (kill-line 1)
+ (setq curline (calc-edit-macro-command)))
+ (when match
+ (kill-line 1)
+ (setq line (concat line (substring curline 0 match))))
+ (setq line (replace-regexp-in-string " " "" line))
+ (insert line "\t\t\t")
+ (if (> (current-column) 24)
+ (delete-char -1))
+ (insert ";; calc variable\n")
+ (if match
+ (insert "RET\t\t\t;; calc-enter\n")))))
+
+(defun calc-edit-macro-combine-digits ()
+ "Put an entire sequence of digits on a single line."
+ (let ((line (calc-edit-macro-command))
+ curline)
+ (goto-char (line-beginning-position))
+ (kill-line 1)
+ (while (string-equal (calc-edit-macro-command-type) "calcDigit-start")
+ (setq line (concat line (calc-edit-macro-command)))
+ (kill-line 1))
+ (insert line "\t\t\t")
+ (if (> (current-column) 24)
+ (delete-char -1))
+ (insert ";; calc digits\n")))
+
+(defun calc-edit-format-macro-buffer ()
+ "Rewrite the Calc macro editing buffer."
+ (calc-edit-macro-adjust-buffer)
+ (goto-char calc-edit-top)
+ (let ((type (calc-edit-macro-command-type)))
+ (while (not (string-equal type ""))
+ (cond
+ ((or
+ (string-equal type "calc-algebraic-entry")
+ (string-equal type "calc-auto-algebraic-entry"))
+ (calc-edit-macro-combine-alg-ent))
+ ((string-equal type "calc-execute-extended-command")
+ (calc-edit-macro-combine-ext-command))
+ ((string-equal type "calcDigit-start")
+ (calc-edit-macro-combine-digits))
+ ((or
+ (string-equal type "calc-store")
+ (string-equal type "calc-store-into")
+ (string-equal type "calc-store-neg")
+ (string-equal type "calc-store-plus")
+ (string-equal type "calc-store-minus")
+ (string-equal type "calc-store-div")
+ (string-equal type "calc-store-times")
+ (string-equal type "calc-store-power")
+ (string-equal type "calc-store-concat")
+ (string-equal type "calc-store-inv")
+ (string-equal type "calc-store-dec")
+ (string-equal type "calc-store-incr")
+ (string-equal type "calc-store-exchange")
+ (string-equal type "calc-unstore")
+ (string-equal type "calc-recall")
+ (string-equal type "calc-let")
+ (string-equal type "calc-permanent-variable"))
+ (forward-line 1)
+ (calc-edit-macro-combine-var-name))
+ ((or
+ (string-equal type "calc-copy-variable")
+ (string-equal type "calc-copy-special-constant")
+ (string-equal type "calc-declare-variable"))
+ (forward-line 1)
+ (calc-edit-macro-combine-var-name)
+ (calc-edit-macro-combine-var-name))
+ (t (forward-line 1)))
+ (setq type (calc-edit-macro-command-type))))
+ (goto-char calc-edit-top))
+
+;; Finish editing the macro
+
+(defun calc-edit-macro-pre-finish-edit ()
+ (goto-char calc-edit-top)
+ (while (re-search-forward "\\(^\\| \\)RET\\($\\|\t\\| \\)" nil t)
+ (search-backward "RET")
+ (delete-char 3)
+ (insert "<return>")))
+
+(defun calc-edit-macro-finish-edit (cmdname key)
+ "Finish editing a Calc macro.
+Redefine the corresponding command."
+ (interactive)
+ (let ((cmd (intern cmdname)))
+ (calc-edit-macro-pre-finish-edit)
+ (let* ((str (buffer-substring calc-edit-top (point-max)))
+ (mac (edmacro-parse-keys str t)))
+ (if (= (length mac) 0)
+ (fmakunbound cmd)
+ (fset cmd
+ (list 'lambda '(arg)
+ '(interactive "P")
+ (list 'calc-execute-kbd-macro
+ (vector (key-description mac)
+ mac)
+ 'arg key)))))))
(defun calc-finish-formula-edit (func)
(let ((buf (current-buffer))
- (str (buffer-substring (point) (point-max)))
+ (str (buffer-substring calc-edit-top (point-max)))
(start (point))
(body (calc-valid-formula-func func)))
(set-buffer calc-original-buffer)
(vectorp (nth 1 (nth 3 fcmd)))
(progn (and (fboundp 'edit-kbd-macro)
(edit-kbd-macro nil))
- (fboundp 'MacEdit-parse-keys))
+ (fboundp 'edmacro-parse-keys))
(setq q-ok t)
(aset (nth 1 (nth 3 fcmd)) 1 nil))
(insert (setq str (prin1-to-string
(calc-execute-kbd-macro last-kbd-macro arg))
(defun calc-execute-kbd-macro (mac arg &rest prefix)
+ (if calc-keep-args-flag
+ (calc-keep-args))
(if (and (vectorp mac) (> (length mac) 0) (stringp (aref mac 0)))
(setq mac (or (aref mac 1)
(aset mac 1 (progn (and (fboundp 'edit-kbd-macro)
(edit-kbd-macro nil))
- (MacEdit-parse-keys (aref mac 0)))))))
+ (edmacro-parse-keys (aref mac 0)))))))
(if (< (prefix-numeric-value arg) 0)
(execute-kbd-macro mac (- (prefix-numeric-value arg)))
(if calc-executing-macro
(error "Unbalanced Z' in keyboard macro")))
-(defun calc-kbd-report (msg)
- (interactive "sMessage: ")
- (calc-wrapper
- (math-working msg (calc-top-n 1))))
+;; (defun calc-kbd-report (msg)
+;; (interactive "sMessage: ")
+;; (calc-wrapper
+;; (math-working msg (calc-top-n 1))))
-(defun calc-kbd-query (msg)
- (interactive "sPrompt: ")
- (calc-wrapper
- (calc-alg-entry nil (and (not (equal msg "")) msg))))
+(defun calc-kbd-query ()
+ (interactive)
+ (let ((defining-kbd-macro nil)
+ (executing-kbd-macro nil)
+ (msg (calc-top 1)))
+ (if (not (eq (car-safe msg) 'vec))
+ (error "No prompt string provided")
+ (setq msg (math-vector-to-string msg))
+ (calc-wrapper
+ (calc-pop-stack 1)
+ (calc-alg-entry nil (and (not (equal msg "")) msg))))))
;;;; Logical operations.