Below is an implementation in Dybvig's portable syntax-case, using the
included macro system. Tested on Petite Chez v6.9c.
Andre
;============================================================================================
; DEPENDENCIES:
;
; Andre van Tonder, 2005.
;
; This section contains an implementation of SRFI-9 and the
; necessary procedures from SRFI-1. May be omitted if these
; SRFIs are already available.
;
;============================================================================================
; Only the necessary procedures adapted from the SRFI-1 reference
; implementation. If you have SRFI-1, this may be omitted. Here I
; didn't bother with optional arguments since only fixed-arity
; versions are needed.
(module srfi-1 (s1:assoc
s1:lset-intersection
s1:lset-difference
s1:delete-duplicates
s1:fold-right
s1:filter
s1:member)
(define (find pred list)
(cond ((find-tail pred list) => car)
(else #f)))
(define (s1:member x lis =)
(find-tail (lambda (y) (= x y)) lis))
(define (find-tail pred list)
(let lp ((list list))
(and (not (null-list? list))
(if (pred (car list)) list
(lp (cdr list))))))
(define (s1:assoc x lis =)
(find (lambda (entry) (= x (car entry))) lis))
(define (s1:lset-intersection = lis1 . lists)
(let ((lists (delete lis1 lists eq?)))
(cond ((any null-list? lists) '())
((null? lists) lis1)
(else (s1:filter (lambda (x)
(every (lambda (lis) (s1:member x lis =))
lists))
lis1)))))
(define (s1:lset-difference = lis1 . lists)
(let ((lists (s1:filter pair? lists)))
(cond ((null? lists) lis1)
((memq lis1 lists) '())
(else (s1:filter (lambda (x)
(every (lambda (lis) (not (s1:member x lis =)))
lists))
lis1)))))
(define (every pred list)
(let lp ((list list))
(or (not (pair? list))
(and (pred (car list))
(lp (cdr list))))))
(define (delete x lis =)
(s1:filter (lambda (y) (not (= x y))) lis))
(define (any pred lis1)
(and (not (null-list? lis1))
(let lp ((head (car lis1)) (tail (cdr lis1)))
(if (null-list? tail)
(pred head)
(or (pred head) (lp (car tail) (cdr tail)))))))
(define (s1:delete-duplicates lis elt=)
(let recur ((lis lis))
(if (null-list? lis) lis
(let* ((x (car lis))
(tail (cdr lis))
(new-tail (recur (delete x tail elt=))))
(if (eq? tail new-tail) lis (cons x new-tail))))))
(define (s1:fold-right kons knil lis1)
(let recur ((lis lis1))
(if (null-list? lis) knil
(let ((head (car lis)))
(kons head (recur (cdr lis)))))))
(define null-list? null?)
(define (s1:filter pred lis)
(let recur ((lis lis))
(if (null-list? lis) lis
(let ((head (car lis))
(tail (cdr lis)))
(if (pred head)
(let ((new-tail (recur tail)))
(if (eq? tail new-tail) lis
(cons head new-tail)))
(recur tail))))))
)
;======================================================================================
; SRFI-9 implementation, based on implementation by Felix Winkelmann.
; If you have SRFI-9, this may be omitted.
(module srfi-9 (s9:define-record-type)
(import srfi-1)
(define-syntax (s9:define-record-type x)
(syntax-case x ()
((_ t (conser vars ...) pred slots ...)
(syntax-case #'(slots ...) ()
(((slotnames . _) ...)
(with-syntax ((t (datum->syntax-object #'t (gensym)))
((slotvars ...) (map (lambda (sname)
(if (s1:member sname #'(vars ...) literal-identifier=?)
sname
#''<undefined>))
#'(slotnames ...))))
(with-syntax (((accforms ...)
(let loop ((slots #'(slots ...)) (i 2))
(if (null? slots)
#'()
(with-syntax ((ii i)
((rest ...) (loop (cdr slots) (+ 1 i))))
(syntax-case (car slots) ()
((name get set)
#'((define (get x)
(vector-ref x ii))
(define (set x y)
(vector-set! x ii y))
rest ...))
((name get)
#'((define (get x)
(vector-ref x ii))
rest ...))))))))
#'(begin
(define (conser vars ...) (vector '<record> 't slotvars ...))
(define (pred x) (and (vector? x)
(>= (vector-length x) 2)
(eqv? '<record> (vector-ref x 0))
(eqv? 't (vector-ref x 1))))
accforms ...))))))))
) ; srfi-9
;===========================================================================================
; SRFI-57: RECORDS IMPLEMENTATION IN PORTABLE SYNTAX-CASE:
;
; Andre van Tonder, 2005.
;
;============================================================================================
(module registry (register
make-entry
lookup-entry
lookup-scheme?
lookup-getter
lookup-setter
lookup-labels
lookup-supers
lookup-copier
lookup-predicate)
(import srfi-1)
(import srfi-9)
(define reg '())
(s9:define-record-type entry
(make-entry name
is-scheme?
predicate
supers
labels
pos-labels
fields
copier)
entry?
(name entry.name)
(is-scheme? entry.is-scheme?)
(predicate entry.predicate)
(supers entry.supers)
(labels entry.labels)
(pos-labels entry.pos-labels)
(fields entry.fields)
(copier entry.copier))
(define (register name entry)
(cond ((s1:assoc name reg literal-identifier=?)
=> (lambda (pair)
(set-cdr! pair entry)))
(else
(set! reg (cons (cons name entry)
reg)))))
(define (lookup-entry name)
(s1:assoc name reg literal-identifier=?))
(define (lookup-getter name label)
(cond ((s1:assoc label
(entry.fields (cdr (lookup-entry name)))
literal-identifier=?)
=> cadr)
(else #f)))
(define (lookup-setter name label)
(cond ((s1:assoc label
(entry.fields (cdr (lookup-entry name)))
literal-identifier=?)
=> caddr)
(else #f)))
(define (lookup-scheme? name) (entry.is-scheme? (cdr (lookup-entry name))))
(define (lookup-labels name) (entry.labels (cdr (lookup-entry name))))
(define (lookup-supers name) (entry.supers (cdr (lookup-entry name))))
(define (lookup-copier name) (entry.copier (cdr (lookup-entry name))))
(define (lookup-predicate name) (entry.predicate (cdr (lookup-entry name))))
) ; registry
(module portability (syntax->list)
(define (syntax->list x)
(syntax-case x ()
(() '())
((h . t) (cons #'h
(syntax->list #'t)))))
)
(module helpers (parse-declaration
build-record
extend-predicates
extend-copiers
extend-accessors
populate
define-generic
make-generic
define-method
any?)
(import registry)
(import srfi-1)
(import srfi-9)
(import portability)
(define-syntax parse-declaration
(syntax-rules ()
((parse-declaration is-scheme? (name super ...) (constructor pos-label ...) predicate field-clause ...)
(build-record (constructor pos-label ...) #f (super ...) (field-clause ...) name predicate is-scheme?))
((parse-declaration is-scheme? (name super ...) constructor predicate field-clause ...)
(build-record (constructor) #t (super ...) (field-clause ...) name predicate is-scheme?))
((parse-declaration is-scheme? (name super ...) constructor-clause)
(parse-declaration is-scheme? (name super ...) constructor-clause #f))
((parse-declaration is-scheme? (name super ...))
(parse-declaration is-scheme? (name super ...) #f #f))
((parse-declaration is-scheme? name . rest)
(parse-declaration is-scheme? (name) . rest))))
(define-syntax build-record
(let ()
(define (build-record stx)
(syntax-case stx ()
((build-record (constructor pos-label ...)
default-order?
(super ...)
((field-label . accessors) ...)
name
predicate
is-scheme?)
(with-syntax
(((label ...)
(s1:delete-duplicates (s1:fold-right append
(syntax->list #'(pos-label ... field-label ...))
(map lookup-labels
(syntax->list #'(super ...))))
literal-identifier=?))
((super ...)
(s1:delete-duplicates (s1:fold-right append
'()
(map lookup-supers
(syntax->list #'(super ...))))
literal-identifier=?)))
(with-syntax
(((pos-label ...)
(if (syntax-object->datum #'default-order?)
#'(label ...)
#'(pos-label ...)))
(((field-label getter setter) ...)
(append (map augment-field
(syntax->list #'((field-label . accessors) ...)))
(map (lambda (label)
(maybe-generate #'name `(,label getter setter)))
(s1:lset-difference literal-identifier=?
(syntax->list #'(label ...))
(syntax->list #'(field-label ...)))))))
(with-syntax ((supers #'(super ...))
((pos-temp ...) (generate-temporaries #'(pos-label ...)))
((constructor predicate maker copier)
(maybe-generate #'name `(,#'constructor ,#'predicate maker copier))))
(begin
(register #'name (make-entry #'name
(syntax-object->datum #'is-scheme?)
#'predicate
(syntax->list #'(super ... name))
(syntax->list #'(label ...))
(syntax->list #'(pos-label ...))
(map syntax->list
(syntax->list #'((field-label getter setter) ...)))
#'copier))
(if (syntax-object->datum #'is-scheme?)
#'(begin
(define-generic (predicate x) (lambda (x) #f))
(define-generic (getter x))
...
(define-generic (setter x v))
...
(define-generic (copier x)))
#'(begin
(s9:define-record-type internal-name
(maker field-label ...)
predicate
(field-label getter setter) ...)
(define constructor
(lambda (pos-temp ...)
(populate name maker (field-label ...) (pos-label pos-temp) ...)))
(extend-predicates supers predicate)
(extend-accessors supers field-label predicate getter setter)
...
(define (copier x)
(maker (getter x) ...))
(extend-copiers supers copier predicate)
(define-method (show (r predicate))
(list 'name
(list 'field-label (getter r))
...))
(define-syntax name
(syntax-rules ()
((name . bindings) (populate name maker (field-label ...) . bindings))))
))))))))) ; build-record
(define (maybe-generate context maybe-identifiers)
(map (lambda (elem)
(if (identifier? elem)
elem
(datum->syntax-object context (gensym))))
maybe-identifiers))
(define (augment-field clause)
(syntax-case clause ()
((label) (maybe-generate #'label `(,#'label getter setter)))
((label getter) (maybe-generate #'label `(,#'label ,#'getter setter)))
((label getter setter) (maybe-generate #'label `(,#'label ,#'getter ,#'setter)))))
build-record))
(define-syntax extend-predicates
(lambda (stx)
(syntax-case stx ()
((extend-predicates (super ...) new-type)
(with-syntax (((predicate ...) (map lookup-predicate
(syntax->list #'(super ...)))))
#'(begin
(define-method predicate (new-type) (x) any?)
...))))))
(define-syntax extend-copiers
(lambda (stx)
(syntax-case stx ()
((extend-copiers (super ...) copy new-type)
(with-syntax (((copier ...) (map lookup-copier
(syntax->list #'(super ...)))))
#'(begin
(define-method copier (new-type) (x) copy)
...))))))
(define-syntax extend-accessors
(lambda (stx)
(syntax-case stx ()
((extend-accessors (super ...) label new-type selector modifier)
(with-syntax (((getter ...) (s1:filter (lambda (id)
(not (eqv? id #f)))
(map (lambda (super)
(lookup-getter super #'label))
(syntax->list #'(super ...)))))
((setter ...) (s1:filter (lambda (id)
(not (eqv? id #f)))
(map (lambda (super)
(lookup-setter super #'label))
(syntax->list #'(super ...))))))
#'(begin
(define-method getter (new-type) (x) selector)
...
(define-method setter (new-type any?) (x v) modifier)
...))))))
(define-syntax populate
(lambda (stx)
(define (order name ordering bindings default)
(if (null? (s1:lset-difference literal-identifier=?
(map car bindings)
ordering))
(map (lambda (label)
(cond ((s1:assoc label bindings literal-identifier=?) => (lambda (x) x))
(else `(,label ,default))))
ordering)
(error 'populate "Bindings ~s contains illegal labels. Legal labels for record type ~s are ~s"
(syntax-object->datum bindings)
(syntax-object->datum name)
(syntax-object->datum ordering))))
(syntax-case stx ()
((populate name maker labels . bindings)
(with-syntax ((((label exp) ...) (order #'name
(syntax->list #'labels)
(map syntax->list
(syntax->list #'bindings))
#''<undefined>)))
#'(maker exp ...))))))
; Simple generic functions suitable for our disjoint base record types:
(define-syntax define-generic
(syntax-rules ()
((define-generic (name arg ...))
(define-generic (name arg ...)
(lambda (arg ...) (error "Inapplicable method:" 'name
"Arguments:" (show arg) ... ))))
((define-generic (name arg ...) proc)
(define name (make-generic (arg ...) proc)))))
(define-syntax define-method
(syntax-rules ()
((define-method (generic (arg pred?) ...) . body)
(define-method generic (pred? ...) (arg ...) (lambda (arg ...) . body)))
((define-method generic (pred? ...) (arg ...) procedure)
(let ((next ((generic) 'get-proc))
(proc procedure))
(((generic) 'set-proc)
(lambda (arg ...)
(if (and (pred? arg) ...)
(proc arg ...)
(next arg ...))))))))
(define-syntax make-generic
(syntax-rules ()
((make-generic (arg arg+ ...) default-proc)
(let ((proc default-proc))
(case-lambda
((arg arg+ ...)
(proc arg arg+ ...))
(()
(lambda (msg)
(case msg
((get-proc) proc)
((set-proc) (lambda (new)
(set! proc new)))))))))))
(define (any? x) #t)
) ; helpers
(module records (define-record-type
define-record-scheme
record-update
record-update!
record-compose
show)
(import srfi-1)
(import registry)
(import portability)
(import helpers)
(define-syntax define-record-type
(syntax-rules ()
((define-record-type . body)
(parse-declaration #f . body))))
(define-syntax define-record-scheme
(syntax-rules ()
((define-record-scheme . body)
(parse-declaration #t . body))))
(define-syntax record-update!
(lambda (stx)
(syntax-case stx ()
((_ record name (label exp) ...)
(with-syntax (((setter ...)
(map (lambda (label)
(lookup-setter #'name label))
(syntax->list #'(label ...)))))
#'(let ((r record))
(setter r exp)
...
r))))))
(define-syntax record-update
(lambda (stx)
(syntax-case stx ()
((_ record name (label exp) ...)
(if (lookup-scheme? #'name)
(with-syntax ((copier (lookup-copier #'name)))
#'(let ((new (copier record)))
(record-update! new name (label exp) ...)))
#'(record-compose (name record) (name (label exp) ...)))))))
(define-syntax record-compose
(lambda (stx)
(syntax-case stx ()
((record-compose (export-name (label exp) ...))
#'(export-name (label exp) ...))
((record-compose (import-name record) import ... (export-name (label exp) ...))
(with-syntax
(((copy-label ...)
(s1:lset-intersection literal-identifier=?
(lookup-labels #'export-name)
(s1:lset-difference literal-identifier=?
(lookup-labels #'import-name)
(syntax->list #'(label ...))))))
(with-syntax (((getter ...)
(map (lambda (label)
(lookup-getter #'import-name label))
(syntax->list #'(copy-label ...)))))
#'(let ((r record))
(record-compose import ...
(export-name (copy-label (getter r))
...
(label exp)
...)))))))))
(define-generic (show x)
(lambda (x) x))
) ; records