93 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Scheme
		
	
	
	
			
		
		
	
	
			93 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Scheme
		
	
	
	
| ;;; Handle fatal errors in a sensible way. -*- Scheme -*-
 | |
| ;;; Copyright (c) 1995 by Olin Shivers.
 | |
| 
 | |
| ;;; (with-fatal-error-handler* handler thunk)
 | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | |
| ;;; Call THUNK, and return whatever it returns. If THUNK signals a condition,
 | |
| ;;; and that condition is an error condition (or a subtype of error), then
 | |
| ;;; HANDLER gets a chance to handle it.
 | |
| ;;; The HANDLER proc is applied to two values: 
 | |
| ;;;     (HANDLER condition decline)
 | |
| ;;; HANDLER's continuation is WITH-FATAL-ERROR-HANDLER*'s; whatever HANDLER
 | |
| ;;; returns is returned from WITH-FATAL-ERROR-HANDLER. HANDLER declines to
 | |
| ;;; handle the error by throwing to DECLINE, a nullary continuation.
 | |
| ;;;
 | |
| ;;; Why is it called with-FATAL-error-handler*? Because returning to the
 | |
| ;;; guy that signalled the error is not an option.
 | |
| ;;;
 | |
| ;;; Why the nested outer pair of CALL/CC's? Well, what happens if the user's
 | |
| ;;; error handler *itself* raises an error? This could potentially give
 | |
| ;;; rise to an infinite loop, because WITH-HANDLER runs its handler in
 | |
| ;;; the original condition-signaller's context, so you'd search back for a
 | |
| ;;; handler, and find yourself again. For example, here is an infinite loop:
 | |
| ;;;
 | |
| ;;;   (with-handler (lambda (condition more)
 | |
| ;;;                   (display "Loop!")
 | |
| ;;;                   (error "ouch"))	; Get back, Loretta.
 | |
| ;;;     (lambda () (error "start me up")))
 | |
| ;;;
 | |
| ;;; I could require W-F-E-H* users to code carefully, but instead I make sure
 | |
| ;;; the user's fatal-error handler runs in w-f-e-h*'s handler context, so
 | |
| ;;; if it signals a condition, we'll start the search from there. That's the
 | |
| ;;; point of continuation K. When the original thunk completes successfully,
 | |
| ;;; we dodge the K hackery by using ACCEPT to make a normal return.
 | |
| 
 | |
| (define (with-fatal-error-handler* handler thunk)
 | |
|   (call-with-current-continuation
 | |
|     (lambda (accept)
 | |
|       ((call-with-current-continuation
 | |
|          (lambda (k)
 | |
| 	   (with-handler (lambda (condition more)
 | |
| 			   (if (error? condition)
 | |
| 			       (call-with-current-continuation
 | |
| 				 (lambda (decline)
 | |
| 				   (k (lambda () (handler condition decline))))))
 | |
| 			   (more))	; Keep looking for a handler.
 | |
| 	      (lambda () (call-with-values thunk accept)))))))))
 | |
| 		  
 | |
| (define-syntax with-fatal-error-handler 
 | |
|   (syntax-rules ()
 | |
|     ((with-fatal-error-handler handler body ...)
 | |
|      (with-fatal-error-handler* handler
 | |
|        (lambda () body ...)))))
 | |
| 
 | |
| ;This one ran HANDLER in the signaller's condition-handler context.
 | |
| ;It was therefore susceptible to infinite loops if you didn't code 
 | |
| ;your handler's carefully.
 | |
| ;
 | |
| ;(define (with-fatal-error-handler* handler thunk)
 | |
| ;  (call-with-current-continuation
 | |
| ;    (lambda (accept)
 | |
| ;      (with-handler (lambda (condition more)
 | |
| ;		      (if (error? condition)
 | |
| ;			  (call-with-current-continuation
 | |
| ;		            (lambda (decline)
 | |
| ;			      (accept (handler condition decline)))))
 | |
| ;		      (more))	; Keep looking for a handler.
 | |
| ;        thunk))))
 | |
| 
 | |
| ;;; (%error-handler-cond kont eh-clauses cond-clauses)
 | |
| ;;; Transform error-handler clauses into COND clauses by wrapping continuation
 | |
| ;;; KONT around the body of each e-h clause, so that if it fires, the result
 | |
| ;;; is thrown to KONT, but if no clause fires, the cond returns to the default
 | |
| ;;; continuation.
 | |
| 
 | |
| ;(define-syntax %error-handler-cond
 | |
| ;  (syntax-rules (=> else)
 | |
| ;
 | |
| ;   ((%error-handler-cond kont ((test => proc) clause ...) (ans ...))
 | |
| ;    (%error-handler-cond kont
 | |
| ;			 (clause ...)
 | |
| ;			 ((test => (lambda (v) (kont (proc v)))) ans ...)))
 | |
| ;
 | |
| ;   ((%error-handler-cond kont ((test body ...) clause ...) (ans ...))
 | |
| ;    (%error-handler-cond kont
 | |
| ;			 (clause ...)
 | |
| ;			 ((test (kont (begin body ...))) ans ...)))
 | |
| ;
 | |
| ;   ((%error-handler-cond kont ((else body ...)) (ans-clause ...))
 | |
| ;    (cond (else body ...) ans-clause ...))
 | |
| ;
 | |
| ;   ((%error-handler-cond kont () (ans-clause ...))
 | |
| ;    (cond ans-clause ...))))
 |