1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-24 20:30:28 +02:00

deprecate lazy-catch

* libguile/deprecated.h:
* libguile/deprecated.c (scm_internal_lazy_catch, scm_lazy_catch):
  Deprecate, and print out a nasty warning that people should change to
  with-throw-handler.

* libguile/throw.h:
* libguile/throw.c (scm_c_with_throw_handler): Deprecate the use of the
  lazy_catch_p argument, printing out a nasty warning if someone
  actually passes 1 as that argument. The combination of the pre-unwind
  and post-unwind handlers should be sufficient.

* test-suite/tests/exceptions.test: Remove lazy-catch tests, as they are
  deprecated. Two of them fail:
  * throw/catch: effect of lazy-catch unwinding on throw to another key
  * throw/catch: repeat of previous test but with lazy-catch
  Hopefully people are not depending on this behavior, and the warning is
  sufficiently nasty for people to switch. We will see.

* test-suite/tests/eval.test ("promises"): Use with-throw-handler
  instead of lazy-catch.

* doc/ref/api-debug.texi:
* doc/ref/api-control.texi: Update to remove references to lazy-catch,
  folding in the useful bits to with-throw-handler.
This commit is contained in:
Andy Wingo 2010-01-31 12:35:19 +01:00
parent d296431516
commit e10cf6b9c7
8 changed files with 169 additions and 377 deletions

View file

