Parboiled2:如何处理依赖字段?
Parboiled2: How to process dependent fields?
我正在尝试使用出色的 parboiled2 库解析一种文件格式,其中某些字段的存在取决于一个或多个已处理字段的值。
例如,假设我有两个字段,第一个是指示第二个是否存在的标志。也就是说,如果第一个字段是 true
,那么第二个字段(在这个例子中是一个整数值)存在并且必须被处理 - 但如果它是 false
,那么第二个字段是根本不在场。请注意,第二个字段不是 可选的 - 它要么 必须 被处理(如果第一个字段是 true
)或 不能被处理(如果第一个字段是false
)。
因此,如果第三个字段(我们假设它始终存在)是带引号的字符串,则以下两行均有效:
true 52 "Some quoted string"
false "Some other quoted string"
但这将是无效的:
false 25 "Yet another quoted string"
忽略第三个字段,如何编写规则来解析前两个? (我无法从文档中看出,谷歌搜索 到目前为止没有帮助...)
UPDATE:我应该澄清我不能使用如下规则,因为我正在解析的格式实际上比我的示例复杂得多:
import org.parboiled2._
class MyParser(override val input: ParserInput)
extends Parser {
def ws = // whitepsace rule, puts nothing on the stack.
def intField = // parse integer field, pushes Int onto stack...
def dependentFields = rule {
("true" ~ ws ~ intField) | "false" ~> //etc.
}
}
更新 2:我修改了以下内容以使我的意图更清楚:
我正在寻找的是以下(不存在的)规则的有效等效项,该规则仅在满足条件时才执行匹配:
import org.parboiled2._
class MyParser(input: ParserInput)
extends Parser {
def ws = // whitepsace rule, puts nothing on the stack.
def intField = // parse integer field, pushes Int onto stack...
def boolField = // parse boolean field, pushes Boolean onto stack...
def dependentFields = rule {
boolField ~> {b =>
// Match "ws ~ intField" only if b is true. If match succeeds, push Some(Int); if match
// fails, the rule fails. If b is false, pushes None without attempting the match.
conditional(b, ws ~ intField)
}
}
}
也就是说,ws ~ intField
仅在 boolField
产生 true
值时匹配。这样的事情可能吗?
我会这样做:
extends Parser {
def dependentFields: Rule1[(Boolean, Option[Int], String)] = rule {
("true" ~ ws ~ trueBranch | "false" ~ ws ~ falseBranch)
}
def trueBranch = rule {
intField ~ ws ~ stringField ~> { (i, s) => (true, Some(i), s) }
}
def falseBranch = rule {
stringField ~> { s => (false, None, s) }
}
}
是的,您可以借助 test
解析器操作来实现这样的功能:
def conditional[U](bool: Boolean, parse: () => Rule1[U]): Rule1[Option[U]] = rule {
test(bool) ~ parse() ~> (Some(_)) | push(None)
}
根据文档的Meta-Rules section,它只能通过传递一个函数来产生规则来工作。您必须按如下方式定义 dependentFields
规则:
def dependentFields = rule {
boolField ~> (conditional(_, () => rule { ws ~ intField }))
}
更新:
虽然 test(pred) ~ opt1 | opt2
是一种常用技术,但它 会 回溯并尝试应用 opt2
,如果 test
成功 test
,但 opt1
失败。这里有两种可能的解决方案来防止这种回溯。
您可以使用 ~!~
规则组合器,它具有 "cut" 语义并禁止回溯自身:
def conditional2[U](bool: Boolean, parse: () => Rule1[U]): Rule1[Option[U]] = rule {
test(bool) ~!~ parse() ~> (Some(_)) | push(None)
}
或者您实际上在规则的 if
之外使用 来检查布尔参数和 return 两个可能的规则之一:
def conditional3[U](bool: Boolean, parse: () => Rule1[U]): Rule1[Option[U]] =
if (bool) rule { parse() ~> (Some(_: U)) }
else rule { push(None) }
我正在尝试使用出色的 parboiled2 库解析一种文件格式,其中某些字段的存在取决于一个或多个已处理字段的值。
例如,假设我有两个字段,第一个是指示第二个是否存在的标志。也就是说,如果第一个字段是 true
,那么第二个字段(在这个例子中是一个整数值)存在并且必须被处理 - 但如果它是 false
,那么第二个字段是根本不在场。请注意,第二个字段不是 可选的 - 它要么 必须 被处理(如果第一个字段是 true
)或 不能被处理(如果第一个字段是false
)。
因此,如果第三个字段(我们假设它始终存在)是带引号的字符串,则以下两行均有效:
true 52 "Some quoted string"
false "Some other quoted string"
但这将是无效的:
false 25 "Yet another quoted string"
忽略第三个字段,如何编写规则来解析前两个? (我无法从文档中看出,谷歌搜索 到目前为止没有帮助...)
UPDATE:我应该澄清我不能使用如下规则,因为我正在解析的格式实际上比我的示例复杂得多:
import org.parboiled2._
class MyParser(override val input: ParserInput)
extends Parser {
def ws = // whitepsace rule, puts nothing on the stack.
def intField = // parse integer field, pushes Int onto stack...
def dependentFields = rule {
("true" ~ ws ~ intField) | "false" ~> //etc.
}
}
更新 2:我修改了以下内容以使我的意图更清楚:
我正在寻找的是以下(不存在的)规则的有效等效项,该规则仅在满足条件时才执行匹配:
import org.parboiled2._
class MyParser(input: ParserInput)
extends Parser {
def ws = // whitepsace rule, puts nothing on the stack.
def intField = // parse integer field, pushes Int onto stack...
def boolField = // parse boolean field, pushes Boolean onto stack...
def dependentFields = rule {
boolField ~> {b =>
// Match "ws ~ intField" only if b is true. If match succeeds, push Some(Int); if match
// fails, the rule fails. If b is false, pushes None without attempting the match.
conditional(b, ws ~ intField)
}
}
}
也就是说,ws ~ intField
仅在 boolField
产生 true
值时匹配。这样的事情可能吗?
我会这样做:
extends Parser {
def dependentFields: Rule1[(Boolean, Option[Int], String)] = rule {
("true" ~ ws ~ trueBranch | "false" ~ ws ~ falseBranch)
}
def trueBranch = rule {
intField ~ ws ~ stringField ~> { (i, s) => (true, Some(i), s) }
}
def falseBranch = rule {
stringField ~> { s => (false, None, s) }
}
}
是的,您可以借助 test
解析器操作来实现这样的功能:
def conditional[U](bool: Boolean, parse: () => Rule1[U]): Rule1[Option[U]] = rule {
test(bool) ~ parse() ~> (Some(_)) | push(None)
}
根据文档的Meta-Rules section,它只能通过传递一个函数来产生规则来工作。您必须按如下方式定义 dependentFields
规则:
def dependentFields = rule {
boolField ~> (conditional(_, () => rule { ws ~ intField }))
}
更新:
虽然 test(pred) ~ opt1 | opt2
是一种常用技术,但它 会 回溯并尝试应用 opt2
,如果 test
成功 test
,但 opt1
失败。这里有两种可能的解决方案来防止这种回溯。
您可以使用 ~!~
规则组合器,它具有 "cut" 语义并禁止回溯自身:
def conditional2[U](bool: Boolean, parse: () => Rule1[U]): Rule1[Option[U]] = rule {
test(bool) ~!~ parse() ~> (Some(_)) | push(None)
}
或者您实际上在规则的 if
之外使用 来检查布尔参数和 return 两个可能的规则之一:
def conditional3[U](bool: Boolean, parse: () => Rule1[U]): Rule1[Option[U]] =
if (bool) rule { parse() ~> (Some(_: U)) }
else rule { push(None) }