From 8d20a7ea1903fbe3d3211c70f262414d41203c10 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Fri, 6 Jul 2012 10:28:51 -0400 Subject: [PATCH] repeatDep parser combinator --- util/complete/Parsers.scala | 9 +++++ util/complete/src/test/scala/ParserTest.scala | 35 +++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/util/complete/Parsers.scala b/util/complete/Parsers.scala index 772ec20d5..5e4d46bc1 100644 --- a/util/complete/Parsers.scala +++ b/util/complete/Parsers.scala @@ -95,6 +95,15 @@ trait Parsers def flag[T](p: Parser[T]): Parser[Boolean] = (p ^^^ true) ?? false + def repeatDep[A](p: Seq[A] => Parser[A], sep: Parser[Any]): Parser[Seq[A]] = + { + def loop(acc: Seq[A]): Parser[Seq[A]] = { + val next = (sep ~> p(acc)) flatMap { result => loop(acc :+ result) } + next ?? acc + } + p(Vector()) flatMap { first => loop(Seq(first)) } + } + def trimmed(p: Parser[String]) = p map { _.trim } lazy val basicUri = mapOrFail(URIClass)( uri => new URI(uri)) def Uri(ex: Set[URI]) = basicUri examples(ex.map(_.toString)) diff --git a/util/complete/src/test/scala/ParserTest.scala b/util/complete/src/test/scala/ParserTest.scala index 9c6ec091d..a7d276a38 100644 --- a/util/complete/src/test/scala/ParserTest.scala +++ b/util/complete/src/test/scala/ParserTest.scala @@ -41,6 +41,7 @@ object JLineTest object ParserTest extends Properties("Completing Parser") { import Parsers._ + import DefaultParsers.matches val nested = (token("a1") ~ token("b2")) ~ "c3" val nestedDisplay = (token("a1", "") ~ token("b2", "")) ~ "c3" @@ -54,13 +55,23 @@ object ParserTest extends Properties("Completing Parser") ( ("display '" + in + "'") |: checkOne(in, nestedDisplay, expectDisplay) ) def checkOne(in: String, parser: Parser[_], expect: Completion): Prop = - p(completions(parser, in, 1)) == Completions.single(expect) + completions(parser, in, 1) == Completions.single(expect) + def checkAll(in: String, parser: Parser[_], expect: Completions): Prop = + { + val cs = completions(parser, in, 1) + ("completions: " + cs) |: ("Expected: " + expect) |: ( (cs == expect): Prop) + } + def checkInvalid(in: String) = ( ("token '" + in + "'") |: checkInv(in, nested) ) && ( ("display '" + in + "'") |: checkInv(in, nestedDisplay) ) + def checkInv(in: String, parser: Parser[_]): Prop = - p(completions(parser, in, 1)) == Completions.nil + { + val cs = completions(parser, in, 1) + ("completions: " + cs) |: (( cs == Completions.nil): Prop) + } property("nested tokens a") = checkSingle("", Completion.tokenStrict("","a1") )( Completion.displayStrict("")) property("nested tokens a1") = checkSingle("a", Completion.tokenStrict("a","1") )( Completion.displayStrict("")) @@ -78,6 +89,26 @@ object ParserTest extends Properties("Completing Parser") property("no suggest at token end") = checkOne("asdf", token("asdf"), Completion.suggestStrict("")) property("empty suggest for examples") = checkOne("asdf", any.+.examples("asdf", "qwer"), Completion.suggestStrict("")) property("empty suggest for examples token") = checkOne("asdf", token(any.+.examples("asdf", "qwer")), Completion.suggestStrict("")) + + val colors = Set("blue", "green", "red") + val base = (seen: Seq[String]) => token( ID examples (colors -- seen) ) + val sep = token( Space ) + val repeat = repeatDep( base, sep) + def completionStrings(ss: Set[String]): Completions = Completions(ss.map { s => Completion.tokenStrict("", s) }) + + property("repeatDep no suggestions for bad input") = checkInv(".", repeat) + property("repeatDep suggest all") = checkAll("", repeat, completionStrings(colors)) + property("repeatDep suggest remaining two") = { + val first = colors.toSeq.head + checkAll(first + " ", repeat, completionStrings(colors - first)) + } + property("repeatDep suggest remaining one") = { + val take = colors.toSeq.take(2) + checkAll(take.mkString("", " ", " "), repeat, completionStrings(colors -- take)) + } + property("repeatDep requires at least one token") = !matches(repeat, "") + property("repeatDep accepts one token") = matches(repeat, colors.toSeq.head) + property("repeatDep accepts two tokens") = matches(repeat, colors.toSeq.take(2).mkString(" ")) } object ParserExample {