(library (slib record)
(export make-record-type record-constructor
record-predicate record-accessor record-modifier)
(import (rnrs)
(ubik define-values)
(primitives make-struct-type
make-struct-field-accessor
make-struct-field-mutator
void andmap))
(define-values (make-rtd rtd-name rtd-field-names
rtd-mz-type rtd-mz-cons rtd-mz-pred rtd-mz-ref rtd-mz-set)
(call-with-values
(lambda ()
(make-struct-type 'record-descriptor #f 7 0))
(lambda (record-type constructor record-type? accessor mutator)
(values constructor
(make-struct-field-accessor accessor 0 'rtd-name)
(make-struct-field-accessor accessor 1 'rtd-field-names)
(make-struct-field-accessor accessor 2 'rtd-mz-type)
(make-struct-field-accessor accessor 3 'rtd-mz-cons)
(make-struct-field-accessor accessor 4 'rtd-mz-pred)
(make-struct-field-accessor accessor 5 'rtd-mz-ref)
(make-struct-field-accessor accessor 6 'rtd-mz-set)))))
(define make-record-type
(lambda (type-name field-names)
(call-with-values
(lambda ()
(make-struct-type (string->symbol type-name) #f (length field-names) 0))
(lambda (mz-type mz-cons mz-pred mz-ref mz-set)
(make-rtd type-name field-names
mz-type mz-cons mz-pred mz-ref mz-set)))))
(define record-constructor
(case-lambda
((rtd) (rtd-mz-cons rtd))
((rtd field-names)
(assert (distinct? field-names))
(let ((rtd-field-names (rtd-field-names rtd)))
(assert (andmap
(lambda (s) (memq s rtd-field-names))
field-names))
(lambda args
(assert (= (length args) (length field-names)))
(let* ((r (apply (rtd-mz-cons rtd)
(map (lambda (_) (void)) rtd-field-names)))
(set-r (lambda (i v)
((rtd-mz-set rtd) r i v))))
(let loop ((args args) (field-names field-names))
(if (null? args)
r
(begin
(set-r (field-index rtd (car field-names))
(car args))
(loop (cdr args) (cdr field-names)))))))))))
(define record-predicate
(lambda (rtd)
(rtd-mz-pred rtd)))
(define record-accessor
(lambda (rtd field-name)
(assert (memq field-name (rtd-field-names rtd)))
(let ((ref (rtd-mz-ref rtd))
(i (field-index rtd field-name)))
(lambda (x)
(ref x i)))))
(define record-modifier
(lambda (rtd field-name)
(assert (memq field-name (rtd-field-names rtd)))
(let ((set (rtd-mz-set rtd))
(i (field-index rtd field-name)))
(lambda (x v)
(set x i v)))))
(define (field-index rtd field-name)
(- (length (rtd-field-names rtd))
(length (memq field-name
(rtd-field-names rtd)))))
(define (distinct? ls) (or (null? ls)
(and (not (memq (car ls) (cdr ls)))
(distinct? (cdr ls)))))
)