records.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. (module (arguile data records)
  2. #:export (define-record-type record record? record-type?))
  3. (use (srfi srfi-1)
  4. (system base ck))
  5. ;; 0: type-name, 1: fields, 2: constructor
  6. (define record-type-vtable
  7. (let ((s (make-app-vtable (string-append standard-vtable-fields "prprpw")
  8. (lambda (s p)
  9. (display "#<data-type " p)
  10. (display (record-type-name s) p)
  11. (display ">" p)))))
  12. (set-struct-vtable-name! s 'data-type)
  13. s))
  14. (define (record-type-name obj)
  15. (if (record-type? obj)
  16. (struct-ref obj vtable-offset-user)
  17. (error 'not-a-record-type obj)))
  18. (define (record-type-fields obj)
  19. (if (record-type? obj)
  20. (struct-ref obj (+ 1 vtable-offset-user))
  21. (error 'not-a-record-type obj)))
  22. (define* (record-constructor rtd #:optional field-names)
  23. (if (not field-names)
  24. (struct-ref rtd (+ 2 vtable-offset-user))
  25. (primitive-eval
  26. `(lambda ,field-names
  27. (make-struct ',rtd 0 ,@(map (lambda (f)
  28. (if (memq f field-names)
  29. f
  30. #f))
  31. (record-type-fields rtd)))))))
  32. (define (record-predicate rtd)
  33. (lambda (obj) (and (struct? obj) (eq? rtd (struct-vtable obj)))))
  34. (define (%record-type-error rtd obj) ;; private helper
  35. (or (eq? rtd (record-type-descriptor obj))
  36. (scm-error 'wrong-type-arg "%record-type-check"
  37. "Wrong type record (want `~S'): ~S"
  38. (list (record-type-name rtd) obj)
  39. #f)))
  40. (define (record-accessor rtd field-name)
  41. (let ((pos (list-index (record-type-fields rtd) field-name)))
  42. (if (not pos)
  43. (error 'no-such-field field-name))
  44. (lambda (obj)
  45. (if (eq? (struct-vtable obj) rtd)
  46. (struct-ref obj pos)
  47. (%record-type-error rtd obj)))))
  48. (define (record-modifier rtd field-name)
  49. (let ((pos (list-index (record-type-fields rtd) field-name)))
  50. (if (not pos)
  51. (error 'no-such-field field-name))
  52. (lambda (obj val)
  53. (if (eq? (struct-vtable obj) rtd)
  54. (struct-set! obj pos val)
  55. (%record-type-error rtd obj)))))
  56. (define (record? obj)
  57. (and (struct? obj) (record-type? (struct-vtable obj))))
  58. (define (record-type-descriptor obj)
  59. (if (struct? obj)
  60. (struct-vtable obj)
  61. (error 'not-a-record obj)))
  62. (define (record-type? obj)
  63. (and (struct? obj) (eq? record-type-vtable (struct-vtable obj))))
  64. ;; Roll our own instead of using the public `define-inlinable'. This is
  65. ;; because the public one has a different `make-procedure-name', so
  66. ;; using it would require users to recompile code that uses SRFI-9. See
  67. ;; <http://lists.gnu.org/archive/html/guile-devel/2011-04/msg00111.html>.
  68. ;;
  69. (define-syntax-rule (define-inlinable (name formals ...) body ...)
  70. (define-tagged-inlinable () (name formals ...) body ...))
  71. ;; 'define-tagged-inlinable' has an additional feature: it stores a map
  72. ;; of keys to values that can be retrieved at expansion time. This is
  73. ;; currently used to retrieve the rtd id, field index, and record copier
  74. ;; macro for an arbitrary getter.
  75. (define-syntax-rule (%%on-error err) err)
  76. (define %%type #f) ; a private syntax literal
  77. (define-syntax getter-type
  78. (syntax-rules (quote)
  79. ((_ s 'getter 'err)
  80. (getter (%%on-error err) %%type s))))
  81. (define %%index #f) ; a private syntax literal
  82. (define-syntax getter-index
  83. (syntax-rules (quote)
  84. ((_ s 'getter 'err)
  85. (getter (%%on-error err) %%index s))))
  86. (define %%copier #f) ; a private syntax literal
  87. (define-syntax getter-copier
  88. (syntax-rules (quote)
  89. ((_ s 'getter 'err)
  90. (getter (%%on-error err) %%copier s))))
  91. (define-syntax define-tagged-inlinable
  92. (lambda (x)
  93. (define (make-procedure-name name)
  94. (datum->syntax name
  95. (symbol-append '% (syntax->datum name)
  96. '-procedure)))
  97. (syntax-case x ()
  98. ((_ ((key value) ...) (name formals ...) body ...)
  99. (identifier? #'name)
  100. (with-syntax ((proc-name (make-procedure-name #'name))
  101. ((args ...) (generate-temporaries #'(formals ...))))
  102. #`(begin
  103. (define (proc-name formals ...)
  104. body ...)
  105. (define-syntax name
  106. (lambda (x)
  107. (syntax-case x (%%on-error key ...)
  108. ((_ (%%on-error err) key s) #'(ck s 'value)) ...
  109. ((_ args ...)
  110. #'((lambda (formals ...)
  111. body ...)
  112. args ...))
  113. ((_ a (... ...))
  114. (syntax-violation 'name "Wrong number of arguments" x))
  115. (_
  116. (identifier? x)
  117. #'proc-name))))))))))
  118. (define (default-record-printer s p)
  119. (display "#<" p)
  120. (display (record-type-name (record-type-descriptor s)) p)
  121. (let loop ((fields (cdr (record-type-fields (record-type-descriptor s))))
  122. (off 1))
  123. (cond
  124. ((not (null? fields))
  125. (display " " p)
  126. (display (car fields) p)
  127. (display ": " p)
  128. (write (struct-ref s off) p)
  129. (loop (cdr fields) (+ 1 off)))))
  130. (display ">" p))
  131. (define-syntax-rule (throw-bad-struct s who)
  132. (let ((s* s))
  133. (throw 'wrong-type-arg who
  134. "Wrong type argument: ~S" (list s*)
  135. (list s*))))
  136. (define (make-copier-id type-name)
  137. (datum->syntax type-name
  138. (symbol-append '%% (syntax->datum type-name)
  139. '-set-fields)))
  140. (define-syntax %%set-fields
  141. (lambda (x)
  142. (syntax-case x ()
  143. ((_ type-name (getter-id ...) check? s (getter expr) ...)
  144. (every identifier? #'(getter ...))
  145. (let ((copier-name (syntax->datum (make-copier-id #'type-name)))
  146. (getter+exprs #'((getter expr) ...))
  147. (nfields (length #'(getter-id ...))))
  148. (define (lookup id default-expr)
  149. (let ((results
  150. (filter (lambda (g+e)
  151. (free-identifier=? id (car g+e)))
  152. getter+exprs)))
  153. (case (length results)
  154. ((0) default-expr)
  155. ((1) (cadar results))
  156. (else (syntax-violation
  157. copier-name "duplicate getter" x id)))))
  158. (for-each (lambda (id)
  159. (or (find (lambda (getter-id)
  160. (free-identifier=? id getter-id))
  161. #'(getter-id ...))
  162. (syntax-violation
  163. copier-name "unknown getter" x id)))
  164. #'(getter ...))
  165. (with-syntax ((unsafe-expr
  166. #`(let ((new (allocate-struct type-name #,nfields)))
  167. #,@(map (lambda (getter index)
  168. #`(struct-set!
  169. new
  170. #,index
  171. #,(lookup getter
  172. #`(struct-ref s #,index))))
  173. #'(getter-id ...)
  174. (iota nfields))
  175. new)))
  176. (if (syntax->datum #'check?)
  177. #`(if (eq? (struct-vtable s) type-name)
  178. unsafe-expr
  179. (throw-bad-struct
  180. s '#,(datum->syntax #'here copier-name)))
  181. #'unsafe-expr)))))))
  182. (define-syntax %define-record-type
  183. (lambda (x)
  184. (define (field-identifiers field-specs)
  185. (map (lambda (field-spec)
  186. (syntax-case field-spec ()
  187. ((name getter) #'name)
  188. ((name getter setter) #'name)))
  189. field-specs))
  190. (define (getter-identifiers field-specs)
  191. (map (lambda (field-spec)
  192. (syntax-case field-spec ()
  193. ((name getter) #'getter)
  194. ((name getter setter) #'getter)))
  195. field-specs))
  196. (define (constructor form type-name constructor-spec field-ids)
  197. (syntax-case constructor-spec ()
  198. ((ctor field ...)
  199. (every identifier? #'(field ...))
  200. (let ((slots (map (lambda (field)
  201. (or (list-index (lambda (x)
  202. (free-identifier=? x field))
  203. field-ids)
  204. (syntax-violation
  205. (syntax-case form ()
  206. ((macro . args)
  207. (syntax->datum #'macro)))
  208. "unknown field in constructor spec"
  209. form field)))
  210. #'(field ...))))
  211. #`(define-inlinable #,constructor-spec
  212. (let ((s (allocate-struct #,type-name #,(length field-ids))))
  213. #,@(map (lambda (arg slot)
  214. #`(struct-set! s #,slot #,arg))
  215. #'(field ...) slots)
  216. s))))))
  217. (define (getters type-name getter-ids copier-id)
  218. (map (lambda (getter index)
  219. #`(define-tagged-inlinable
  220. ((%%type #,type-name)
  221. (%%index #,index)
  222. (%%copier #,copier-id))
  223. (#,getter s)
  224. (if (eq? (struct-vtable s) #,type-name)
  225. (struct-ref s #,index)
  226. (throw-bad-struct s '#,getter))))
  227. getter-ids
  228. (iota (length getter-ids))))
  229. (define (copier type-name getter-ids copier-id)
  230. #`(define-syntax-rule
  231. (#,copier-id check? s (getter expr) (... ...))
  232. (%%set-fields #,type-name #,getter-ids
  233. check? s (getter expr) (... ...))))
  234. (define (setters type-name field-specs)
  235. (filter-map (lambda (field-spec index)
  236. (syntax-case field-spec ()
  237. ((name getter) #f)
  238. ((name getter setter)
  239. #`(define-inlinable (setter s val)
  240. (if (eq? (struct-vtable s) #,type-name)
  241. (struct-set! s #,index val)
  242. (throw-bad-struct s 'setter))))))
  243. field-specs
  244. (iota (length field-specs))))
  245. (define (functional-setters copier-id field-specs)
  246. (filter-map (lambda (field-spec index)
  247. (syntax-case field-spec ()
  248. ((name getter) #f)
  249. ((name getter setter)
  250. #`(define-inlinable (setter s val)
  251. (#,copier-id #t s (getter val))))))
  252. field-specs
  253. (iota (length field-specs))))
  254. (define (record-layout immutable? count)
  255. ;; Mutability is expressed on the record level; all structs in the
  256. ;; future will be mutable.
  257. (string-concatenate (make-list count "pw")))
  258. (syntax-case x ()
  259. ((_ immutable? form type-name constructor-spec predicate-name
  260. field-spec ...)
  261. (let ()
  262. (define (syntax-error message subform)
  263. (syntax-violation (syntax-case #'form ()
  264. ((macro . args) (syntax->datum #'macro)))
  265. message #'form subform))
  266. (and (boolean? (syntax->datum #'immutable?))
  267. (or (identifier? #'type-name)
  268. (syntax-error "expected type name" #'type-name))
  269. (syntax-case #'constructor-spec ()
  270. ((ctor args ...)
  271. (every identifier? #'(ctor args ...))
  272. #t)
  273. (_ (syntax-error "invalid constructor spec"
  274. #'constructor-spec)))
  275. (or (identifier? #'predicate-name)
  276. (syntax-error "expected predicate name" #'predicate-name))
  277. (every (lambda (spec)
  278. (syntax-case spec ()
  279. ((field getter) #t)
  280. ((field getter setter) #t)
  281. (_ (syntax-error "invalid field spec" spec))))
  282. #'(field-spec ...))))
  283. (let* ((field-ids (field-identifiers #'(field-spec ...)))
  284. (getter-ids (getter-identifiers #'(field-spec ...)))
  285. (field-count (length field-ids))
  286. (immutable? (syntax->datum #'immutable?))
  287. (layout (record-layout immutable? field-count))
  288. (ctor-name (syntax-case #'constructor-spec ()
  289. ((ctor args ...) #'ctor)))
  290. (copier-id (make-copier-id #'type-name)))
  291. #`(begin
  292. #,(constructor #'form #'type-name #'constructor-spec field-ids)
  293. (define type-name
  294. (let ((rtd (make-struct/no-tail
  295. record-type-vtable
  296. '#,(datum->syntax #'here (make-struct-layout layout))
  297. default-record-printer
  298. 'type-name
  299. '#,field-ids)))
  300. (set-struct-vtable-name! rtd 'type-name)
  301. (struct-set! rtd (+ 2 vtable-offset-user) #,ctor-name)
  302. rtd))
  303. (define-inlinable (predicate-name obj)
  304. (and (struct? obj)
  305. (eq? (struct-vtable obj) type-name)))
  306. #,@(getters #'type-name getter-ids copier-id)
  307. #,(copier #'type-name getter-ids copier-id)
  308. #,@(if immutable?
  309. (functional-setters copier-id #'(field-spec ...))
  310. (setters #'type-name #'(field-spec ...))))))
  311. ((_ immutable? form . rest)
  312. (syntax-violation
  313. (syntax-case #'form ()
  314. ((macro . args) (syntax->datum #'macro)))
  315. "invalid record definition syntax"
  316. #'form)))))
  317. (define-syntax-rule (define-record-type name ctor pred fields ...)
  318. (%define-record-type #f (define-record-type name ctor pred fields ...)
  319. name ctor pred fields ...))