Questions about nanopass use in DrRacket

53 views
Skip to first unread message

Matt J

unread,
Nov 7, 2017, 9:49:33 AM11/7/17
to nanopass-framework
Hi all,

A few questions. On one hand, I'm confident that staring at sample code and documentation will answer my questions. I'm also confident a pointer from someone in the community will move me along faster.

For context, I think I was in the first 523 course at IUB where this approach was introduced. So, I'm mostly wrestling with questions about how the macros behave.

I have a sloppy language definition. It has silly redundancies in it, but I'm playing.

(define-language LP
  (terminals
   (variable (var))
   (primitive (pr))
   (datum (d))
   (constant (c))
   (rhs (rhs))
   (atomic (a)))
  (Program ()
           (prog tp ...))
  (TLP (tp)
       (include c)
       (proc d ([d0 d1] ...) p)
       )
  (Process (p)
    (while e0 p)
    (if e0 p0 p1)
    (set var rhs)
    (send var rhs)
    (recv var rhs)
    (seq p0 p ...)
    (par p0 p ...)
    (decl ([var* rhs*] ...) p)
    a
    )
  (Expr (e)
    (pr e0 e1)
    c
    d)
  )

I want to write an LX -> LX pass.

(define-pass rename : LP (ir) -> LP ()
  (definitions
    (let ([counter 0])
      (define (uniq id)
        (set! counter (add1 counter))
        (string->symbol (format "~a.~a" id counter))))
    )
  (Process : Process (ir) -> Process ()
           [(decl ([,var* ,rhs*] ...) ,[p])
            (let* ([renamed (map uniq var*)]
                   [processed (map Expr rhs*)]
                   [combined (map (λ (v r) (list v r))
                                  renamed processed)])
              `(decl ,combined ,p))])
  (Expr : Expr (ir) -> Expr ())
           
  )

I have been reading through some of the sample compiler (it looks remarkably familiar, even all these years later...), and I'm probably not spending enough time thinking about the documentation. So, feel free to point me back at docs in answer to my questions.

Do I need empty... productions?... for the parts of the grammar that I want pass-through on? Eg. do I need the Expr empty transform, and also need empty Program and TLP transforms if those are supposed to be "default" or "pass-through" spaces in this particular pass?

The scoping of "counter" breaks something in the above example; if I make "counter" global, I then face the issue that 'decl' is considered an invalid pattern or template. 

(define counter 0)
(define-pass rename : LP (ir) -> LP ()
  (definitions
    
      (define (uniq id)
        (set! counter (add1 counter))
        (string->symbol (format "~a.~a" id counter)))
    )
  (Process : Process (ir) -> Process ()
           [(decl ([,var* ,rhs*] ...) ,[p])
            (let* ([renamed (map uniq var*)]
                   [processed (map Expr rhs*)]
                   [combined (map (λ (v r) (list v r))
                                  renamed processed)])
              `(decl ,combined ,p))])
  (Expr : Expr (ir) -> Expr ())
           
  )

Does this mean I'm generating an output that fails to match my specified output language? If so, is there a recommended approach to debugging, given that the issue is caught (I assume) as part of the define-pass macro? Is this because I'm generating symbols ("renamed"), and those are breaking my output? Or, something else entirely?

Finally, any thoughts about how to convince Dr Racket to indent these passes/language definitions nicely?

I'm sure I'll have more questions, but I thought I'd ask and see if there were any quick/easy answers to these questions from the community. I can put the rest of the code somewhere if it helps.

Many thanks,
Matt




Leif Andersen

unread,
Nov 7, 2017, 12:45:59 PM11/7/17
to Matt J, nanopass-framework
Oh boy, there is a lot to unpack here. But I *think* it boils down to
you want to know why this pattern isn't working:

