Êíèãà: Learning GNU Emacs, 3rd Edition
11.5.3 The Calculator Mode
11.5.3 The Calculator Mode
The complete Lisp code for the calculator mode appears at the end of this section; you should refer to it while reading the following explanation. If you download or type the code in, you can use the calculator by typing M-x calc-mode Enter. You will be put in the buffer *Calc*
. You can type a line of numbers and operators and then type C-j to evaluate the line. Table 11-7 lists the three commands in calculator mode.
Table 11-7. Calculator mode commands
Command | Action |
---|---|
= |
Print the value at the top of the stack. |
p | Print the entire stack contents. |
c |
Clear the stack. |
Blank spaces are not necessary, except to separate numbers. For example, typing this:
4 17*6-=
followed by C-j, evaluates (4 * 17) - 6 and causes the result, 62, to be printed.
The heart of the code for the calculator mode is the functions calc-eval and calc-next-token. (See the code at the end of this section for these.) calc-eval is bound to C-j in Calculator mode. Starting at the beginning of the line preceding C-j, it calls calc-next-token to grab each token (number, operator, or command letter) in the line and evaluate it.
calc-next-token uses a cond construct to see if there is a number, operator, or command letter at point by using the regular expressions calc-number-regexp, calc-operator-regexp, and calc-command-regexp. According to which regular expression was matched, it sets the variable calc-proc-fun to the name (symbol) of the function that should be run (either calc-push-number, calc-operate, or calc-command), and it sets tok
to the result of the regular expression match.
In calc-eval, we see where the idea of a function as a list comes in. The funcall function reflects the fact that there is little difference between code and data in Lisp. We can put together a list consisting of a symbol and a bunch of expressions and evaluate it as a function, using the symbol as the function name and the expressions as arguments; this is what funcall does. In this case, the following:
(funcall calc-proc-fun tok)
treats the symbol value of calc-proc-fun as the name of the function to be called and calls it with the argument tok
. Then the function does one of three things:
• If the token is a number, calc-push-number pushes the number onto the stack.
• If the token is an operator, calc-operate performs the operation on the top two numbers on the stack (see below).
• If the token is a command, calc-command performs the appropriate command.
The function calc-operate takes the idea of functions as lists of data a step further by converting the token from the user directly into a function (an arithmetic operator). This step is accomplished by the function read, which takes a character string and converts it into a symbol. Thus, calc-operate uses funcall and read in combination as follows:
(defun calc-operate (tok)
(let ((op1 (calc-pop))
(op2 (calc-pop)))
(calc-push (funcall (read tok) op2 op1))))
This function takes the name of an arithmetic operator (as a string) as its argument. As we saw earlier, the string tok
is a token extracted from the *Calc*
buffer, in this case, an arithmetic operator such as +
or *
. The calc-operate function pops the top two arguments off the stack by using the pop function, which is similar to the use of cdr
earlier. read converts the token to a symbol, and thus to the name of an arithmetic function. So, if the operator is +
, then funcall is called as here:
(funcall '+ op2 op1)
Thus, the function + is called with the two arguments, which is exactly equivalent to simply (+ op2 op1). Finally, the result of the function is pushed back onto the stack.
All this voodoo is necessary so that, for example, the user can type a plus sign and Lisp automatically converts it into a plus function. We could have done the same thing less elegantly—and less efficiently—by writing calc-operate with a cond construct (as in calc-next-token), which would look like this:
(defun calc-operate (tok)
(let ((op1 (calc-pop))
(op2 (calc-pop)))
(cond ((equal tok "+")
(+ op2 op1))
((equal tok "-")
(- op2 op1))
((equal tok "*")
(* op2 op1))
((equal tok "/")
(/ op2 op1))
(t
(% op2 op1)))))
The final thing to notice in the calculator mode code is the function calc-mode, which starts the mode. It creates (and pops to) the *Calc*
buffer. Then it kills all existing local variables in the buffer, initializes the stack to nil
(empty), and creates the local variable calc-proc-fun (see the earlier discussion). Finally it sets Calculator mode as the major mode, sets the mode name, and activates the local keymap.
- 11.5 Programming a Major Mode
- 11.5.1 Components of a Major Mode
- 11.5.4 Lisp Code for the Calculator Mode
- 4.4.4 The Dispatcher
- About the author
- Chapter 7. The state machine
- Appendix E. Other resources and links
- Example NAT machine in theory
- The final stage of our NAT machine
- Compiling the user-land applications
- The conntrack entries
- Untracked connections and the raw table