变量转换器是否可以使用非文字标记?

Is it possible for a variable transformer to work with non-literal tokens?

make-variable-transformer(或 make-set!-transformer,在 Racket 中是这样称呼的)可以使用硬编码在宏定义的文字列表中的标识符。当提到变量转换器时,下面带有 set! 的例子总是会出现:

(make-variable-transformer
  (λ (stx)
    (syntax-case stx (set!)
      ((set! id _) ...)
      (id ...))))

这很好,对于透明地将外部结构与提前已知的原始操作集成很有用,而且它可以通过标识符语法和重命名转换器工作,这是一个好处。

但我想知道是否可以像这样动态地使用语法:

(let-syntax ((@ (make-variable-transformer
                  (λ (stx)
                    (syntax-case stx ()
                     ((v @ i) (vector? #'v) #'(vector-ref v i)))))))
  (#(0 1 2) @ 1))
=> 1

这不起作用,因为宏调用与模板不匹配,因为 syntax-case 期望 @ 处于初始位置,因为文字列表中没有 v (并且它可能将 @ 分配给 v 模式变量)。

简而言之:是否可以编写一个语法转换器来完成此操作而无需 reader 扩展或覆盖应用程序,也许通过重写内部 syntax-case 的文字标记列表的元宏( à la Petrofsky 提取)?

注意:矢量示例本身并不重要,我对这个确切用例的替代解决方案不感兴趣。

since there's no v in the literal list (and it probably assigns @ to v pattern variable).

不是真的。 set!是宏扩展器专门处理的一个special case,使其与make-variable-transformer配合。但是对于其他文字,它们将失败。例如,

(let-syntax ((@ (make-variable-transformer
                 (λ (stx)
                   (syntax-case stx (v)
                     ((v @ i) #'1))))))
  (v @ 1))

失败 v: unbound identifier

上面代码的第二个问题是副条件(vector? #'v)#'v 是一个语法对象,因此 (vector? #'v) 将始终导致 #f。目前还不清楚什么是正确的行为。例如,您打算:

(define v (vector 1 2 3))
(v @ 1)

上班?如果是这样,编译时的边条件将是不合适的,因为在编译时不知道 v 是否是向量。


对于你的主要问题,答案是否定的。在您施加的限制下,这是不可能的。 expansion steps 在这里详细介绍,none 的步骤超出了列表的头部。

但是如果我们不关心约束的话。即,覆盖 #%app 是可以的。它可以工作。

您需要考虑的一个问题是,假设您有 (a b c),其中 b 是您的宏类型,而 a 是常规宏。谁应该首先获得控制权?如果 a 应该首先获得控制权,您可以覆盖 #%app 来实现这种宏。这是我的快速实现。

#lang racket

(require syntax/parse/define
         (only-in racket [#%app racket:#%app])
         (for-syntax syntax/apply-transformer))

(begin-for-syntax
  (struct my-transformer (t)))

(define-syntax-parser #%app
  [(_ x ...)
   (define transformer
     (for/first ([operand (attribute x)]
                 #:when (and (identifier? operand)
                             (my-transformer?
                              (syntax-local-value operand (λ () #f)))))
       
       (syntax-local-value operand)))
   (cond
     [transformer (local-apply-transformer
                   (my-transformer-t transformer)
                   #'(x ...)
                   'expression)]
     [else #'(racket:#%app x ...)])])

(define-syntax @
  (my-transformer
   (syntax-parser
     [(v _ i) #'(vector-ref v i)])))

(define v (vector 42 1337 1729))
(v @ 1) ;=> 1337

最后,您始终可以覆盖 #%module-begin 并模拟宏扩展器。这是一个矫枉过正的解决方案,但如果您想要更高级的功能,例如允许用户自定义优先级,以便 ba.

之前展开,则它可能是合适的