gpt4 book ai didi

macros - 如何对使用语法解析捕获的可选属性进行分组?

转载 作者:行者123 更新时间:2023-12-04 12:46:46 25 4
gpt4 key购买 nike

编写使用 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 的内容?我?

最佳答案

有一种方法可以使用 ~groups-no-order 来做到这一点。像这样的模式扩展器:

(define-splicing-syntax-class opts
#:attributes ([first-opts 1] [second-opts 1])
[pattern (~groups-no-order
[first-opts
(~optional (~seq #:a a))
(~optional (~seq #:b b))]
[second-opts
(~optional (~seq #:x x))
(~optional (~seq #:y y))])])

(syntax-parse #'(foobar #:b 3 #:y 7 #:a 2)
[(foobar opts:opts)
(values #'(opts.first-opts ...)
#'(opts.second-opts ...))])
; #<syntax (#:a 2 #:b 3)>
; #<syntax (#:y 7)>

哪里 ~groups-no-order可以这样定义:
#lang racket
(provide ~groups-no-order)

(require syntax/parse
seq-no-order
(for-syntax racket/syntax
syntax/stx))

(define-syntax ~groups-no-order
(pattern-expander
(lambda (stx)
(syntax-case stx ()
[(groups [group-name member-pat ...] ...)
(with-syntax ([ooo (quote-syntax ...)])
(define/with-syntax [[member-tmp ...] ...]
(stx-map generate-temporaries #'[[member-pat ...] ...]))
(define/with-syntax [group-tmp ...]
(generate-temporaries #'[group-name ...]))
#'(~and (~seq-no-order (~and (~seq (~var member-tmp) ooo)
member-pat)
... ...)
(~parse [[(~var group-tmp) ooo] ooo] #'[[member-tmp ooo] ...])
...
(~parse [group-name ooo] #'[group-tmp ooo ooo])
...))]))))

这与您使用 #:with 的第一个解决方案的作用相同,但它将这些东西抽象成一个可重用的模式扩展器。

关于macros - 如何对使用语法解析捕获的可选属性进行分组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34551203/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com