Reputation: 38721
I'd like to inject special variables into a macro for compile time binding.
So, for example, the following form:
(define-route (my/route)
(some-proc @@request)
(some-other-proc @@request))
should expand to:
(some-code)
...
(lambda (request)
...
(some-proc request)
(some-other-proc request))
where request is bound to the request in the generated code, at all the places specified by the caller.
I'm not sure how to best go about it. Is this a more common pattern that racket macros provide the ability to do, or do I have to do it manually?
If so, should I recursively loop through the body pattern, looking for any symbols matching '@@request and then replace them? I notice that syntax->list isn't recursive in this way, ie
(syntax->list #'(printf "first: ~a\" (get-var (other-proc)))
is 3 syntax objects not 4, and the third syntax-obj is 2 more. So I assume I need to manually recur here, but maybe there is a better way to do this?
Thanks for your help.
Upvotes: 4
Views: 239
Reputation: 12003
Perhaps using syntax-parameterize (and its variant, splicing-syntax-parameterize) should do the trick? It's a standard tool when you want to have keyword-like things, and where the macro needs to rewire the keyword's meaning based on the context.
Here's what it looks like:
#lang racket/base
(require racket/stxparam
racket/splicing
(for-syntax syntax/parse racket/base))
(define-syntax-parameter @@request
(lambda (stx)
(raise-syntax-error #f "Use outside of a define-route" stx)))
(define-syntax (define-route stx)
(syntax-parse stx
[(_ (name:id) body:expr ...)
(syntax/loc stx
(begin
(printf "blah blah blah\n")
(define name
(lambda (request)
(splicing-syntax-parameterize ([@@request
(make-rename-transformer #'request)])
body ...)))))]))
;; example
(define (f r)
(printf "f sees: ~s\n" r))
(define-route (my/route)
(f @@request))
(my/route 42)
The syntax-parameterization rewires uses of @@request
in the lexical context of a define-route
so that it refers back to the request
that's being bound by the lambda
. If used outside the context of a define-route
, it'll try to raise a sensible error message.
(You could do this the way you initially planned, to recursively walk though the structure of the syntax object and pick out the @@request
elements. It's just more work than you need to do, unless the requirements are really unusual.)
Upvotes: 6