```
[(decl ([,var* ,rhs*] ...) ,[p])
(let* ([renamed (map uniq var*)]
[processed (map Expr rhs*)]
[combined (map (λ (v r) (list v r))
renamed processed)])
`(decl ,combined ,p))])
```

And the answer is that the pattern matcher just isn't that smart
sadly. (At least not the one for Racket's version).

What you *could* do however is rewrite it as:

```
[(decl ([,var* ,rhs*] ...) ,[p])
(let* ([renamed (map uniq var*)]
[processed (map Expr rhs*)])
`(decl ([,renamed ,processed] ...) ,p))])
```

Which should work.

This stems from your original pattern for `decl`, which is:
```
(decl ([var* rhs*] ...) p)
```

Therefor when you are constructing a decl in the template of a pass,
it expects your code to be of that form. While you and I know that
combined is a zipped list, the pattern matcher is not that clever, so
you have to help it out a little.

> Do I need empty... productions?... for the parts of the grammar that I want pass-through on? Eg. do I need the Expr empty transform, and also need empty Program and TLP transforms if those are supposed to be "default" or "pass-through" spaces in this particular pass?

If you are asking what I think you are asking, the answer is no, you
usually don't need empty productions for non-terminals. There are a
few cases where you might, but those don't apply here.

Hope that helps.

~Leif Andersen
> --
> You received this message because you are subscribed to the Google Groups
> "nanopass-framework" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to nanopass-framew...@googlegroups.com.
> To post to this group, send email to nanopass-...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/nanopass-framework/d06cb242-e96c-4c6e-9b77-ae5022525f31%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Matt Jadud

unread,
Nov 7, 2017, 1:20:22 PM11/7/17
to Leif Andersen, nanopass-framework
Many thanks, Leif.

Your intuition on the first part (the output pattern) was correct. It makes sense why it works, but I wouldn't have thought about it. Or, now that I think about it, it makes sense that the framework is walking the output patterns to make sure they conform to the output language. 

It appears, at the moment, that I need to include:

(Expr : Expr (ir) -> Expr ())

or Expr is unknown when I attempt to apply it in my 'map.'

However, I'll continue to play. I'm very glad to see the port to Racket, and if nothing else will contribute use experiences as I explore. Now, I just need an excuse to "remove-complex-opera-*".

Cheers,
Matt


> email to nanopass-framework+unsub...@googlegroups.com.
> To post to this group, send email to nanopass-framework@googlegroups.com.

Leif Andersen

unread,
Nov 7, 2017, 1:50:18 PM11/7/17
to Matt Jadud, nanopass-framework
Ah, okay. The reason you needed to name `Expr` here is because the one
nanopass creates does not have a name (at least not one easy to
access). So you have two options:

1. Create an empty one like you did, thus giving it a name.
2. Use Nanopass's catamorphisms.

If you go with #2, your pattern will look something like this:

```
[(decl ([,var* ,[rhs*]] ...) ,[p])
(let* ([renamed (map uniq var*)])
`(decl ([,renamed ,rhs*] ...) ,p))])
```



~Leif Andersen
>> > email to nanopass-framew...@googlegroups.com.
>> > To post to this group, send email to
>> > nanopass-...@googlegroups.com.
> --
> You received this message because you are subscribed to the Google Groups
> "nanopass-framework" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to nanopass-framew...@googlegroups.com.
> To post to this group, send email to nanopass-...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/nanopass-framework/CAAGM454wXeku4RSuc%3DktNxE8V3WkCzUBbF3X1BVMyg7N1bzNsQ%40mail.gmail.com.

Shiwei Weng

unread,
Nov 30, 2017, 1:11:57 PM11/30/17
to nanopass-framework
Thanks for your explanation and I want to ask a follow-up question here on `catamorphisms` .

How could I use `catamorphisms` in define-pass when there are intermediate nodes?

In this case,

``` 

           [(decl ([,var* ,[rhs*]] ...) ,[p]) 
            (let* ([renamed (map uniq var*)]) 
              `(decl ([,renamed ,rhs*] ...) ,p))]) 
``` 
we can read `p` from the pattern and use it in the result.

How about when I have an Expression, which contains Clauses, which contains Function, which contains Expression in its body. I want to express the idea that for any Expression, which is a top node or inside Function body, use a processor for that?

My current approach is to write many processors to any occurrences of Expression.
>> > To post to this group, send email to
>> > nanopass-...@googlegroups.com.
>> > To view this discussion on the web visit
>> >
>> > https://groups.google.com/d/msgid/nanopass-framework/d06cb242-e96c-4c6e-9b77-ae5022525f31%40googlegroups.com.
>> > For more options, visit https://groups.google.com/d/optout.
>
>
> --
> You received this message because you are subscribed to the Google Groups
> "nanopass-framework" group.
> To unsubscribe from this group and stop receiving emails from it, send an
Reply all
Reply to author
Forward
0 new messages