srfi-9.scm 13 KB

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