racket/collects/mzlib/structure.ss
2005-05-27 18:56:37 +00:00

225 lines
10 KiB
Scheme

(module structure mzscheme
(require (lib "etc.ss"))
(require-for-syntax "private/structure-helper.ss"
(lib "kerncase.ss" "syntax")
(lib "stx.ss" "syntax")
(lib "list.ss"))
(provide structure dot open open-in-context open-as)
;; Dangerous, but seems to work.
(define-syntax define-syntaxes-ml
(syntax-rules ()
((_ . x) (define-syntaxes . x))))
(define-syntax-set (structure)
(define kernel-form-identifier-list/no-begin
(append (map (lambda (x) (datum->syntax-object #'here x))
`(define-values-ml define-syntaxes-ml))
(filter (lambda (id) (not (eq? 'begin (syntax-e id))))
(kernel-form-identifier-list #'here))))
(define (stx-assoc id renames)
(cond
((null? renames) #f)
((bound-identifier=? id (caar renames)) (car renames))
(else (stx-assoc id (cdr renames)))))
(define (remove-begins def)
(kernel-syntax-case def #f
((begin defs ...)
(apply append (map remove-begins (syntax->list #'(defs ...)))))
(_ (list def))))
(define (fix-expr e)
(kernel-syntax-case e #f
((define-values x y) e)
((define-syntaxes x y) e)
((d x y) (or (module-identifier=? (quote-syntax define-values-ml) #'d)
(module-identifier=? (quote-syntax define-syntaxes-ml) #'d))
e)
(x #`(define-values () (begin x (values))))))
(define (get-defs defs)
(map fix-expr
(apply append
(map (lambda (d)
(remove-begins
(local-expand d
(syntax-local-context)
kernel-form-identifier-list/no-begin)))
defs))))
(define (get-ids def)
(kernel-syntax-case def #f
((define-syntaxes vars body) (syntax->list #'vars))
((define-values vars body) (syntax->list #'vars))
((d vars body) (or (module-identifier=? (quote-syntax define-values-ml) #'d)
(module-identifier=? (quote-syntax define-syntaxes-ml) #'d))
(syntax->list #'vars))
(_ (raise-syntax-error 'structure "Internal error" def))))
(define (rebuild ctxt val)
(if (syntax? ctxt)
(datum->syntax-object ctxt val ctxt ctxt)
val))
(define (rebuild-cons car cdr stx)
(rebuild stx (cons car cdr)))
(define (mark-ids def introducers)
(let ((new-ids (map (lambda (id) (cons id (make-syntax-introducer)))
(get-ids def))))
(values
(syntax-case def ()
((ds . x) (module-identifier=? (quote-syntax define-syntaxes) #'ds)
(rebuild-cons #'ds (mark-ids-helper #'x (append new-ids introducers)) def))
((dv . x) (module-identifier=? (quote-syntax define-values) #'dv)
(rebuild-cons #'dv (mark-ids-helper #'x (append new-ids introducers)) def))
((d vars body) (module-identifier=? (quote-syntax define-values-ml) #'d)
(rebuild def `(,(datum->syntax-object #'here 'define-values #'d #'d)
,(mark-ids-helper #'vars (append new-ids introducers))
,(mark-ids-helper #'body introducers))))
((d vars body) (module-identifier=? (quote-syntax define-syntaxes-ml) #'d)
(rebuild def `(,(datum->syntax-object #'here 'define-syntaxes #'d #'d)
,(mark-ids-helper #'vars (append new-ids introducers))
,(mark-ids-helper #'body introducers)))))
new-ids)))
(define (mark-ids-helper def introducers)
(let ((contents
(if (syntax? def)
(syntax-e def)
def)))
(cond
((symbol? contents)
(let ((introducer (stx-assoc def introducers)))
(if introducer ((cdr introducer) def) def)))
((pair? contents)
(rebuild-cons (mark-ids-helper (car contents) introducers)
(mark-ids-helper (cdr contents) introducers)
def))
((vector? contents)
(rebuild def (list->vector
(map (lambda (x) (mark-ids-helper x introducers))
(vector->list contents)))))
(else def))))
(define (structure/proc stx)
(syntax-case stx ()
((_ name provides body ...)
(let ((defs (get-defs (syntax->list #'(body ...)))))
(unless (identifier? #'name)
(raise-syntax-error 'structure "Structure name must be an identifier" #'name))
#`(begin
#,@(let loop ((defined-ids null)
(defs defs))
(cond
((null? defs)
(list
#`(define-syntaxes-ml (name)
(make-str
(remove-dups
(list
#,@(syntax-case #'provides ()
(all (and (identifier? #'all)
(module-identifier=? (quote-syntax provide-all)
#'all))
(map (lambda (id)
`(cons
(quote ,(car id))
(quote-syntax ,((cdr id)
(car id)))))
(filter (lambda (id)
(bound-identifier=?
(car id)
(datum->syntax-object
#'provides
(syntax-object->datum (car id)))))
defined-ids)))
((provides ...)
(map (lambda (provide)
(let ((introducer (stx-assoc provide defined-ids)))
(unless introducer
(raise-syntax-error
'structure
"Attempt to export undefined identifier"
provide))
`(cons
(quote ,provide)
(quote-syntax ,((cdr introducer) provide)))))
(syntax->list #'(provides ...))))
(p
(cond
((eq? 'provide-all (syntax-e #'p))
(raise-syntax-error
'structure
"provide-all has been rebound"
#'provides))
(else
(raise-syntax-error
'structure
"Export must have the form \"provide-all\" or \"(identifier ...)\""
#'provides)))))))))))
(else
(let-values (((marked-def new-defined-ids)
(mark-ids (car defs) defined-ids)))
(cons marked-def
(loop (append new-defined-ids defined-ids)
(cdr defs))))))))))))
)
(define-syntax (open stx)
(syntax-case stx ()
((_ top-name path ...)
(datum->syntax-object #'here
`(open-in-context ,#'top-name ,#'top-name ,@(syntax->list #'(path ...)))
stx))))
(define-syntax (open-in-context stx)
(syntax-case stx ()
((_ context top-name path ...)
(let ((env (open (cons #'top-name (syntax->list #'(path ...))) 'open)))
(with-syntax ((((pub . hid) ...)
(map (lambda (x)
(cons (datum->syntax-object #'context (car x) stx)
(cdr x)))
env)))
#`(define-syntaxes-ml (pub ...)
(values (make-rename-transformer (quote-syntax hid)) ...)))))))
(define-syntax (dot-helper stx)
(syntax-case stx ()
((_ path field)
(begin
(unless (identifier? #'field)
(raise-syntax-error 'dot "Field to open must be an identifier" #'field))
(cond
((stx-null? #'path) #'field)
(else
(let ((hid (assq (syntax-object->datum #'field)
(open (stx->list #'path) 'dot))))
(unless hid
(raise-syntax-error 'dot "Unknown field" #'field))
(cdr hid))))))))
(define-syntax (dot stx)
(syntax-case stx ()
((_ path1 path-rest ...)
(let*-values (((path field)
(split (cons #'path1 (syntax->list #'(path-rest ...))))))
#`(begin0 (dot-helper #,path #,field))))))
(define-syntax (open-as stx)
(syntax-case stx ()
((_ rename top-name path1 path-rest ...)
(let-values (((path field)
(split (cons #'top-name (cons #'path1 (syntax->list #'(path-rest ...)))))))
(unless (identifier? #'rename)
(raise-syntax-error 'open-as "First position must be an identifier" #'rename))
#`(define-syntaxes-ml (rename)
(open-as-helper #'#,path #'#,field))))))
)