record-procedural.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. (define-record-type :record-type-data
  3. (make-record-type-data uid sealed? opaque? field-specs immutable?)
  4. record-type-data?
  5. (uid record-type-data-uid) ; not to be confused with the generated uid
  6. (sealed? record-type-data-sealed?)
  7. (opaque? record-type-data-opaque?)
  8. (field-specs record-type-data-field-specs)
  9. (immutable? record-type-data-immutable?))
  10. (define make-field-spec cons)
  11. (define field-spec-mutable? car)
  12. (define field-spec-name cdr)
  13. (define (field-spec=? spec-1 spec-2)
  14. (equal? spec-1 spec-2))
  15. (define (record-type-uid rtd)
  16. (record-type-data-uid (record-type-data rtd)))
  17. (define (record-type-sealed? rtd)
  18. (record-type-data-sealed? (record-type-data rtd)))
  19. (define (record-type-opaque? rtd)
  20. (record-type-data-opaque? (record-type-data rtd)))
  21. (define (record-type-field-specs rtd)
  22. (record-type-data-field-specs (record-type-data rtd)))
  23. (define (record-type-immutable? rtd)
  24. (record-type-data-immutable? (record-type-data rtd)))
  25. (define (record-type-descriptor=? rtd-1 rtd-2)
  26. (and (eq? (record-type-parent rtd-1) (record-type-parent rtd-2))
  27. (eq? (record-type-uid rtd-1) (record-type-uid rtd-2))
  28. (for-all field-spec=?
  29. (record-type-field-specs rtd-1)
  30. (record-type-field-specs rtd-2))))
  31. (define nongenerative-record-types-table
  32. (user-context-accessor 'nongenerative-record-types-table
  33. (lambda () #f))) ; initializers don't work after the fact anyway
  34. (define set-nongenerative-record-types-table!
  35. (user-context-modifier 'nongenerative-record-types-table))
  36. (define nongenerative-record-types-table-lock (make-lock))
  37. (define (record-type-generative? rtd)
  38. (not (record-type-uid rtd)))
  39. (define (nongenerative-record-types)
  40. (obtain-lock nongenerative-record-types-table-lock)
  41. (let ((l
  42. (table->entry-list (nongenerative-record-types-table))))
  43. (release-lock nongenerative-record-types-table-lock)
  44. l))
  45. (define (delete-nongenerative-record-type thing)
  46. (let ((name (cond
  47. ((symbol? thing)
  48. thing)
  49. ((record-type? thing)
  50. (record-type-uid thing))
  51. (else
  52. (assertion-violation 'delete-nongenerative-record-type "invalid argument" thing))))
  53. (table
  54. (nongenerative-record-types-table)))
  55. (if (not (symbol? name))
  56. (assertion-violation 'delete-nongenerative-record-type "generative record type" name))
  57. (obtain-lock nongenerative-record-types-table-lock)
  58. (cond
  59. ((table-ref table name)
  60. (table-set! table name #f)
  61. (release-lock nongenerative-record-types-table-lock)
  62. #t)
  63. (else
  64. (release-lock nongenerative-record-types-table-lock)
  65. #f))))
  66. (define (make-record-type-descriptor name parent uid sealed? opaque? fields)
  67. (check-parent-type 'make-record-type-descriptor name parent uid sealed? opaque? fields)
  68. (let ((opaque? (if parent
  69. (or (record-type-opaque? parent)
  70. opaque?)
  71. opaque?))
  72. (field-specs (map parse-field-spec (vector->list fields))))
  73. (let ((rtd (make-record-type name (map field-spec-name field-specs) parent))
  74. (data (make-record-type-data uid sealed? opaque? field-specs
  75. (not (exists field-spec-mutable? field-specs)))))
  76. (record-record-type-data! rtd data)
  77. rtd)))
  78. (define (check-parent-type caller name parent uid sealed? opaque? fields)
  79. (if (and parent
  80. (record-type-sealed? parent))
  81. (assertion-violation caller "can't extend a sealed parent class"
  82. name parent uid sealed? opaque? fields))
  83. (if (and parent
  84. (not (record-type-uid parent)) ; parent generative
  85. uid) ; ... but this one is non-generative
  86. (assertion-violation caller
  87. "a generative type can only be extended to give a generative type"
  88. name parent uid sealed? opaque? fields)))
  89. (define (record-record-type-data! rtd data)
  90. (set-record-type-data! rtd data)
  91. (cond
  92. ((record-type-data-uid data)
  93. => (lambda (uid)
  94. (let ((table (nongenerative-record-types-table)))
  95. (obtain-lock nongenerative-record-types-table-lock)
  96. (cond
  97. ((table-ref table uid)
  98. => (lambda (old-rtd)
  99. (release-lock nongenerative-record-types-table-lock)
  100. (if (record-type-descriptor=? rtd old-rtd)
  101. old-rtd
  102. (assertion-violation "mismatched nongenerative record types with identical uids"
  103. old-rtd rtd))))
  104. (else
  105. (table-set! table uid rtd)
  106. (release-lock nongenerative-record-types-table-lock))))))))
  107. ; making non-R6RS record types into R6RS record types
  108. (define (retrofit-record-type! rtd uid sealed? opaque? fields)
  109. (let ((parent (record-type-parent rtd))
  110. (name (record-type-name rtd)))
  111. (if (and parent
  112. (not (record-type-data? (record-type-data parent))))
  113. (assertion-violation 'retrofit-record-type!
  114. "parent type not an R6RS record type"
  115. parent))
  116. (check-parent-type 'retrofit-record-type! name parent uid sealed? opaque? fields)
  117. (let ((opaque? (if parent
  118. (or (record-type-opaque? parent)
  119. opaque?)
  120. opaque?))
  121. (field-specs (map parse-field-spec (vector->list fields))))
  122. (record-record-type-data! rtd
  123. (make-record-type-data uid sealed? opaque? field-specs
  124. (not (exists field-spec-mutable? field-specs)))))))
  125. (define (record-type-descriptor? thing)
  126. (and (record-type? thing)
  127. (record-type-data? (record-type-data thing))))
  128. (define (ensure-rtd who thing)
  129. (if (not (record-type-descriptor? thing))
  130. (assertion-violation who "not a record-type descriptor" thing)))
  131. (define (parse-field-spec spec)
  132. (apply (lambda (mutability name)
  133. (make-field-spec
  134. (case mutability
  135. ((mutable) #t)
  136. ((immutable) #f)
  137. (else
  138. (assertion-violation 'parse-field-spec
  139. "field spec with invalid mutability specification" spec)))
  140. name))
  141. spec))
  142. (define (record? thing)
  143. (and (primitive:record? thing)
  144. (let ((rtd (primitive:record-type thing)))
  145. (and (record-type-descriptor? rtd)
  146. (not (record-type-opaque? rtd))))))
  147. (define (record-rtd rec)
  148. (primitive:record-type rec))
  149. ; Constructing constructors
  150. (define-record-type :record-constructor-descriptor
  151. (really-make-record-constructor-descriptor rtd protocol custom-protocol? previous)
  152. (rtd record-constructor-descriptor-rtd)
  153. (protocol record-constructor-descriptor-protocol)
  154. (custom-protocol? record-constructor-descriptor-custom-protocol?)
  155. (previous record-constructor-descriptor-previous))
  156. (define (make-record-constructor-descriptor rtd previous protocol)
  157. (let ((parent (record-type-parent rtd)))
  158. (if (and previous (not parent))
  159. (assertion-violation 'make-record-constructor-descriptor
  160. "mismatch between rtd and constructor descriptor" rtd previous))
  161. (if (and previous
  162. (not protocol)
  163. (record-constructor-descriptor-custom-protocol? previous))
  164. (assertion-violation 'make-record-constructor-descriptor
  165. "default protocol requested when parent constructor descriptor has custom one"
  166. protocol previous))
  167. (let ((custom-protocol? (and protocol #t))
  168. (protocol (or protocol (default-protocol rtd)))
  169. (previous
  170. (if (or previous
  171. (not parent))
  172. previous
  173. (make-record-constructor-descriptor parent #f #f))))
  174. (really-make-record-constructor-descriptor rtd protocol custom-protocol? previous))))
  175. (define (default-protocol rtd)
  176. (let ((parent (record-type-parent rtd)))
  177. (if (not parent)
  178. (lambda (p)
  179. (lambda field-values
  180. (apply p field-values)))
  181. (let ((parent-field-count (record-type-size parent)))
  182. (lambda (p)
  183. (lambda all-field-values
  184. (call-with-values
  185. (lambda () (split-at all-field-values parent-field-count))
  186. (lambda (parent-field-values this-field-values)
  187. (apply (apply p parent-field-values) this-field-values)))))))))
  188. ; from SRFI 1
  189. (define (split-at lis i)
  190. (let loop ((i i)
  191. (lis lis)
  192. (rev '()))
  193. (if (zero? i)
  194. (values (reverse rev) lis)
  195. (loop (- i 1) (cdr lis) (cons (car lis) rev)))))
  196. ; A "seeder" is the procedure passed to the protocol, used to seed the
  197. ; initial field values.
  198. (define (make-make-seeder real-rtd for-desc)
  199. (let recur ((for-desc for-desc))
  200. (let* ((for-rtd (record-constructor-descriptor-rtd for-desc))
  201. (for-rtd-field-count (length (record-type-field-specs for-rtd))))
  202. (cond
  203. ((record-constructor-descriptor-previous for-desc)
  204. => (lambda (parent-desc)
  205. (let ((parent-protocol (record-constructor-descriptor-protocol parent-desc))
  206. (parent-make-seeder (recur parent-desc)))
  207. (lambda extension-field-values
  208. (lambda parent-protocol-args
  209. (lambda for-rtd-field-values
  210. (if (not (= (length for-rtd-field-values) for-rtd-field-count))
  211. (assertion-violation 'record-constructor
  212. "wrong number of arguments to record constructor"
  213. for-rtd for-rtd-field-values))
  214. (apply (parent-protocol
  215. (apply parent-make-seeder
  216. (append for-rtd-field-values extension-field-values)))
  217. parent-protocol-args)))))))
  218. (else
  219. (let-syntax ((construct-with-wrap
  220. (syntax-rules ()
  221. ((construct-with-wrap ?wrap)
  222. (lambda extension-field-values
  223. (lambda for-rtd-field-values
  224. (if (not (= (length for-rtd-field-values) for-rtd-field-count))
  225. (assertion-violation 'record-constructor
  226. "wrong number of arguments to record constructor"
  227. for-rtd for-rtd-field-values))
  228. (?wrap
  229. (apply record real-rtd
  230. (append for-rtd-field-values extension-field-values)))))))))
  231. (if (record-type-immutable? real-rtd)
  232. (construct-with-wrap (lambda (r)
  233. (make-immutable! r)
  234. r))
  235. (construct-with-wrap values))))))))
  236. ; needs optimization
  237. (define (record rtd . field-vals)
  238. (let ((r (primitive:make-record (+ 1 (length field-vals)) (unspecific))))
  239. (primitive:record-set! r 0 rtd)
  240. (let loop ((i 1)
  241. (field-vals field-vals))
  242. (if (null? field-vals)
  243. r
  244. (begin
  245. (primitive:record-set! r i (car field-vals))
  246. (loop (+ 1 i) (cdr field-vals)))))))
  247. (define (record-constructor desc)
  248. (let ((rtd (record-constructor-descriptor-rtd desc)))
  249. (if (record-constructor-descriptor-custom-protocol? desc) ; +++
  250. ((record-constructor-descriptor-protocol desc)
  251. ((make-make-seeder rtd desc)))
  252. (let ((construct (record-standard-constructor rtd)))
  253. (if (record-type-immutable? rtd)
  254. (lambda args
  255. (let ((r (apply construct args)))
  256. (make-immutable! r)
  257. r))
  258. construct)))))
  259. (define (record-with-rtd? obj rtd)
  260. (and (primitive:record? obj)
  261. (record-type<=? (primitive:record-type obj) rtd)))
  262. (define (record-accessor rtd field-id)
  263. (let ((index (+ 1 (field-id-index rtd field-id))))
  264. (lambda (thing)
  265. (if (not (record-with-rtd? thing rtd))
  266. (assertion-violation 'record-accessor "not a record of record type" thing rtd))
  267. (primitive:record-ref thing index))))
  268. (define (record-mutator rtd field-id)
  269. (if (not (record-field-mutable? rtd field-id))
  270. (assertion-violation 'record-mutator
  271. "record-mutator called on immutable field" rtd field-id))
  272. (let ((index (+ 1 (field-id-index rtd field-id))))
  273. (lambda (thing val)
  274. (if (not (record-with-rtd? thing rtd))
  275. (assertion-violation 'record-mutator "not a record of record type" thing rtd))
  276. (primitive:record-set! thing index val))))
  277. ; A FIELD-ID is an index, which refers to a field in RTD itself.
  278. (define (field-id-index rtd field-id)
  279. (+ (record-type-parent-size rtd)
  280. field-id))
  281. (define (record-field-mutable? rtd field-id)
  282. (field-spec-mutable? (list-ref (record-type-field-specs rtd) field-id)))
  283. (define (record-type-parent-size rt)
  284. (cond
  285. ((record-type-parent rt)
  286. => record-type-size)
  287. (else 0)))
  288. ; Initialization
  289. (set-nongenerative-record-types-table! (make-symbol-table))