[racket] syntax-parse, macros, and literal-sets
I was writing a macro that generated a literal-set and ran into some
confusing behavior which I have distilled to the following program.
#lang racket
(require syntax/parse
(for-syntax syntax/parse))
(define-syntax (define-ls1 stx)
(syntax-parse stx
((_ name:id (lit:id ...))
#'(define-literal-set name (lit ...)))))
(define-ls1 ls1 (+))
(define-syntax-class sc1
#:literal-sets (ls1)
(pattern +))
(for/list ((x (list #'+ #'*)))
(syntax-parse x
(x:sc1 #t)
(_ #f)))
(define-syntax (define-sc2 stx)
(syntax-parse stx
((_ name:id (lit:id ...))
#'(begin
(define-literal-set inner (lit ...))
(define-syntax-class name
#:literal-sets (inner)
(pattern lit) ...)))))
(define-sc2 sc2 (+))
(for/list ((x (list #'+ #'*)))
(syntax-parse x
(x:sc2 #t)
(_ #f)))
(define-syntax (define-sc3 stx)
(syntax-parse stx
((_ name:id inner:id (lit:id ...))
#'(begin
(define-literal-set inner (lit ...))
(define-syntax-class name
#:literal-sets (inner)
(pattern lit) ...)))))
(define-sc3 sc3 inner3 (+))
(for/list ((x (list #'+ #'*)))
(syntax-parse x
(x:sc3 #t)
(_ #f)))
(define-syntax (define-sc4 stx)
(syntax-parse stx
((_ name:id (lit:id ...))
#'(begin
(define-literal-set inner (lit ...))
(define-syntax-class name
#:literal-sets ((inner #:at name))
(pattern lit) ...)))))
(define-sc4 sc4 (+))
(for/list ((x (list #'+ #'*)))
(syntax-parse x
(x:sc4 #t)
(_ #f)))
This produces the output:
'(#t #f)
'(#t #t)
'(#t #f)
'(#t #f)
I would have expected the second one to return '(#t #f) like the first
but it doesn't. The third and fourth are ways to get it to, but I'm
not sure why they work. The third seems to show that the meaning of
how a literal set binds variables depends on the context of the
literal-set name and not the actual literals. The fourth way uses #:at
which is documented as being useful to macros, but not much else. Can
someone explain how literal-sets are supposed to determine whether or
not a variable in the pattern is treated as a literal or as a pattern
variable?