srfi-35.scm 12 KB

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