变量转换器是否可以使用非文字标记?
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
并模拟宏扩展器。这是一个矫枉过正的解决方案,但如果您想要更高级的功能,例如允许用户自定义优先级,以便 b
在 a
.
之前展开,则它可能是合适的
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
并模拟宏扩展器。这是一个矫枉过正的解决方案,但如果您想要更高级的功能,例如允许用户自定义优先级,以便 b
在 a
.