我试图扩大可选省略号(~optional datum:my-class ...)成一种形式,使用从属性,my-class像这样:#'(list datum.attr ...)。
但是由于表单是可选的,因此当表单不存在时,它将解析为#f,省略号模式不喜欢它:
;; ?: attribute contains non-list value
;; value: #f
Run Code Online (Sandbox Code Playgroud)
我尝试使用的#:defaults参数~optional,如下所示:
(~optional datum:my-class ... #:defaults ([(datum 1) null]))
Run Code Online (Sandbox Code Playgroud)
;; which works for usage:
#'(list datum ...)
;; but not for:
#'(list datum.attr ...)
Run Code Online (Sandbox Code Playgroud)
我通过使用以下技巧使它起作用:
(~optional datum:my-class ...)
#'(list #,@(if (attribute datum) #'(datum.attr ...) #'()))
Run Code Online (Sandbox Code Playgroud)
我正在尝试寻找是否有更好的方法。
我尝试将其保持在最低限度。检出测试子模块。要查看此问题,请取消注释parse-bag和运行的四个实现之一raco test file.rkt。
#lang racket/base
(provide parse-bag)
(require
(for-syntax racket/base syntax/parse)
syntax/parse)
(struct bag (label objects) #:transparent)
(struct object (name) #:transparent)
(begin-for-syntax
(define-syntax-class obj-exp
#:datum-literals (object name)
(pattern (object (name <name>:str))
#:with result #'(object <name>))))
;; IMPLEMENTATION ONE
;; DESC: The naive but failing approach
;; UNCOMMENT TO TEST
#;(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 ...))]))
;; ?: attribute contains non-list value
;; value: #f
;; IMPLEMENTATION TWO
;; DESC: adding defaults will fail too
;; UNCOMMENT TO TEST
#;(define-syntax (parse-bag stx)
(syntax-parse stx
#:datum-literals (label objects)
[(_ (label <label>:str)
(~optional (objects <object>:obj-exp ...)
#:defaults ([(<object> 1) null]))) ; (HERE)
#'(bag <label>
(list <object>.result ...))]))
;; ?: attribute contains non-list value
;; value: #f
;; IMPLEMENTATION THREE
;; DESC: it won't fail when not using syntax-class attributes
;; UNCOMMENT TO TEST
#;(define-syntax (parse-bag stx)
(syntax-parse stx
#:datum-literals (label objects)
[(_ (label <label>:str)
(~optional (objects <object>:obj-exp ...)
#:defaults ([(<object> 1) null])))
#'(bag <label>
(list <object> ...))])) ; (HERE)
;; name: unbound identifier
;; IMPLEMENTATION FOUR
;; DESC: it works, but I find it ugly
;; UNCOMMENT TO TEST
#;(define-syntax (parse-bag stx)
(syntax-parse stx
#:datum-literals (label objects)
[(_ (label <label>:str)
(~optional (objects <object>:obj-exp ...)))
#`(bag <label>
(list #,@(if (attribute <object>)
#'(<object>.result ...)
#'())))]))
(module+ test (require rackunit)
(check-equal?
(parse-bag
(label "Biscuits")
(objects (object (name "Cookie"))
(object (name "Brownie"))))
(bag "Biscuits"
(list (object "Cookie")
(object "Brownie"))))
(check-equal?
(parse-bag (label "Sweets"))
(bag "Sweets" '()))
)
Run Code Online (Sandbox Code Playgroud)
Ale*_*uth 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]))
Run Code Online (Sandbox Code Playgroud)
如果您使用的语法类的多种属性,如<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]))
Run Code Online (Sandbox Code Playgroud)
(~or (objects <object>:obj-exp ...)
(~and (~seq) (~parse (<object>:obj-exp ...) null)))
Run Code Online (Sandbox Code Playgroud)
(define-splicing-syntax-class maybe-objects
#:datum-literals (objects)
[pattern (objects <object>:obj-exp ...)]
[pattern (~seq) #:with (<object>:obj-exp ...) null])
Run Code Online (Sandbox Code Playgroud)
问题中的实现4使用unsyntax-splicing和if,因此是2(a)的示例:
#,@(if (attribute <object>)
#'(<object>.result ...)
#'())
Run Code Online (Sandbox Code Playgroud)
但是,正如您指出的那样,这看起来很丑陋。而且它还有另一个问题。如果其本身在省略号下,则会分解,因为椭圆不会在#,或内携带其效果#,@。
因此~?存在2(b)。除了#,@(if ....)可以使用,您还可以使用:
(~? (<object>.result ...) ())
Run Code Online (Sandbox Code Playgroud)
那不是很有效,但是它的这个变体可以做到:
(~? (list <object>.result ...) (list))
Run Code Online (Sandbox Code Playgroud)
在实现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)))]))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
183 次 |
| 最近记录: |