Repeating dependent parsers with Scala / SBT parser combinators

Parsing a string of the form Value[,Value]+ can be easily done using rep1sep(Value, ',') . Is there a way to achieve rep1sep functionality when the Value parser depends on previously parsed values ​​in repetition? For example, compliance with the requirement that each value be unique?

The standard method for dependent parsers is flatMap, but I am having problems with its operation. Here is 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) } 

In general, I need a form 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 understand that the use case here is doubtful and that the uniqueness check is better handled after parsing. I ran into this problem when using SBT parser compilers to complete a tab - in particular, I would like to present completion options for key / value pairs only for keys that the user has not entered yet. See this analyzer for a complete example and SBT construction project.

+4
source share
2 answers

The following is not exactly how 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 instance:

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

What 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 ^ 

This is what we want.

+4
source

Here is a solution to select some items with auxiliary completion, avoiding duplicate 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())) } 

}

Usage example:

 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.

0
source

All Articles