如何在包含省略号的〜Optional中访问语法类属性?

时间:2019-03-07 13:54:00

标签: racket

TL; DR

我正在尝试将可选的省略号(~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

2 个答案:

答案 0 :(得分:10)

有两种策略可以解决“属性值为假”错误:

  1. 设置默认值或替代值,以使该属性永远不会为假。

    (a)将~optional#:defaults一起使用

    (b)使用~or,可能使用~parse

    (c)使用具有多个模式的语法类

  2. 处理时该属性在主体中为false。

    (a)使用unsyntaxif

    (b)使用~?

1(a)

问题中的实施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]))

1(b)

如果您使用语法类中的多个属性,例如<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~parse

(~or (objects <object>:obj-exp ...)
     (~and (~seq) (~parse (<object>:obj-exp ...) null)))

1(c)

(define-splicing-syntax-class maybe-objects
  #:datum-literals (objects)
  [pattern (objects <object>:obj-exp ...)]
  [pattern (~seq) #:with (<object>:obj-exp ...) null])

2(a)和(b)

问题中的实现4使用unsyntax-splicingif,因此它是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