说我有以下宏:
(define-syntax-rule (qq x) '(1 x))
我可以制作看似(qq (qq 2))
扩展为'(1 (1 2))
而不是(1 (qq 2)
的内容吗?
我说“看起来像”的原因是因为到目前为止唯一的hint I found表明内向外的宏扩展是棘手的,所以我想知道最新的做法是否能达到我想要的最终结果。
我最初的动机与Racket的parser generator library有关:为了创建一个语法,库提供了一个parser
宏,如下所示:
(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)])
)
))
我的语法有很多样板,我想抽象出去。例如,我希望能够定义某种list-rules
抽象,让我写一些类似于
(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)
)
))
但是,如果parser
首先展开,它会将list-rules
本身视为非终端,而不是将其扩展为真正的非终端(numbers
和{{1} })。
答案 0 :(得分:2)
你可以试试local-expand
,虽然我承认我没有花时间去理解你问题的第二部分,足以知道这是否正确使用。
#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)))
=>
'(1 2)
'(1 (1 2))
'(1 (1 (1 2)))
答案 1 :(得分:1)
您需要有2个语法规则,首先是更通用的规则;也不是使用quasiquote和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))))
这也是可以组合的:
(define-syntax qq
(syntax-rules ()
((_ (x ...) ...) `(1 ,(x ...) ...))
((_ x) `(1 x))))
(define-syntax hh
(syntax-rules ()
((_ (x ...) ...) `(2 ,(x ...) ...))
((_ x) `(2 x))))
,例如
-> (qq (hh x))
'(1 (2 x))
-> (hh (qq x))
'(2 (1 x))
答案 2 :(得分:1)
好吧,您可以对形状进行宏检查:
(define-syntax qq
(syntax-rules ()
[(qq (qq x)) (list 1 (qq x))]
[(qq x) (list 1 x)]))
好消息是,它“递归地”起作用:
(qq 2) ; => '(1 2)
(qq (qq 2)) ; => '(1 (1 2))
(qq (qq (qq 2))) ; => '(1 (1 (1 2)))
但即使提供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)]))
示例的结果相同,除了last现在给出语法错误:
(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))
更新:使用syntax-parse
可以更清晰地编写第二个版本:
(require (for-syntax syntax/parse))
(define-syntax (rr stx)
(syntax-parse stx
[(_ ((~literal rr) x)) #'(list 1 (rr x))]
[(_ (_ x)) (raise-syntax-error #f "Expected rr" stx #'rr)]
[(_ x) #'(list 1 x)]))