calc: Calling Calc from Your Programs
18.5.6 Calling Calc from Your Lisp Programs
-------------------------------------------
A later section (Internals) gives a full description of Calc’s
internal Lisp functions. It’s not hard to call Calc from inside your
programs, but the number of these functions can be daunting. So Calc
provides one special “programmer-friendly” function called ‘calc-eval’
that can be made to do just about everything you need. It’s not as fast
as the low-level Calc functions, but it’s much simpler to use!
It may seem that ‘calc-eval’ itself has a daunting number of options,
but they all stem from one simple operation.
In its simplest manifestation, ‘(calc-eval "1+2")’ parses the string
‘"1+2"’ as if it were a Calc algebraic entry and returns the result
formatted as a string: ‘"3"’.
Since ‘calc-eval’ is on the list of recommended ‘autoload’ functions,
you don’t need to make any special preparations to load Calc before
calling ‘calc-eval’ the first time. Calc will be loaded and initialized
for you.
All the Calc modes that are currently in effect will be used when
evaluating the expression and formatting the result.
18.5.6.1 Additional Arguments to ‘calc-eval’
............................................
If the input string parses to a list of expressions, Calc returns the
results separated by ‘", "’. You can specify a different separator by
giving a second string argument to ‘calc-eval’: ‘(calc-eval "1+2,3+4"
";")’ returns ‘"3;7"’.
The “separator” can also be any of several Lisp symbols which request
other behaviors from ‘calc-eval’. These are discussed one by one below.
You can give additional arguments to be substituted for ‘$’, ‘$$’,
and so on in the main expression. For example, ‘(calc-eval "$/$$" nil
"7" "1+1")’ evaluates the expression ‘"7/(1+1)"’ to yield the result
‘"3.5"’ (assuming Fraction mode is not in effect). Note the ‘nil’ used
as a placeholder for the item-separator argument.
18.5.6.2 Error Handling
.......................
If ‘calc-eval’ encounters an error, it returns a list containing the
character position of the error, plus a suitable message as a string.
Note that ‘1 / 0’ is _not_ an error by Calc’s standards; it simply
returns the string ‘"1 / 0"’ which is the division left in symbolic
form. But ‘(calc-eval "1/")’ will return the list ‘(2 "Expected a
number")’.
If you bind the variable ‘calc-eval-error’ to ‘t’ using a ‘let’ form
surrounding the call to ‘calc-eval’, errors instead call the Emacs
‘error’ function which aborts to the Emacs command loop with a beep and
an error message.
If you bind this variable to the symbol ‘string’, error messages are
returned as strings instead of lists. The character position is
ignored.
As a courtesy to other Lisp code which may be using Calc, be sure to
bind ‘calc-eval-error’ using ‘let’ rather than changing it permanently
with ‘setq’.
18.5.6.3 Numbers Only
.....................
Sometimes it is preferable to treat ‘1 / 0’ as an error rather than
returning a symbolic result. If you pass the symbol ‘num’ as the second
argument to ‘calc-eval’, results that are not constants are treated as
errors. The error message reported is the first ‘calc-why’ message if
there is one, or otherwise “Number expected.”
A result is “constant” if it is a number, vector, or other object
that does not include variables or function calls. If it is a vector,
the components must themselves be constants.
18.5.6.4 Default Modes
......................
If the first argument to ‘calc-eval’ is a list whose first element is a
formula string, then ‘calc-eval’ sets all the various Calc modes to
their default values while the formula is evaluated and formatted. For
example, the precision is set to 12 digits, digit grouping is turned
off, and the Normal language mode is used.
This same principle applies to the other options discussed below. If
the first argument would normally be X, then it can also be the list
‘(X)’ to use the default mode settings.
If there are other elements in the list, they are taken as
variable-name/value pairs which override the default mode settings.
Look at the documentation at the front of the ‘calc.el’ file to find the
names of the Lisp variables for the various modes. The mode settings
are restored to their original values when ‘calc-eval’ is done.
For example, ‘(calc-eval '("$+$$" calc-internal-prec 8) 'num a b)’
computes the sum of two numbers, requiring a numeric result, and using
default mode settings except that the precision is 8 instead of the
default of 12.
It’s usually best to use this form of ‘calc-eval’ unless your program
actually considers the interaction with Calc’s mode settings to be a
feature. This will avoid all sorts of potential “gotchas”; consider
what happens with ‘(calc-eval "sqrt(2)" 'num)’ when the user has left
Calc in Symbolic mode or No-Simplify mode.
As another example, ‘(equal (calc-eval '("$<$$") nil a b) "1")’
checks if the number in string ‘a’ is less than the one in string ‘b’.
Without using a list, the integer 1 might come out in a variety of
formats which would be hard to test for conveniently: ‘"1"’, ‘"8#1"’,
‘"00001"’. (But see “Predicates” mode, below.)
18.5.6.5 Raw Numbers
....................
Normally all input and output for ‘calc-eval’ is done with strings. You
can do arithmetic with, say, ‘(calc-eval "$+$$" nil a b)’ in place of
‘(+ a b)’, but this is very inefficient since the numbers must be
converted to and from string format as they are passed from one
‘calc-eval’ to the next.
If the separator is the symbol ‘raw’, the result will be returned as
a raw Calc data structure rather than a string. You can read about how
these objects look in the following sections, but usually you can treat
them as “black box” objects with no important internal structure.
There is also a ‘rawnum’ symbol, which is a combination of ‘raw’
(returning a raw Calc object) and ‘num’ (signaling an error if that
object is not a constant).
You can pass a raw Calc object to ‘calc-eval’ in place of a string,
either as the formula itself or as one of the ‘$’ arguments. Thus
‘(calc-eval "$+$$" 'raw a b)’ is an addition function that operates on
raw Calc objects. Of course in this case it would be easier to call the
low-level ‘math-add’ function in Calc, if you can remember its name.
In particular, note that a plain Lisp integer is acceptable to Calc
as a raw object. (All Lisp integers are accepted on input, but integers
of more than six decimal digits are converted to “big-integer” form for
output. Data Type Formats.)
When it comes time to display the object, just use ‘(calc-eval a)’ to
format it as a string.
It is an error if the input expression evaluates to a list of values.
The separator symbol ‘list’ is like ‘raw’ except that it returns a list
of one or more raw Calc objects.
Note that a Lisp string is not a valid Calc object, nor is a list
containing a string. Thus you can still safely distinguish all the
various kinds of error returns discussed above.
18.5.6.6 Predicates
...................
If the separator symbol is ‘pred’, the result of the formula is treated
as a true/false value; ‘calc-eval’ returns ‘t’ or ‘nil’, respectively.
A value is considered “true” if it is a non-zero number, or false if it
is zero or if it is not a number.
For example, ‘(calc-eval "$<$$" 'pred a b)’ tests whether one value
is less than another.
As usual, it is also possible for ‘calc-eval’ to return one of the
error indicators described above. Lisp will interpret such an indicator
as “true” if you don’t check for it explicitly. If you wish to have an
error register as “false”, use something like ‘(eq (calc-eval ...) t)’.
18.5.6.7 Variable Values
........................
Variables in the formula passed to ‘calc-eval’ are not normally replaced
by their values. If you wish this, you can use the ‘evalv’ function
(Algebraic Manipulation). For example, if 4 is stored in Calc
variable ‘a’ (i.e., in Lisp variable ‘var-a’), then ‘(calc-eval "a+pi")’
will return the formula ‘"a + pi"’, but ‘(calc-eval "evalv(a+pi)")’ will
return ‘"7.14159265359"’.
To store in a Calc variable, just use ‘setq’ to store in the
corresponding Lisp variable. (This is obtained by prepending ‘var-’ to
the Calc variable name.) Calc routines will understand either string or
raw form values stored in variables, although raw data objects are much
more efficient. For example, to increment the Calc variable ‘a’:
(setq var-a (calc-eval "evalv(a+1)" 'raw))
18.5.6.8 Stack Access
.....................
If the separator symbol is ‘push’, the formula argument is evaluated
(with possible ‘$’ expansions, as usual). The result is pushed onto the
Calc stack. The return value is ‘nil’ (unless there is an error from
evaluating the formula, in which case the return value depends on
‘calc-eval-error’ in the usual way).
If the separator symbol is ‘pop’, the first argument to ‘calc-eval’
must be an integer instead of a string. That many values are popped
from the stack and thrown away. A negative argument deletes the entry
at that stack level. The return value is the number of elements
remaining in the stack after popping; ‘(calc-eval 0 'pop)’ is a good way
to measure the size of the stack.
If the separator symbol is ‘top’, the first argument to ‘calc-eval’
must again be an integer. The value at that stack level is formatted as
a string and returned. Thus ‘(calc-eval 1 'top)’ returns the
top-of-stack value. If the integer is out of range, ‘nil’ is returned.
The separator symbol ‘rawtop’ is just like ‘top’ except that the
stack entry is returned as a raw Calc object instead of as a string.
In all of these cases the first argument can be made a list in order
to force the default mode settings, as described above. Thus
‘(calc-eval '(2 calc-number-radix 16) 'top)’ returns the second-to-top
stack entry, formatted as a string using the default instead of current
display modes, except that the radix is hexadecimal instead of decimal.
It is, of course, polite to put the Calc stack back the way you found
it when you are done, unless the user of your program is actually
expecting it to affect the stack.
Note that you do not actually have to switch into the ‘*Calculator*’
buffer in order to use ‘calc-eval’; it temporarily switches into the
stack buffer if necessary.
18.5.6.9 Keyboard Macros
........................
If the separator symbol is ‘macro’, the first argument must be a string
of characters which Calc can execute as a sequence of keystrokes. This
switches into the Calc buffer for the duration of the macro. For
example, ‘(calc-eval "vx5\rVR+" 'macro)’ pushes the vector ‘[1,2,3,4,5]’
on the stack and then replaces it with the sum of those numbers. Note
that ‘\r’ is the Lisp notation for the carriage-return, <RET>,
character.
If your keyboard macro wishes to pop the stack, ‘\C-d’ is safer than
‘\177’ (the <DEL> character) because some installations may have
switched the meanings of <DEL> and ‘C-h’. Calc always interprets ‘C-d’
as a synonym for “pop-stack” regardless of key mapping.
If you provide a third argument to ‘calc-eval’, evaluation of the
keyboard macro will leave a record in the Trail using that argument as a
tag string. Normally the Trail is unaffected.
The return value in this case is always ‘nil’.
18.5.6.10 Lisp Evaluation
.........................
Finally, if the separator symbol is ‘eval’, then the Lisp ‘eval’
function is called on the first argument, which must be a Lisp
expression rather than a Calc formula. Remember to quote the expression
so that it is not evaluated until inside ‘calc-eval’.
The difference from plain ‘eval’ is that ‘calc-eval’ switches to the
Calc buffer before evaluating the expression. For example, ‘(calc-eval
'(setq calc-internal-prec 17) 'eval)’ will correctly affect the
buffer-local Calc precision variable.
An alternative would be ‘(calc-eval '(calc-precision 17) 'eval)’.
This is evaluating a call to the function that is normally invoked by
the ‘p’ key, giving it 17 as its “numeric prefix argument.” Note that
this function will leave a message in the echo area as a side effect.
Also, all Calc functions switch to the Calc buffer automatically if not
invoked from there, so the above call is also equivalent to
‘(calc-precision 17)’ by itself. In all cases, Calc uses
‘save-excursion’ to switch back to your original buffer when it is done.
As usual the first argument can be a list that begins with a Lisp
expression to use default instead of current mode settings.
The result of ‘calc-eval’ in this usage is just the result returned
by the evaluated Lisp expression.
18.5.6.11 Example
.................
Here is a sample Emacs command that uses ‘calc-eval’. Suppose you have
a document with lots of references to temperatures on the Fahrenheit
scale, say “98.6 F”, and you wish to convert these references to
Centigrade. The following command does this conversion. Place the
Emacs cursor right after the letter “F” and invoke the command to change
“98.6 F” to “37 C”. Or, if the temperature is already in Centigrade
form, the command changes it back to Fahrenheit.
(defun convert-temp ()
(interactive)
(save-excursion
(re-search-backward "[^-.0-9]\\([-.0-9]+\\) *\\([FC]\\)")
(let* ((top1 (match-beginning 1))
(bot1 (match-end 1))
(number (buffer-substring top1 bot1))
(top2 (match-beginning 2))
(bot2 (match-end 2))
(type (buffer-substring top2 bot2)))
(if (equal type "F")
(setq type "C"
number (calc-eval "($ - 32)*5/9" nil number))
(setq type "F"
number (calc-eval "$*9/5 + 32" nil number)))
(goto-char top2)
(delete-region top2 bot2)
(insert-before-markers type)
(goto-char top1)
(delete-region top1 bot1)
(if (string-match "\\.$" number) ; change "37." to "37"
(setq number (substring number 0 -1)))
(insert number))))
Note the use of ‘insert-before-markers’ when changing between “F” and
“C”, so that the character winds up before the cursor instead of after
it.