Racket - 将特殊变量注入宏代码

时间:2013-01-14 23:00:50

标签: macros racket

我想将特殊变量注入宏中以进行编译时绑定。

因此,例如,以下形式:

(define-route (my/route)
  (some-proc @@request)
  (some-other-proc @@request))

应扩展为:

(some-code)
...
(lambda (request)
  ...
  (some-proc request)
  (some-other-proc request))

其中请求绑定到生成的代码中的请求,在调用者指定的所有位置。

我不确定如何做到最好。这是一种更常见的模式,球拍宏提供了做的能力,还是我必须手动完成?

如果是这样,我是否应该递归遍历主体模式,寻找符合'@@ request的符号,然后替换它们?我注意到syntax-> list不是以这种方式递归的,即

(syntax->list #'(printf "first: ~a\" (get-var (other-proc)))

是3个语法对象而不是4个,第三个syntax-obj是2个。所以我假设我需要在这里手动重复,但也许有更好的方法来做到这一点?

感谢您的帮助。

1 个答案:

答案 0 :(得分:4)

也许使用syntax-parameterize(及其变体,splicing-syntax-parameterize)应该可以做到这一点?当你想拥有类似关键字的东西时,它是一个标准的工具,宏需要根据上下文重新连接关键字的含义。

这是它的样子:

#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)

语法参数化在@@request的词汇上下文中重新使用define-route,以便它返回requestlambda约束的define-route。如果在@@request的上下文之外使用,它将尝试提出合理的错误消息。

(你可以按照你最初计划的方式执行此操作,以递归方式遍历语法对象的结构并选择{{1}}元素。这比你需要的工作更多除非要求真的不寻常。)