(module struct-helper mzscheme
(require (lib "list.ss")
"../stx.ss"
"compat-for-399.ss")
(require-for-template mzscheme)
(provide (all-defined))
(define-struct field-decl (field ref mut posn immutable? auto?) (make-inspector))
(define-struct info2 (type fdecls ht))
(define (fetch-super-struct type)
(define (err)
(raise-syntax-error 'define-struct*
"not defined as a struct"
type))
(let ([struct-info (syntax-local-value type err)])
(car (get-struct-info-as-list struct-info))))
(define (info-lookup info key)
(hash-table-get (info2-ht info) key (lambda () #f)))
(define (info-lookup-list info key)
(hash-table-get (info2-ht info) key (lambda () null)))
(define info-put-fresh!
(case-lambda
[(info key value fail)
(let/ec k
(hash-table-get (info2-ht info)
key
(lambda ()
(k (hash-table-put! (info2-ht info) key value))))
(fail))]
[(info key value)
(info-put-fresh!
info
key
value
(lambda ()
(error 'info-put-fresh! "option ~s specified twice" key)))]))
(define (info-add! info key value)
(hash-table-put! (info2-ht info)
key
(cons value
(info-lookup-list info key))))
(define (identifier/tf? stx)
(or (identifier? stx) (stx-boolean? stx)))
(define (id/tf stx stx2)
(cond [(identifier? stx)
stx]
[(eq? (syntax-e stx) #t)
stx2]
[else #f]))
(define (memq/f item items)
(and items (memq item items)))
(define (mk-parse-field-decl name-id)
(define (parse-field-decl stx)
(syntax-case stx ()
[(field (flag ...) ref mut)
(begin
(unless (identifier? #'field)
(raise-syntax-error 'define-struct*
"field name must be identifier"
#'field))
(unless (identifier/tf? #'ref)
(raise-syntax-error 'define-struct*
"accessor name must be identifier, #t, or #f"
#'ref))
(unless (identifier/tf? #'mut)
(raise-syntax-error 'define-struct*
"mutator name must be identifier, #t, or #f"
#'ref))
(let ((flags (syntax-object->datum #'(flag ...))))
(make-field-decl
#'field
(id/tf #'ref (name:ref name-id #'field))
(id/tf #'mut (name:mut name-id #'field))
#f
(memq '#:immutable flags)
(memq '#:auto flags))))]
[(field (flag ...) ref)
(parse-field-decl #'(field (flag ...) ref #t))]
[(field (flag ...))
(parse-field-decl
#`(field
(flag ...)
#t
#t))]
[field
(identifier? #'field)
(parse-field-decl
#`(field () #t #t))]
[_
(raise-syntax-error 'define-struct* "bad field declaration" stx)]))
parse-field-decl)
(define (mk-parse-decl info)
(define (check!/1 info blamestx sym)
(when (info-lookup info sym)
(raise-syntax-error 'define-struct*
(format "option repetition or conflict with earlier '~s" sym)
blamestx)))
(define (check! info blamestx . syms)
(for-each (lambda (s) check!/1 info blamestx s) syms))
(define (parse-decl stx)
(syntax-case stx ()
[(#:super type)
(identifier? #'type)
(begin (check! info stx 'super 'super-struct)
(info-put-fresh! info 'super #'type)
(info-put-fresh! info 'super-struct (fetch-super-struct #'type)))]
[(#:super-struct value)
(begin (check! info stx 'super-struct)
(info-put-fresh! info 'super-struct #'value))]
[(#:auto-value value)
(begin (check! info stx 'auto-value)
(info-put-fresh! info 'auto-value #'value))]
[(#:property key value)
(info-add! info 'properties (cons #'key #'value))]
[(#:inspector value)
(begin (check! info stx 'transparent 'inspector)
(info-put-fresh! info 'inspector #'value))]
[#:transparent
(begin (check! info stx 'transparent 'inspector)
(info-put-fresh! info 'transparent #t))]
[(#:procedure proc)
(begin (check! info stx 'procedure-field 'procedure)
(info-put-fresh! info 'procedure #'proc)
(info-put-fresh! info 'procedure-spec #'proc))]
[(#:procedure-field field)
(identifier? #'field)
(begin (check! info stx 'procedure-field 'procedure)
(info-put-fresh!
info
'procedure-spec
(let loop ((fdecls (info2-fdecls info)))
(cond [(null? fdecls)
(raise-syntax-error 'define-struct*
"procedure-field not in field set"
stx)]
[(module-identifier=? #'field (field-decl-field (car fdecls)))
(field-decl-posn (car fdecls))]
[else (loop (cdr fdecls))])))
(info-put-fresh! info 'procedure-field #'field))]
[(#:guard proc)
(begin (check! info stx 'guard)
(info-put-fresh! info 'guard #'proc))]
[#:omit-define-values
(info-add! info 'options 'omit-define-values)]
[#:omit-define-syntaxes
(info-add! info 'options 'omit-define-syntaxes)]
[_
(raise-syntax-error 'define-struct* "unknown option" stx)]))
parse-decl)
(define (add-positions-to-field-decls! fdecls)
(let loop ((fdecls fdecls) (posn 0) (first-auto #f))
(when (pair? fdecls)
(let ((fdecl (car fdecls)))
(set-field-decl-posn! fdecl posn)
(when (and first-auto (not (field-decl-auto? fdecl)))
(raise-syntax-error 'define-struct*
"non-auto field after auto field"
(field-decl-field fdecl)))
(loop (cdr fdecls)
(add1 posn)
(or first-auto (if (field-decl-auto? fdecl) posn #f)))))))
(define (new-info type fdecls)
(make-info2 type fdecls (make-hash-table)))
(define (create-info type decls fdecls)
(let ((info (new-info type fdecls)))
(add-positions-to-field-decls! fdecls)
(for-each (mk-parse-decl info) decls)
(when (and (info-include-subst? info) (pair? (info-auto-fields info)))
(error 'define-struct* "cannot define substitutions with auto-fields"))
info))
(define (info-super info)
(info-lookup info 'super))
(define (info-super-struct info)
(info-lookup info 'super-struct))
(define (info-auto-k info)
(length (filter field-decl-auto? (info2-fdecls info))))
(define (info-auto-v info)
(info-lookup info 'auto-value))
(define (info-props info)
(info-lookup-list info 'properties))
(define (info-insp info)
(info-lookup info 'inspector))
(define (info-proc-spec info)
(info-lookup info 'procedure-spec))
(define (info-imm-k-list info)
(map field-decl-posn
(filter field-decl-immutable?
(info2-fdecls info))))
(define (info-guard info)
(info-lookup info 'guard))
(define (info-ref-fields info)
(map field-decl-field (filter field-decl-ref (info2-fdecls info))))
(define (info-ref-posns info)
(map field-decl-posn (filter field-decl-ref (info2-fdecls info))))
(define (info-ref-names info)
(map field-decl-ref (filter field-decl-ref (info2-fdecls info))))
(define (info-mut-fields info)
(map field-decl-field (filter field-decl-mut (info2-fdecls info))))
(define (info-mut-posns info)
(map field-decl-posn (filter field-decl-mut (info2-fdecls info))))
(define (info-mut-names info)
(map field-decl-mut (filter field-decl-mut (info2-fdecls info))))
(define (info-options info)
(info-lookup info 'options))
(define (info-init-fields info)
(filter (lambda (fdecl) (not (field-decl-auto? fdecl)))
(info2-fdecls info)))
(define (info-auto-fields info)
(filter (lambda (fdecl) (field-decl-auto? fdecl))
(info2-fdecls info)))
(define (info-include-define-values? info)
(not (memq/f 'omit-define-values (info-options info))))
(define (info-include-static-info? info)
(not (memq/f 'omit-define-syntaxes (info-options info))))
(define (info-include-subst? info)
(memq/f 'include-subst (info-options info)))
(define (info-include-clone? info)
(memq/f 'include-clone (info-options info)))
(define (info-name:struct-record info)
(let ((type (info2-type info)))
(name:struct-record type)))
(define (info-name:constructor info)
(let ((type (info2-type info)))
(name:make type)))
(define (info-name:predicate info)
(let ((type (info2-type info)))
(name:pred type)))
(define (info-name:gen-accessor info)
(let ((type (info2-type info)))
(name:gen-accessor type)))
(define (info-name:gen-mutator info)
(let ((type (info2-type info)))
(name:gen-mutator type)))
(define (info-core-names info)
(let ((type (info2-type info)))
(list (info-name:struct-record info)
(info-name:constructor info)
(info-name:predicate info)
(info-name:gen-accessor info)
(info-name:gen-mutator info))))
(define (name:make type)
(datum->syntax-object type (symbol-append 'make- type)))
(define (name:pred type)
(datum->syntax-object type (symbol-append type '?)))
(define (name:struct-record type)
(datum->syntax-object type (symbol-append 'struct: type)))
(define (name:gen-accessor type)
(datum->syntax-object type (symbol-append type '-ref)))
(define (name:gen-mutator type)
(datum->syntax-object type (symbol-append type 'set!)))
(define (name:ref type field)
(datum->syntax-object type (symbol-append type '- field)))
(define (name:mut type field)
(datum->syntax-object type (symbol-append 'set- type '- field '!)))
)