srfi-35.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. ;;; srfi-35.scm --- Conditions
  2. ;; Copyright (C) 2007, 2008 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 2.1 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 (srfi srfi-1)
  25. #:export (make-condition-type condition-type?
  26. make-condition condition? condition-has-type? condition-ref
  27. make-compound-condition extract-condition
  28. define-condition-type condition
  29. &condition
  30. &message message-condition? condition-message
  31. &serious serious-condition?
  32. &error error?))
  33. (cond-expand-provide (current-module) '(srfi-35))
  34. ;;;
  35. ;;; Condition types.
  36. ;;;
  37. (define %condition-type-vtable
  38. ;; The vtable of all condition types.
  39. ;; vtable fields: vtable, self, printer
  40. ;; user fields: id, parent, all-field-names
  41. (make-vtable-vtable "prprpr" 0
  42. (lambda (ct port)
  43. (if (eq? ct %condition-type-vtable)
  44. (display "#<condition-type-vtable>")
  45. (format port "#<condition-type ~a ~a>"
  46. (condition-type-id ct)
  47. (number->string (object-address ct)
  48. 16))))))
  49. (define (condition-type? obj)
  50. "Return true if OBJ is a condition type."
  51. (and (struct? obj)
  52. (eq? (struct-vtable obj)
  53. %condition-type-vtable)))
  54. (define (condition-type-id ct)
  55. (and (condition-type? ct)
  56. (struct-ref ct 3)))
  57. (define (condition-type-parent ct)
  58. (and (condition-type? ct)
  59. (struct-ref ct 4)))
  60. (define (condition-type-all-fields ct)
  61. (and (condition-type? ct)
  62. (struct-ref ct 5)))
  63. (define (struct-layout-for-condition field-names)
  64. ;; Return a string denoting the layout required to hold the fields listed
  65. ;; in FIELD-NAMES.
  66. (let loop ((field-names field-names)
  67. (layout '("pr")))
  68. (if (null? field-names)
  69. (string-concatenate/shared layout)
  70. (loop (cdr field-names)
  71. (cons "pr" layout)))))
  72. (define (print-condition c port)
  73. (format port "#<condition ~a ~a>"
  74. (condition-type-id (condition-type c))
  75. (number->string (object-address c) 16)))
  76. (define (make-condition-type id parent field-names)
  77. "Return a new condition type named ID, inheriting from PARENT, and with the
  78. fields whose names are listed in FIELD-NAMES. FIELD-NAMES must be a list of
  79. symbols and must not contain names already used by PARENT or one of its
  80. supertypes."
  81. (if (symbol? id)
  82. (if (condition-type? parent)
  83. (let ((parent-fields (condition-type-all-fields parent)))
  84. (if (and (every symbol? field-names)
  85. (null? (lset-intersection eq?
  86. field-names parent-fields)))
  87. (let* ((all-fields (append parent-fields field-names))
  88. (layout (struct-layout-for-condition all-fields)))
  89. (make-struct %condition-type-vtable 0
  90. (make-struct-layout layout) ;; layout
  91. print-condition ;; printer
  92. id parent all-fields))
  93. (error "invalid condition type field names"
  94. field-names)))
  95. (error "parent is not a condition type" parent))
  96. (error "condition type identifier is not a symbol" id)))
  97. (define (make-compound-condition-type id parents)
  98. ;; Return a compound condition type made of the types listed in PARENTS.
  99. ;; All fields from PARENTS are kept, even same-named ones, since they are
  100. ;; needed by `extract-condition'.
  101. (cond ((null? parents)
  102. (error "`make-compound-condition-type' passed empty parent list"
  103. id))
  104. ((null? (cdr parents))
  105. (car parents))
  106. (else
  107. (let* ((all-fields (append-map condition-type-all-fields
  108. parents))
  109. (layout (struct-layout-for-condition all-fields)))
  110. (make-struct %condition-type-vtable 0
  111. (make-struct-layout layout) ;; layout
  112. print-condition ;; printer
  113. id
  114. parents ;; list of parents!
  115. all-fields
  116. all-fields)))))
  117. ;;;
  118. ;;; Conditions.
  119. ;;;
  120. (define (condition? c)
  121. "Return true if C is a condition."
  122. (and (struct? c)
  123. (condition-type? (struct-vtable c))))
  124. (define (condition-type c)
  125. (and (struct? c)
  126. (let ((vtable (struct-vtable c)))
  127. (if (condition-type? vtable)
  128. vtable
  129. #f))))
  130. (define (condition-has-type? c type)
  131. "Return true if condition C has type TYPE."
  132. (if (and (condition? c) (condition-type? type))
  133. (let loop ((ct (condition-type c)))
  134. (or (eq? ct type)
  135. (and ct
  136. (let ((parent (condition-type-parent ct)))
  137. (if (list? parent)
  138. (any loop parent) ;; compound condition
  139. (loop (condition-type-parent ct)))))))
  140. (throw 'wrong-type-arg "condition-has-type?"
  141. "Wrong type argument")))
  142. (define (condition-ref c field-name)
  143. "Return the value of the field named FIELD-NAME from condition C."
  144. (if (condition? c)
  145. (if (symbol? field-name)
  146. (let* ((type (condition-type c))
  147. (fields (condition-type-all-fields type))
  148. (index (list-index (lambda (name)
  149. (eq? name field-name))
  150. fields)))
  151. (if index
  152. (struct-ref c index)
  153. (error "invalid field name" field-name)))
  154. (error "field name is not a symbol" field-name))
  155. (throw 'wrong-type-arg "condition-ref"
  156. "Wrong type argument: ~S" c)))
  157. (define (make-condition-from-values type values)
  158. (apply make-struct type 0 values))
  159. (define (make-condition type . field+value)
  160. "Return a new condition of type TYPE with fields initialized as specified
  161. by FIELD+VALUE, a sequence of field names (symbols) and values."
  162. (if (condition-type? type)
  163. (let* ((all-fields (condition-type-all-fields type))
  164. (inits (fold-right (lambda (field inits)
  165. (let ((v (memq field field+value)))
  166. (if (pair? v)
  167. (cons (cadr v) inits)
  168. (error "field not specified"
  169. field))))
  170. '()
  171. all-fields)))
  172. (make-condition-from-values type inits))
  173. (throw 'wrong-type-arg "make-condition"
  174. "Wrong type argument: ~S" type)))
  175. (define (make-compound-condition . conditions)
  176. "Return a new compound condition composed of CONDITIONS."
  177. (let* ((types (map condition-type conditions))
  178. (ct (make-compound-condition-type 'compound types))
  179. (inits (append-map (lambda (c)
  180. (let ((ct (condition-type c)))
  181. (map (lambda (f)
  182. (condition-ref c f))
  183. (condition-type-all-fields ct))))
  184. conditions)))
  185. (make-condition-from-values ct inits)))
  186. (define (extract-condition c type)
  187. "Return a condition of condition type TYPE with the field values specified
  188. by C."
  189. (define (first-field-index parents)
  190. ;; Return the index of the first field of TYPE within C.
  191. (let loop ((parents parents)
  192. (index 0))
  193. (let ((parent (car parents)))
  194. (cond ((null? parents)
  195. #f)
  196. ((eq? parent type)
  197. index)
  198. ((pair? parent)
  199. (or (loop parent index)
  200. (loop (cdr parents)
  201. (+ index
  202. (apply + (map condition-type-all-fields
  203. parent))))))
  204. (else
  205. (let ((shift (length (condition-type-all-fields parent))))
  206. (loop (cdr parents)
  207. (+ index shift))))))))
  208. (define (list-fields start-index field-names)
  209. ;; Return a list of the form `(FIELD-NAME VALUE...)'.
  210. (let loop ((index start-index)
  211. (field-names field-names)
  212. (result '()))
  213. (if (null? field-names)
  214. (reverse! result)
  215. (loop (+ 1 index)
  216. (cdr field-names)
  217. (cons* (struct-ref c index)
  218. (car field-names)
  219. result)))))
  220. (if (and (condition? c) (condition-type? type))
  221. (let* ((ct (condition-type c))
  222. (parent (condition-type-parent ct)))
  223. (cond ((eq? type ct)
  224. c)
  225. ((pair? parent)
  226. ;; C is a compound condition.
  227. (let ((field-index (first-field-index parent)))
  228. ;;(format #t "field-index: ~a ~a~%" field-index
  229. ;; (list-fields field-index
  230. ;; (condition-type-all-fields type)))
  231. (apply make-condition type
  232. (list-fields field-index
  233. (condition-type-all-fields type)))))
  234. (else
  235. ;; C does not have type TYPE.
  236. #f)))
  237. (throw 'wrong-type-arg "extract-condition"
  238. "Wrong type argument")))
  239. ;;;
  240. ;;; Syntax.
  241. ;;;
  242. (define-macro (define-condition-type name parent pred . field-specs)
  243. `(begin
  244. (define ,name
  245. (make-condition-type ',name ,parent
  246. ',(map car field-specs)))
  247. (define (,pred c)
  248. (condition-has-type? c ,name))
  249. ,@(map (lambda (field-spec)
  250. (let ((field-name (car field-spec))
  251. (accessor (cadr field-spec)))
  252. `(define (,accessor c)
  253. (condition-ref c ',field-name))))
  254. field-specs)))
  255. (define-macro (condition . type-field-bindings)
  256. (cond ((null? type-field-bindings)
  257. (error "`condition' syntax error" type-field-bindings))
  258. (else
  259. ;; the poor man's hygienic macro
  260. (let ((mc (gensym "mc"))
  261. (mcct (gensym "mcct")))
  262. `(let ((,mc (@ (srfi srfi-35) make-condition))
  263. (,mcct (@@ (srfi srfi-35) make-compound-condition-type)))
  264. (,mc (,mcct 'compound (list ,@(map car type-field-bindings)))
  265. ,@(append-map (lambda (type-field-binding)
  266. (append-map (lambda (field+value)
  267. (let ((f (car field+value))
  268. (v (cadr field+value)))
  269. `(',f ,v)))
  270. (cdr type-field-binding)))
  271. type-field-bindings)))))))
  272. ;;;
  273. ;;; Standard condition types.
  274. ;;;
  275. (define &condition
  276. ;; The root condition type.
  277. (make-struct %condition-type-vtable 0
  278. (make-struct-layout "")
  279. (lambda (c port)
  280. (display "<&condition>"))
  281. '&condition #f '() '()))
  282. (define-condition-type &message &condition
  283. message-condition?
  284. (message condition-message))
  285. (define-condition-type &serious &condition
  286. serious-condition?)
  287. (define-condition-type &error &serious
  288. error?)
  289. ;;; Local Variables:
  290. ;;; coding: latin-1
  291. ;;; End:
  292. ;;; srfi-35.scm ends here