Repeating dependent parsers with Scala/SBT parser combinators

寵の児 提交于 2019-12-22 09:38:45

问题


Parsing a string of the form Value[,Value]+ can be easily accomplished via rep1sep(Value, ','). Is there a way to achieve rep1sep functionality when the Value parser is dependent on the previously parsed Values in the repetition? For example, enforcing a requirement that each value must be unique?

The standard technique for dependent parsers is flatMap, but I am having trouble getting that working. Here's one such attempt:

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

Generically, I need a form of rep1sep where the parser argument is a function from Seq[A] to Parser[A]:

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

Note: I realize the use case is questionable here and that validating uniqueness is better handled post parsing. I've encountered this problem while using the SBT parsing combinators for the purpose of tab completion -- specifically, I'd like to present completion options for key/value pairs for only those keys that the user has not already entered. See this parser for a full example and buildable SBT project.


回答1:


The following isn't quite as generic as your rep1sepDependent, but it works:

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

For example:

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

Which gives us:

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
          ^

Which is what we want.




回答2:


Here is a solution for selecting some items with assisted completion, avoiding duplicated items:

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

}

Example of use:

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

Tested with SBT 0.13.9.



来源:https://stackoverflow.com/questions/11303997/repeating-dependent-parsers-with-scala-sbt-parser-combinators

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!