mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-20 19:50:24 +02:00
Allow records to be subtyped
* module/ice-9/boot-9.scm (record-type-vtable): Add slots for "flags" and a parent vector. (record-type-name, record-type-fields): Move up in the file. (record-type-constructor, record-type-flags, record-type-parents): New accessors. (make-record-type): Take #:final? and #:parent keyword arguments. (record-constructor): Delegate to record-type-constructor. (record-predicate): For non-final types --types that can be extended by subtyping -- implement an O(1) type predicate. (define-record-type): Initialize the new fields. * module/srfi/srfi-9.scm (%define-record-type): Initialize flags and parent fields.
This commit is contained in:
parent
6205c2d7d4
commit
4bec125e63
2 changed files with 96 additions and 32 deletions
|
@ -1191,9 +1191,10 @@ VALUE."
|
||||||
;;
|
;;
|
||||||
;; It should print OBJECT to PORT.
|
;; It should print OBJECT to PORT.
|
||||||
|
|
||||||
;; 0: type-name, 1: fields, 2: constructor
|
;; 0: type-name, 1: fields, 2: constructor, 3: flags, 4: parents
|
||||||
(define record-type-vtable
|
(define record-type-vtable
|
||||||
(let ((s (make-vtable (string-append standard-vtable-fields "pwpwpw")
|
(let ((s (make-vtable (string-append standard-vtable-fields
|
||||||
|
"pwpwpwpwpw")
|
||||||
(lambda (s p)
|
(lambda (s p)
|
||||||
(display "#<record-type " p)
|
(display "#<record-type " p)
|
||||||
(display (record-type-name s) p)
|
(display (record-type-name s) p)
|
||||||
|
@ -1204,7 +1205,33 @@ VALUE."
|
||||||
(define (record-type? obj)
|
(define (record-type? obj)
|
||||||
(and (struct? obj) (eq? record-type-vtable (struct-vtable obj))))
|
(and (struct? obj) (eq? record-type-vtable (struct-vtable obj))))
|
||||||
|
|
||||||
(define* (make-record-type type-name fields #:optional printer)
|
(define (record-type-name rtd)
|
||||||
|
(unless (record-type? rtd)
|
||||||
|
(error 'not-a-record-type rtd))
|
||||||
|
(struct-ref rtd vtable-offset-user))
|
||||||
|
|
||||||
|
(define (record-type-fields rtd)
|
||||||
|
(unless (record-type? rtd)
|
||||||
|
(error 'not-a-record-type rtd))
|
||||||
|
(struct-ref rtd (+ 1 vtable-offset-user)))
|
||||||
|
|
||||||
|
(define (record-type-constructor rtd)
|
||||||
|
(unless (record-type? rtd)
|
||||||
|
(error 'not-a-record-type rtd))
|
||||||
|
(struct-ref rtd (+ 2 vtable-offset-user)))
|
||||||
|
|
||||||
|
(define (record-type-flags rtd)
|
||||||
|
(unless (record-type? rtd)
|
||||||
|
(error 'not-a-record-type rtd))
|
||||||
|
(struct-ref rtd (+ 3 vtable-offset-user)))
|
||||||
|
|
||||||
|
(define (record-type-parents rtd)
|
||||||
|
(unless (record-type? rtd)
|
||||||
|
(error 'not-a-record-type rtd))
|
||||||
|
(struct-ref rtd (+ 4 vtable-offset-user)))
|
||||||
|
|
||||||
|
(define* (make-record-type type-name fields #:optional printer #:key
|
||||||
|
(final? #t) parent)
|
||||||
;; Pre-generate constructors for nfields < 20.
|
;; Pre-generate constructors for nfields < 20.
|
||||||
(define-syntax make-constructor
|
(define-syntax make-constructor
|
||||||
(lambda (x)
|
(lambda (x)
|
||||||
|
@ -1255,37 +1282,53 @@ VALUE."
|
||||||
(loop (cdr fields) (+ 1 off)))))
|
(loop (cdr fields) (+ 1 off)))))
|
||||||
(display ">" p))
|
(display ">" p))
|
||||||
|
|
||||||
(let ((rtd (make-struct/no-tail
|
(define parents
|
||||||
|
(cond
|
||||||
|
((record-type? parent)
|
||||||
|
(let* ((parent-parents (record-type-parents parent))
|
||||||
|
(parent-nparents (vector-length parent-parents))
|
||||||
|
(parents (make-vector (1+ parent-nparents))))
|
||||||
|
(vector-move-left! parent-parents 0 parent-nparents parents 0)
|
||||||
|
(vector-set! parents parent-nparents parent)
|
||||||
|
parents))
|
||||||
|
(parent
|
||||||
|
(error "expected parent to be a record type" parent))
|
||||||
|
(else
|
||||||
|
#())))
|
||||||
|
|
||||||
|
(define computed-fields
|
||||||
|
(if parent
|
||||||
|
(append (record-type-fields parent) fields)
|
||||||
|
fields))
|
||||||
|
|
||||||
|
(define rtd
|
||||||
|
(make-struct/no-tail
|
||||||
record-type-vtable
|
record-type-vtable
|
||||||
(make-struct-layout
|
(make-struct-layout
|
||||||
(apply string-append
|
(apply string-append
|
||||||
(map (lambda (f) "pw") fields)))
|
(map (lambda (f) "pw") computed-fields)))
|
||||||
(or printer default-record-printer)
|
(or printer default-record-printer)
|
||||||
type-name
|
type-name
|
||||||
(copy-tree fields))))
|
computed-fields
|
||||||
|
#f ; Constructor initialized below.
|
||||||
|
(if final? '(final) '())
|
||||||
|
parents))
|
||||||
|
|
||||||
(struct-set! rtd (+ vtable-offset-user 2)
|
(struct-set! rtd (+ vtable-offset-user 2)
|
||||||
(make-constructor rtd (length fields)))
|
(make-constructor rtd (length computed-fields)))
|
||||||
|
|
||||||
;; Temporary solution: Associate a name to the record type descriptor
|
;; Temporary solution: Associate a name to the record type descriptor
|
||||||
;; so that the object system can create a wrapper class for it.
|
;; so that the object system can create a wrapper class for it.
|
||||||
(set-struct-vtable-name! rtd (if (symbol? type-name)
|
(set-struct-vtable-name! rtd (if (symbol? type-name)
|
||||||
type-name
|
type-name
|
||||||
(string->symbol type-name)))
|
(string->symbol type-name)))
|
||||||
rtd))
|
|
||||||
|
|
||||||
(define (record-type-name obj)
|
rtd)
|
||||||
(if (record-type? obj)
|
|
||||||
(struct-ref obj vtable-offset-user)
|
|
||||||
(error 'not-a-record-type obj)))
|
|
||||||
|
|
||||||
(define (record-type-fields obj)
|
|
||||||
(if (record-type? obj)
|
|
||||||
(struct-ref obj (+ 1 vtable-offset-user))
|
|
||||||
(error 'not-a-record-type obj)))
|
|
||||||
|
|
||||||
(define record-constructor
|
(define record-constructor
|
||||||
(case-lambda
|
(case-lambda
|
||||||
((rtd)
|
((rtd)
|
||||||
(struct-ref rtd (+ 2 vtable-offset-user)))
|
(record-type-constructor rtd))
|
||||||
((rtd field-names)
|
((rtd field-names)
|
||||||
(issue-deprecation-warning
|
(issue-deprecation-warning
|
||||||
"Calling `record-constructor' with two arguments (the record type"
|
"Calling `record-constructor' with two arguments (the record type"
|
||||||
|
@ -1302,7 +1345,22 @@ VALUE."
|
||||||
(record-type-fields rtd))))))))
|
(record-type-fields rtd))))))))
|
||||||
|
|
||||||
(define (record-predicate rtd)
|
(define (record-predicate rtd)
|
||||||
(lambda (obj) (and (struct? obj) (eq? rtd (struct-vtable obj)))))
|
(unless (record-type? rtd)
|
||||||
|
(error 'not-a-record-type rtd))
|
||||||
|
(if (memq 'final (record-type-flags rtd))
|
||||||
|
(lambda (obj) (and (struct? obj) (eq? rtd (struct-vtable obj))))
|
||||||
|
(let ((pos (vector-length (record-type-parents rtd))))
|
||||||
|
;; Extensible record types form a forest of DAGs, with each
|
||||||
|
;; record type recording an ordered vector of its ancestors. If
|
||||||
|
;; A is a subtype of B, and B has N parents, then A.parents[N]
|
||||||
|
;; will be B.
|
||||||
|
(lambda (obj)
|
||||||
|
(and (struct? obj)
|
||||||
|
(let* ((v (struct-vtable obj)))
|
||||||
|
(or (eq? v rtd)
|
||||||
|
(let ((parents (record-type-parents v)))
|
||||||
|
(and (< pos (vector-length parents))
|
||||||
|
(eq? (vector-ref parents pos) rtd))))))))))
|
||||||
|
|
||||||
(define (%record-type-error rtd obj) ;; private helper
|
(define (%record-type-error rtd obj) ;; private helper
|
||||||
(or (eq? rtd (record-type-descriptor obj))
|
(or (eq? rtd (record-type-descriptor obj))
|
||||||
|
@ -1963,7 +2021,10 @@ name extensions listed in %load-extensions."
|
||||||
'#,(make-layout)
|
'#,(make-layout)
|
||||||
#,printer
|
#,printer
|
||||||
'#,type-name
|
'#,type-name
|
||||||
'#,(field-list fields)))
|
'#,(field-list fields)
|
||||||
|
#f ; constructor; set later
|
||||||
|
'() ; flags
|
||||||
|
#())) ; parents
|
||||||
(set-struct-vtable-name! #,rtd '#,type-name)))))
|
(set-struct-vtable-name! #,rtd '#,type-name)))))
|
||||||
|
|
||||||
(syntax-case x ()
|
(syntax-case x ()
|
||||||
|
|
|
@ -320,7 +320,10 @@
|
||||||
'#,(datum->syntax #'here (make-struct-layout layout))
|
'#,(datum->syntax #'here (make-struct-layout layout))
|
||||||
default-record-printer
|
default-record-printer
|
||||||
'type-name
|
'type-name
|
||||||
'#,field-ids)))
|
'#,field-ids
|
||||||
|
#f ; Constructor.
|
||||||
|
'(final) ; Flags.
|
||||||
|
#()))) ; Parents.
|
||||||
(set-struct-vtable-name! rtd 'type-name)
|
(set-struct-vtable-name! rtd 'type-name)
|
||||||
(struct-set! rtd (+ 2 vtable-offset-user) #,ctor-name)
|
(struct-set! rtd (+ 2 vtable-offset-user) #,ctor-name)
|
||||||
rtd))
|
rtd))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue