[racket] syntax-parse #:at

From: Jon Rafkind (rafkind at cs.utah.edu)
Date: Mon May 21 00:42:30 EDT 2012

The following example has some tricky hygiene issues that I don't quite understand. The point of the code is to have a form that creates macros by analyzing the pattern and template.

The issue is why do I have to use a name other than 'name' for the pattern variable on the line where it says 'HERE'. If I use the 'name' pattern variable then the syntax-parse form generated by 'make-transformer' will use the wrong lexical context for the literal set. I don't see how the lexical context of the outer 'name' differs from the inner 'new-name', other than the 'new-name' has an extra mark on it. Is that the issue? I tried all sorts of debugging techniques but I can't tell if 'name' has a different lexical context than 'new-name'. I see that marks affect 'bound-identifier=?' but they don't seem to effect 'free-identifier=?' which is what syntax/parse uses to compare literals.

#lang racket

(require (for-syntax syntax/parse)
         (for-meta 2 racket/base
                   syntax/parse))

;; defines a literal and a literal-set that contains the literal
(module literals racket/base
  (require syntax/parse)
  (provide literal1 literals)
  (define literal1 0)
  (define-literal-set literals (literal1)))

(module transformer racket/base
  (require (for-syntax syntax/parse
                       (submod "." ".." literals)
                       racket/base)
           racket/splicing
           (for-meta 2 syntax/parse
                     racket/base))

  (begin-for-syntax
    (define-syntax (make-transformer stx)
      (syntax-parse stx
        [(_ name pattern template)
         #'#'(lambda (stx)
             (syntax-parse stx
               #:literal-sets ([literals #:at name])
               [pattern template]))])))

  (provide define-new-syntax)
  (define-syntax (define-new-syntax stx)
  (syntax-parse stx
    [(_ name pattern template)
     #'(splicing-let-syntax
         ([make (lambda (stx)
                  (syntax-parse stx
                    [(_ new-name) ;; HERE! If you change this to 'name' it will break
                     (with-syntax ([output (make-transformer name pattern template)])
                       #'(define-syntax new-name output))]))]) ; change to 'name' too
         (make name))])))

(require (submod "." literals)
         (for-syntax (submod "." literals))
         (for-meta 2 (submod "." literals))
         (submod "." transformer))

(begin-for-syntax
  (define-syntax (test stx)
    (syntax-parse stx
      #:literal-sets (literals)
      [(_ literal1) #''ok])))

;; if `literal1' isn't matched as a literal then the call to 
;; (test literal1) will fail
(define-new-syntax x
                   [_ literal1]
                   (begin
                     (test literal1)
                     #'0))

(x literal1)

More explanation about this code:

It was recommended to me that the code which analyzes the pattern and template should execute at a phase higher than the phase of the pattern/template. Since the macro is defined at phase 0 the pattern and template live in phase 1 and should be analyzed in phase 2. I cannot just invoke a macro inside the body of 'define-new-syntax' on the 'pattern' and 'template' parameters because then the macro would get the literal syntax objects #'pattern and #'template instead of the syntax they are bound to. As in

(define-syntax (define-new-syntax stx)
  (syntax-parse stx
    [(_ name pattern template)
     (analyze pattern template)]))

Nor would it be right to make 'analyze' a function at phase 1 and invoke it as (analyze #'pattern #'template) because I need to execute analyze at phase 2.

I get to phase 2 by using a let-syntax trampoline in the output of the 'define-new-syntax' macro. The 'pattern' and 'template' variables will have been replaced with their matched syntax as appropriate so the 'make-transformer' macro will operate on the arguments used at the 'define-new-syntax' use-site.

The resulting 'x' macro does nothing except invoke a phase 1 macro. This test is necessary to insure that 'literal1' was matched as a literal and not a pattern variable.



On 05/17/2012 02:52 PM, Jon Rafkind wrote:
> Ok I see from the syntax/parse source that it uses lctx as the first argument to datum->syntax, so that mostly answers my question.
>
> On 05/17/2012 02:41 PM, Jon Rafkind wrote:
>> What exactly are the semantics of #:at inside a #:literal-sets ([x #:at y]) ?
>>
>> From the docs " If the #:at keyword is given, the lexical context of the lctx term is used to determine which identifiers in the patterns are treated as literals;". In what way is 'lctx' used?
>>
>>
>> ____________________
>>   Racket Users list:
>>   http://lists.racket-lang.org/users
>
>
>
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20120520/0eee8bdc/attachment-0001.html>

Posted on the users mailing list.