[plt-scheme] Detecting cycles when traversing through shared graph syntax objects?

From: Robby Findler (robby at cs.uchicago.edu)
Date: Wed Apr 12 17:58:04 EDT 2006

I think that in your case, you can take advantage of the fact that
cycles are really only legal in in a quoted datum. Note that mzscheme's
compiler also fails to terminate for actually cyclic code:

Welcome to MzScheme version 301.12, Copyright (c) 2004-2006 PLT Scheme Inc.
> (define x #1=(list #1#))
... wait forever.

So just have a more complete pattern match in the syntax case (see the
documentation for `expand' to get the entire data definition).

Robby

At Wed, 12 Apr 2006 14:49:54 -0700 (PDT), Danny Yoo wrote:
> Hi everyone,
> 
> I've been recently trying to understand how people deal with graphs in 
> syntax objects.  One example of this is:
> 
> 
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> (module test-evil mzscheme
>    (require (lib "stx.ss" "syntax")
>             (lib "list.ss"))
> 
>    (define nice-syntax-object (syntax (hello (world))))
> 
>    ;; The syntax object representing an infinite series of ones.
>    (define evil-syntax-object
>      (read-syntax
>       #f
>       (open-input-string "#0=(1 . #0#)")))
> 
> 
>    ;; Go through syntax, return list of all identifiers we see, free or
>    ;; bound.
>    (define (find-identifiers-broken stx)
>      (let ([seen-stxs (make-hash-table)])
>        (let loop ([stx stx])
>          (cond [(hash-table-get seen-stxs stx (lambda () #f))
>                 empty]
>                [else
>                 (hash-table-put! seen-stxs stx #t)
>                 (syntax-case stx ()
>                   [_ (identifier? stx)
>                      (list stx)]
>                   [(x y ...)
>                    (append (loop #'x)
>                            (loop #'(y ...)))]
>                   [else empty])])))))
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> 
> 
> Trying to evaluate find-identifiers-broken on evil-syntax-object is an 
> infinite loop. I naively thought that keeping some hash-table to traverse 
> the structure would allow me to avoid cycles, but unfortunately, nope. 
> My question is: how do people handle this?
> 
> (This actually seems like a problem for other things like the DrScheme 
> repl, which doesn't appear to capture the cycles either when it tries to 
> display the evil-syntax-object.)
> 
> I see that Help Desk's documentation on syntax-graph? recommends using 
> syntax-object->datum to detect cycles, so I suppose I could do some kind 
> of parallel traversal of the syntax object and its datum partner.
> 
> Ah.  This appears to do the trick:
> 
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
>    (define (find-identifiers stx)
>      (let ([seen-data (make-hash-table)])
>        ;; subtle: we can't reliably do eq? on syntax to detect sharing,
>        ;; but we CAN do it on the syntax-object->datum-ed value.  So
>        ;; we keep both, and do a parallel traversal.
>        (let loop ([stx stx]
>                   [datum (syntax-object->datum stx)])
>          (cond [(hash-table-get seen-data datum (lambda () #f))
>                 empty]
>                [else
>                 (hash-table-put! seen-data datum #t)
>                 (cond
>                   [(identifier? stx)
>                    (list stx)]
>                   [(stx-pair? stx)
>                    (append (loop (stx-car stx)
>                                  (car datum))
>                            (loop (stx-cdr stx)
>                                  (cdr datum)))]
>                   [else empty])]))))
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> 
> Am I understanding the situation properly?
> 
> 
> Thanks in advance!
> 
> _________________________________________________
>   For list-related administrative tasks:
>   http://list.cs.brown.edu/mailman/listinfo/plt-scheme


Posted on the users mailing list.