## 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.

Оглавление книги

Генерация: 0.509. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз