calc: Defining Stack Commands

 
 18.5.3 Defining New Stack-Based Commands
 ----------------------------------------
 
 To define a new computational command which takes and/or leaves
 arguments on the stack, a special form of ‘interactive’ clause is used.
 
      (interactive NUM TAG)
 
 where NUM is an integer, and TAG is a string.  The effect is to pop NUM
 values off the stack, resimplify them by calling ‘calc-normalize’, and
 hand them to your function according to the function’s argument list.
 Your function may include ‘&optional’ and ‘&rest’ parameters, so long as
 calling the function with NUM parameters is valid.
 
    Your function must return either a number or a formula in a form
 acceptable to Calc, or a list of such numbers or formulas.  These
 value(s) are pushed onto the stack when the function completes.  They
 are also recorded in the Calc Trail buffer on a line beginning with TAG,
 a string of (normally) four characters or less.  If you omit TAG or use
 ‘nil’ as a tag, the result is not recorded in the trail.
 
    As an example, the definition
 
      (defmath myfact (n)
        "Compute the factorial of the integer at the top of the stack."
        (interactive 1 "fact")
        (if (> n 0)
            (* n (myfact (1- n)))
          (and (= n 0) 1)))
 
 is a version of the factorial function shown previously which can be
 used as a command as well as an algebraic function.  It expands to
 
      (defun calc-myfact ()
        "Compute the factorial of the integer at the top of the stack."
        (interactive)
        (calc-slow-wrapper
         (calc-enter-result 1 "fact"
           (cons 'calcFunc-myfact (calc-top-list-n 1)))))
 
      (defun calcFunc-myfact (n)
        "Compute the factorial of the integer at the top of the stack."
        (if (math-posp n)
            (math-mul n (calcFunc-myfact (math-add n -1)))
          (and (math-zerop n) 1)))
 
    The ‘calc-slow-wrapper’ function is a version of ‘calc-wrapper’ that
 automatically puts up a ‘Working...’ message before the computation
 begins.  (This message can be turned off by the user with an ‘m w’
 (‘calc-working’) command.)
 
    The ‘calc-top-list-n’ function returns a list of the specified number
 of values from the top of the stack.  It resimplifies each value by
 calling ‘calc-normalize’.  If its argument is zero it returns an empty
 list.  It does not actually remove these values from the stack.
 
    The ‘calc-enter-result’ function takes an integer NUM and string TAG
 as described above, plus a third argument which is either a Calculator
 data object or a list of such objects.  These objects are resimplified
 and pushed onto the stack after popping the specified number of values
 from the stack.  If TAG is non-‘nil’, the values being pushed are also
 recorded in the trail.
 
    Note that if ‘calcFunc-myfact’ returns ‘nil’ this represents “leave
 the function in symbolic form.” To return an actual empty list, in the
 sense that ‘calc-enter-result’ will push zero elements back onto the
 stack, you should return the special value ‘'(nil)’, a list containing
 the single symbol ‘nil’.
 
    The ‘interactive’ declaration can actually contain a limited
 Emacs-style code string as well which comes just before NUM and TAG.
 Currently the only Emacs code supported is ‘"p"’, as in
 
      (defmath foo (a b &optional c)
        (interactive "p" 2 "foo")
        BODY)
 
    In this example, the command ‘calc-foo’ will evaluate the expression
 ‘foo(a,b)’ if executed with no argument, or ‘foo(a,b,n)’ if executed
 with a numeric prefix argument of ‘n’.
 
    The other code string allowed is ‘"m"’ (unrelated to the usual ‘"m"’
 code as used with ‘defun’).  It uses the numeric prefix argument as the
 number of objects to remove from the stack and pass to the function.  In
 this case, the integer NUM serves as a default number of arguments to be
 used when no prefix is supplied.