calc: Calling Calc from Your Programs

 
 18.5.6 Calling Calc from Your Lisp Programs
 -------------------------------------------
 
 A later section (SeeInternals) 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.  SeeData 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
 (SeeAlgebraic 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.