Racket 如何访问包含省略号的`~optional`中的语法类属性? TL;博士
我正在尝试将可选省略号Racket 如何访问包含省略号的`~optional`中的语法类属性? TL;博士,racket,Racket,我正在尝试将可选省略号(~optional datum:my class…扩展为使用my class属性的表单,例如:#'(list datum.attr…) 但由于表单是可选的,当它不存在时,它会解析为#f,省略号模式不喜欢: ;; ?: attribute contains non-list value ;; value: #f 我尝试使用~ optional的:defaults参数,如下所示: (~optional datum:my-class ... #:defaults ([(d
(~optional datum:my class…
扩展为使用my class
属性的表单,例如:#'(list datum.attr…
)
但由于表单是可选的,当它不存在时,它会解析为#f
,省略号模式不喜欢:
;; ?: attribute contains non-list value
;; value: #f
我尝试使用~ optional
的:defaults
参数,如下所示:
(~optional datum:my-class ... #:defaults ([(datum 1) null]))
我通过使用以下技巧实现了这一点:
(~optional datum:my-class ...)
#'(list #,@(if (attribute datum) #'(datum.attr ...) #'()))
我在想是否有更好的办法
完整的可运行示例
我尽量把它控制在最低限度。检查测试子模块。
要查看问题,请取消注释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" '()))
)
#朗格球拍/球座
(提供解析包)
(要求
(用于语法racket/base语法/parse)
语法/语法分析)
(结构包(标签对象)#:透明)
(结构对象(名称)#:透明)
(从语法开始
(定义语法类obj exp
#:基准文字(对象名称)
(图案(对象(名称:str))
#:结果为#'(对象)))
;; 实施一
;; 描述:天真但失败的方法
;; 取消对测试的注释
#;(定义语法(解析包stx)
(语法解析stx)
#:基准文字(标签对象)
[((标签:str)
(~可选(对象:obj exp…)
#"(包)
(列表、结果…))
;; ?: 属性包含非列表值
;; 值:#f
;; 实施二
;; DESC:添加默认值也会失败
;; 取消对测试的注释
#;(定义语法(解析包stx)
(语法解析stx)
#:基准文字(标签对象)
[((标签:str)
(~可选(对象:obj exp…)
#:默认值([(1)null]);(此处)
#"(包)
(列表、结果…))
;; ?: 属性包含非列表值
;; 值:#f
;; 实施三
;; DESC:不使用语法类属性时不会失败
;; 取消对测试的注释
#;(定义语法(解析包stx)
(语法解析stx)
#:基准文字(标签对象)
[((标签:str)
(~可选(对象:obj exp…)
#:默认值([(1)null]))
#"(包)
(名单…);(这里)
;; 名称:未绑定标识符
;; 实施四
;; 这很管用,但我觉得很难看
;; 取消对测试的注释
#;(定义语法(解析包stx)
(语法解析stx)
#:基准文字(标签对象)
[((标签:str)
(~可选(对象:obj exp…)
#`(袋
(列表#,@(如果(属性))
#“(.结果…)
#'())))]))
(模块+测试(需要机架单元)
(检查是否相等?
(解析包
(贴上“饼干”标签)
(对象(对象(名称为“Cookie”))
(对象(名称为“布朗尼”))
(袋装“饼干”
(列表(对象“Cookie”)
(对象“布朗尼”))
(检查是否相等?
(包装袋(标签为“糖果”))
(包"糖果"())
)
使用#:默认值时,需要指定属性:
(~optional <object>:obj-exp ... #:defaults ([(<object>.result 1) null]))
(~可选:obj exp…#:默认值([(.result 1)null]))
完整代码:
(定义语法(解析包stx)
(语法解析stx)
#:基准文字(标签对象)
[((标签:str)
(~可选(对象:obj exp…)
#:默认值([(.result 1)null]);+此处的属性
#"(包)
(列表、结果…);现在它工作了!
另一种方法是将省略号的用法移动到语法类,如在这个问题中:有两类策略可以修复“attribute value is false”错误:
放置默认值或备选值,以便属性永远不会为false
(a) 与#一起使用:默认值
(b) 使用,可能与
(c) 使用具有多个模式的语法类
处理在主体中为false的属性
(a) 使用及
(b) 使用
1(a)
问题中的实现2和3,以及Jérôme回答中的代码,都与#:默认值一起使用,因此它们都试图用1(a)来修复它
Jérôme的回答表明了这一点的正确使用。要点是,如果使用像.result
这样的属性,则需要为.result
指定默认值,而不仅仅是
但是,如果需要使用obj exp
类中的多个属性,则会出现这种情况的一个缺点。这将要求您为每一项指定默认值:
(~optional (objects <object>:obj-exp ...)
#:defaults ([(<object>.result 1) null]
[(<object>.x 1) null]))
1(c)
然而,正如你所说,这看起来有点难看。它还有另一个问题。如果它本身在省略号下,则会发生故障,因为省略号不会在or中携带其效果
这就是2(b)存在的原因。您可以使用以下命令,而不是使用,@(如果…)
:
(~? (<object>.result ...) ())
(~?(.result…)
这不太管用,但它的这种变体可以:
(~? (list <object>.result ...) (list))
(~?(list.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)))]))
(定义语法(解析包stx)
(语法解析stx)
#:基准文字(标签对象)
[((标签:str)
(~可选(对象:obj exp…)
#`(袋
(?(list.result…(list))))
太棒了!现在我知道了很多不同的方法来解决一些语法分析问题,使用~?
和~parse
,这是我以前不知道的。谢谢:)
(~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])
#,@(if (attribute <object>)
#'(<object>.result ...)
#'())
(~? (<object>.result ...) ())
(~? (list <object>.result ...) (list))
(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)))]))