What does #:track-literals do?

What does #:track-literals do in syntax-parse? Why doesn't define-syntax-class also have #:track-literals?

2 Likes

Literals and disappeared-uses

Suppose you have a macro like cond that recognizes the literal identifier else and treats it specially. In Racket, literal identifiers are recognized by binding, so else is defined somewhere and exported from the racket language, etc.

When you use cond and have an else clause, you'd like DrRacket to tell you that else comes from racket. On the other hand, DrRacket should not say else comes from racket in the program (let ([else 5]) (add1 else)) or in the program (quote (if then else))---neither of those are references to Racket's else. So how should DrRacket decide what to say?

It has to look at the expanded program, because in Racket expansion is when scoping and references get figured out. So it looks at the expanded program, and whenever it sees a reference to a variable (like add1) or primitive syntax (like quote) that corresponds to an original identifier, it records the source of the identifier and its origin (see identifier-binding). But cond and else aren't in the expanded program. So there's a convention: when the macro expander expands cond, it adds the cond identifier to a syntax property named 'origin on the result expression, and DrRacket looks there too. So that's how it knows to talk about cond. There's another convention: the cond macro records the else identifier in a different syntax property called 'disappeared-uses on the syntax it expands to, and DrRacket looks there too.

syntax-parse and #:track-literals

If a macro doesn't bother to add the 'disappeared-uses property for a literal, DrRacket doesn't see it. The macro expander cannot automatically track literals like else, because the knowledge of their interpretation is inside of the individual macros, but syntax-parse has patterns and features dedicated to literals, so it can automatically record literals that occur in the input, in a backtracking-safe way. If you include #:track-literals directive, then it adds those recorded literals to the 'disappeared-uses property on the result. (It doesn't do so by default because the result of a syntax-parse expression does not have to be syntax, even though that's the most common use.) The define-syntax-class form doesn't have the option because the 'disappeared-use property is customarily only placed on the macro's final result.

tl,dr: Typically, a syntax-parse form that implements a macro should use #:track-literals.

8 Likes

Lucid. This would be a great addition to the syntax-parse documentation. Thank you.

1 Like