record-syntactic.scm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Mike Sperber
  3. (define-syntax define-record-type
  4. (let ((uid-count 0))
  5. (lambda (e r c)
  6. ;; returns clause or #f
  7. (define (search-clause keyword clauses)
  8. (let loop ((clauses clauses))
  9. (cond
  10. ((null? clauses)
  11. #f)
  12. ((c keyword (caar clauses))
  13. (let ((clause (car clauses)))
  14. ;; rudimentary checks
  15. (cond
  16. ((not (list? clause))
  17. (syntax-violation 'define-record-type "invalid clause" e clause))
  18. ((search-clause keyword (cdr clauses))
  19. => (lambda (duplicate)
  20. (syntax-violation 'define-record-type "duplicate clause" e clause)))
  21. (else
  22. clause))))
  23. (else (loop (cdr clauses))))))
  24. (define (clause-value clause value-ok?)
  25. (cond
  26. ((not clause) #f)
  27. ((not (= (length clause) 2))
  28. (syntax-violation 'define-record-type "invalid clause" clause))
  29. ((not (value-ok? (cadr clause)))
  30. (syntax-violation 'define-record-type "invalid clause value" clause))
  31. (else
  32. (cadr clause))))
  33. (define s->s symbol->string)
  34. (define s-conc (lambda args (string->symbol (apply string-append args))))
  35. (define %mutable (r 'mutable))
  36. (define %immutable (r 'immutable))
  37. ;; returns a field desc; each desc is a three-element list:
  38. ;; field-name accessor-name (maybe mutator-name)
  39. (define (parse-field-spec record-name-string field-spec)
  40. (cond
  41. ((symbol? field-spec)
  42. (list field-spec
  43. (s-conc record-name-string "-" (s->s field-spec))
  44. #f))
  45. ((and (pair? field-spec)
  46. (pair? (cdr field-spec)))
  47. (let ((tag (car field-spec))
  48. (field (cadr field-spec))
  49. (size (length field-spec)))
  50. (cond
  51. ((c tag %immutable)
  52. (case size
  53. ((2)
  54. (list field
  55. (s-conc record-name-string "-" (s->s field))
  56. #f))
  57. ((3)
  58. (list field
  59. (caddr field-spec)
  60. #f))
  61. (else
  62. (syntax-violation 'define-record-type "invalid field spec" e field-spec))))
  63. ((c tag %mutable)
  64. (case size
  65. ((2)
  66. (list field
  67. (s-conc record-name-string "-" (s->s field))
  68. (s-conc record-name-string "-" (s->s field) "-set!")))
  69. ((4)
  70. (list field
  71. (caddr field-spec)
  72. (cadddr field-spec)))
  73. (else
  74. (syntax-violation 'define-record-type "invalid field spec" e field-spec))))
  75. (else
  76. (syntax-violation 'define-record-type "invalid field spec" e field-spec)))))
  77. (else
  78. (syntax-violation 'define-record-type "invalid field spec" e field-spec))))
  79. (let ((name-spec (cadr e))
  80. (clauses (cddr e)))
  81. (call-with-values
  82. (lambda ()
  83. (cond ((symbol? name-spec) ; probably barfs on generated name
  84. (values name-spec
  85. (s-conc "make-" (s->s name-spec))
  86. (s-conc (s->s name-spec) "?")))
  87. ((or (not (list? name-spec))
  88. (not (= 3 (length name-spec))))
  89. (syntax-violation 'define-record-type "invalid name spec" e name-spec))
  90. (else
  91. (apply values name-spec))))
  92. (lambda (record-name constructor-name predicate-name)
  93. (let ((record-name-string (s->s record-name))
  94. (field-specs
  95. (cond
  96. ((search-clause (r 'fields) clauses)
  97. => cdr)
  98. (else '())))
  99. (parent (clause-value (search-clause (r 'parent) clauses) symbol?)) ; probably barfs on generated names
  100. (protocol (clause-value (search-clause (r 'protocol) clauses) values))
  101. (sealed? (clause-value (search-clause (r 'sealed) clauses) boolean?))
  102. (opaque? (clause-value (search-clause (r 'opaque) clauses) boolean?))
  103. (nongenerative-clause (search-clause (r 'nongenerative) clauses))
  104. (parent-rtd-clause (search-clause (r 'parent-rtd) clauses)))
  105. (if (and parent parent-rtd-clause)
  106. (syntax-violation 'define-record-type "can't have both `parent' and `parent-rtd' clauses"
  107. e))
  108. (if (and parent-rtd-clause
  109. (or (not (list? parent-rtd-clause))
  110. (not (= 3 (length parent-rtd-clause)))))
  111. (syntax-violation 'define-record-type "invalid `parent-rtd' clause" e parent-rtd-clause))
  112. (let ((field-descs
  113. ;; cons field index onto descs
  114. (let loop ((i 0) (field-specs field-specs) (res '()))
  115. (if (null? field-specs)
  116. (reverse res)
  117. (loop (+ 1 i)
  118. (cdr field-specs)
  119. (cons (cons i (parse-field-spec record-name-string (car field-specs)))
  120. res)))))
  121. (nongenerative-uid
  122. (and nongenerative-clause
  123. (let ((size (length nongenerative-clause)))
  124. (cond
  125. ((= size 1)
  126. (set! uid-count (+ 1 uid-count)) ; #### not enough in the presence of separate compilation
  127. (s-conc "record-type-" (number->string uid-count)))
  128. ((= size 2)
  129. (cadr nongenerative-clause))
  130. (else
  131. (syntax-violation 'define-record-type "invalid `nongenerative' clause" e nongenerative-clause))))))
  132. (parent-rtd
  133. (cond
  134. (parent
  135. `(,(r 'record-type-descriptor) ,parent))
  136. (parent-rtd-clause => cadr)
  137. (else #f)))
  138. (parent-cd
  139. (cond
  140. (parent
  141. `(,(r 'record-constructor-descriptor) ,parent))
  142. (parent-rtd-clause => caddr)
  143. (else #f)))
  144. (rtd-name (r 'record-rtd))
  145. (cd-name (r 'cd))
  146. (%define (r 'define)))
  147. `(,(r 'begin)
  148. (,%define ,rtd-name
  149. (,(r 'make-record-type-descriptor)
  150. ',record-name
  151. ,parent-rtd
  152. ',nongenerative-uid
  153. ,sealed?
  154. ,opaque?
  155. ',(list->vector
  156. (map
  157. (lambda (desc)
  158. (apply (lambda (index name accessor mutator)
  159. (list (if mutator
  160. 'mutable
  161. 'immutable)
  162. name))
  163. desc))
  164. field-descs))))
  165. (,%define ,cd-name
  166. (,(r 'make-record-constructor-descriptor)
  167. ,rtd-name
  168. ,parent-cd
  169. ,protocol))
  170. ,(cond
  171. (parent
  172. `(,parent
  173. ,(r 'dispatch)
  174. (,record-name
  175. ,constructor-name ,predicate-name
  176. ,rtd-name ,cd-name
  177. ,parent ,protocol ,sealed? ,opaque? ,nongenerative-uid ,parent-rtd ,parent-cd
  178. ,field-descs)
  179. ,(r 'define-known-record-type-helper)
  180. ,(r 'define-unknown-record-type-helper)))
  181. (parent-rtd-clause
  182. `(,(r 'define-unknown-record-type-helper)
  183. ,record-name
  184. ,constructor-name ,predicate-name
  185. ,rtd-name ,cd-name
  186. ,parent ,protocol ,sealed? ,opaque? ,nongenerative-uid ,parent-rtd ,parent-cd
  187. ,field-descs))
  188. (else
  189. `(,(r 'define-known-record-type-helper)
  190. 0 #t
  191. ,record-name
  192. ,constructor-name ,predicate-name
  193. ,rtd-name ,cd-name
  194. ,parent ,protocol ,sealed? ,opaque? ,nongenerative-uid ,parent-rtd ,parent-cd
  195. ,field-descs))))))))))))
  196. ; fallback: the supertype isn't completely known statically
  197. (define-syntax define-unknown-record-type-helper
  198. (lambda (e r c)
  199. (define %begin (r 'begin))
  200. (define %define (r 'define))
  201. (define %record-accessor (r 'record-accessor))
  202. (define %record-mutator (r 'record-mutator))
  203. (define %loophole (r 'loophole))
  204. (define %:value (r ':value))
  205. (define %:unspecific (r ':value))
  206. (define %proc (r 'proc))
  207. (apply
  208. (lambda (_ record-name constructor-name predicate-name rtd-name cd-name
  209. parent protocol sealed? opaque? nongenerative-uid parent-rtd parent-cd
  210. field-descs)
  211. `(,%begin
  212. (,(r 'define-unknown-record-type-name) ,record-name ,rtd-name ,cd-name)
  213. (,%define ,constructor-name
  214. (,(r 'record-constructor) ,cd-name))
  215. (,%define ,predicate-name (,(r 'record-predicate) ,rtd-name))
  216. ,@(map (lambda (desc)
  217. (apply (lambda (index name accessor mutator)
  218. (let ((acc
  219. `(,%define ,accessor
  220. (,%loophole (,%proc (,rtd-name) ,%:value)
  221. (,%record-accessor ,rtd-name ,index)))))
  222. (if mutator
  223. `(,%begin
  224. ,acc
  225. (,%define ,mutator
  226. (,%loophole (,%proc (,rtd-name ,%:value) ,%:unspecific)
  227. (,%record-mutator ,rtd-name ,index))))
  228. acc)))
  229. desc))
  230. field-descs)))
  231. e)))
  232. ; This knows about the implementation of records and creates
  233. ; constructor, accessors, mutators, etc. directly instead of calling
  234. ; the procedures from r6rs-records-procedural. This is done to allow
  235. ; the optional auto-inlining optimizer to inline the accessors,
  236. ; mutators, etc.
  237. (define-syntax define-known-record-type-helper
  238. (lambda (e r c)
  239. (define %begin (r 'begin))
  240. (define %define (r 'define))
  241. (define %record (r 'record))
  242. (define %checked-record-ref (r 'checked-record-ref))
  243. (define %checked-record-set! (r 'checked-record-set!))
  244. (define %r (r 'r))
  245. (define %val (r 'val))
  246. (define %loophole (r 'loophole))
  247. (define %:value (r ':value))
  248. (define %:unspecific (r ':value))
  249. (define %proc (r 'proc))
  250. (define (build-args count)
  251. (let loop ((i count)
  252. (args '()))
  253. (if (zero? i)
  254. args
  255. (loop (- i 1)
  256. (cons (r (string->symbol (string-append "arg-" (number->string i))))
  257. args)))))
  258. (apply
  259. (lambda (_ parent-field-count parent-default-constructor?
  260. record-name constructor-name predicate-name rtd-name cd-name
  261. parent protocol sealed? opaque? nongenerative-uid parent-rtd parent-cd
  262. field-descs)
  263. (let ((field-count (+ parent-field-count (length field-descs)))
  264. (default-constructor? (and parent-default-constructor? (not protocol))))
  265. `(,%begin
  266. (,(r 'define-known-record-type-name) ,record-name ,rtd-name ,cd-name
  267. ,field-count ,default-constructor?)
  268. ,(if default-constructor?
  269. (let ((args (build-args field-count)))
  270. `(,%define (,constructor-name . ,args)
  271. (,%loophole (,%proc ,(map (lambda (_) %:value) args) ,rtd-name)
  272. (,%record ,rtd-name . ,args))))
  273. `(,%define ,constructor-name (,(r 'record-constructor) ,cd-name)))
  274. (,%define ,predicate-name (,(r 'record-predicate) ,rtd-name))
  275. ,@(map (lambda (desc)
  276. (apply (lambda (index name accessor mutator)
  277. (let* ((real-index (+ 1 parent-field-count index))
  278. (acc
  279. `(,%define (,accessor ,%r)
  280. (,%loophole (,%proc (,rtd-name) ,%:value)
  281. (,%checked-record-ref ,%r ,rtd-name
  282. ,real-index)))))
  283. (if mutator
  284. `(,%begin
  285. ,acc
  286. (,%define (,mutator ,%r ,%val)
  287. (,%loophole (,%proc (,rtd-name ,%:value) ,%:unspecific)
  288. (,%checked-record-set! ,%r ,rtd-name ,real-index ,%val))))
  289. acc)))
  290. desc))
  291. field-descs))))
  292. e)))
  293. (define-syntax define-known-record-type-name
  294. (syntax-rules ()
  295. ((define-known-record-type-name ?name ?rtd ?constructor-descriptor ?field-count ?default-constructor?)
  296. (define-syntax ?name
  297. (syntax-rules (descriptor constructor-descriptor dispatch)
  298. ((?name descriptor) ?rtd)
  299. ((?name constructor-descriptor) ?constructor-descriptor)
  300. ((?name dispatch ?args ?known ?unknown) (?known ?field-count ?default-constructor? . ?args)))))))
  301. (define-syntax define-unknown-record-type-name
  302. (syntax-rules ()
  303. ((define-unknown-record-type-name ?name ?rtd ?constructor-descriptor)
  304. (define-syntax ?name
  305. (syntax-rules (descriptor constructor-descriptor dispatch)
  306. ((?name descriptor) ?rtd)
  307. ((?name constructor-descriptor) ?constructor-descriptor)
  308. ((?name dispatch ?args ?known ?unknown) (?unknown . ?args)))))))
  309. ; Retrofitting RTS record types to R6RS record types.
  310. ; For now, we do default constructors only.
  311. ; (define-retrofitted-record-type r6rs-type rts-type (mutable field1) (immutable field2) ...)
  312. ; (define-retrofitted-record-type (r6rs-type r6rs-base-type) rts-type (mutable field1) ...)
  313. (define-syntax define-retrofitted-record-type
  314. (lambda (e r c)
  315. (cons (r 'define-retrofitted-record-type-helper)
  316. (cons (length (list-tail e 6))
  317. (cdr e)))))
  318. (define-syntax define-retrofitted-record-type-helper
  319. (syntax-rules ()
  320. ((define-retrofitted-record-type-helper ?field-count
  321. (?r6rs-type ?r6rs-parent-type) ?rts-type ?uid ?sealed? ?opaque? ?field-spec1 ...)
  322. (begin
  323. (retrofit-record-type! ?rts-type ?uid ?sealed? ?opaque? '#(?field-spec1 ...))
  324. (define cd (make-record-constructor-descriptor ?rts-type #f #f))
  325. (?r6rs-parent-type dispatch (?r6rs-type ?rts-type cd ?field-count)
  326. define-known-retrofitted-record-type-helper
  327. define-unknown-retrofitted-record-type-helper)))
  328. ((define-retrofitted-record-type-helper ?field-count
  329. ?r6rs-type ?rts-type ?uid ?sealed? ?opaque? ?field-spec1 ...)
  330. (begin
  331. (retrofit-record-type! ?rts-type ?uid ?sealed? ?opaque? '#(?field-spec1 ...))
  332. (define cd (make-record-constructor-descriptor ?rts-type #f #f))
  333. (define-known-record-type-name ?r6rs-type ?rts-type cd ?field-count #t)))))
  334. (define-syntax define-known-retrofitted-record-type-helper
  335. (lambda (e r c)
  336. (apply
  337. (lambda (_ parent-field-count parent-default-constructor? r6rs-type rts-type cd field-count)
  338. `(,(r 'define-known-record-type-name)
  339. ,r6rs-type ,rts-type ,cd ,(+ parent-field-count field-count) ,parent-default-constructor?))
  340. e)))
  341. (define-syntax define-unknown-retrofitted-record-type-helper
  342. (lambda (e r c)
  343. (apply
  344. (lambda (_ r6rs-type rts-type cd field-count)
  345. `(,(r 'define-unknown-record-type-name) ,r6rs-type ,rts-type ,cd))
  346. e)))
  347. (define-syntax record-type-descriptor
  348. (syntax-rules ()
  349. ((record-type-descriptor ?type)
  350. (?type descriptor))))
  351. (define-syntax record-constructor-descriptor
  352. (syntax-rules ()
  353. ((record-constructor-descriptor ?type)
  354. (?type constructor-descriptor))))