syntactic.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. ;;; syntactic.scm --- Syntactic support for R6RS records
  2. ;; Copyright (C) 2010 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 records syntactic (6))
  18. (export define-record-type
  19. record-type-descriptor
  20. record-constructor-descriptor)
  21. (import (only (guile) and=> gensym)
  22. (rnrs base (6))
  23. (rnrs conditions (6))
  24. (rnrs exceptions (6))
  25. (rnrs hashtables (6))
  26. (rnrs lists (6))
  27. (rnrs records procedural (6))
  28. (rnrs syntax-case (6))
  29. (only (srfi :1) take))
  30. (define record-type-registry (make-eq-hashtable))
  31. (define (guess-constructor-name record-name)
  32. (string->symbol (string-append "make-" (symbol->string record-name))))
  33. (define (guess-predicate-name record-name)
  34. (string->symbol (string-append (symbol->string record-name) "?")))
  35. (define (register-record-type name rtd rcd)
  36. (hashtable-set! record-type-registry name (cons rtd rcd)))
  37. (define (lookup-record-type-descriptor name)
  38. (and=> (hashtable-ref record-type-registry name #f) car))
  39. (define (lookup-record-constructor-descriptor name)
  40. (and=> (hashtable-ref record-type-registry name #f) cdr))
  41. (define-syntax define-record-type
  42. (lambda (stx)
  43. (syntax-case stx ()
  44. ((_ (record-name constructor-name predicate-name) record-clause ...)
  45. #'(define-record-type0
  46. (record-name constructor-name predicate-name)
  47. record-clause ...))
  48. ((_ record-name record-clause ...)
  49. (let* ((record-name-sym (syntax->datum #'record-name))
  50. (constructor-name
  51. (datum->syntax
  52. #'record-name (guess-constructor-name record-name-sym)))
  53. (predicate-name
  54. (datum->syntax
  55. #'record-name (guess-predicate-name record-name-sym))))
  56. #`(define-record-type0
  57. (record-name #,constructor-name #,predicate-name)
  58. record-clause ...))))))
  59. (define (sequence n)
  60. (define (seq-inner n) (if (= n 0) '(0) (cons n (seq-inner (- n 1)))))
  61. (reverse (seq-inner n)))
  62. (define (number-fields fields)
  63. (define (number-fields-inner fields counter)
  64. (if (null? fields)
  65. '()
  66. (cons (cons fields counter)
  67. (number-fields-inner (cdr fields) (+ counter 1)))))
  68. (number-fields-inner fields 0))
  69. (define (process-fields record-name fields)
  70. (define (wrap x) (datum->syntax record-name x))
  71. (define (id->string x)
  72. (symbol->string (syntax->datum x)))
  73. (define record-name-str (id->string record-name))
  74. (define (guess-accessor-name field-name)
  75. (wrap
  76. (string->symbol (string-append
  77. record-name-str "-" (id->string field-name)))))
  78. (define (guess-mutator-name field-name)
  79. (wrap
  80. (string->symbol
  81. (string-append
  82. record-name-str "-" (id->string field-name) "-set!"))))
  83. (define (f x)
  84. (syntax-case x (immutable mutable)
  85. [(immutable name)
  86. (list (wrap `(immutable ,(syntax->datum #'name)))
  87. (guess-accessor-name #'name)
  88. #f)]
  89. [(immutable name accessor)
  90. (list (wrap `(immutable ,(syntax->datum #'name))) #'accessor #f)]
  91. [(mutable name)
  92. (list (wrap `(mutable ,(syntax->datum #'name)))
  93. (guess-accessor-name #'name)
  94. (guess-mutator-name #'name))]
  95. [(mutable name accessor mutator)
  96. (list (wrap `(mutable ,(syntax->datum #'name))) #'accessor #'mutator)]
  97. [name
  98. (identifier? #'name)
  99. (list (wrap `(immutable ,(syntax->datum #'name)))
  100. (guess-accessor-name #'name)
  101. #f)]
  102. [else
  103. (syntax-violation 'define-record-type "invalid field specifier" x)]))
  104. (map f fields))
  105. (define-syntax define-record-type0
  106. (lambda (stx)
  107. (define *unspecified* (cons #f #f))
  108. (define (unspecified? obj)
  109. (eq? *unspecified* obj))
  110. (syntax-case stx ()
  111. ((_ (record-name constructor-name predicate-name) record-clause ...)
  112. (let loop ((_fields *unspecified*)
  113. (_parent *unspecified*)
  114. (_protocol *unspecified*)
  115. (_sealed *unspecified*)
  116. (_opaque *unspecified*)
  117. (_nongenerative *unspecified*)
  118. (_constructor *unspecified*)
  119. (_parent-rtd *unspecified*)
  120. (record-clauses #'(record-clause ...)))
  121. (syntax-case record-clauses
  122. (fields parent protocol sealed opaque nongenerative
  123. constructor parent-rtd)
  124. [()
  125. (let* ((fields (if (unspecified? _fields) '() _fields))
  126. (field-names (list->vector (map car fields)))
  127. (field-accessors
  128. (fold-left (lambda (lst x c)
  129. (cons #`(define #,(cadr x)
  130. (record-accessor record-name #,c))
  131. lst))
  132. '() fields (sequence (length fields))))
  133. (field-mutators
  134. (fold-left (lambda (lst x c)
  135. (if (caddr x)
  136. (cons #`(define #,(caddr x)
  137. (record-mutator record-name
  138. #,c))
  139. lst)
  140. lst))
  141. '() fields (sequence (length fields))))
  142. (parent-cd (cond ((not (unspecified? _parent))
  143. #`(record-constructor-descriptor
  144. #,_parent))
  145. ((not (unspecified? _parent-rtd))
  146. (cadr _parent-rtd))
  147. (else #f)))
  148. (parent-rtd (cond ((not (unspecified? _parent))
  149. #`(record-type-descriptor #,_parent))
  150. ((not (unspecified? _parent-rtd))
  151. (car _parent-rtd))
  152. (else #f)))
  153. (protocol (if (unspecified? _protocol) #f _protocol))
  154. (uid (if (unspecified? _nongenerative) #f _nongenerative))
  155. (sealed? (if (unspecified? _sealed) #f _sealed))
  156. (opaque? (if (unspecified? _opaque) #f _opaque)))
  157. #`(begin
  158. (define record-name
  159. (make-record-type-descriptor
  160. (quote record-name)
  161. #,parent-rtd #,uid #,sealed? #,opaque?
  162. #,field-names))
  163. (define constructor-name
  164. (record-constructor
  165. (make-record-constructor-descriptor
  166. record-name #,parent-cd #,protocol)))
  167. (define dummy
  168. (let ()
  169. (register-record-type
  170. (quote record-name)
  171. record-name (make-record-constructor-descriptor
  172. record-name #,parent-cd #,protocol))
  173. 'dummy))
  174. (define predicate-name (record-predicate record-name))
  175. #,@field-accessors
  176. #,@field-mutators))]
  177. [((fields record-fields ...) . rest)
  178. (if (unspecified? _fields)
  179. (loop (process-fields #'record-name #'(record-fields ...))
  180. _parent _protocol _sealed _opaque _nongenerative
  181. _constructor _parent-rtd #'rest)
  182. (raise (make-assertion-violation)))]
  183. [((parent parent-name) . rest)
  184. (if (not (unspecified? _parent-rtd))
  185. (raise (make-assertion-violation))
  186. (if (unspecified? _parent)
  187. (loop _fields #'parent-name _protocol _sealed _opaque
  188. _nongenerative _constructor _parent-rtd #'rest)
  189. (raise (make-assertion-violation))))]
  190. [((protocol expression) . rest)
  191. (if (unspecified? _protocol)
  192. (loop _fields _parent #'expression _sealed _opaque
  193. _nongenerative _constructor _parent-rtd #'rest)
  194. (raise (make-assertion-violation)))]
  195. [((sealed sealed?) . rest)
  196. (if (unspecified? _sealed)
  197. (loop _fields _parent _protocol #'sealed? _opaque
  198. _nongenerative _constructor _parent-rtd #'rest)
  199. (raise (make-assertion-violation)))]
  200. [((opaque opaque?) . rest)
  201. (if (unspecified? _opaque)
  202. (loop _fields _parent _protocol _sealed #'opaque?
  203. _nongenerative _constructor _parent-rtd #'rest)
  204. (raise (make-assertion-violation)))]
  205. [((nongenerative) . rest)
  206. (if (unspecified? _nongenerative)
  207. (loop _fields _parent _protocol _sealed _opaque
  208. #`(quote #,(datum->syntax #'record-name (gensym)))
  209. _constructor _parent-rtd #'rest)
  210. (raise (make-assertion-violation)))]
  211. [((nongenerative uid) . rest)
  212. (if (unspecified? _nongenerative)
  213. (loop _fields _parent _protocol _sealed
  214. _opaque #''uid _constructor
  215. _parent-rtd #'rest)
  216. (raise (make-assertion-violation)))]
  217. [((parent-rtd rtd cd) . rest)
  218. (if (not (unspecified? _parent))
  219. (raise (make-assertion-violation))
  220. (if (unspecified? _parent-rtd)
  221. (loop _fields _parent _protocol _sealed _opaque
  222. _nongenerative _constructor #'(rtd cd)
  223. #'rest)
  224. (raise (make-assertion-violation))))]))))))
  225. (define-syntax record-type-descriptor
  226. (lambda (stx)
  227. (syntax-case stx ()
  228. ((_ name) #`(lookup-record-type-descriptor
  229. #,(datum->syntax
  230. stx (list 'quote (syntax->datum #'name))))))))
  231. (define-syntax record-constructor-descriptor
  232. (lambda (stx)
  233. (syntax-case stx ()
  234. ((_ name) #`(lookup-record-constructor-descriptor
  235. #,(datum->syntax
  236. stx (list 'quote (syntax->datum #'name))))))))
  237. )