record-procedural.scm 12 KB

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