Macros 如何在外部宏之前展开内部宏?

Macros 如何在外部宏之前展开内部宏?,macros,lisp,scheme,racket,Macros,Lisp,Scheme,Racket,假设我有以下宏: (define-syntax-rule (qq x) '(1 x)) 我可以把看起来像(qq(qq 2))的东西扩展成(1(12))而不是(1(qq 2) 我之所以说“看起来像”,是因为到目前为止,唯一的迹象表明,由内而外的宏观扩张是棘手的,所以我想知道,要获得我想要的最终结果,更明智的方法是什么 我最初的动机与Racket有关:为了创建语法,库提供了一个解析器宏,如下所示: (define my-parser (parser (start start) (end EOF

假设我有以下宏:

(define-syntax-rule (qq x) '(1 x))
我可以把看起来像
(qq(qq 2))
的东西扩展成
(1(12))
而不是
(1(qq 2)

我之所以说“看起来像”,是因为到目前为止,唯一的迹象表明,由内而外的宏观扩张是棘手的,所以我想知道,要获得我想要的最终结果,更明智的方法是什么

我最初的动机与Racket有关:为了创建语法,库提供了一个
解析器
宏,如下所示:

(define my-parser (parser
  (start start) (end EOF)
  (tokens value-tokens op-tokens)
  (error (lambda (a b c) (void)))

  (grammar
   (start [(numbers) $1])

   (numbers [(numberx) (reverse $1)])
   (numberx [() empty]
            [(numberx NUM) (cons $2 $1)])
  )
))
我的语法有很多我想抽象掉的样板文件。例如,我希望能够定义某种
列表规则
抽象,让我编写类似的东西

(define my-parser (parser
  (start start) (end EOF)
  (tokens value-tokens op-tokens)
  (error (lambda (a b c) (void)))

  (grammar
   (start [(numbers) $1])

   (list-rules NUM numbers numberx)
  )
))

但是,如果
解析器
首先展开,它会将
列表规则
本身视为非终端,而不是将其展开为真正的非终端(
numbers
numberx
).

您需要有两个语法规则,首先是更一般的规则;也不需要使用准引号和unquote运算符,以便可以计算内部形式:

(define-syntax qq
   (syntax-rules ()
     ((_ (x ...) ...)  `(1 ,(x ...) ...))
     ((_ x)            `(1 x))))
比如

-> (qq x)
'(1 x)
-> (qq (qq x))
'(1 (1 x))
-> (qq (qq (qq x)))
'(1 (1 (1 x)))
-> (qq (qq (qq (qq y))))
'(1 (1 (1 (1 y))))
-> (qq (hh x))
'(1 (2 x))
-> (hh (qq x))
'(2 (1 x))
这也是可组合的:

(define-syntax qq
   (syntax-rules ()
     ((_ (x ...) ...)  `(1 ,(x ...) ...))
     ((_ x)            `(1 x))))

(define-syntax hh
   (syntax-rules ()
     ((_ (x ...) ...)  `(2 ,(x ...) ...))
     ((_ x)            `(2 x))))
比如

-> (qq x)
'(1 x)
-> (qq (qq x))
'(1 (1 x))
-> (qq (qq (qq x)))
'(1 (1 (1 x)))
-> (qq (qq (qq (qq y))))
'(1 (1 (1 (1 y))))
-> (qq (hh x))
'(1 (2 x))
-> (hh (qq x))
'(2 (1 x))

您需要有两个语法规则,首先是更一般的规则;也不需要使用quasiquete和unquote运算符,以便可以计算内部表单:

(define-syntax qq
   (syntax-rules ()
     ((_ (x ...) ...)  `(1 ,(x ...) ...))
     ((_ x)            `(1 x))))
比如

-> (qq x)
'(1 x)
-> (qq (qq x))
'(1 (1 x))
-> (qq (qq (qq x)))
'(1 (1 (1 x)))
-> (qq (qq (qq (qq y))))
'(1 (1 (1 (1 y))))
-> (qq (hh x))
'(1 (2 x))
-> (hh (qq x))
'(2 (1 x))
这也是可组合的:

(define-syntax qq
   (syntax-rules ()
     ((_ (x ...) ...)  `(1 ,(x ...) ...))
     ((_ x)            `(1 x))))

(define-syntax hh
   (syntax-rules ()
     ((_ (x ...) ...)  `(2 ,(x ...) ...))
     ((_ x)            `(2 x))))
比如

-> (qq x)
'(1 x)
-> (qq (qq x))
'(1 (1 x))
-> (qq (qq (qq x)))
'(1 (1 (1 x)))
-> (qq (qq (qq (qq y))))
'(1 (1 (1 (1 y))))
-> (qq (hh x))
'(1 (2 x))
-> (hh (qq x))
'(2 (1 x))

嗯,您可以对形状进行宏检查:

好消息是,这是“递归的”:

但是,即使提供了
qq
以外的东西,它也可以工作:

(qq (+ 1))       ; => '(1 2)
如果这对于您的预期用途是一个问题——如果您希望将其标记为错误——您可以这样做:

(define-syntax (rr stx)
  (syntax-case stx ()
    [(_ (rr x))
     (cond [(equal? 'rr (syntax->datum #'rr))
            #'(list 1 (rr x))]
           [else (raise-syntax-error #f "Expected rr" stx #'rr)])]
    [(_ x)
     #'(list 1 x)]))
这些示例的结果相同,但最后一个示例出现语法错误:

(rr 2)           ; => '(1 2)
(rr (rr 2))      ; => '(1 (1 2))
(rr (rr (rr 2))) ; => '(1 (1 (1 2)))
(rr (+ 1))       ; =>
; src/scheme/misc/so-syntax.rkt:27:5: rr: Expected rr
;  at: +
;  in: (rr (+ 1))
更新:第二个版本可以使用以下方法编写得更干净一些:


嗯,您可以对形状进行宏检查:

好消息是,这是“递归的”:

但是,即使提供了
qq
以外的东西,它也可以工作:

(qq (+ 1))       ; => '(1 2)
如果这对于您的预期用途是一个问题——如果您希望将其标记为错误——您可以这样做:

(define-syntax (rr stx)
  (syntax-case stx ()
    [(_ (rr x))
     (cond [(equal? 'rr (syntax->datum #'rr))
            #'(list 1 (rr x))]
           [else (raise-syntax-error #f "Expected rr" stx #'rr)])]
    [(_ x)
     #'(list 1 x)]))
这些示例的结果相同,但最后一个示例出现语法错误:

(rr 2)           ; => '(1 2)
(rr (rr 2))      ; => '(1 (1 2))
(rr (rr (rr 2))) ; => '(1 (1 (1 2)))
(rr (+ 1))       ; =>
; src/scheme/misc/so-syntax.rkt:27:5: rr: Expected rr
;  at: +
;  in: (rr (+ 1))
更新:第二个版本可以使用以下方法编写得更干净一些:

你可以试试,尽管我承认我没有花足够的时间来理解你问题的第二部分,以知道它是否是正确的使用方法

#lang racket
(define-syntax (qq stx)
  (syntax-case stx ()
    [(_ x) 
     (with-syntax ([y (local-expand #'x 'expression '())])
       #'`(1 ,y))]))
(qq 2)
(qq (qq 2))
(qq (qq (qq 2)))
=>

你可以试试,尽管我承认我没有花足够的时间来理解你问题的第二部分,以知道它是否是正确的使用方法

#lang racket
(define-syntax (qq stx)
  (syntax-case stx ()
    [(_ x) 
     (with-syntax ([y (local-expand #'x 'expression '())])
       #'`(1 ,y))]))
(qq 2)
(qq (qq 2))
(qq (qq (qq 2)))
=>


看起来这解决了简化递归宏问题,但我看不出这将如何应用于我的原始问题:(看起来这解决了简化递归宏问题,但我看不出这将如何应用于我的原始问题:(请花几分钟学习如何。请花几分钟学习如何。这似乎解决了递归宏的简化问题,但我似乎不明白如何使用它将参数从库扩展到现有宏。@missingo我的理解是,这取决于库宏的使用方式。)igned.sure。如果库宏不起作用,我该怎么办?看起来这解决了递归宏的简化问题,但我似乎不明白如何使用它将参数从库扩展到现有宏。@missingo我的理解是,这取决于库宏的设计。当然。我该怎么办如果库宏不起作用,该怎么办?本地扩展不起作用。我尝试在“grammar”参数中生成一个本地扩展所有产品,但1)它会尝试扩展不应扩展的内容(错误可能会抱怨“start”未绑定)和2)我的“list rules”将需要扩展为两个列表项,我不认为可以通过宏扩展来实现这一点……现在我想起来了,如果这只是生成一个具有我想要的结构的列表或语法对象的问题,我可以使用准旋转(对于“列表规则”使用
、@
#、@
)但是我不知道如何从中定义我的解析器。
local expand
的第三个参数是一个停止标识符列表,这样你可以控制扩展的程度。将
start
添加到此列表中可以修复错误吗?将
#f
作为第三个参数传递给local expand解决了第一个问题,但现在我得到了一个de>let values:无法绑定受宏扩展污染的标识符,当我尝试使用
list rules
宏时,出现:numberx
错误。此外,还有一个问题,我最终需要将列表规则扩展到2个非终端,而不是仅一个,本地扩展不起作用。我尝试制作一个本地扩展扩展了“grammar”参数中的所有产品,但1)它会尝试扩展不应扩展的内容(错误可能会抱怨“start”未绑定)和2)我的“列表规则”需要扩展为两个列表项,我不认为你可以通过宏扩展来实现这一点……现在我想到了,如果这只是一个生成具有我想要的结构的列表或语法对象的问题,我可以使用准旋转(对于“列表规则”使用
、@
#、@
)但是我不知道如何从中定义我的解析器。
local expand
的第三个参数是一个停止标识符列表,因此您可以控制扩展的程度。将
start
添加到此列表中是否修复了错误?将
#f
作为第三个参数传递给local expand解决了第一个问题,但现在我得到了一个
let values:cannot bind identifier受:numberx中的宏扩展污染