/collects/redex/private/term.rkt
http://github.com/agocke/racket · Racket · 225 lines · 205 code · 20 blank · 0 comment · 14 complexity · 5018e77dc151a765449a4f06c0f07a58 MD5 · raw file
- #lang scheme/base
- (require (for-syntax scheme/base
- "term-fn.ss"
- syntax/boundmap
- racket/syntax)
- "error.rkt"
- "matcher.ss")
- (provide term term-let term-let/error-name term-let-fn term-define-fn hole in-hole)
- (define-syntax (hole stx) (raise-syntax-error 'hole "used outside of term"))
- (define-syntax (in-hole stx) (raise-syntax-error 'in-hole "used outside of term"))
- (define (with-syntax* stx)
- (syntax-case stx ()
- [(_ () e) (syntax e)]
- [(_ (a b ...) e) (syntax (with-syntax (a) (with-syntax* (b ...) e)))]))
- (define-syntax-rule (term t)
- (#%expression (term/private t)))
- (define-syntax (term/private orig-stx)
- (define outer-bindings '())
- (define applied-metafunctions
- (make-free-identifier-mapping))
-
- (define (rewrite stx)
- (let-values ([(rewritten _) (rewrite/max-depth stx 0)])
- rewritten))
-
- (define (rewrite-application fn args depth)
- (let-values ([(rewritten max-depth) (rewrite/max-depth args depth)])
- (let ([result-id (car (generate-temporaries '(f-results)))])
- (with-syntax ([fn fn])
- (let loop ([func (syntax (λ (x) (fn (syntax->datum x))))]
- [args-stx rewritten]
- [res result-id]
- [args-depth (min depth max-depth)])
- (with-syntax ([func func]
- [args args-stx]
- [res res])
- (if (zero? args-depth)
- (begin
- (set! outer-bindings
- (cons (syntax [res (func (quasisyntax args))])
- outer-bindings))
- (values result-id (min depth max-depth)))
- (loop (syntax (λ (l) (map func (syntax->list l))))
- (syntax/loc args-stx (args (... ...)))
- (syntax (res (... ...)))
- (sub1 args-depth)))))))))
-
- (define (rewrite/max-depth stx depth)
- (syntax-case stx (unquote unquote-splicing in-hole hole)
- [(metafunc-name arg ...)
- (and (identifier? (syntax metafunc-name))
- (term-fn? (syntax-local-value (syntax metafunc-name) (λ () #f))))
- (let ([f (term-fn-get-id (syntax-local-value/record (syntax metafunc-name) (λ (x) #t)))])
- (free-identifier-mapping-put! applied-metafunctions f #t)
- (rewrite-application f (syntax/loc stx (arg ...)) depth))]
- [f
- (and (identifier? (syntax f))
- (term-fn? (syntax-local-value (syntax f) (λ () #f))))
- (raise-syntax-error 'term "metafunction must be in an application" orig-stx stx)]
- [x
- (and (identifier? (syntax x))
- (term-id? (syntax-local-value (syntax x) (λ () #f))))
- (let ([id (syntax-local-value/record (syntax x) (λ (x) #t))])
- (values (datum->syntax (term-id-id id) (syntax-e (term-id-id id)) (syntax x))
- (term-id-depth id)))]
- [(unquote x)
- (values (syntax (unsyntax x)) 0)]
- [(unquote . x)
- (raise-syntax-error 'term "malformed unquote" orig-stx stx)]
- [(unquote-splicing x)
- (values (syntax (unsyntax-splicing x)) 0)]
- [(unquote-splicing . x)
- (raise-syntax-error 'term "malformed unquote splicing" orig-stx stx)]
- [(in-hole id body)
- (rewrite-application (syntax (λ (x) (apply plug x))) (syntax/loc stx (id body)) depth)]
- [(in-hole . x)
- (raise-syntax-error 'term "malformed in-hole" orig-stx stx)]
- [hole (values (syntax (unsyntax the-hole)) 0)]
-
-
- [() (values stx 0)]
- [(x ... . y)
- (not (null? (syntax->list #'(x ...))))
- (let-values ([(x-rewrite max-depth)
- (let i-loop ([xs (syntax->list (syntax (x ...)))])
- (cond
- [(null? xs) (rewrite/max-depth #'y depth)]
- [else
- (let ([new-depth (if (and (not (null? (cdr xs)))
- (identifier? (cadr xs))
- (free-identifier=? (quote-syntax ...)
- (cadr xs)))
- (+ depth 1)
- depth)])
- (let-values ([(fst fst-max-depth)
- (rewrite/max-depth (car xs) new-depth)]
- [(rst rst-max-depth)
- (i-loop (cdr xs))])
- (values (cons fst rst)
- (max fst-max-depth rst-max-depth))))]))])
- (values (datum->syntax stx x-rewrite stx) max-depth))]
-
- [_ (values stx 0)]))
-
- (syntax-case orig-stx ()
- [(_ arg)
- (with-disappeared-uses
- (with-syntax ([rewritten (rewrite (syntax arg))])
- #`(begin
- #,@(free-identifier-mapping-map
- applied-metafunctions
- (λ (f _)
- (if (eq? (identifier-binding f) 'lexical)
- #`(check-defined-lexical #,f '#,f)
- #`(check-defined-module (λ () #,f) '#,f))))
- #,(let loop ([bs (reverse outer-bindings)])
- (cond
- [(null? bs) (syntax (syntax->datum (quasisyntax rewritten)))]
- [else (with-syntax ([rec (loop (cdr bs))]
- [fst (car bs)])
- (syntax (with-syntax (fst)
- rec)))])))))]))
- (define (check-defined-lexical value name)
- (when (eq? (letrec ([x x]) x) value)
- (report-undefined-metafunction name)))
- (define (check-defined-module thunk name)
- (with-handlers ([exn:fail:contract:variable?
- (λ (_) (report-undefined-metafunction name))])
- (thunk)))
- (define (report-undefined-metafunction name)
- (redex-error #f "metafunction ~s applied before its definition" name))
- (define-syntax (term-let-fn stx)
- (syntax-case stx ()
- [(_ ([f rhs] ...) body1 body2 ...)
- (with-syntax ([(g ...) (generate-temporaries (syntax (f ...)))])
- (syntax
- (let ([g rhs] ...)
- (let-syntax ([f (make-term-fn #'g)] ...)
- body1
- body2 ...))))]))
- (define-syntax (term-define-fn stx)
- (syntax-case stx ()
- [(_ id exp)
- (with-syntax ([id2 (datum->syntax #'here (syntax-e #'id))])
- (syntax
- (begin
- (define id2 exp)
- (define-syntax id
- (make-term-fn ((syntax-local-certifier) #'id2))))))]))
- (define-syntax (term-let/error-name stx)
- (syntax-case stx ()
- [(_ error-name ([x1 rhs1] [x rhs] ...) body1 body2 ...)
- (let-values ([(orig-names new-names depths new-x1)
- (let loop ([stx #'x1] [depth 0])
- (define ((combine orig-names new-names depths new-pat)
- orig-names* new-names* depths* new-pat*)
- (values (append orig-names orig-names*)
- (append new-names new-names*)
- (append depths depths*)
- (cons new-pat new-pat*)))
- (syntax-case stx (...)
- [x
- (and (identifier? #'x)
- (not (free-identifier=? (quote-syntax ...) #'x)))
- (let ([new-name (datum->syntax #'here (syntax-e #'x))])
- (values (list #'x)
- (list new-name)
- (list depth)
- new-name))]
- [(x (... ...) . xs)
- (let-values ([(orig-names new-names depths new-pat)
- (call-with-values
- (λ () (loop #'xs depth))
- (call-with-values
- (λ () (loop #'x (add1 depth)))
- combine))])
- (values orig-names new-names depths
- (list* (car new-pat) #'(... ...) (cdr new-pat))))]
- [(x . xs)
- (call-with-values
- (λ () (loop #'xs depth))
- (call-with-values
- (λ () (loop #'x depth))
- combine))]
- [_
- (values '() '() '() stx)]))])
- (with-syntax ([(orig-names ...) orig-names]
- [(new-names ...) new-names]
- [(depths ...) depths]
- [new-x1 new-x1]
- [no-match (syntax/loc (syntax rhs1)
- (error 'error-name "term ~s does not match pattern ~s" rhs1 'x1))])
- (syntax
- (syntax-case rhs1 ()
- [new-x1
- (let-syntax ([orig-names (make-term-id #'new-names (syntax-e #'depths))] ...)
- (term-let/error-name error-name ((x rhs) ...) body1 body2 ...))]
- [_ no-match]))))]
- [(_ error-name () body1 body2 ...)
- (syntax
- (begin body1 body2 ...))]
- [(_ x)
- (raise-syntax-error 'term-let "expected at least one body" stx)]))
- (define-syntax (term-let stx)
- (syntax-case stx ()
- [(_ () body1)
- #'body1]
- [(_ ([x rhs] ...) body1 body2 ...)
- (syntax
- (term-let/error-name term-let ((x rhs) ...) body1 body2 ...))]
- [(_ x)
- (raise-syntax-error 'term-let "expected at least one body" stx)]))