scheme - 方案 - 重新定义辅助关键字后语法错误
问题描述
请看下面的宏用法。
(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
in caseb
视为关键字,则不会匹配第一个 case。
解决方案
正如文档所建议的那样,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
named有一个变体syntax-case*
。其文档指出:
与 syntax-case 类似,但 id-compare-expr 必须生成一个接受两个参数的过程。模式中的literal-id 匹配一个标识符,当给定要匹配的标识符(作为第一个参数)和模式中的标识符(作为第二个参数)时,过程返回true。
换句话说,syntax-case 就像带有 id-compare-expr 的 syntax-case*,它产生 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 时会变得乏味。
推荐阅读
- ios - 如何使用移动箭头创建滑动开始按钮
- c# - 我想将列数据拆分为不同的列
- python - 尝试使用 Tkinter 在 python 中显示图像
- angular - Angular 中的过渡
- elasticsearch - 如何在 ElasticSearch 中实现此类查询?
- laravel - 如何使用 octobercms 创建待办事项列表?
- azure - AKS 上托管的 kubernetes 集群中 docker 网桥地址的意义是什么?
- powershell - 将批处理命令重写为 Jenkins 构建步骤的 Powershell
- react-native - 如何将访问令牌从 API 存储到异步存储
- ruby-on-rails - Rails:我可以直接通过连接引用表吗?