我正在使用Racket宏扩展syntax-id-rules
,其他一些Scheme实现以名称identifier-syntax
提供。这些允许您指定即使定义的标识符不在头部位置也会发生的宏扩展。例如:
(define hidden #f)
(define-syntax proxy
(syntax-id-rules (set!)
[(set! proxy v) (set! hidden v)]
[proxy hidden]))
会将标识符proxy
设置为hidden
的代理。这是一个无用的例子,但它说明了用法。
我发现自己处于这样一种情况:我想要一个全局普通的宏,让我们称之为foo
,我想在我使用标识符宏的某些情况下覆盖proxy
{ {1}}。也就是说,我希望能够做到这样的事情:
(define-syntax foo
(syntax-rules ()
[(foo arg ...) 'default]))
(define hidden #f)
(define-syntax proxy
(syntax-id-rules (foo set!)
[(foo proxy arg ...) 'special]
[(set! proxy v) (set! hidden v)]
[proxy hidden]))
(foo proxy) ; should return 'special
但实际上最后一行会返回'default
,因为foo
宏会在proxy
之前展开。
我是如何在这些方面实现某些目标的,但是使用proxy
标识符宏覆盖foo
的默认宏定义?我并没有特别致力于上述架构。
补充:这不适用于任何实际使用,但是在形式语义学中证明理论点的一部分。
答案 0 :(得分:3)
在我看来,你需要找到另一种策略。如果您提供有关您想要使用它的情况的更多详细信息,我们可以找到解决方案。
无论如何,这就是为什么你的策略不起作用的原因。当你写
(define-syntax proxy ...)
将语法变换器与标识符proxy
相关联。扩展器在看到(proxy ...)
,(set! proxy ...)
或proxy
时调用该变换器。
为了控制(foo proxy arg ...)
展开的内容,您需要在与foo
相关联的语法转换器中指定它。
现在视情况而定,可能会有一些技巧可以播放。
例如,可以想象用新表单包装程序,将(foo proxy arg ...)
重写为(proxy 'was-a-foo-originally arg ...)
,然后让proxy
的语法变换器处理其余部分。
简单的解决方案是将(foo proxy arg ...)
的处理移至foo
的变换器中,但您明确要求找到foo
未更改的解决方案。
答案 1 :(得分:3)
@soegaard完美地解释了这一点。如果不修改宏扩展器,则无法直接执行所需操作。
为了扩展@ soegaard的答案,这里有一种模拟你要求的方法。它本质上是一个“双重调度”的宏观扩张。正如soegaard所指出的那样,根据你的目标,可能有更多惯用的方式来实现你想要的目标。
#lang racket
(require (for-syntax syntax/parse))
(begin-for-syntax
(define (special-condition? id)
(and (identifier? id)
(regexp-match #rx"^p" ; starts with "p"
(symbol->string (syntax->datum id))))))
(define-syntax foo
(syntax-parser
[(_ special-case arg ...)
#:when (special-condition? #'special-case)
#'(special-case 'hidden-special-case-tag arg ...)]
; else
[(_ arg ...) #''default]))
(define hidden #f)
(define-syntax proxy
(syntax-id-rules (quote set!)
[(proxy (quote hidden-special-case-tag) arg ...) 'special]
[(set! proxy v) (set! hidden v)]
[(proxy arg ...) 'other]
[proxy hidden]))
(foo non-proxy) ; => 'default
(foo proxy) ; => 'special
(proxy) ; => 'other
proxy ; => #f
(set! proxy #t)
proxy ; => #t