Parboiled2:如何处理依赖字段?

时间:2018-02-21 02:06:20

标签: scala parboiled2

我正在尝试使用优秀的parboiled2库解析文件格式,其中某些字段的存在取决于已处理的一个或多个字段的值。

例如,假设我有两个字段,第一个字段是指示第二个字段是否存在的标志。也就是说,如果第一个字段是true,则第二个字段(在此示例中为整数值)存在且必须处理 - 但如果它是false,则第二个字段不是根本不存在。请注意,第二个字段不是可选 - 它必须必须处理(如果第一个字段为true)或必须 >被处理(如果第一个字段是false)。

因此,如果第三个字段(我们假设它始终存在)是带引号的字符串,则以下两行都是有效的:

true 52 "Some quoted string"
false "Some other quoted string"

但这可能无效:

false 25 "Yet another quoted string"

忽略第三个字段,如何编写规则来解析前两个字段? (我无法从文档中看出来,谷歌搜索到目前为止还没有帮助......)

更新:我应该澄清一下,我不能使用以下规则,因为我正在解析的格式实际上比我的例子复杂得多:

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值时才会匹配。这样的事情可能吗?

2 个答案:

答案 0 :(得分:2)

是的,您可以在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,但{ {1}}失败。以下是防止此类回溯的两种可能解决方案。

你可以使用opt1规则组合器,它具有&#34; cut&#34;语义并禁止自我回溯:

~!~

或者您实际使用规则外的def conditional2[U](bool: Boolean, parse: () => Rule1[U]): Rule1[Option[U]] = rule { test(bool) ~!~ parse() ~> (Some(_)) | push(None) } 来检查布尔参数并返回两个可能的规则之一:

if

答案 1 :(得分:0)

我会做这样的事情:

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) }
  }
}