(module dmhelp mzscheme (require syntax/stx) (provide dm-syntax->datum dm-subst) ;; `dm-syntax->datum' is like syntax-object->datum, but it also ;; builds a hash table that maps generated data to original syntax ;; objects. The hash table can then be used with `dm-subst' to ;; replace each re-used, unmodified datum with the original syntax ;; object. (define (dm-syntax->datum stx ht) ;; Easiest to handle cycles by letting `syntax-object->datum' ;; do all the work. (let ([v (syntax-object->datum stx)]) (let loop ([stx stx][v v]) (let ([already (hash-table-get ht v (lambda () #f))]) (if already (hash-table-put! ht v #t) ;; not stx => don't subst later (hash-table-put! ht v stx)) (cond [(stx-pair? stx) (loop (stx-car stx) (car v)) (loop (stx-cdr stx) (cdr v))] [(stx-null? stx) null] [(vector? (syntax-e stx)) (for-each loop (vector->list (syntax-e stx)) (vector->list v))] [(box? (syntax-e stx)) (loop (unbox (syntax-e stx)) (unbox v))] [else (void)]))) v)) (define (dm-subst ht v) (define cycle-ht (make-hash-table)) (let loop ([v v]) (if (hash-table-get cycle-ht v (lambda () #f)) v (begin (hash-table-put! cycle-ht v #t) (let ([m (hash-table-get ht v (lambda () #f))]) (cond [(syntax? m) m] ;; subst back! [(pair? v) (cons (loop (car v)) (loop (cdr v)))] [(vector? v) (list->vector (map loop (vector->list v)))] [(box? v) (box (loop (unbox v)))] [else v])))))))