
7.4     Non-Local Exits
The functions catch and throw are very useful for discontinuing a computation. As return
provides for local exit, this pair of functions provide for non-local exit. They should not,
however, be used indiscriminately. The lexical restrictions on their more local counterparts
ensure that the flow of control can be ascertained by looking at a single piece of code.
With catch and throw, control may be passed to and from totally unrelated pieces of
code.
(catch TAG:id [FORM:form]): any                                                Open-Compiled fexpr
Catch  evaluates  TAG  to  establish  a  name  for  this  catcher,  called  the
catch-tag,  and  then  evaluates  the  FORM’s  in  a  protected  environment.  If
during  this  evaluation  a  throw  occurs  with  a  tag  that  is  the  same  as  the
catch-tag  (as  defined  by  the  function  eq),  catch  immediately  returns  the
result of the form given as the second argument to the throw. If no throw
occurs, the results returned by the last FORM are returned as the result of
the catch. A catch-tag of nil is considered special, it serves to match any
catch-tag specified by throw.
 
(throw TAG:id VALUE:any): None Returned                                                          expr
Throw  evaluates  TAG  to  produce  a  catch-tag  and  evaluates  VALUE  to
produce a result value. At this point, an error is signalled if there is no active
catch with the same catch-tag (as determined by the function eq). Otherwise,
control is passed to the most recent such catch, and the results of evaluating
VALUE become the results of the catch.
 
In the process of transferring control to the catch, all intervening constructs are exited. Exiting a
construct that binds variables has the effect of unbinding those variables.
throwsignal* [Initially: nil] global This fluid variable is set to t if the most recent invocation of
Catch was thrown to. throwsignal* is set to nil upon a normal exit from a catch and to t upon a
normal exit from a throw.
throwtag* [Initially: nil] global This fluid variable is set to the catch-tag of the most recent
throw
The catch, throw pair supply a construct which allows for some control over the evaluation of an
expression. Exceptions can be detected during the evaluation of the expression and appropriate
action can be taken. The functions which follow define a simple parser. The parse is done inside
a catch. If there are no errors then the result of the parse is returned. When an error arises the
computation is aborted with a call on throw. An error message is printed prior to aborting the
parse.
(de parse (⋆buffer⋆)
 
  (catch 'parse-error (list 's (parse-np) (parse-vp))))
 
 
(de parse-np ()
 
  (if (memq (car ⋆buffer⋆) '(a an the))
 
    ‘(np (det ,(pop ⋆buffer⋆)) (n ,(pop ⋆buffer⋆)))
 
    (parse-error "Bad word in noun phrase: %w%n")))
 
 
(de parse-vp ()
 
  (if (memq (car ⋆buffer⋆) '(sings talks))
 
    ‘(vp (v ,(pop ⋆buffer⋆)))
 
    (parse-error "Not a verb: %w%n")))
 
 
 
(de parse-error (format-string)
 
  (throw 'parse-error (printf format-string (car ⋆buffer⋆))))
1 lisp> (parse '(the bird sings))
 
(S (NP (DET THE) (N BIRD)) (VP (V SINGS)))
 
2 lisp> (parse '(the bird eats))
 
Not a verb: eats
 
nil
 
3 lisp> (parse '(it is small))
 
Bad word in noun phrase: it
 
nil
The following macros are provided to aid in the use of catch and throw with a nil catch-tag, by
examining throwsignal* and throwtag*:
(catch-all HANDLER:function [FORM:form]): any                                            macro
Has  the  same  semantics  as  catch  except  that  all  throws,  independent  of
catch-tag,  will  be  caught.  The  HANDLER  must  be  a  function  of  two
arguments. If a throw occurs, the HANDLER will be called on the catch-tag
and the value passed by the throw. The HANDLER may itself issue a throw,
in which case the catch-all acts as a filter.
 
(unwind-all HANDLER:function [FORM:form]): any                                        macro
This function is very similar to catch-all. However, if no throw occurs the
HANDLER will be called on nil and the value returned.
 
(unwind-protect FORM:form [CLEANUPFORM:form]): any                           macro
The FORM is evaluated and, regardless of how it is exited (a normal return,
throw, or error), the CLEANUPFORMs will be evaluated. One common use
of unwind-protect is to ensure that a file will be closed after processing.
 
    (setq channel (open file ....))
 
    (unwind-protect (process-file)
 
                (close channel))
This primitive can be used to implement arbitrary kinds of state-binding without fear that an
unusual return (an error or throw), will violate the binding.
    (defmacro bind ((name value) . body)
 
      (let ((old-value (gensym)))
 
        ‘(let ((,old-value ,name))
 
           (unwind-protect
 
        (progn (setq ,name ,value)
 
               ,@body)
 
        (setq ,name ,old-value)))))
    1 lisp> (setq number 5)
 
    5
 
    2 lisp> (bind (number 2) (print number) (/ number 0))
 
    2
 
    ⋆⋆⋆⋆⋆ Attempt to divide by zero in Quotient
 
    3 lisp> number
 
    5
Note: Certain special tags are used in the PSL system, and should not be interfered with
casually:
| $error$                 | Used by error and errorset which are                            | 
|              | implemented in terms of catch and throw,                     | 
|              | (see Chapter 16).                                                            | 
| $unwind-protect$ | A special catch-tag placed to ensure                             | 
|              | that all throws pause at the                                            | 
|              | unwind-protect ”mark”.                                                 | 
| $prog$                  | Used to communicate between interpreted progs, gos. | 
|              |