scheme-引号和辅助语法

scheme-引号和辅助语法,scheme,racket,Scheme,Racket,由于某些原因,以下宏将无法与报价单一起使用 (define-syntax expand (lambda (stx) (syntax-case stx (break) [(_ k () (ys ...)) (begin (println (syntax->datum #'(ys ...))) #'(ys ...))] [(_ k ((break) xs ...) (ys ...)) #'(expand k (xs ...) (ys ...

由于某些原因,以下宏将无法与报价单一起使用

(define-syntax expand
    (lambda (stx)
      (syntax-case stx (break)
        [(_ k () (ys ...)) (begin (println (syntax->datum #'(ys ...))) #'(ys ...))]
        [(_ k ((break) xs ...) (ys ...)) #'(expand k (xs ...) (ys ... (k (void))))]
        [(_ k ((es ...) xs ...) (ys ...)) #'(expand k (xs ...) (ys ... (expand k (es ...) ())))]
        [(_ k (a xs ...) (ys ...)) #'(expand k (xs ...) (ys ... a))])))

(define-syntax loop
    (syntax-rules ()
      [(_ e es ...)
       (call/cc (lambda (k)
                  (let l ()
                    (expand k (begin e es ...) ())
                    (l))))]))

(loop (list 1 (break)))
;; => works fine 

(loop (quasiquote (1 (unquote (break)))))
;; => break: unbound identifier in module in: break
看到第二个案例失败的原因,我有点惊讶

对于这两种情况,都会打印以下调试信息

;; case 1
'(begin (expand k (list 1 (break)) ()))
'(list 1 (k (void)))

;; case 2
'(begin (expand k `(1 ,(break)) ()))
'`(expand k (1 ,(break)) ()) 
请注意,在案例2的输出中,在
quasikote
展开之后,其余的
(1,(break))
不知何故没有展开

不知道为什么会发生这种情况


谢谢

问题在于宏扩展器不会展开出现在
引号
准引号
下的宏调用。例如:

(define-syntax-rule (pipe) "|")

> (quote (pipe))
'(pipe)                ; not "|"
> (quasiquote (pipe))
'(pipe)                ; not "|"
这可以通过在编译时直接对语法对象执行递归来解决,而不是通过返回包含宏调用的语法对象来执行递归

一般来说,翻译代码如下:

(define-syntax expand
  (lambda (stx)
    (syntax-case stx literals
      cases
      [pattern #'(.... (expand stuff) ...)]
      cases)))
(begin-for-syntax
  (define (expand stx)
    (syntax-case stx literals
      cases
      [pattern #`(.... #,(expand stuff) ...)]
      cases)))
转换成如下代码:

(define-syntax expand
  (lambda (stx)
    (syntax-case stx literals
      cases
      [pattern #'(.... (expand stuff) ...)]
      cases)))
(begin-for-syntax
  (define (expand stx)
    (syntax-case stx literals
      cases
      [pattern #`(.... #,(expand stuff) ...)]
      cases)))
在您的特定情况下,您可能希望
expand
成为一个3参数函数,它在编译时完全运行和递归

(begin-for-syntax
  (define (expand k xs ys)
    (with-syntax ([(ys ...) ys])
      (syntax-case xs (break)
        [()                (begin (println (syntax->datum #'(ys ...))) #'(ys ...))]
        [((break) xs ...)  (expand k #'(xs ...) #'(ys ... (k (void))))]
        [((es ...) xs ...) (expand k #'(xs ...) #`(ys ... #,(expand k #'(es ...) #'())))]
        [(a xs ...)        (expand k #'(xs ...) #'(ys ... a))]))))
然后,您可以在
循环
宏的实现中调用此编译时函数:

(define-syntax loop
  (lambda (stx)
    (syntax-case stx ()
      [(_ e es ...)
       #`(call/cc (lambda (k)
                    (let l ()
                      #,(expand #'k #'(begin e es ...) #'())
                      (l))))])))
但是,这不是执行循环宏的最佳方法。 我希望上面的编译时函数能帮助您理解宏的可能性。但是,对于
循环
宏,应该不需要它。语法参数提供了一种更简单的方法

(define-syntax-parameter break
  (lambda (stx) (raise-syntax-error #f "cannot be used outside of loop" stx)))

(define-syntax loop
  (syntax-rules ()
    [(_ e es ...)
     (call/cc (lambda (k)
                (define (break-function) (k (void)))
                (syntax-parameterize ([break (make-rename-transformer #'break-function)])
                  (let l ()
                    (begin e es ...)
                    (l)))))]))
事实上,像这样的
循环
宏是本文中使用的示例之一,称为
永久
,其中它调用中断语法参数
中止

案例2在
Quasikote
展开后的输出我在您的问题中看不到它。最后一行
'`(展开k(1,(break))())
表示展开前的状态。