1 ;;; srfi-35.scm --- Conditions
3 ;; Copyright (C) 2007, 2008 Free Software Foundation, Inc.
5 ;; This library is free software; you can redistribute it and/or
6 ;; modify it under the terms of the GNU Lesser General Public
7 ;; License as published by the Free Software Foundation; either
8 ;; version 2.1 of the License, or (at your option) any later version.
10 ;; This library is distributed in the hope that it will be useful,
11 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ;; Lesser General Public License for more details.
15 ;; You should have received a copy of the GNU Lesser General Public
16 ;; License along with this library; if not, write to the Free Software
17 ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 ;;; Author: Ludovic Courtès <ludo@gnu.org>
23 ;; This is an implementation of SRFI-35, "Conditions". Conditions are a
24 ;; means to convey information about exceptional conditions between parts of
29 (define-module (srfi srfi-35)
30 #:use-module (srfi srfi-1)
31 #:export (make-condition-type condition-type?
32 make-condition condition? condition-has-type? condition-ref
33 make-compound-condition extract-condition
34 define-condition-type condition
36 &message message-condition? condition-message
37 &serious serious-condition?
40 (cond-expand-provide (current-module) '(srfi-35))
47 (define %condition-type-vtable
48 ;; The vtable of all condition types.
49 ;; vtable fields: vtable, self, printer
50 ;; user fields: id, parent, all-field-names
51 (make-vtable-vtable "prprpr" 0
53 (if (eq? ct %condition-type-vtable)
54 (display "#<condition-type-vtable>")
55 (format port "#<condition-type ~a ~a>"
56 (condition-type-id ct)
57 (number->string (object-address ct)
60 (define (condition-type? obj)
61 "Return true if OBJ is a condition type."
63 (eq? (struct-vtable obj)
64 %condition-type-vtable)))
66 (define (condition-type-id ct)
67 (and (condition-type? ct)
70 (define (condition-type-parent ct)
71 (and (condition-type? ct)
74 (define (condition-type-all-fields ct)
75 (and (condition-type? ct)
79 (define (struct-layout-for-condition field-names)
80 ;; Return a string denoting the layout required to hold the fields listed
82 (let loop ((field-names field-names)
84 (if (null? field-names)
85 (string-concatenate/shared layout)
86 (loop (cdr field-names)
87 (cons "pr" layout)))))
89 (define (print-condition c port)
90 (format port "#<condition ~a ~a>"
91 (condition-type-id (condition-type c))
92 (number->string (object-address c) 16)))
94 (define (make-condition-type id parent field-names)
95 "Return a new condition type named ID, inheriting from PARENT, and with the
96 fields whose names are listed in FIELD-NAMES. FIELD-NAMES must be a list of
97 symbols and must not contain names already used by PARENT or one of its
100 (if (condition-type? parent)
101 (let ((parent-fields (condition-type-all-fields parent)))
102 (if (and (every symbol? field-names)
103 (null? (lset-intersection eq?
104 field-names parent-fields)))
105 (let* ((all-fields (append parent-fields field-names))
106 (layout (struct-layout-for-condition all-fields)))
107 (make-struct %condition-type-vtable 0
108 (make-struct-layout layout) ;; layout
109 print-condition ;; printer
110 id parent all-fields))
111 (error "invalid condition type field names"
113 (error "parent is not a condition type" parent))
114 (error "condition type identifier is not a symbol" id)))
116 (define (make-compound-condition-type id parents)
117 ;; Return a compound condition type made of the types listed in PARENTS.
118 ;; All fields from PARENTS are kept, even same-named ones, since they are
119 ;; needed by `extract-condition'.
120 (cond ((null? parents)
121 (error "`make-compound-condition-type' passed empty parent list"
123 ((null? (cdr parents))
126 (let* ((all-fields (append-map condition-type-all-fields
128 (layout (struct-layout-for-condition all-fields)))
129 (make-struct %condition-type-vtable 0
130 (make-struct-layout layout) ;; layout
131 print-condition ;; printer
133 parents ;; list of parents!
142 (define (condition? c)
143 "Return true if C is a condition."
145 (condition-type? (struct-vtable c))))
147 (define (condition-type c)
149 (let ((vtable (struct-vtable c)))
150 (if (condition-type? vtable)
154 (define (condition-has-type? c type)
155 "Return true if condition C has type TYPE."
156 (if (and (condition? c) (condition-type? type))
157 (let loop ((ct (condition-type c)))
160 (let ((parent (condition-type-parent ct)))
162 (any loop parent) ;; compound condition
163 (loop (condition-type-parent ct)))))))
164 (throw 'wrong-type-arg "condition-has-type?"
165 "Wrong type argument")))
167 (define (condition-ref c field-name)
168 "Return the value of the field named FIELD-NAME from condition C."
170 (if (symbol? field-name)
171 (let* ((type (condition-type c))
172 (fields (condition-type-all-fields type))
173 (index (list-index (lambda (name)
174 (eq? name field-name))
178 (error "invalid field name" field-name)))
179 (error "field name is not a symbol" field-name))
180 (throw 'wrong-type-arg "condition-ref"
181 "Wrong type argument: ~S" c)))
183 (define (make-condition-from-values type values)
184 (apply make-struct type 0 values))
186 (define (make-condition type . field+value)
187 "Return a new condition of type TYPE with fields initialized as specified
188 by FIELD+VALUE, a sequence of field names (symbols) and values."
189 (if (condition-type? type)
190 (let* ((all-fields (condition-type-all-fields type))
191 (inits (fold-right (lambda (field inits)
192 (let ((v (memq field field+value)))
194 (cons (cadr v) inits)
195 (error "field not specified"
199 (make-condition-from-values type inits))
200 (throw 'wrong-type-arg "make-condition"
201 "Wrong type argument: ~S" type)))
203 (define (make-compound-condition . conditions)
204 "Return a new compound condition composed of CONDITIONS."
205 (let* ((types (map condition-type conditions))
206 (ct (make-compound-condition-type 'compound types))
207 (inits (append-map (lambda (c)
208 (let ((ct (condition-type c)))
211 (condition-type-all-fields ct))))
213 (make-condition-from-values ct inits)))
215 (define (extract-condition c type)
216 "Return a condition of condition type TYPE with the field values specified
219 (define (first-field-index parents)
220 ;; Return the index of the first field of TYPE within C.
221 (let loop ((parents parents)
223 (let ((parent (car parents)))
224 (cond ((null? parents)
229 (or (loop parent index)
232 (apply + (map condition-type-all-fields
235 (let ((shift (length (condition-type-all-fields parent))))
237 (+ index shift))))))))
239 (define (list-fields start-index field-names)
240 ;; Return a list of the form `(FIELD-NAME VALUE...)'.
241 (let loop ((index start-index)
242 (field-names field-names)
244 (if (null? field-names)
248 (cons* (struct-ref c index)
252 (if (and (condition? c) (condition-type? type))
253 (let* ((ct (condition-type c))
254 (parent (condition-type-parent ct)))
258 ;; C is a compound condition.
259 (let ((field-index (first-field-index parent)))
260 ;;(format #t "field-index: ~a ~a~%" field-index
261 ;; (list-fields field-index
262 ;; (condition-type-all-fields type)))
263 (apply make-condition type
264 (list-fields field-index
265 (condition-type-all-fields type)))))
267 ;; C does not have type TYPE.
269 (throw 'wrong-type-arg "extract-condition"
270 "Wrong type argument")))
277 (define-macro (define-condition-type name parent pred . field-specs)
280 (make-condition-type ',name ,parent
281 ',(map car field-specs)))
283 (condition-has-type? c ,name))
284 ,@(map (lambda (field-spec)
285 (let ((field-name (car field-spec))
286 (accessor (cadr field-spec)))
287 `(define (,accessor c)
288 (condition-ref c ',field-name))))
291 (define-macro (condition . type-field-bindings)
292 (cond ((null? type-field-bindings)
293 (error "`condition' syntax error" type-field-bindings))
295 ;; the poor man's hygienic macro
296 (let ((mc (gensym "mc"))
297 (mcct (gensym "mcct")))
298 `(let ((,mc (@ (srfi srfi-35) make-condition))
299 (,mcct (@@ (srfi srfi-35) make-compound-condition-type)))
300 (,mc (,mcct 'compound (list ,@(map car type-field-bindings)))
301 ,@(append-map (lambda (type-field-binding)
302 (append-map (lambda (field+value)
303 (let ((f (car field+value))
304 (v (cadr field+value)))
306 (cdr type-field-binding)))
307 type-field-bindings)))))))
311 ;;; Standard condition types.
315 ;; The root condition type.
316 (make-struct %condition-type-vtable 0
317 (make-struct-layout "")
319 (display "<&condition>"))
320 '&condition #f '() '()))
322 (define-condition-type &message &condition
324 (message condition-message))
326 (define-condition-type &serious &condition
329 (define-condition-type &error &serious
337 ;;; srfi-35.scm ends here