[plt-scheme] Can generated lexical (syntax) bindings work?
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
>