record.scm 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. ;;;; Records
  3. ; Every record in the image is assumed to be made either by MAKE-RECORD-TYPE
  4. ; or by a procedure returned by record-constructor. A record-type is a
  5. ; record that describes a type of record. At the end of the file we create
  6. ; a record type that describes record types.
  7. ; We number the record types for debugging purposes.
  8. (define *record-type-uid* -1)
  9. ; This is the record type that describes record types. It is set a the end
  10. ; of the file. Its first slot points to itself.
  11. (define *record-type* #f)
  12. ; Make a record type from a name, used for printing and debugging, and
  13. ; a list of field names.
  14. ;
  15. ; The VM references both the record type and the resumer, so their offsets
  16. ; should not be changed.
  17. (define (make-record-type name field-names)
  18. (set! *record-type-uid* (+ *record-type-uid* 1))
  19. (let ((r (make-record 7 (unspecific))))
  20. (record-set! r 0 *record-type*)
  21. (record-set! r 1 default-record-resumer)
  22. (record-set! r 2 *record-type-uid*)
  23. (record-set! r 3 name)
  24. (record-set! r 4 field-names)
  25. (record-set! r 5 (length field-names))
  26. (record-set! r 6 (make-default-record-discloser name))
  27. r))
  28. (define (record-type? obj)
  29. (and (record? obj)
  30. (eq? (record-type obj) *record-type*)))
  31. ; The various fields in a record type.
  32. (define (record-type-resumer rt) (record-ref rt 1))
  33. (define (set-record-type-resumer! rt r) (record-set! rt 1 r))
  34. (define (record-type-uid rt) (record-ref rt 2))
  35. (define (record-type-name rt) (record-ref rt 3))
  36. (define (record-type-field-names rt) (record-ref rt 4))
  37. (define (record-type-number-of-fields rt) (record-ref rt 5))
  38. (define (record-type-discloser rt) (record-ref rt 6))
  39. (define (set-record-type-discloser! rt d) (record-set! rt 6 d))
  40. ; This is a hack; it is read by the script that makes c/scheme48.h.
  41. (define record-type-fields
  42. '(resumer uid name field-names number-of-fields discloser))
  43. ;----------------
  44. ; Given a record type and the name of a field, return the field's index.
  45. (define (record-field-index rt name)
  46. (let loop ((names (record-type-field-names rt))
  47. (i 1))
  48. (cond ((null? names)
  49. (error "unknown field"
  50. (record-type-name rt)
  51. name))
  52. ((eq? name (car names))
  53. i)
  54. (else
  55. (loop (cdr names) (+ i 1))))))
  56. ; Return procedure for contstruction records of type RT. NAMES is a list of
  57. ; field names which the constructor will take as arguments. Other fields are
  58. ; uninitialized.
  59. (define (record-constructor rt names)
  60. (let ((indexes (map (lambda (name)
  61. (record-field-index rt name))
  62. names))
  63. (size (+ 1 (record-type-number-of-fields rt))))
  64. (lambda args
  65. (let ((r (make-record size (unspecific))))
  66. (record-set! r 0 rt)
  67. (let loop ((is indexes) (as args))
  68. (if (null? as)
  69. (if (null? is)
  70. r
  71. (error "too few arguments to record constructor"
  72. rt names args))
  73. (if (null? is)
  74. (error "too many arguments to record constructor"
  75. rt names args)
  76. (begin (record-set! r (car is) (car as))
  77. (loop (cdr is) (cdr as))))))))))
  78. ; Making accessors, modifiers, and predicates for record types.
  79. (define (record-accessor rt name)
  80. (let ((index (record-field-index rt name))
  81. (error-cruft `(record-accessor ,rt ',name)))
  82. (lambda (r)
  83. (if (eq? (record-type r) rt)
  84. (record-ref r index)
  85. (call-error "invalid record access" error-cruft r)))))
  86. (define (record-modifier rt name)
  87. (let ((index (record-field-index rt name))
  88. (error-cruft `(record-modifier ,rt ',name)))
  89. (lambda (r x)
  90. (if (eq? (record-type r) rt)
  91. (record-set! r index x)
  92. (call-error "invalid record modification" error-cruft r x)))))
  93. (define (record-predicate rt)
  94. (lambda (x)
  95. (and (record? x)
  96. (eq? (record-type x) rt))))
  97. ;----------------
  98. ; A discloser is a procedure that takes a record of a particular type and
  99. ; returns a list whose head is a string or symbol and whose tail is other
  100. ; stuff.
  101. ;
  102. ; Set the discloser for record type RT.
  103. (define (define-record-discloser rt proc)
  104. (if (and (record-type? rt)
  105. (procedure? proc))
  106. (set-record-type-discloser! rt proc)
  107. (call-error "invalid argument" define-record-discloser rt proc)))
  108. ; By default we just return the name of the record type.
  109. (define (make-default-record-discloser record-type-name)
  110. (lambda (r)
  111. (list record-type-name)))
  112. ; DISCLOSE-RECORD calls the record's discloser procedure to obtain a list.
  113. (define (disclose-record r)
  114. (if (record? r)
  115. (let ((rt (record-type r)))
  116. (if (record-type? rt)
  117. ((record-type-discloser rt) r)
  118. #f))
  119. #f))
  120. ;----------------
  121. ; A resumer is a procedure that the VM calls on all records of a given
  122. ; type on startup.
  123. ;
  124. ; A resumer may be:
  125. ; #t -> do nothing on startup.
  126. ; #f -> records of this type do not survive a dump/resume; in images they
  127. ; are replaced by their first slot (so we make sure they have one)
  128. ; a one-argument procedure -> pass the record to this procedure
  129. ;
  130. ; Resumers are primarily intended for use by external code which keeps
  131. ; fields in records which do not survive a dump under their own power.
  132. ; For example, a record may contain a reference to a OS-dependent value.
  133. ;
  134. ; Resumers are called by the VM on startup.
  135. (define (define-record-resumer rt resumer)
  136. (if (and (record-type? rt)
  137. (or (eq? #t resumer)
  138. (and (eq? #f resumer)
  139. (< 0 (record-type-number-of-fields rt)))
  140. (procedure? resumer)))
  141. (set-record-type-resumer! rt resumer)
  142. (call-error "invalid argument" define-record-resumer rt resumer)))
  143. ; By default we leave records alone.
  144. (define default-record-resumer
  145. #t)
  146. (define (initialize-records! resumer-records)
  147. (if (vector? resumer-records)
  148. (do ((i 0 (+ i 1)))
  149. ((= i (vector-length resumer-records)))
  150. (resume-record (vector-ref resumer-records i)))))
  151. (define (resume-record record)
  152. ((record-type-resumer (record-type record))
  153. record))
  154. ;----------------
  155. ; Initializing *RECORD-TYPE* and making a type.
  156. (set! *record-type*
  157. (make-record-type 'record-type record-type-fields))
  158. (record-set! *record-type* 0 *record-type*)
  159. (define :record-type *record-type*)
  160. (define-record-discloser :record-type
  161. (lambda (rt)
  162. (list 'record-type
  163. (record-type-uid rt)
  164. (record-type-name rt))))