使用Scala / SBT解析器组合器重复依赖解析器

时间:2012-07-03 03:15:23

标签: scala sbt

通过Value[,Value]+可以轻松解析rep1sep(Value, ',')形式的字符串。当Value解析器依赖于重复中先前解析的值时,有没有办法实现rep1sep功能?例如,强制要求每个值必须是唯一的?

依赖解析器的标准技术是flatMap,但是我无法正常工作。这是一次这样的尝试:

def Values(soFar: Set[Value]): Parser[Set[Value]] =
  Value(soFar) flatMap { v => (',' ~> Values(soFar + v)).?.map { _ getOrElse soFar } }

def Value(soFar: Set[Value]): Parser[Value] =
  Num+ flatMap { v => if (soFar.contains(v)) failure("%d already appears".format(v)) else success(v) }

通常,我需要一种rep1sep形式,其中解析器参数是从Seq[A]Parser[A]的函数:

def rep1sepDependent(rep: Seq[A] => Parser[A], sep: Parser[Any]): Seq[A] = ???

注意:我意识到用例在这里是有问题的,并且在解析后更好地处理验证唯一性。我在使用SBT解析组合器来完成选项卡时遇到了这个问题 - 具体来说,我想仅为那些用户尚未输入的键提供键/值对的完成选项。请参阅this parser for a full example和可构建的SBT项目。

2 个答案:

答案 0 :(得分:4)

以下内容并不像rep1sepDependent那样通用,但可行:

def rep1sepUnique[T](p: => Parser[T], q: => Parser[Any]) = {
  def checkIfSeen(seen: Set[T]): Parser[Set[T]] = q ~> p >> (v =>
    if (seen(v)) failure("Duplicate: %s".format(v)) else checkIfSeen(seen + v)
  ) | success(seen)
  p >> (v => checkIfSeen(Set(v)))
}

例如:

import scala.util.parsing.combinator._

object parseUniqueWords extends RegexParsers {
  def rep1sepUnique[T](p: => Parser[T], q: => Parser[Any]) = {
    def checkIfSeen(seen: Set[T]): Parser[Set[T]] = q ~> p >> (v =>
      if (seen(v)) failure("Duplicate: %s".format(v)) else checkIfSeen(seen + v)
    ) | success(seen)
    p >> (v => checkIfSeen(Set(v)))
  }

  def apply(s: String) = parseAll(rep1sepUnique("\\w+".r, ","), s)
}

这给了我们:

scala> parseUniqueWords("aaa,bb,c")
res0: parseUniqueWords.ParseResult[Set[String]] = [1.9] parsed: Set(aaa, bb, c)

scala> parseUniqueWords("aaa,bb,aaa")
res1: parseUniqueWords.ParseResult[Set[String]] = 
[1.11] failure: Duplicate: aaa

aaa,bb,aaa
          ^

这就是我们想要的。

答案 1 :(得分:0)

这是一个选择辅助完成项目的解决方案,避免重复项目:

def select1(items: Iterable[String], separator: Parser[_] = Space) =
  token(separator ~> StringBasic.examples(FixedSetExamples(items)))

def selectSome(items: Seq[String], separator: Parser[_] = Space): Parser[Seq[String]] = {
   select1(items, separator).flatMap { v ⇒
   val remaining = items filter { _ != v }
   if (remaining.size == 0)
     success(v :: Nil)
   else
     selectSome(remaining).?.map(v +: _.getOrElse(Seq()))
 }

}

使用示例:

val myTask = inputTask[Unit]("Print selected numbers")
myTask := {
  val numbers = selectSome(Seq("One", "Two", "Three", "Four")).parsed
  numbers.foreach{ println _ }
}

使用SBT 0.13.9测试。