[plt-scheme] Can generated lexical (syntax) bindings work?

From: Brian Chin (naerbnic at ucla.edu)
Date: Mon Apr 9 05:40:01 EDT 2007

Hello, all

First, thanks for your help. I somehow skipped over the 'make-rename- 
transformer' function when I first read the docs.

I tried your approach, but I can't seem to do precisely what you're  
asking. The most obvious problem is how to insert the syntax object  
(not the symbol) into the registration call inside begin-for-syntax.  
Without that, all I can do is recreate the fresh identifier of the  
top-level syntax element from the symbol, which doesn't seem to be  
working (getting a "reference to undefined identifier: <fresh id  
name>" error). Any help you can give clarifying the reason for this  
problem would be quite helpful.

The code follows. It's a little long, but hopefully its clear enough:

(begin-for-syntax
   (define *context-table* (make-hash-table))

   (define (ensure-context context-name)
     (if (eq? (hash-table-get *context-table*
                              context-name
                              'failed)
              'failed)
         (hash-table-put! *context-table*
                          context-name
                          (make-hash-table))))

   (define (get-context context-name)
     (hash-table-get *context-table*
                     context-name))

   (define (add-syntax-id-to-context context name fresh-id)
     (ensure-context context)
     (hash-table-put! (get-context context)
                      name
                      fresh-id)))

(define-syntax (define-syntax-in-context stx)
   (syntax-case stx ()
     ;; To handle terms like (define-syntax (foo stx) ...)
     ((_ (term args ...) context body ...)
      (syntax (define-syntax-in-context term context (lambda  
(args ...) body ...))))
     ;; Handles terms which just define the name (define-syntax foo ...)
     ((_ name context transformer)
      (with-syntax (((fresh-id) (generate-temporaries '(fresh-top- 
level))))
        (syntax (begin
                  (define-syntax fresh-id transformer)
                  (begin-for-syntax
                    (add-syntax-id-to-context 'context 'name 'fresh- 
id))))))))

(define-syntax (with-context stx)
   (syntax-case stx ()
     ((_ context body ...)
      (let* ((context-id (syntax context))
             (context-bindings-list
              (hash-table-map (get-context (syntax-object->datum  
context-id))
                              (lambda (name value)
                                (with-syntax ((syntax-name-id (datum- 
 >syntax-object context-id name))
                                              (transformer (make- 
rename-transformer
                                                            (datum- 
 >syntax-object context-id value))))
                                  (syntax (syntax-name-id  
transformer)))))))
        (with-syntax (((bindings ...) context-bindings-list))
          (syntax (let-syntax (bindings ...) body ...)))))))

(define-syntax-in-context foo my-context (syntax-rules () ((foo) 5)))

(with-context my-context (foo))

Thank you very much for your help,
- Brian Chin

On Apr 7, 2007, at 6:40 PM, Matthew Flatt wrote:

> I recommend:
>
>  1. Have `define-syntax-in-context' bind a fresh name to the
>     transformer using `define-syntax', and also add a mapping in the
>     table from the context name and definition symbol to a syntax
>     object for the fresh name.
>
>     Do not have the `define-syntax-in-context' macro perform the table
>     registration directly. Instead, have it expand to
>     `begin-for-syntax' to perform the registration.
>
>  2. Have `with-context' expand to a `let-syntax' that uses
>     `make-rename-transformer' to map the defined name to the fresh  
> name
>     generated by `define-syntax-in-context'.
>
>     To generate the identifier bound by `let-syntax', use
>     `datum->syntax-object' with the context identifier supplied to
>     `with-context' and the symbol for the defined name.
>
> Using `make-rename-transformer' is the key part of this strategy. It
> only works when the context for a use is nested within the context for
> a definition (where all module top levels count as the same top level
> for this notion of "nesting"); my guess is that's fine for your
> purposes.
>
> If it doesn't work out, post the code for your attempt, and we can  
> help
> more from there.
>
> Matthew
>
> At Fri, 6 Apr 2007 13:27:22 -0700, Brian Chin wrote:
>> Hey all, I have a bit of a long problem (with a possibly shorter
>> answer). I'm trying to write a small module which allows you to
>> define a syntax definition in a separate context, so that it isn't
>> actually defined at the top level. Later when you want to use it, you
>> can declare that some syntax be run in that context, thus enabling
>> those syntax bindings. A quick example:
>>
>> (define-syntax-in-context foo my-context
>>    (syntax-rules () ((foo) 5)))
>>
>> (foo) ; Syntax error, foo is not defined
>>
>> (with-context my-context (foo)) ; Equivalent to just 5
>>
>> Now, the problem I'm having is in saving the symbol foo from the
>> define-syntax-in-context call to actually bind to the "foo" in the
>> with-context call. Most of the approaches I've tried hasn't allowed
>> with-context to generate a new lexical binding for "foo". This is
>> definitely non-hygienic, but it seems like it should still be
>> possible. I've tried three things, all of which save information
>> about mappings from (context, syntax-name) -> transformer in one
>> fashion or another:
>>
>> 1. define-syntax-in-context saves the transformer syntax object in a
>> hash table keyed by the context symbol and the syntax name symbol,
>> both extracted using syntax-object->datum. With-context generates a
>> let-syntax form, and generates new syntax objects for the syntax-name
>> using datum->syntax-object. This doesn't seem to work, as (foo) is
>> still being interpreted as a top-level form.
>>
>> The code it's generating is something like this:
>>
>> (let-syntax ((foo (syntax-rules () ((foo) 5)))) (foo))
>>
>> But it's still failing.
>>
>> 2. define-syntax-in-context effectively executes the transformer
>> expression, creating the actual transformer function. It also
>> declares a top-level macro with the same name. When that macro is
>> called, it looks up the current context from a global variable with
>> dynamic scope, and then applies the appropriate transformer. This
>> works, but since this requires a top-level syntax declaration and
>> uses dynamic scope, I find this solution unsatisfying.
>>
>> 3. Similar to the original, I save the transformer syntax objects in
>> a hash table. Instead of making let-syntax terms, I create a syntax
>> environment using syntax-local-make-definition-context and syntax-
>> local-bind-syntaxes, and apply it to the body using local-expand, and
>> return the result. This seems to work part of the time, as long as
>> the with-context is being called in the module where the syntax was
>> originally declared. However, it fails at weird times. For instance,
>> in this same macro, I made another syntax object which uses with-
>> context to process one set of syntax multiple times in different
>> contexts. That doesn't seem to work.
>>
>> It seems that one of my troubles relates to the actual generated
>> symbol syntax-object made from datum->syntax-object, possibly related
>> to the context syntax object I have to pass. Am I making some sort of
>> silly mistake, or is there something wrong with the way I'm
>> approaching this problem?
>>
>> Thanks in advance,
>> - Brian Chin
>



Posted on the users mailing list.