ssax/srfi-12.rkt
#lang racket/base
(provide (all-defined-out))

;; oog, change to use racket exceptions.... -- JBC, 2011-02-15

;************************************************************************
; srfi-12.scm
; This file is the part of SSAX package (http://ssax.sourceforge.net),
; which is in public domain.



;************************************************************************
;		Implementation of SRFI-12
;
; Most of the generic code and the comments are taken from
;
; SRFI-12: Exception Handling
; By William Clinger, R. Kent Dybvig, Matthew Flatt, and Marc Feeley
; http://srfi.schemers.org/srfi-12/

; The SRFI-12 Reference implementation has been amended where needed with
; a platform-specific code
;


;------------------------------------------------------------------------
; Catching exceptions
; The platform-specific part

; Procedure: with-exception-handler HANDLER THUNK
; Returns the result(s) of invoking thunk. The handler procedure is
; installed as the current exception handler in the dynamic context of
; invoking thunk.

(define (with-exception-handler handler thunk)
  (with-handlers
      (((lambda (x) #t)
        (lambda (x)
          (handler (exn:exception->condition x)))))
    (thunk)))

; Procedure: abort OBJ
; Raises a non-continuable exception represented by OBJ.
; The abort procedure does not ensure that its argument is a
; condition. If its argument is a condition, abort does not ensure that
; the condition indicates a non-continuable exception.

; This implementation was borrowed from Gambit's cond-expand branch
(define (abort obj)
  (raise (list obj)))

; Procedure: exc:signal OBJ
; Raises a continuable exception represented by OBJ.
; In SRFI-12, this procedure is named 'signal'. However, this name
; clashes with the name of an internal Bigloo procedure. In a compiled
; code, this clash leads to a Bus error.

(define (exc:signal obj)
  (raise (list obj)))

(define (signal obj)
  (raise (list obj)))

; Procedure: current-exception-handler
; Returns the current exception handler.
;; ryanc: unused

; A helper function which converts an exception (PLT internal exception
; or SRFI-12 exception) into CONDITION
(define (exn:exception->condition obj)
  (cond
   ((exn? obj)  ; PLT internal exception
    (make-property-condition
     'exn		; condition kind required by SRFI-12
     'message
     (exn-message obj)))
   ((pair? obj)  ; exception generated by ABORT or EXN:SIGNAL
    (car obj))
   (else  ; some more conditions should be added, I guess
    obj)))

; Evaluates the body expressions expr1, expr2, ... in sequence with an
; exception handler constructed from var and handle-expr. Assuming no
; exception is raised, the result(s) of the last body expression is(are)
; the result(s) of the HANDLE-EXCEPTIONS expression.
; The exception handler created by HANDLE-EXCEPTIONS restores the dynamic
; context (continuation, exception handler, etc.) of the HANDLE-EXCEPTIONS
; expression, and then evaluates handle-expr with var bound to the value
; provided to the handler.
(define-syntax-rule (handle-exceptions var handle-expr . body)
  (with-exception-handler (lambda (var) handle-expr) (lambda () . body)))


;------------------------------------------------------------------------
; Exception conditions
; The following is an approximate implementation of conditions that
; uses lists, instead of a disjoint class of values
; The code below is basically the reference SRFI-12 implementation,
; with a few types fixed.

; A condition is represented as a pair where the first value of the
; pair is this function. A program could forge conditions, and they're
; not disjoint from Scheme pairs.
; Exception conditions are disjoint from any other Scheme values
; (or so should appear).
(define (condition? obj)
  (and (pair? obj)
       (eq? condition? (car obj))))


; Procedure: make-property-condition KIND-KEY PROP-KEY VALUE ...
; This procedure accepts any even number of arguments after kind-key,
; which are regarded as a sequence of alternating prop-key and value
; objects. Each prop-key is regarded as the name of a property, and
; each value is regarded as the value associated with the key that
; precedes it. Returns a kind-key condition that associates the given
; prop-keys with the given values.
(define (make-property-condition kind-key . prop-vals)
  (cons condition? (list (cons kind-key prop-vals))))


; Procedure: make-composite-condition CONDITION ...
; Returns a newly-allocated condition whose components correspond to
; the the given conditions. A predicate created by CONDITION-PREDICATE
; returns true for the new condition if and only if it returns true
; for one or more of its component conditions.
(define (make-composite-condition . conditions)
  (cons condition? (apply append (map cdr conditions))))
 

; Procedure: condition-predicate KIND-KEY
; Returns a predicate that can be called with any object as its
; argument. Given a condition that was created by
; make-property-condition, the predicate returns #t if and only if
; kind-key is EQV? to the kind key that was passed to
; make-property-condition. Given a composite condition created with
; make-composite-condition, the predicate returns #t if and only if
; the predicate returns #t for at least one of its components.
(define (condition-predicate kind-key)
  (lambda (exn)
    (and (condition? exn) (assv kind-key (cdr exn)))))

; Procedure: condition-property-accessor KIND-KEY PROP-KEY
; Returns a procedure that can be called with any condition that satisfies
; (condition-predicate KIND-KEY). Given a condition that was created by
; make-property-condition and KIND-KEY, the procedure returns the value
; that is associated with prop-key. Given a composite condition created with
; make-composite-condition, the procedure returns the value that is
; associated with prop-key in one of the components that
; satisfies (condition-predicate KIND-KEY).
; Otherwise, the result will be #f

(define (condition-property-accessor kind-key prop-key)
  (lambda (exn)
    (let* ((p ((condition-predicate kind-key) exn))
	   (prop-lst (and p (pair? p) (memq prop-key (cdr p)))))
      (and prop-lst (pair? (cdr prop-lst)) (cadr prop-lst)))))