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中的宏扩展污染