srfi-35.scm 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. ;;; srfi-35.scm --- Conditions -*- coding: utf-8 -*-
  2. ;; Copyright (C) 2007-2011, 2017, 2022 Free Software Foundation, Inc.
  3. ;;
  4. ;; This library is free software; you can redistribute it and/or
  5. ;; modify it under the terms of the GNU Lesser General Public
  6. ;; License as published by the Free Software Foundation; either
  7. ;; version 3 of the License, or (at your option) any later version.
  8. ;;
  9. ;; This library is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;; Lesser General Public License for more details.
  13. ;;
  14. ;; You should have received a copy of the GNU Lesser General Public
  15. ;; License along with this library; if not, write to the Free Software
  16. ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. ;;; Author: Ludovic Courtès <ludo@gnu.org>
  18. ;;; Commentary:
  19. ;; This is an implementation of SRFI-35, "Conditions". Conditions are a
  20. ;; means to convey information about exceptional conditions between parts of
  21. ;; a program.
  22. ;;; Code:
  23. (define-module (srfi srfi-35)
  24. #:use-module (ice-9 match)
  25. #:use-module (ice-9 exceptions)
  26. #:re-export ((make-exception-type . make-condition-type)
  27. (exception-type? . condition-type?)
  28. (exception? . condition?)
  29. (make-exception . make-compound-condition)
  30. (&exception . &condition)
  31. &message
  32. (exception-with-message? . message-condition?)
  33. (exception-message . condition-message)
  34. (&error . &serious)
  35. (error? . serious-condition?)
  36. (external-error? . error?))
  37. #:re-export-and-replace ((&external-error . &error))
  38. #:export (make-condition
  39. define-condition-type
  40. condition-has-type?
  41. condition-ref
  42. extract-condition
  43. condition))
  44. (cond-expand-provide (current-module) '(srfi-35))
  45. (define (make-condition type . field+value)
  46. "Return a new condition of type TYPE with fields initialized as specified
  47. by FIELD+VALUE, a sequence of field names (symbols) and values."
  48. (unless (exception-type? type)
  49. (scm-error 'wrong-type-arg "make-condition" "Not a condition type: ~S"
  50. (list type) #f))
  51. (let* ((fields (record-type-fields type))
  52. (uninitialized (list 'uninitialized))
  53. (inits (make-vector (length fields) uninitialized)))
  54. (let lp ((args field+value))
  55. (match args
  56. (()
  57. (let lp ((i 0) (fields fields))
  58. (when (< i (vector-length inits))
  59. (when (eq? (vector-ref inits i) uninitialized)
  60. (error "field not specified" (car fields)))
  61. (lp (1+ i) (cdr fields))))
  62. (apply make-struct/simple type (vector->list inits)))
  63. (((and (? symbol?) field) value . args)
  64. (let lp ((i 0) (fields fields))
  65. (when (null? fields)
  66. (error "unknown field" field))
  67. (cond
  68. ((eq? field (car fields))
  69. (unless (eq? (vector-ref inits i) uninitialized)
  70. (error "duplicate initializer" field))
  71. (vector-set! inits i value))
  72. (else
  73. (lp (1+ i) (cdr fields)))))
  74. (lp args))
  75. (inits
  76. (scm-error 'wrong-type-arg "make-condition"
  77. "Bad initializer list tail: ~S"
  78. (list inits) #f))))))
  79. (define (condition-has-type? c type)
  80. "Return true if condition C has type TYPE."
  81. (unless (exception-type? type)
  82. (scm-error 'wrong-type-arg "condition-has-type?" "Not a condition type: ~S"
  83. (list type) #f))
  84. (or-map (record-predicate type) (simple-exceptions c)))
  85. ;; Precondition: C is a simple condition.
  86. (define (simple-condition-ref c field-name not-found)
  87. (match (list-index (record-type-fields (struct-vtable c)) field-name)
  88. (#f (not-found))
  89. (pos (struct-ref c pos))))
  90. (define (condition-ref c field-name)
  91. "Return the value of the field named FIELD-NAME from condition C."
  92. (let lp ((conditions (simple-exceptions c)))
  93. (match conditions
  94. (() (error "invalid field name" field-name))
  95. ((c . conditions)
  96. (simple-condition-ref c field-name (lambda () (lp conditions)))))))
  97. (define (make-condition-from-values type values)
  98. (apply make-struct/simple type values))
  99. (define (extract-condition c type)
  100. "Return a condition of condition type TYPE with the field values specified
  101. by C."
  102. (unless (exception-type? type)
  103. (scm-error 'wrong-type-arg "extract-condition" "Not a condition type: ~S"
  104. (list type) #f))
  105. (let ((pred (record-predicate type)))
  106. (or-map (lambda (x) (and (pred x) x)) (simple-exceptions c))))
  107. (define-syntax define-condition-type
  108. (lambda (s)
  109. (syntax-case s ()
  110. ((_ type parent predicate (field accessor) ...)
  111. ;; The constructor is unused, but generate a new name for each
  112. ;; condition to avoid '-Wshadowed-toplevel' warnings when several
  113. ;; condition types are defined in the same compilation unit.
  114. (with-syntax ((unused-constructor
  115. (datum->syntax
  116. #'type
  117. (symbol-append '#{ make-}# (syntax->datum #'type)))))
  118. #'(define-exception-type type parent
  119. unused-constructor predicate
  120. (field accessor) ...))))))
  121. (define-syntax condition-instantiation
  122. ;; Build the `(make-condition type ...)' call.
  123. (syntax-rules ()
  124. ((_ type (out ...))
  125. (make-condition type out ...))
  126. ((_ type (out ...) (field-name field-value) rest ...)
  127. (condition-instantiation type (out ... 'field-name field-value) rest ...))))
  128. (define-syntax condition
  129. (syntax-rules ()
  130. ((_ (type field ...))
  131. (condition-instantiation type () field ...))
  132. ((_ (type field ...) ...)
  133. (make-exception (condition-instantiation type () field ...)
  134. ...))))