exceptions.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. ;;; exceptions.scm --- The R6RS exceptions library
  2. ;; Copyright (C) 2010, 2011, 2013 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. (library (rnrs exceptions (6))
  18. (export guard with-exception-handler raise raise-continuable)
  19. (import (rnrs base (6))
  20. (rnrs control (6))
  21. (rnrs conditions (6))
  22. (rnrs records procedural (6))
  23. (rnrs records inspection (6))
  24. (only (guile)
  25. format
  26. newline
  27. display
  28. filter
  29. acons
  30. assv-ref
  31. throw
  32. set-exception-printer!
  33. with-throw-handler
  34. *unspecified*
  35. @@))
  36. ;; When a native guile exception is caught by an R6RS exception
  37. ;; handler, we convert it to an R6RS compound condition that includes
  38. ;; not only the standard condition objects expected by R6RS code, but
  39. ;; also a special &guile condition that preserves the original KEY and
  40. ;; ARGS passed to the native Guile catch handler.
  41. (define-condition-type &guile &condition
  42. make-guile-condition guile-condition?
  43. (key guile-condition-key)
  44. (args guile-condition-args))
  45. (define (default-guile-condition-converter key args)
  46. (condition (make-serious-condition)
  47. (guile-common-conditions key args)))
  48. (define (guile-common-conditions key args)
  49. (apply (case-lambda
  50. ((subr msg margs . _)
  51. (condition (make-who-condition subr)
  52. (make-message-condition msg)
  53. (make-irritants-condition margs)))
  54. (_ (make-irritants-condition args)))
  55. args))
  56. (define (convert-guile-condition key args)
  57. (let ((converter (assv-ref guile-condition-converters key)))
  58. (condition (or (and converter (converter key args))
  59. (default-guile-condition-converter key args))
  60. ;; Preserve the original KEY and ARGS in the R6RS
  61. ;; condition object.
  62. (make-guile-condition key args))))
  63. ;; If an R6RS exception handler chooses not to handle a given
  64. ;; condition, it will re-raise the condition to pass it on to the next
  65. ;; handler. If the condition was converted from a native Guile
  66. ;; exception, we must re-raise using the native Guile facilities and
  67. ;; the original exception KEY and ARGS. We arrange for this in
  68. ;; 'raise' so that native Guile exception handlers will continue to
  69. ;; work when mixed with R6RS code.
  70. (define (raise obj)
  71. (if (guile-condition? obj)
  72. (apply throw (guile-condition-key obj) (guile-condition-args obj))
  73. ((@@ (rnrs records procedural) r6rs-raise) obj)))
  74. (define raise-continuable
  75. (@@ (rnrs records procedural) r6rs-raise-continuable))
  76. (define raise-object-wrapper?
  77. (@@ (rnrs records procedural) raise-object-wrapper?))
  78. (define raise-object-wrapper-obj
  79. (@@ (rnrs records procedural) raise-object-wrapper-obj))
  80. (define raise-object-wrapper-continuation
  81. (@@ (rnrs records procedural) raise-object-wrapper-continuation))
  82. (define (with-exception-handler handler thunk)
  83. (with-throw-handler #t
  84. thunk
  85. (lambda (key . args)
  86. (cond ((not (eq? key 'r6rs:exception))
  87. (let ((obj (convert-guile-condition key args)))
  88. (handler obj)
  89. (raise (make-non-continuable-violation))))
  90. ((and (not (null? args))
  91. (raise-object-wrapper? (car args)))
  92. (let* ((cargs (car args))
  93. (obj (raise-object-wrapper-obj cargs))
  94. (continuation (raise-object-wrapper-continuation cargs))
  95. (handler-return (handler obj)))
  96. (if continuation
  97. (continuation handler-return)
  98. (raise (make-non-continuable-violation)))))))))
  99. (define-syntax guard0
  100. (syntax-rules ()
  101. ((_ (variable cond-clause ...) . body)
  102. (call/cc (lambda (continuation)
  103. (with-exception-handler
  104. (lambda (variable)
  105. (continuation (cond cond-clause ...)))
  106. (lambda () . body)))))))
  107. (define-syntax guard
  108. (syntax-rules (else)
  109. ((_ (variable cond-clause ... . ((else else-clause ...))) . body)
  110. (guard0 (variable cond-clause ... (else else-clause ...)) . body))
  111. ((_ (variable cond-clause ...) . body)
  112. (guard0 (variable cond-clause ... (else (raise variable))) . body))))
  113. ;;; Exception printing
  114. (define (exception-printer port key args punt)
  115. (cond ((and (= 1 (length args))
  116. (raise-object-wrapper? (car args)))
  117. (let ((obj (raise-object-wrapper-obj (car args))))
  118. (cond ((condition? obj)
  119. (display "ERROR: R6RS exception:\n" port)
  120. (format-condition port obj))
  121. (else
  122. (format port "ERROR: R6RS exception: `~s'" obj)))))
  123. (else
  124. (punt))))
  125. (define (format-condition port condition)
  126. (let ((components (simple-conditions condition)))
  127. (if (null? components)
  128. (format port "Empty condition object")
  129. (let loop ((i 1) (components components))
  130. (cond ((pair? components)
  131. (format port " ~a. " i)
  132. (format-simple-condition port (car components))
  133. (when (pair? (cdr components))
  134. (newline port))
  135. (loop (+ i 1) (cdr components))))))))
  136. (define (format-simple-condition port condition)
  137. (define (print-rtd-fields rtd field-names)
  138. (let ((n-fields (vector-length field-names)))
  139. (do ((i 0 (+ i 1)))
  140. ((>= i n-fields))
  141. (format port " ~a: ~s"
  142. (vector-ref field-names i)
  143. ((record-accessor rtd i) condition))
  144. (unless (= i (- n-fields 1))
  145. (newline port)))))
  146. (let ((condition-name (record-type-name (record-rtd condition))))
  147. (let loop ((rtd (record-rtd condition))
  148. (rtd.fields-list '())
  149. (n-fields 0))
  150. (cond (rtd
  151. (let ((field-names (record-type-field-names rtd)))
  152. (loop (record-type-parent rtd)
  153. (cons (cons rtd field-names) rtd.fields-list)
  154. (+ n-fields (vector-length field-names)))))
  155. (else
  156. (let ((rtd.fields-list
  157. (filter (lambda (rtd.fields)
  158. (not (zero? (vector-length (cdr rtd.fields)))))
  159. (reverse rtd.fields-list))))
  160. (case n-fields
  161. ((0) (format port "~a" condition-name))
  162. ((1) (format port "~a: ~s"
  163. condition-name
  164. ((record-accessor (caar rtd.fields-list) 0)
  165. condition)))
  166. (else
  167. (format port "~a:\n" condition-name)
  168. (let loop ((lst rtd.fields-list))
  169. (when (pair? lst)
  170. (let ((rtd.fields (car lst)))
  171. (print-rtd-fields (car rtd.fields) (cdr rtd.fields))
  172. (when (pair? (cdr lst))
  173. (newline port))
  174. (loop (cdr lst)))))))))))))
  175. (set-exception-printer! 'r6rs:exception exception-printer)
  176. ;; Guile condition converters
  177. ;;
  178. ;; Each converter is a procedure (converter KEY ARGS) that returns
  179. ;; either an R6RS condition or #f. If #f is returned,
  180. ;; 'default-guile-condition-converter' will be used.
  181. (define (guile-syntax-violation-converter key args)
  182. (apply (case-lambda
  183. ((who what where form subform . extra)
  184. (condition (make-syntax-violation form subform)
  185. (make-who-condition who)
  186. (make-message-condition what)))
  187. (_ #f))
  188. args))
  189. (define (guile-lexical-violation-converter key args)
  190. (condition (make-lexical-violation) (guile-common-conditions key args)))
  191. (define (guile-assertion-violation-converter key args)
  192. (condition (make-assertion-violation) (guile-common-conditions key args)))
  193. (define (guile-undefined-violation-converter key args)
  194. (condition (make-undefined-violation) (guile-common-conditions key args)))
  195. (define (guile-implementation-restriction-converter key args)
  196. (condition (make-implementation-restriction-violation)
  197. (guile-common-conditions key args)))
  198. (define (guile-error-converter key args)
  199. (condition (make-error) (guile-common-conditions key args)))
  200. (define (guile-system-error-converter key args)
  201. (apply (case-lambda
  202. ((subr msg msg-args errno . rest)
  203. ;; XXX TODO we should return a more specific error
  204. ;; (usually an I/O error) as expected by R6RS programs.
  205. ;; Unfortunately this often requires the 'filename' (or
  206. ;; other?) which is not currently provided by the native
  207. ;; Guile exceptions.
  208. (condition (make-error) (guile-common-conditions key args)))
  209. (_ (guile-error-converter key args)))
  210. args))
  211. ;; TODO: Arrange to have the needed information included in native
  212. ;; Guile I/O exceptions, and arrange here to convert them to the
  213. ;; proper conditions. Remove the earlier exception conversion
  214. ;; mechanism: search for 'with-throw-handler' in the 'rnrs'
  215. ;; tree, e.g. 'with-i/o-filename-conditions' and
  216. ;; 'with-i/o-port-error' in (rnrs io ports).
  217. ;; XXX TODO: How should we handle the 'misc-error', 'vm-error', and
  218. ;; 'signal' native Guile exceptions?
  219. ;; XXX TODO: Should we handle the 'quit' exception specially?
  220. ;; An alist mapping native Guile exception keys to converters.
  221. (define guile-condition-converters
  222. `((read-error . ,guile-lexical-violation-converter)
  223. (syntax-error . ,guile-syntax-violation-converter)
  224. (unbound-variable . ,guile-undefined-violation-converter)
  225. (wrong-number-of-args . ,guile-assertion-violation-converter)
  226. (wrong-type-arg . ,guile-assertion-violation-converter)
  227. (keyword-argument-error . ,guile-assertion-violation-converter)
  228. (out-of-range . ,guile-assertion-violation-converter)
  229. (regular-expression-syntax . ,guile-assertion-violation-converter)
  230. (program-error . ,guile-assertion-violation-converter)
  231. (goops-error . ,guile-assertion-violation-converter)
  232. (null-pointer-error . ,guile-assertion-violation-converter)
  233. (system-error . ,guile-system-error-converter)
  234. (host-not-found . ,guile-error-converter)
  235. (getaddrinfo-error . ,guile-error-converter)
  236. (no-data . ,guile-error-converter)
  237. (no-recovery . ,guile-error-converter)
  238. (try-again . ,guile-error-converter)
  239. (stack-overflow . ,guile-implementation-restriction-converter)
  240. (numerical-overflow . ,guile-implementation-restriction-converter)
  241. (memory-allocation-error . ,guile-implementation-restriction-converter)))
  242. (define (set-guile-condition-converter! key proc)
  243. (set! guile-condition-converters
  244. (acons key proc guile-condition-converters))))