;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ERR5RS Records. ; ; This is a quick-and-dirty reference implementation that favors ; simplicity over quality error messages and performance. It is ; implemented using the R6RS procedural and inspection layers, ; with which it interoperates nicely. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This library breaks a circular interdependence between the ; procedural and inspection layers. (library (err5rs-helpers records rtd?) (export rtd?) (import (rnrs base) (rnrs records procedural)) (define rtd? record-type-descriptor?) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; (err5rs records inspection) ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (library (err5rs records inspection) (export record? record-rtd rtd-name rtd-parent rtd-field-names rtd-all-field-names rtd-field-mutable?) (import (rnrs base) (rnrs lists) (rnrs records inspection) (err5rs-helpers records rtd?)) ; The record? predicate is already defined by (rnrs records inspection). ; The record-rtd procedure is already defined by (rnrs records inspection). (define rtd-name record-type-name) (define rtd-parent record-type-parent) (define rtd-field-names record-type-field-names) (define (rtd-all-field-names rtd) (define (loop rtd othernames) (let ((parent (rtd-parent rtd)) (names (append (vector->list (rtd-field-names rtd)) othernames))) (if parent (loop parent names) (list->vector names)))) (loop rtd '())) (define (rtd-field-mutable? rtd0 fieldname) (define (loop rtd) (if (rtd? rtd) (let* ((names (vector->list (rtd-field-names rtd))) (probe (memq fieldname names))) (if probe (record-field-mutable? rtd (- (length names) (length probe))) (loop (rtd-parent rtd)))) (assertion-violation 'rtd-field-mutable? "illegal argument" rtd0 fieldname))) (loop rtd0)) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; (err5rs records procedural) ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (library (err5rs records procedural) (export make-rtd rtd? rtd-constructor rtd-predicate rtd-accessor rtd-mutator) (import (rnrs base) (rnrs lists) (rnrs records procedural) (err5rs records inspection)) (define (make-rtd name fieldspecs . rest) (make-record-type-descriptor name (if (null? rest) #f (car rest)) #f #f #f (vector-map (lambda (fieldspec) (if (symbol? fieldspec) (list 'mutable fieldspec) fieldspec)) fieldspecs))) (define rtd? record-type-descriptor?) (define (rtd-constructor rtd . rest) ; Computes permutation and allocates permutation buffer ; when the constructor is created, not when the constructor ; is called. More error checking is recommended. (define (make-constructor fieldspecs allnames maker) (let* ((k (length fieldspecs)) (n (length allnames)) (buffer (make-vector n 'some-unspecified-value)) (reverse-all-names (reverse allnames))) (define (position fieldname) (let ((names (memq fieldname reverse-all-names))) (assert names) (- (length names) 1))) (let ((indexes (map position fieldspecs))) ; The following can be made quite efficient by ; hand-coding it in some lower-level language, ; e.g. Larceny's mal. Even case-lambda would ; be good enough in most systems. (lambda args (assert (= (length args) k)) (for-each (lambda (arg posn) (vector-set! buffer posn arg)) args indexes) (apply maker (vector->list buffer)))))) (if (null? rest) (record-constructor (make-record-constructor-descriptor rtd #f #f)) (begin (assert (null? (cdr rest))) (make-constructor (vector->list (car rest)) (vector->list (rtd-all-field-names rtd)) (record-constructor (make-record-constructor-descriptor rtd #f #f)))))) (define rtd-predicate record-predicate) (define (rtd-accessor rtd0 fieldname) (define (loop rtd) (if (rtd? rtd) (let* ((names (vector->list (rtd-field-names rtd))) (probe (memq fieldname names))) (if probe (record-accessor rtd (- (length names) (length probe))) (loop (rtd-parent rtd)))) (assertion-violation 'rtd-accessor "illegal argument" rtd0 fieldname))) (loop rtd0)) (define (rtd-mutator rtd0 fieldname) (define (loop rtd) (if (rtd? rtd) (let* ((names (vector->list (rtd-field-names rtd))) (probe (memq fieldname names))) (if probe (record-mutator rtd (- (length names) (length probe))) (loop (rtd-parent rtd)))) (assertion-violation 'rtd-mutator "illegal argument" rtd0 fieldname))) (loop rtd0)) )