在编写使用的宏时syntax/parse
,我创建了一个拼接语法类,用于捕获可能提供给宏的选项.这些选项都是可选的,它们可以按任何顺序提供.使用~optional
省略号头部图案使这很容易:
(define-splicing-syntax-class opts (pattern (~seq (~or (~optional (~seq #:a a)) (~optional (~seq #:b b)) (~optional (~seq #:x x)) (~optional (~seq #:y y))) ...))
然而,有一个问题:我希望能够到组这些选项分为两组:含组a
和b
,和包含组x
和y
.但是,用户仍可以按任何顺序指定选项,因此对于此示例输入:
(foobar #:b 3 #:y 7 #:a 2)
我希望能够生成以下属性:
first-opts: (#:a 2 #:b 3) second-opts: (#:y 7)
到目前为止,我已经设法使用手动执行此操作#:with
,但它并不漂亮:
(define-splicing-syntax-class opts #:attributes ([first-opts 1] [second-opts 1]) (pattern (~seq (~or (~optional (~seq #:a a)) (~optional (~seq #:b b)) (~optional (~seq #:x x)) (~optional (~seq #:y y))) ...) #:with (first-opts ...) #`(#,@(if (attribute a) #'(#:a a) #'()) #,@(if (attribute b) #'(#:b b) #'())) #:with (second-opts ...) #`(#,@(if (attribute x) #'(#:x x) #'()) #,@(if (attribute y) #'(#:y y) #'()))))
这可以简化使用一点点template
距离syntax/parse/experimental/template
:
(define-splicing-syntax-class opts #:attributes ([first-opts 1] [second-opts 1]) (pattern (~seq (~or (~optional (~seq #:a a)) (~optional (~seq #:b b)) (~optional (~seq #:x x)) (~optional (~seq #:y y))) ...) #:with (first-opts ...) (template ((?? (?@ #:a a)) (?? (?@ #:b b)))) #:with (second-opts ...) (template ((?? (?@ #:a x)) (?? (?@ #:b y))))))
但是,这实际上只是上面的一些糖,它实际上并没有解决必须枚举每个子句中的每个选项的问题.例如,如果我添加了一个#:c
选项,我需要记住将它添加到first-opts
组中,否则它将被完全忽略.
我真正想要的是一些声明性的方法来分组这些可选值集.例如,我想要这样的语法:
(define-splicing-syntax-class opts #:attributes ([first-opts 1] [second-opts 1]) (pattern (~seq (~or (~group first-opts (~optional (~seq #:a a)) (~optional (~seq #:b b))) (~group second-opts (~optional (~seq #:x x)) (~optional (~seq #:y y)))) ...)))
或者,更好的是,如果我可以使用现有的原语,这将是很好的,如下所示:
(define-splicing-syntax-class opts #:attributes ([first-opts 1] [second-opts 1]) (pattern (~seq (~or (~and first-opts (~seq (~optional (~seq #:a a)) (~optional (~seq #:b b)))) (~and second-opts (~seq (~optional (~seq #:x x)) (~optional (~seq #:y y))))) ...)))
但是,这些都不起作用.有没有办法使用提供的内置提取syntax/parse
?如果没有,有没有简单的方法来定义像~group
我这样的东西?