我正在尝试将可选的省略号(~optional datum:my-class ...)
扩展为使用my-class
的属性的形式,例如:#'(list datum.attr ...)
。
但是由于该表单是可选的,因此当它不存在时,它将解析为#f
,省略号模式不喜欢它:
;; ?: attribute contains non-list value
;; value: #f
我尝试使用#:defaults
的{{1}}参数,如下所示:
~optional
(~optional datum:my-class ... #:defaults ([(datum 1) null]))
我通过以下技巧使它起作用:
;; which works for usage:
#'(list datum ...)
;; but not for:
#'(list datum.attr ...)
我正在寻找是否有更好的方法。
我尝试将其保持在最低限度。检出测试子模块。
要查看此问题,请取消注释(~optional datum:my-class ...)
#'(list #,@(if (attribute datum) #'(datum.attr ...) #'()))
的四个实现之一,然后运行parse-bag
。
raco test file.rkt
答案 0 :(得分:10)
有两种策略可以解决“属性值为假”错误:
问题中的实施2和3以及Jérôme的答案中的代码都使用~optional
和#:defaults
,所以他们都试图用1(a)对其进行修复。
Jérôme的答案显示了对此的正确使用。要点是,如果使用<object>.result
之类的属性,则需要为<object>.result
指定默认值,而不仅仅是<object>
。
但是,如果您需要使用obj-exp
类中的多个属性,则有一个缺点。这将要求您为每个选项指定默认值:
(~optional (objects <object>:obj-exp ...)
#:defaults ([(<object>.result 1) null]
[(<object>.x 1) null]))
如果您使用语法类中的多个属性,例如<object>.result
,<object>.x
,<object>.y
,<object>.z
等,则1(a)将要求您为每个单独指定一个默认值。为了避免这种情况,请不要编写以下代码:
(~optional (objects <object>:obj-exp ...)
#:defaults ([(<object>.result 1) null]
[(<object>.x 1) null]
[(<object>.y 1) null]
[(<object>.z 1) null]))
(~or (objects <object>:obj-exp ...)
(~and (~seq) (~parse (<object>:obj-exp ...) null)))
(define-splicing-syntax-class maybe-objects
#:datum-literals (objects)
[pattern (objects <object>:obj-exp ...)]
[pattern (~seq) #:with (<object>:obj-exp ...) null])
问题中的实现4使用unsyntax-splicing
和if
,因此它是2(a)的示例:
#,@(if (attribute <object>)
#'(<object>.result ...)
#'())
但是,正如您所指出的,这看起来很丑。而且它还有另一个问题。如果它本身是省略号,则会分解,因为椭圆不会在#,
或#,@
内携带其效果。
这就是~?
存在2(b)的原因。除了使用#,@(if ....)
,还可以使用:
(~? (<object>.result ...) ())
那不是很有效,但是它的这种变体可以做到:
(~? (list <object>.result ...) (list))
在实现4的变体中使用它:
(define-syntax (parse-bag stx)
(syntax-parse stx
#:datum-literals (label objects)
[(_ (label <label>:str)
(~optional (objects <object>:obj-exp ...)))
#`(bag <label>
(~? (list <object>.result ...) (list)))]))
答案 1 :(得分:4)
使用#:defaults
时,您需要指定属性:
(~optional <object>:obj-exp ... #:defaults ([(<object>.result 1) null]))
(define-syntax (parse-bag stx)
(syntax-parse stx
#:datum-literals (label objects)
[(_ (label <label>:str)
(~optional (objects <object>:obj-exp ...)
#:defaults ([(<object>.result 1) null]))) ; + attribute here
#'(bag <label>
(list <object>.result ...))])) ; now it works!
另一种方法是将省略号用法移至语法类,如以下问题所示:A splicing syntax class that matches an optional pattern and binds attributes