首页 > 解决方案 > 方案 - 引用和辅助语法

问题描述

由于某种原因,以下宏将无法与引号一起使用。

(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)) ()) 

请注意,在quasiquote扩展后案例 2 的输出中,其余部分(1 ,(break))未扩展。

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

谢谢

标签: schemeracket

解决方案


问题是宏扩展器不会扩展出现在 aquote或下的宏调用quasiquote。例如:

(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)))

在您的特定情况下,您可能希望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))]))))

loop然后你可以在宏的实现中调用这个编译时函数:

(define-syntax loop
  (lambda (stx)
    (syntax-case stx ()
      [(_ e es ...)
       #`(call/cc (lambda (k)
                    (let l ()
                      #,(expand #'k #'(begin e es ...) #'())
                      (l))))])))

但是,这不是执行循环宏的最佳方式。

我希望上面的编译时功能可以帮助您了解宏的可能性。但是,对于loop宏来说,它不应该是必需的。语法参数提供了一种更简单的方法来执行此操作。

(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)))))]))

事实上,loop像这样的宏是论文使用语法参数保持清洁第 4 节中使用的示例之一,称为forever,它调用破坏语法参数abort


推荐阅读