PageRenderTime 51ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/racket/collects/unstable/error.rkt

http://github.com/plt/racket
Racket | 177 lines | 12 code | 1 blank | 164 comment | 0 complexity | 22a1076c4eca946c8105fb06ad43e5db MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, BSD-3-Clause, CC-BY-SA-3.0
  1. #lang racket/base
  2. (require racket/contract/base
  3. racket/string
  4. racket/list
  5. syntax/srcloc
  6. syntax/stx)
  7. #|
  8. TODO
  9. - more options
  10. - 'pretty : pretty-print, then use multi-line format as necessary
  11. - need no-contracts version?
  12. - document or remove #:details arg
  13. |#
  14. ;; A DetailsTable is (listof (cons Field any))
  15. ;; A Field is one of
  16. ;; - string
  17. ;; - (cons string (listof FieldOption))
  18. ;; A FieldOption is one of
  19. ;; - 'multi
  20. ;; - 'value
  21. ;; - 'maybe
  22. (define field-option/c (or/c 'multi 'value 'maybe))
  23. (define field/c (or/c string? (cons/c string? (listof field-option/c))))
  24. (define details-list/c
  25. (recursive-contract
  26. (or/c '() (cons/c field/c (cons/c any/c details-list/c)))))
  27. (provide/contract
  28. [error*
  29. (->* [symbol? string?]
  30. [#:continued (or/c string? (listof string))]
  31. #:rest details-list/c
  32. any)]
  33. [raise-syntax-error*
  34. (->* [string? (or/c syntax? #f) (or/c syntax? #f)]
  35. [#:continued (or/c string? (listof string))]
  36. #:rest details-list/c
  37. any)]
  38. [compose-error-message
  39. (->* [(or/c symbol? #f) string?]
  40. [#:continued (or/c string? (listof string))]
  41. #:rest details-list/c
  42. string?)]
  43. [compose-error-detail
  44. (-> string? (listof field-option/c) any/c
  45. string?)])
  46. ;; ----
  47. (define (error* who message
  48. #:continued [continued-message null]
  49. . field+detail-list)
  50. (raise
  51. (exn:fail
  52. (compose* who message
  53. continued-message
  54. (field+detail-list->table 'error* field+detail-list null))
  55. (current-continuation-marks))))
  56. (define (raise-syntax-error* message0 stx sub-stx
  57. #:who [who #f]
  58. #:continued [continued-message null]
  59. #:extra-sources [extra-stxs null]
  60. . field+detail-list)
  61. (let* ([source-stx (or stx sub-stx)]
  62. [who (or who
  63. (let* ([maybe-id (if (stx-pair? stx) (stx-car stx) stx)])
  64. (if (identifier? maybe-id) (syntax-e maybe-id) '?)))]
  65. [message
  66. (apply compose-error-message who message0
  67. #:continued continued-message
  68. '("at" maybe) (and sub-stx
  69. (error-print-source-location)
  70. (format "~.s" (syntax->datum sub-stx)))
  71. '("in" maybe) (and stx
  72. (error-print-source-location)
  73. (format "~.s" (syntax->datum stx)))
  74. field+detail-list)]
  75. [message
  76. (if (error-print-source-location)
  77. (string-append (source-location->prefix source-stx) message)
  78. message)])
  79. (raise
  80. (exn:fail:syntax message
  81. (current-continuation-marks)
  82. (cond [sub-stx (cons sub-stx extra-stxs)]
  83. [stx (cons stx extra-stxs)]
  84. [else extra-stxs])))))
  85. ;; ----
  86. ;; compose-error-message : .... -> string
  87. (define (compose-error-message who message
  88. #:continued [continued-message null]
  89. . field+detail-list)
  90. (define details
  91. (field+detail-list->table 'compose-error-message field+detail-list null))
  92. (compose* who message continued-message details))
  93. ;; compose-error-detail : string (listof option) any -> (listof string)
  94. ;; Note: includes a leading newline (unless detail omitted).
  95. (define (compose-error-detail field options value)
  96. (apply string-append (compose-detail* field options value)))
  97. ;; ----
  98. (define (compose* who message continued-message details)
  99. (let* ([parts (apply append
  100. (for/list ([detail (in-list details)])
  101. (let* ([field+opts (car detail)]
  102. [field (if (pair? field+opts) (car field+opts) field+opts)]
  103. [options (if (pair? field+opts) (cdr field+opts) '())]
  104. [value (cdr detail)])
  105. (compose-detail* field options value))))]
  106. [parts (let loop ([continued continued-message])
  107. (cond [(pair? continued) (list* "\n " (car continued) (loop (cdr continued)))]
  108. [(string? continued) (loop (list continued))]
  109. [(null? continued) parts]))]
  110. [parts (list* message (if (null? continued-message) "" ";") parts)]
  111. [parts (if who
  112. (list* (symbol->string who) ": " parts)
  113. parts)])
  114. (apply string-append parts)))
  115. (define (compose-detail* field options value)
  116. (let* ([value? (memq 'value options)]
  117. [multi? (memq 'multi options)]
  118. [maybe? (memq 'maybe options)]
  119. [noindent? (memq 'noindent options)]
  120. [convert-value0
  121. (cond [value?
  122. (lambda (v) ((error-value->string-handler) v (error-print-width)))]
  123. [else
  124. (lambda (v) (format "~a" v))])]
  125. [convert-value
  126. (if noindent?
  127. (lambda (v indent) (list (convert-value0 v)))
  128. (lambda (v indent)
  129. (let* ([s (convert-value0 v)]
  130. [lines (string-split s #rx"[\n]" #:trim? #f)]
  131. [spacing
  132. (case indent
  133. ((3) "\n ") ;; common case, make constant
  134. (else (string-append "\n" (make-string indent #\space))))])
  135. (add-between lines spacing))))])
  136. (cond [(and (or maybe? multi? (not value?))
  137. (not value))
  138. null]
  139. [(and maybe? multi?
  140. (null? value))
  141. null]
  142. [multi?
  143. (list* "\n " field ": "
  144. (let value-loop ([value value])
  145. (cond [(pair? value)
  146. (list* "\n "
  147. (append (convert-value (car value) 3)
  148. (value-loop (cdr value))))]
  149. [(null? value)
  150. null])))]
  151. [else
  152. (list* "\n " field ": "
  153. (convert-value value (+ 4 (string-length field))))])))
  154. ;; ----
  155. (define (field+detail-list->table who lst onto)
  156. (cond [(null? lst) onto]
  157. [else
  158. (let ([field (car lst)]
  159. [value (cadr lst)])
  160. (cons (cons field value)
  161. (field+detail-list->table who (cddr lst) onto)))]))