请查看以下宏用法。
(let ()
(define-syntax minus
(syntax-rules (from)
[(_ e1 from e2) (- e2 e1)]))
; a
(minus 1 from 2)
;; => 1
; b
(define from 3)
(minus from from 2)
;; => -1
; c
(minus 1 from 2)
;; => 1
; d
#;
(let ([from 1])
(minus 1 from 2))
;; bad syntax
)
我注意到from
宏中涉及的辅助语法minus
出乎意料。
在我看来from
可以在任意位置重新定义。但是,看起来d
的情况不起作用。
可以很容易地用Racket
复制。
有人知道为什么语法错误吗?
谢谢
编辑:
请注意,如果我稍加修改定义,它将再次有效。
(let ()
(define-syntax minus
(syntax-rules (from)
[(_ e1 from e2) (- e2 e1)]
[(_ e1 e2 _) (- e2 e1)]))
; a
(minus 1 from 2)
;; => 1
; b
(let ([from 1])
(minus 1 from 2))
;; => 0
)
由于free-identifier=?
用作辅助关键字的隐式保护,因此在from
不被视为关键字的情况下,b
不会被匹配。
答案 0 :(得分:3)
根据documentation的建议,syntax-rules
扩展为syntax-case
。确实,syntax-case
存在相同的问题:
#lang racket
(define-syntax (minus stx)
(syntax-case stx (from)
[(_ e1 from e2) #'(- e2 e1)]))
(let ([from 1])
(minus 1 from 2))
;; => minus: bad syntax in: (minus 1 from 2)
但是,有一个syntax-case
的变体,名为syntax-case*
。其文档指出:
类似于语法情况,但是id-compare-expr必须产生一个接受两个参数的过程。模式中的文字ID与标识符匹配,当给定要匹配的标识符(作为第一个参数)和模式中的标识符(作为第二个参数)时,过程将为true返回
。换句话说,语法情况类似于语法情况*,其id-compare-expr会生成free-identifier =?。
所以我们可以尝试一下:
#lang racket
(define-for-syntax (my-comparator a b)
(println a)
(println b)
(println (free-identifier=? a b))
(free-identifier=? a b))
(define-syntax (minus stx)
(syntax-case* stx (from) my-comparator
[(_ e1 from e2) #'(- e2 e1)]))
(minus 1 from 2)
;; => .#<syntax:unsaved-editor:13:9 from>
;; => .#<syntax:unsaved-editor:11:11 from>
;; => #t
(let ([from 1])
(minus 1 from 2))
;; => .#<syntax:unsaved-editor:19:11 from>
;; => .#<syntax:unsaved-editor:11:11 from>
;; => #f
;; => minus: bad syntax in: (minus 1 from 2)
如您所见,问题在于free-identifier=?
发现在后一种情况下两个from
不相等。
因此,要实现此目的,只需提供自己的不使用free-identifier=?
的比较器即可:
#lang racket
(define-for-syntax (my-comparator a b)
(eq? (syntax->datum a) (syntax->datum b)))
(define-syntax (minus stx)
(syntax-case* stx (from) my-comparator
[(_ e1 from e2) #'(- e2 e1)]))
(minus 1 from 2)
;; => 1
(let ([from 1])
(minus 1 from 2))
;; => 1
如果您无法使用syntax-case*
,也可以执行以下操作:
(define-syntax (minus stx)
(syntax-case stx ()
[(_ e1 from e2)
(eq? (syntax->datum #'from) 'from) ; a guard
#'(- e2 e1)]))
尽管当您有多个文字ID时,它会变得很乏味。