; Copyright (c) 1993, 1994 Richard Kelsey and Jonathan Rees. See file COPYING. ; This is file record.scm. ;;;; Records ; Every record in the image is assumed to be made either by ; make-record-type or by a procedure returned by record-constructor. (define (record-type r) (record-ref r 0)) (define *record-type-uid* -1) (define *record-type* #f) (define (make-record-type name field-names) (set! *record-type-uid* (+ *record-type-uid* 1)) (let ((r (make-record 5 (unspecific)))) (record-set! r 0 *record-type*) (record-set! r 1 *record-type-uid*) (record-set! r 2 name) (record-set! r 3 field-names) (record-set! r 4 default-record-discloser) r)) (define (record-type? obj) (and (record? obj) (eq? (record-type obj) *record-type*))) (define (record-type-uid rt) (record-ref rt 1)) (define (record-type-name rt) (record-ref rt 2)) (define (record-type-field-names rt) (record-ref rt 3)) (define (record-type-discloser rt) (record-ref rt 4)) (define (record-field-index rt name) (let loop ((names (record-type-field-names rt)) (i 1)) (cond ((null? names) (error "unknown field" (record-type-name rt) name)) ((eq? name (car names)) i) (else (loop (cdr names) (+ i 1)))))) (define (record-constructor rt names) (let ((indexes (map (lambda (name) (record-field-index rt name)) names)) (size (+ 1 (length (record-type-field-names rt))))) (lambda args (let ((r (make-record size (unspecific)))) (record-set! r 0 rt) (let loop ((is indexes) (as args)) (if (null? as) (if (null? is) r (error "too few arguments to record constructor" rt names args)) (if (null? is) (error "too many arguments to record constructor" rt names args) (begin (record-set! r (car is) (car as)) (loop (cdr is) (cdr as)))))))))) (define (record-accessor rt name) (let ((index (record-field-index rt name)) (error-cruft `(record-accessor ,rt ',name))) (lambda (r) (if (eq? (record-type r) rt) (record-ref r index) (call-error "invalid record access" error-cruft r))))) (define (record-modifier rt name) (let ((index (record-field-index rt name)) (error-cruft `(record-modifier ,rt ',name))) (lambda (r x) (if (eq? (record-type r) rt) (record-set! r index x) (call-error "invalid record modification" error-cruft r x))))) (define (record-predicate rt) (lambda (x) (and (record? x) (eq? (record-type x) rt)))) ; disclose-record calls the record's discloser procedure to obtain a list ; whose head is a string and whose tail is other stuff. (define (define-record-discloser rt proc) (if (and (record-type? rt) (procedure? proc)) (record-set! rt 4 proc) (call-error "invalid argument" define-record-discloser rt proc))) (define (disclose-record r) (if (record? r) (let ((rt (record-type r))) (if (record-type? rt) (or ((record-type-discloser rt) r) (list (record-type-name rt))) #f)) ;Not one of ours. #f)) (define default-record-discloser (lambda (r) #f)) ; Patch (set! *record-type* (make-record-type 'record-type '(uid name field-names discloser))) (record-set! *record-type* 0 *record-type*) (define :record-type *record-type*) (define-record-discloser :record-type (lambda (rt) (list 'record-type (record-type-uid rt) (record-type-name rt))))