@ -1,6 +1,6 @@
@c -*-texinfo-*-
@c This is part of the GNU Guile Reference Manual.
@c Copyright (C) 1996, 1997, 2000, 2001, 2002, 2003, 2004, 2009
@c Copyright (C) 1996, 1997, 2000, 2001, 2002, 2003, 2004, 2009, 2010
@c Free Software Foundation, Inc.
@c See the file guile.texi for copying conditions.
@ -606,8 +606,7 @@ more conveniently.
@menu
* Exception Terminology:: Different ways to say the same thing.
* Catch:: Setting up to catch exceptions.
* Throw Handlers:: Adding extra handling to a throw.
* Lazy Catch:: Catch without unwinding the stack.
* Throw Handlers:: Handling exceptions before unwinding the stack.
* Throw:: Throwing an exception.
* Exception Implementation:: How Guile implements exceptions.
@end menu
@ -800,17 +799,53 @@ Operations}).
@subsubsection Throw Handlers
It's sometimes useful to be able to intercept an exception that is being
thrown, but without changing where in the dynamic context that exception
will eventually be caught. This could be to clean up some related state
or to pass information about the exception to a debugger, for example.
The @code{with-throw-handler} procedure provides a way to do this.
thrown before the stack is unwound. This could be to clean up some
related state, to print a backtrace, or to pass information about the
exception to a debugger, for example. The @code{with-throw-handler}
procedure provides a way to do this.
@deffn {Scheme Procedure} with-throw-handler key thunk handler
@deffnx {C Function} scm_with_throw_handler (key, thunk, handler)
Add @var{handler} to the dynamic context as a throw handler
for key @var{key}, then invoke @var{thunk}.
This behaves exactly like @code{catch}, except that it does not unwind
the stack before invoking @var{handler}. If the @var{handler} procedure
returns normally, Guile rethrows the same exception again to the next
innermost catch or throw handler. @var{handler} may exit nonlocally, of
course, via an explicit throw or via invoking a continuation.
@end deffn
Typically @var{handler} is used to display a backtrace of the stack at
the point where the corresponding @code{throw} occurred, or to save off
this information for possible display later.
Not unwinding the stack means that throwing an exception that is handled
via a throw handler is equivalent to calling the throw handler handler
inline instead of each @code{throw}, and then omitting the surrounding
@code{with-throw-handler}. In other words,
@lisp
(with-throw-handler 'key
(lambda () @dots{} (throw 'key args @dots{}) @dots{})
handler)
@end lisp
@noindent
is mostly equivalent to
@lisp
((lambda () @dots{} (handler 'key args @dots{}) @dots{}))
@end lisp
In particular, the dynamic context when @var{handler} is invoked is that
of the site where @code{throw} is called. The examples are not quite
equivalent, because the body of a @code{with-throw-handler} is not in
tail position with respect to the @code{with-throw-handler}, and if
@var{handler} exits normally, Guile arranges to rethrow the error, but
hopefully the intention is clear. (For an introduction to what is meant
by dynamic context, @xref{Dynamic Wind}.)
@deftypefn {C Function} SCM scm_c_with_throw_handler (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data, int lazy_catch_p)
The above @code{scm_with_throw_handler} takes Scheme procedures as body
(thunk) and handler arguments. @code{scm_c_with_throw_handler} is an
@ -838,141 +873,13 @@ everything that a @code{catch} would do until the point where
then it rethrows to the next innermost @code{catch} or throw handler
instead.
Note also that since the dynamic context is not unwound, if a
@code{with-throw-handler} handler throws to a key that does not match
the @code{with-throw-handler} expression's @var{key}, the new throw may
be handled by a @code{catch} or throw handler that is @emph{closer} to
the throw than the first @code{with-throw-handler}.
@node Lazy Catch
@subsubsection Catch Without Unwinding
Before version 1.8, Guile's closest equivalent to
@code{with-throw-handler} was @code{lazy-catch}. From version 1.8
onwards we recommend using @code{with-throw-handler} because its
behaviour is more useful than that of @code{lazy-catch}, but
@code{lazy-catch} is still supported as well.
A @dfn{lazy catch} is used in the same way as a normal @code{catch},
with @var{key}, @var{thunk} and @var{handler} arguments specifying the
exception type, normal case code and handler procedure, but differs in
one important respect: the handler procedure is executed without
unwinding the call stack from the context of the @code{throw} expression
that caused the handler to be invoked.
@deffn {Scheme Procedure} lazy-catch key thunk handler
@deffnx {C Function} scm_lazy_catch (key, thunk, handler)
This behaves exactly like @code{catch}, except that it does
not unwind the stack before invoking @var{handler}.
If the @var{handler} procedure returns normally, Guile
rethrows the same exception again to the next innermost catch,
lazy-catch or throw handler. If the @var{handler} exits
non-locally, that exit determines the continuation.
@end deffn
@deftypefn {C Function} SCM scm_internal_lazy_catch (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data)
The above @code{scm_lazy_catch} takes Scheme procedures as body and
handler arguments. @code{scm_internal_lazy_catch} is an equivalent
taking C functions. See @code{scm_internal_catch} (@pxref{Catch}) for
a description of the parameters, the behaviour however of course
follows @code{lazy-catch}.
@end deftypefn
Typically @var{handler} is used to display a backtrace of the stack at
the point where the corresponding @code{throw} occurred, or to save off
this information for possible display later.
Not unwinding the stack means that throwing an exception that is caught
by a @code{lazy-catch} is @emph{almost} equivalent to calling the
@code{lazy-catch}'s handler inline instead of each @code{throw}, and
then omitting the surrounding @code{lazy-catch}. In other words,
@lisp
(lazy-catch 'key
(lambda () @dots{} (throw 'key args @dots{}) @dots{})
handler)
@end lisp
@noindent
is @emph{almost} equivalent to
@lisp
((lambda () @dots{} (handler 'key args @dots{}) @dots{}))
@end lisp
@noindent
But why only @emph{almost}? The difference is that with
@code{lazy-catch} (as with normal @code{catch}), the dynamic context is
unwound back to just outside the @code{lazy-catch} expression before
invoking the handler. (For an introduction to what is meant by dynamic
context, @xref{Dynamic Wind}.)
Then, when the handler @emph{itself} throws an exception, that exception
must be caught by some kind of @code{catch} (including perhaps another
@code{lazy-catch}) higher up the call stack.
The dynamic context also includes @code{with-fluids} blocks
(@pxref{Fluids and Dynamic States}),
so the effect of unwinding the dynamic context can also be seen in fluid
variable values. This is illustrated by the following code, in which
the normal case thunk uses @code{with-fluids} to temporarily change the
value of a fluid:
@lisp
(define f (make-fluid))
(fluid-set! f "top level value")
(define (handler . args)
(cons (fluid-ref f) args))
(lazy-catch 'foo
(lambda ()
(with-fluids ((f "local value"))
(throw 'foo)))
handler)
@result{}
("top level value" foo)
((lambda ()
(with-fluids ((f "local value"))
(handler 'foo))))
@result{}
("local value" foo)
@end lisp
@noindent
In the @code{lazy-catch} version, the unwinding of dynamic context
restores @code{f} to its value outside the @code{with-fluids} block
before the handler is invoked, so the handler's @code{(fluid-ref f)}
returns the external value.
@code{lazy-catch} is useful because it permits the implementation of
debuggers and other reflective programming tools that need to access the
state of the call stack at the exact point where an exception or an
error is thrown. For an example of this, see REFFIXME:stack-catch.
It should be obvious from the above that @code{lazy-catch} is very
similar to @code{with-throw-handler}. In fact Guile implements
@code{lazy-catch} in exactly the same way as @code{with-throw-handler},
except with a flag set to say ``where there are slight differences
between what @code{with-throw-handler} and @code{lazy-catch} would do,
do what @code{lazy-catch} has always done''. There are two such
differences:
@enumerate
@item
@code{with-throw-handler} handlers execute in the full dynamic context
of the originating @code{throw} call. @code{lazy-catch} handlers
execute in the dynamic context of the @code{lazy-catch} expression,
excepting only that the stack has not yet been unwound from the point of
the @code{throw} call.
@item
If a @code{with-throw-handler} handler throws to a key that does not
match the @code{with-throw-handler} expression's @var{key}, the new
throw may be handled by a @code{catch} or throw handler that is _closer_
to the throw than the first @code{with-throw-handler}. If a
@code{lazy-catch} handler throws, it will always be handled by a
@code{catch} or throw handler that is higher up the dynamic context than
the first @code{lazy-catch}.
@end enumerate
Here is an example to illustrate the second difference:
Here is an example to illustrate this behavior:
@lisp
(catch 'a
@ -990,14 +897,7 @@ Here is an example to illustrate the second difference:
@noindent
This code will call @code{inner-handler} and then continue with the
continuation of the inner @code{catch}. If the
@code{with-throw-handler} was changed to @code{lazy-catch}, however, the
code would call @code{outer-handler} and then continue with the
continuation of the outer @code{catch}.
Modulo these two differences, any statements in the previous and
following subsections about throw handlers apply to lazy catches as
well.
continuation of the inner @code{catch}.
@node Throw

View file

@ -1,6 +1,6 @@
@c -*-texinfo-*-
@c This is part of the GNU Guile Reference Manual.
@c Copyright (C) 1996, 1997, 2000, 2001, 2002, 2003, 2004, 2007
@c Copyright (C) 1996, 1997, 2000, 2001, 2002, 2003, 2004, 2007, 2010
@c Free Software Foundation, Inc.
@c See the file guile.texi for copying conditions.
@ -400,13 +400,12 @@ equivalent in C. In Scheme, this means you need something like this:
@end lisp
@noindent
The @code{catch} here can also be @code{lazy-catch} or
@code{with-throw-handler}; see @ref{Throw Handlers} and @ref{Lazy Catch}
for the details of how these differ from @code{catch}. The @code{#t}
means that the catch is applicable to all kinds of error; if you want to
restrict your catch to just one kind of error, you can put the symbol
for that kind of error instead of @code{#t}. The equivalent to this in
C would be something like this:
The @code{catch} here can also be @code{with-throw-handler}; see @ref{Throw
Handlers} for information on the when you might want to use
@code{with-throw-handler} instead of @code{catch}. The @code{#t} means that the
catch is applicable to all kinds of error; if you want to restrict your catch to
just one kind of error, you can put the symbol for that kind of error instead of
@code{#t}. The equivalent to this in C would be something like this:
@lisp
SCM my_body_proc (void *body_data)

View file

@ -1815,6 +1815,56 @@ scm_i_subr_p (SCM x)
return SCM_PRIMITIVE_P (x);
}
SCM
scm_internal_lazy_catch (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data)
{
scm_c_issue_deprecation_warning
("`scm_internal_lazy_catch' is no longer supported. Instead this call will\n"
"dispatch to `scm_c_with_throw_handler'. Your handler will be invoked from\n"
"within the dynamic context of the corresponding `throw'.\n"
"\nTHIS COULD CHANGE YOUR PROGRAM'S BEHAVIOR.\n\n"
"Please modify your program to use `scm_c_with_throw_handler' directly,\n"
"and adapt it (if necessary) to expect to be within the dynamic context\n"
"of the throw.");
return scm_c_with_throw_handler (tag, body, body_data, handler, handler_data, 0);
}
SCM_DEFINE (scm_lazy_catch, "lazy-catch", 3, 0, 0,
(SCM key, SCM thunk, SCM handler),
"This behaves exactly like @code{catch}, except that it does\n"
"not unwind the stack before invoking @var{handler}.\n"
"If the @var{handler} procedure returns normally, Guile\n"
"rethrows the same exception again to the next innermost catch,\n"
"lazy-catch or throw handler. If the @var{handler} exits\n"
"non-locally, that exit determines the continuation.")
#define FUNC_NAME s_scm_lazy_catch
{
struct scm_body_thunk_data c;
SCM_ASSERT (scm_is_symbol (key) || scm_is_eq (key, SCM_BOOL_T),
key, SCM_ARG1, FUNC_NAME);
c.tag = key;
c.body_proc = thunk;
scm_c_issue_deprecation_warning
("`lazy-catch' is no longer supported. Instead this call will dispatch\n"
"to `with-throw-handler'. Your handler will be invoked from within the\n"
"dynamic context of the corresponding `throw'.\n"
"\nTHIS COULD CHANGE YOUR PROGRAM'S BEHAVIOR.\n\n"
"Please modify your program to use `with-throw-handler' directly, and\n"
"adapt it (if necessary) to expect to be within the dynamic context of\n"
"the throw.");
return scm_c_with_throw_handler (key,
scm_body_thunk, &c,
scm_handle_by_proc, &handler, 0);
}
#undef FUNC_NAME
void
scm_i_init_deprecated ()

View file

@ -611,6 +611,16 @@ SCM_DEPRECATED int scm_i_subr_p (SCM x);
/* Deprecated 2010-01-31, use with-throw-handler instead */
SCM_DEPRECATED SCM scm_lazy_catch (SCM tag, SCM thunk, SCM handler);
SCM_DEPRECATED SCM scm_internal_lazy_catch (SCM tag,
scm_t_catch_body body,
void *body_data,
scm_t_catch_handler handler,
void *handler_data);
void scm_i_init_deprecated (void);
#endif

View file

@ -30,6 +30,7 @@
#include "libguile/alist.h"
#include "libguile/eval.h"
#include "libguile/eq.h"
#include "libguile/deprecation.h"
#include "libguile/dynwind.h"
#include "libguile/backtrace.h"
#include "libguile/debug.h"
@ -93,12 +94,11 @@ struct jmp_buf_and_retval /* use only on the stack, in scm_catch */
SCM retval;
};
/* These are the structures we use to store pre-unwind handling (aka
"lazy") information for a regular catch, and put on the wind list
for a "lazy" catch. They store the pre-unwind handler function to
call, and the data pointer to pass through to it. It's not a
Scheme closure, but it is a function with data, so the term
"closure" is appropriate in its broader sense.
/* These are the structures we use to store pre-unwind handling information for
a regular catch, and put on the wind list for a with-throw-handler. They
store the pre-unwind handler function to call, and the data pointer to pass
through to it. It's not a Scheme closure, but it is a function with data, so
the term "closure" is appropriate in its broader sense.
(We don't need anything like this to run the normal (post-unwind)
catch handler, because the same C frame runs both the body and the
@ -108,7 +108,6 @@ struct pre_unwind_data {
scm_t_catch_handler handler;
void *handler_data;
int running;
int lazy_catch_p;
};
@ -187,7 +186,6 @@ scm_c_catch (SCM tag,
pre_unwind.handler = pre_unwind_handler;
pre_unwind.handler_data = pre_unwind_handler_data;
pre_unwind.running = 0;
pre_unwind.lazy_catch_p = 0;
SCM_SETJBPREUNWIND(jmpbuf, &pre_unwind);
if (SCM_I_SETJMP (jbr.buf))
@ -303,9 +301,18 @@ scm_c_with_throw_handler (SCM tag,
c.handler = handler;
c.handler_data = handler_data;
c.running = 0;
c.lazy_catch_p = lazy_catch_p;
pre_unwind = make_pre_unwind_data (&c);
if (lazy_catch_p)
scm_c_issue_deprecation_warning
("The LAZY_CATCH_P argument to `scm_c_with_throw_handler' is no longer.\n"
"supported. Instead the handler will be invoked from within the dynamic\n"
"context of the corresponding `throw'.\n"
"\nTHIS COULD CHANGE YOUR PROGRAM'S BEHAVIOR.\n\n"
"Please modify your program to pass 0 as the LAZY_CATCH_P argument,\n"
"and adapt it (if necessary) to expect to be within the dynamic context\n"
"of the throw.");
SCM_CRITICAL_SECTION_START;
scm_i_set_dynwinds (scm_acons (tag, pre_unwind, scm_i_dynwinds ()));
SCM_CRITICAL_SECTION_END;
@ -319,15 +326,6 @@ scm_c_with_throw_handler (SCM tag,
return answer;
}
/* Exactly like scm_internal_catch, except:
- It does not unwind the stack (this is the major difference).
- The handler is not allowed to return. */
SCM
scm_internal_lazy_catch (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data)
{
return scm_c_with_throw_handler (tag, body, body_data, handler, handler_data, 1);
}
/* scm_internal_stack_catch
Use this one if you want debugging information to be stored in
@ -354,7 +352,7 @@ static SCM
cwss_body (void *data)
{
struct cwss_data *d = data;
return scm_internal_lazy_catch (d->tag, d->body, d->data, ss_handler, NULL);
return scm_c_with_throw_handler (d->tag, d->body, d->data, ss_handler, NULL, 0);
}
SCM
@ -566,7 +564,7 @@ scm_handle_by_throw (void *handler_data SCM_UNUSED, SCM tag, SCM args)
/* the Scheme-visible CATCH, WITH-THROW-HANDLER and LAZY-CATCH functions */
/* the Scheme-visible CATCH and WITH-THROW-HANDLER functions */
SCM_DEFINE (scm_catch_with_pre_unwind_handler, "catch", 3, 1, 0,
(SCM key, SCM thunk, SCM handler, SCM pre_unwind_handler),
@ -664,37 +662,6 @@ SCM_DEFINE (scm_with_throw_handler, "with-throw-handler", 3, 0, 0,
}
#undef FUNC_NAME
SCM_DEFINE (scm_lazy_catch, "lazy-catch", 3, 0, 0,
(SCM key, SCM thunk, SCM handler),
"This behaves exactly like @code{catch}, except that it does\n"
"not unwind the stack before invoking @var{handler}.\n"
"If the @var{handler} procedure returns normally, Guile\n"
"rethrows the same exception again to the next innermost catch,\n"
"lazy-catch or throw handler. If the @var{handler} exits\n"
"non-locally, that exit determines the continuation.")
#define FUNC_NAME s_scm_lazy_catch
{
struct scm_body_thunk_data c;
SCM_ASSERT (scm_is_symbol (key) || scm_is_eq (key, SCM_BOOL_T),
key, SCM_ARG1, FUNC_NAME);
c.tag = key;
c.body_proc = thunk;
/* scm_internal_lazy_catch takes care of all the mechanics of
setting up a lazy catch key; we tell it to call scm_body_thunk to
run the body, and scm_handle_by_proc to deal with any throws to
this catch. The former receives a pointer to c, telling it how
to behave. The latter receives a pointer to HANDLER, so it knows
who to call. */
return scm_internal_lazy_catch (key,
scm_body_thunk, &c,
scm_handle_by_proc, &handler);
}
#undef FUNC_NAME
/* throwing */
@ -815,19 +782,7 @@ scm_ithrow (SCM key, SCM args, int noreturn SCM_UNUSED)
{
struct pre_unwind_data *c =
(struct pre_unwind_data *) SCM_SMOB_DATA_1 (jmpbuf);
SCM handle, answer;
/* For old-style lazy-catch behaviour, we unwind the dynamic
context before invoking the handler. */
if (c->lazy_catch_p)
{
scm_dowinds (wind_goal, (scm_ilength (scm_i_dynwinds ())
- scm_ilength (wind_goal)));
SCM_CRITICAL_SECTION_START;
handle = scm_i_dynwinds ();
scm_i_set_dynwinds (SCM_CDR (handle));
SCM_CRITICAL_SECTION_END;
}
SCM answer;
/* Call the handler, with framing to set the pre-unwind
structure's running field while the handler is running, so we

View file

@ -52,12 +52,6 @@ SCM_API SCM scm_internal_catch (SCM tag,
scm_t_catch_handler handler,
void *handler_data);
SCM_API SCM scm_internal_lazy_catch (SCM tag,
scm_t_catch_body body,
void *body_data,
scm_t_catch_handler handler,
void *handler_data);
SCM_API SCM scm_internal_stack_catch (SCM tag,
scm_t_catch_body body,
void *body_data,
@ -91,7 +85,6 @@ SCM_API int scm_exit_status (SCM args);
SCM_API SCM scm_catch_with_pre_unwind_handler (SCM tag, SCM thunk, SCM handler, SCM lazy_handler);
SCM_API SCM scm_catch (SCM tag, SCM thunk, SCM handler);
SCM_API SCM scm_with_throw_handler (SCM tag, SCM thunk, SCM handler);
SCM_API SCM scm_lazy_catch (SCM tag, SCM thunk, SCM handler);
SCM_API SCM scm_ithrow (SCM key, SCM args, int noreturn);
SCM_API SCM scm_throw (SCM key, SCM args);

View file

@ -1,5 +1,5 @@
;;;; eval.test --- tests guile's evaluator -*- scheme -*-
;;;; Copyright (C) 2000, 2001, 2006, 2007, 2009 Free Software Foundation, Inc.
;;;; Copyright (C) 2000, 2001, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
;;;;
;;;; This library is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU Lesser General Public
@ -306,21 +306,19 @@
exception:wrong-type-arg
(+ (delay (* 3 7)) 13))
;; Tests that require the debugging evaluator...
(with-debugging-evaluator
(pass-if "unmemoizing a promise"
(display-backtrace
(let ((stack #f))
(false-if-exception (lazy-catch #t
(lambda ()
(let ((f (lambda (g) (delay (g)))))
(force (f error))))
(lambda _
(set! stack (make-stack #t)))))
stack)
(%make-void-port "w"))
#t))))
(pass-if "unmemoizing a promise"
(display-backtrace
(let ((stack #f))
(false-if-exception
(with-throw-handler #t
(lambda ()
(let ((f (lambda (g) (delay (g)))))
(force (f error))))
(lambda _
(set! stack (make-stack #t)))))
stack)
(%make-void-port "w"))
#t)))
;;;

View file

@ -1,5 +1,5 @@
;;;; exceptions.test --- tests for Guile's exception handling -*- scheme -*-
;;;; Copyright (C) 2001, 2003, 2004, 2006 Free Software Foundation, Inc.
;;;; Copyright (C) 2001, 2003, 2004, 2006, 2010 Free Software Foundation, Inc.
;;;;
;;;; This library is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU Lesser General Public
@ -75,9 +75,9 @@
(lambda () (throw 'a))
(lambda (x y . rest) #f))))
(with-test-prefix "with lazy handler"
(with-test-prefix "with pre-unwind handler"
(pass-if "lazy fluid state"
(pass-if "pre-unwind fluid state"
(equal? '(inner outer arg)
(let ((fluid-parm (make-fluid))
(inner-val #f))
@ -102,32 +102,34 @@
(lambda (key . args)
(push 2))))
(throw-test "catch and lazy catch"
(throw-test "catch and with-throw-handler"
'(1 2 3 4)
(catch 'a
(lambda ()
(push 1)
(lazy-catch 'a
(lambda ()
(push 2)
(throw 'a))
(lambda (key . args)
(push 3))))
(with-throw-handler
'a
(lambda ()
(push 2)
(throw 'a))
(lambda (key . args)
(push 3))))
(lambda (key . args)
(push 4))))
(throw-test "catch with rethrowing lazy catch handler"
(throw-test "catch with rethrowing throw-handler"
'(1 2 3 4)
(catch 'a
(lambda ()
(push 1)
(lazy-catch 'a
(lambda ()
(push 2)
(throw 'a))
(lambda (key . args)
(push 3)
(apply throw key args))))
(with-throw-handler
'a
(lambda ()
(push 2)
(throw 'a))
(lambda (key . args)
(push 3)
(apply throw key args))))
(lambda (key . args)
(push 4))))
@ -183,27 +185,6 @@
(lambda (key . args)
(push 4))))
(throw-test "effect of lazy-catch unwinding on throw to another key"
'(1 2 3 5 7)
(catch 'a
(lambda ()
(push 1)
(lazy-catch 'b
(lambda ()
(push 2)
(catch 'a
(lambda ()
(push 3)
(throw 'b))
(lambda (key . args)
(push 4))))
(lambda (key . args)
(push 5)
(throw 'a)))
(push 6))
(lambda (key . args)
(push 7))))
(throw-test "effect of with-throw-handler not-unwinding on throw to another key"
'(1 2 3 5 4 6)
(catch 'a
@ -225,27 +206,6 @@
(lambda (key . args)
(push 7))))
(throw-test "lazy-catch chaining"
'(1 2 3 4 6 8)
(catch 'a
(lambda ()
(push 1)
(lazy-catch 'a
(lambda ()
(push 2)
(lazy-catch 'a
(lambda ()
(push 3)
(throw 'a))
(lambda (key . args)
(push 4)))
(push 5))
(lambda (key . args)
(push 6)))
(push 7))
(lambda (key . args)
(push 8))))
(throw-test "with-throw-handler chaining"
'(1 2 3 4 6 8)
(catch 'a
@ -267,48 +227,6 @@
(lambda (key . args)
(push 8))))
(throw-test "with-throw-handler inside lazy-catch"
'(1 2 3 4 6 8)
(catch 'a
(lambda ()
(push 1)
(lazy-catch 'a
(lambda ()
(push 2)
(with-throw-handler 'a
(lambda ()
(push 3)
(throw 'a))
(lambda (key . args)
(push 4)))
(push 5))
(lambda (key . args)
(push 6)))
(push 7))
(lambda (key . args)
(push 8))))
(throw-test "lazy-catch inside with-throw-handler"
'(1 2 3 4 6 8)
(catch 'a
(lambda ()
(push 1)
(with-throw-handler 'a
(lambda ()
(push 2)
(lazy-catch 'a
(lambda ()
(push 3)
(throw 'a))
(lambda (key . args)
(push 4)))
(push 5))
(lambda (key . args)
(push 6)))
(push 7))
(lambda (key . args)
(push 8))))
(throw-test "throw handlers throwing to each other recursively"
'(1 2 3 4 8 6 10 12)
(catch #t
@ -340,37 +258,6 @@
(lambda (key . args)
(push 12))))
(throw-test "repeat of previous test but with lazy-catch"
'(1 2 3 4 8 12)
(catch #t
(lambda ()
(push 1)
(lazy-catch 'a
(lambda ()
(push 2)
(lazy-catch 'b
(lambda ()
(push 3)
(lazy-catch 'c
(lambda ()
(push 4)
(throw 'b)
(push 5))
(lambda (key . args)
(push 6)
(throw 'a)))
(push 7))
(lambda (key . args)
(push 8)
(throw 'c)))
(push 9))
(lambda (key . args)
(push 10)
(throw 'b)))
(push 11))
(lambda (key . args)
(push 12))))
(throw-test "throw handler throwing to lexically inside catch"
'(1 2 7 5 4 6 9)
(with-throw-handler 'a