scheme-重新定义辅助关键字后语法错误

时间:2019-02-25 21:29:10

标签: scheme racket

请查看以下宏用法。

(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不会被匹配。

1 个答案:

答案 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时,它会变得很乏味。