mirror of https://github.com/sbt/sbt.git
fixes and additions to completion combinators
filter,map,flatMap remove incorrect reductions
This commit is contained in:
parent
8e6b42180d
commit
05195ecc7c
|
|
@ -14,7 +14,11 @@ sealed trait Completions
|
|||
final def x(o: Completions): Completions = Completions( for(cs <- get; os <- o.get) yield cs ++ os )
|
||||
final def ++(o: Completions): Completions = Completions( get ++ o.get )
|
||||
final def +:(o: Completion): Completions = Completions(get + o)
|
||||
final def filter(f: Completion => Boolean): Completions = Completions(get filter f)
|
||||
final def filterS(f: String => Boolean): Completions = filter(c => f(c.append))
|
||||
override def toString = get.mkString("Completions(",",",")")
|
||||
final def flatMap(f: Completion => Completions): Completions = Completions(get.flatMap(c => f(c).get))
|
||||
final def map(f: Completion => Completion): Completions = Completions(get map f)
|
||||
}
|
||||
object Completions
|
||||
{
|
||||
|
|
@ -28,10 +32,13 @@ object Completions
|
|||
def get = cs
|
||||
}
|
||||
|
||||
/** A Completions with no suggested completions, not even the empty Completion.*/
|
||||
val empty: Completions = strict(Set.empty)
|
||||
/** No suggested completions, not even the empty Completion.*/
|
||||
val nil: Completions = strict(Set.empty)
|
||||
|
||||
/** A Completions with only the marked empty Completion as a suggestion. */
|
||||
/** Only includes the unmarked empty Completion as a suggestion. */
|
||||
val empty: Completions = strict(Set.empty + Completion.empty)
|
||||
|
||||
/** Includes only the marked empty Completion as a suggestion. */
|
||||
val mark: Completions = strict(Set.empty + Completion.mark)
|
||||
|
||||
/** Returns a strict Completions instance with a single Completion with `s` for `append`.*/
|
||||
|
|
@ -71,6 +78,8 @@ sealed trait Completion
|
|||
/** Appends the completions in `o` with the completions in this unless `o` is marked and this is nonempty.*/
|
||||
final def ++(o: Completion): Completion = if(o.mark && !isEmpty) this else Completion(prepend + o.prepend, append + o.append, mark)
|
||||
|
||||
final def x(o: Completions): Completions = o.map(this ++ _)
|
||||
|
||||
override final def toString = triple.toString
|
||||
override final lazy val hashCode = triple.hashCode
|
||||
override final def equals(o: Any) = o match {
|
||||
|
|
|
|||
|
|
@ -25,14 +25,29 @@ sealed trait RichParser[A]
|
|||
/** Produces a Parser that applies the original Parser zero or one times.*/
|
||||
def ? : Parser[Option[A]]
|
||||
/** Produces a Parser that applies either the original Parser or `next`.*/
|
||||
def ||[B >: A](b: Parser[B]): Parser[B]
|
||||
def |[B >: A](b: Parser[B]): Parser[B]
|
||||
/** Produces a Parser that applies either the original Parser or `next`.*/
|
||||
def |[B](b: Parser[B]): Parser[Either[A,B]]
|
||||
def ||[B](b: Parser[B]): Parser[Either[A,B]]
|
||||
/** Produces a Parser that applies the original Parser to the input and then applies `f` to the result.*/
|
||||
def map[B](f: A => B): Parser[B]
|
||||
/** Returns the original parser. This is useful for converting literals to Parsers.
|
||||
* For example, `'c'.id` or `"asdf".id`*/
|
||||
def id: Parser[A]
|
||||
|
||||
def unary_- : Parser[Unit]
|
||||
def & (o: Parser[_]): Parser[A]
|
||||
def - (o: Parser[_]): Parser[A]
|
||||
/** Explicitly defines the completions for the original Parser.*/
|
||||
def examples(s: String*): Parser[A]
|
||||
/** Explicitly defines the completions for the original Parser.*/
|
||||
def examples(s: Set[String]): Parser[A]
|
||||
/** Converts a Parser returning a Char sequence to a Parser returning a String.*/
|
||||
def string(implicit ev: A <:< Seq[Char]): Parser[String]
|
||||
/** Produces a Parser that filters the original parser.
|
||||
* If 'f' is not true when applied to the output of the original parser, the Parser returned by this method fails.*/
|
||||
def filter(f: A => Boolean): Parser[A]
|
||||
|
||||
def flatMap[B](f: A => Parser[B]): Parser[B]
|
||||
}
|
||||
object Parser
|
||||
{
|
||||
|
|
@ -40,7 +55,7 @@ object Parser
|
|||
(p /: s)(derive1)
|
||||
|
||||
def derive1[T](p: Parser[T], c: Char): Parser[T] =
|
||||
p.derive(c)
|
||||
if(p.valid) p.derive(c) else p
|
||||
|
||||
def completions(p: Parser[_], s: String): Completions = completions( apply(p)(s) )
|
||||
def completions(p: Parser[_]): Completions = Completions.mark x p.completions
|
||||
|
|
@ -48,26 +63,44 @@ object Parser
|
|||
implicit def richParser[A](a: Parser[A]): RichParser[A] = new RichParser[A]
|
||||
{
|
||||
def ~[B](b: Parser[B]) = seqParser(a, b)
|
||||
def |[B](b: Parser[B]) = choiceParser(a,b)
|
||||
def ||[B >: A](b: Parser[B]) = homParser(a,b)
|
||||
def ||[B](b: Parser[B]) = choiceParser(a,b)
|
||||
def |[B >: A](b: Parser[B]) = homParser(a,b)
|
||||
def ? = opt(a)
|
||||
def * = zeroOrMore(a)
|
||||
def + = oneOrMore(a)
|
||||
def map[B](f: A => B) = mapParser(a, f)
|
||||
def id = a
|
||||
|
||||
def unary_- = not(a)
|
||||
def & (o: Parser[_]) = and(a, o)
|
||||
def - (o: Parser[_]) = sub(a, o)
|
||||
def examples(s: String*): Parser[A] = examples(s.toSet)
|
||||
def examples(s: Set[String]): Parser[A] = Parser.examples(a, s, check = true)
|
||||
def filter(f: A => Boolean): Parser[A] = filterParser(a, f)
|
||||
def string(implicit ev: A <:< Seq[Char]): Parser[String] = map(_.mkString)
|
||||
def flatMap[B](f: A => Parser[B]) = bindParser(a, f)
|
||||
}
|
||||
implicit def literalRichParser(c: Char): RichParser[Char] = richParser(c)
|
||||
implicit def literalRichParser(s: String): RichParser[String] = richParser(s)
|
||||
def examples[A](a: Parser[A], completions: Set[String]): Parser[A] =
|
||||
|
||||
def examples[A](a: Parser[A], completions: Set[String], check: Boolean = false): Parser[A] =
|
||||
if(a.valid) {
|
||||
a.result match
|
||||
{
|
||||
case Some(av) => success( av )
|
||||
case None => new Examples(a, completions)
|
||||
case None =>
|
||||
if(check) checkMatches(a, completions.toSeq)
|
||||
new Examples(a, completions)
|
||||
}
|
||||
}
|
||||
else Invalid
|
||||
|
||||
def checkMatches(a: Parser[_], completions: Seq[String])
|
||||
{
|
||||
val bad = completions.filter( apply(a)(_).resultEmpty.isEmpty)
|
||||
if(!bad.isEmpty) error("Invalid example completions: " + bad.mkString("'", "', '", "'"))
|
||||
}
|
||||
|
||||
def mapParser[A,B](a: Parser[A], f: A => B): Parser[B] =
|
||||
if(a.valid) {
|
||||
a.result match
|
||||
|
|
@ -78,6 +111,26 @@ object Parser
|
|||
}
|
||||
else Invalid
|
||||
|
||||
def bindParser[A,B](a: Parser[A], f: A => Parser[B]): Parser[B] =
|
||||
if(a.valid) {
|
||||
a.result match
|
||||
{
|
||||
case Some(av) => f(av)
|
||||
case None => new BindParser(a, f)
|
||||
}
|
||||
}
|
||||
else Invalid
|
||||
|
||||
def filterParser[T](a: Parser[T], f: T => Boolean): Parser[T] =
|
||||
if(a.valid) {
|
||||
a.result match
|
||||
{
|
||||
case Some(av) => if( f(av) ) success( av ) else Invalid
|
||||
case None => new Filter(a, f)
|
||||
}
|
||||
}
|
||||
else Invalid
|
||||
|
||||
def seqParser[A,B](a: Parser[A], b: Parser[B]): Parser[(A,B)] =
|
||||
if(a.valid && b.valid) {
|
||||
(a.result, b.result) match {
|
||||
|
|
@ -92,58 +145,24 @@ object Parser
|
|||
def token[T](t: Parser[T]): Parser[T] = tokenStart(t, "")
|
||||
def tokenStart[T](t: Parser[T], seen: String): Parser[T] =
|
||||
if(t.valid && !t.isTokenStart)
|
||||
{
|
||||
t.result match
|
||||
{
|
||||
case None => new TokenStart(t, seen)
|
||||
case Some(tv) => success(tv)
|
||||
}
|
||||
}
|
||||
if(t.result.isEmpty) new TokenStart(t, seen) else t
|
||||
else
|
||||
t
|
||||
|
||||
def homParser[A](a: Parser[A], b: Parser[A]): Parser[A] =
|
||||
if(a.valid) {
|
||||
if(b.valid) {
|
||||
(a.result orElse b.result) match
|
||||
{
|
||||
case Some(v) => success( v )
|
||||
case None => new HomParser(a, b)
|
||||
}
|
||||
}
|
||||
else a
|
||||
}
|
||||
else b
|
||||
if(a.valid)
|
||||
if(b.valid) new HomParser(a, b) else a
|
||||
else
|
||||
b
|
||||
|
||||
def choiceParser[A,B](a: Parser[A], b: Parser[B]): Parser[Either[A,B]] =
|
||||
if(a.valid) {
|
||||
if(b.valid) {
|
||||
a.result match
|
||||
{
|
||||
case Some(av) => success( Left(av) )
|
||||
case None =>
|
||||
b.result match
|
||||
{
|
||||
case Some(bv) => success( Right(bv) )
|
||||
case None => new HetParser(a, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
a.map( Left(_) )
|
||||
}
|
||||
if(a.valid)
|
||||
if(b.valid) new HetParser(a,b) else a.map( Left(_) )
|
||||
else
|
||||
b.map( Right(_) )
|
||||
|
||||
|
||||
def opt[T](a: Parser[T]): Parser[Option[T]] =
|
||||
if(a.valid) {
|
||||
a.result match
|
||||
{
|
||||
case None => new Optional(a)
|
||||
case x => success(x)
|
||||
}
|
||||
}
|
||||
else success(None)
|
||||
if(a.valid) new Optional(a) else success(None)
|
||||
|
||||
def zeroOrMore[T](p: Parser[T]): Parser[Seq[T]] = repeat(p, 0, Infinite)
|
||||
def oneOrMore[T](p: Parser[T]): Parser[Seq[T]] = repeat(p, 1, Infinite)
|
||||
|
|
@ -152,14 +171,15 @@ object Parser
|
|||
repeat(None, p, min, max, Nil)
|
||||
private[parse] def repeat[T](partial: Option[Parser[T]], repeated: Parser[T], min: Int, max: UpperBound, revAcc: List[T]): Parser[Seq[T]] =
|
||||
{
|
||||
assume(min >= 0, "Minimum must be greater than or equal to zero")
|
||||
|
||||
assume(min >= 0, "Minimum must be greater than or equal to zero (was " + min + ")")
|
||||
assume(max >= min, "Minimum must be less than or equal to maximum (min: " + min + ", max: " + max + ")")
|
||||
|
||||
def checkRepeated(invalidButOptional: => Parser[Seq[T]]): Parser[Seq[T]] =
|
||||
if(repeated.valid)
|
||||
repeated.result match
|
||||
{
|
||||
case Some(value) => success(value :: Nil)
|
||||
case None => new Repeat(partial, repeated, min, max, revAcc)
|
||||
case Some(value) => success(revAcc reverse_::: value :: Nil) // revAcc should be Nil here
|
||||
case None => if(max.isZero) success(revAcc.reverse) else new Repeat(partial, repeated, min, max, revAcc)
|
||||
}
|
||||
else if(min == 0)
|
||||
invalidButOptional
|
||||
|
|
@ -187,6 +207,22 @@ object Parser
|
|||
def completions = Completions.empty
|
||||
}
|
||||
|
||||
val any: Parser[Char] = charClass(_ => true)
|
||||
|
||||
def sub[T](a: Parser[T], b: Parser[_]): Parser[T] = and(a, not(b))
|
||||
|
||||
def and[T](a: Parser[T], b: Parser[_]): Parser[T] =
|
||||
if(a.valid && b.valid) new And(a, b) else Invalid
|
||||
|
||||
def not(p: Parser[_]): Parser[Unit] = new Not(p)
|
||||
|
||||
implicit def range(r: collection.immutable.NumericRange[Char]): Parser[Char] =
|
||||
new CharacterClass(r contains _).examples(r.map(_.toString) : _*)
|
||||
def chars(legal: String): Parser[Char] =
|
||||
{
|
||||
val set = legal.toSet
|
||||
new CharacterClass(set) examples(set.map(_.toString))
|
||||
}
|
||||
def charClass(f: Char => Boolean): Parser[Char] = new CharacterClass(f)
|
||||
implicit def literal(ch: Char): Parser[Char] = new Parser[Char] {
|
||||
def resultEmpty = None
|
||||
|
|
@ -195,14 +231,14 @@ object Parser
|
|||
}
|
||||
implicit def literal(s: String): Parser[String] = stringLiteral(s, s.toList)
|
||||
def stringLiteral(s: String, remaining: List[Char]): Parser[String] =
|
||||
if(remaining.isEmpty) success(s) else if(s.isEmpty) error("String literal cannot be empty") else new StringLiteral(s, remaining)
|
||||
if(s.isEmpty) error("String literal cannot be empty") else if(remaining.isEmpty) success(s) else new StringLiteral(s, remaining)
|
||||
}
|
||||
private final object Invalid extends Parser[Nothing]
|
||||
{
|
||||
def resultEmpty = None
|
||||
def derive(c: Char) = error("Invalid.")
|
||||
override def valid = false
|
||||
def completions = Completions.empty
|
||||
def completions = Completions.nil
|
||||
}
|
||||
private final class SeqParser[A,B](a: Parser[A], b: Parser[B]) extends Parser[(A,B)]
|
||||
{
|
||||
|
|
@ -213,7 +249,7 @@ private final class SeqParser[A,B](a: Parser[A], b: Parser[B]) extends Parser[(A
|
|||
val common = a.derive(c) ~ b
|
||||
a.resultEmpty match
|
||||
{
|
||||
case Some(av) => common || b.derive(c).map(br => (av,br))
|
||||
case Some(av) => common | b.derive(c).map(br => (av,br))
|
||||
case None => common
|
||||
}
|
||||
}
|
||||
|
|
@ -222,16 +258,28 @@ private final class SeqParser[A,B](a: Parser[A], b: Parser[B]) extends Parser[(A
|
|||
|
||||
private final class HomParser[A](a: Parser[A], b: Parser[A]) extends Parser[A]
|
||||
{
|
||||
def derive(c: Char) = (a derive c) || (b derive c)
|
||||
def derive(c: Char) = (a derive c) | (b derive c)
|
||||
lazy val resultEmpty = a.resultEmpty orElse b.resultEmpty
|
||||
lazy val completions = a.completions ++ b.completions
|
||||
}
|
||||
private final class HetParser[A,B](a: Parser[A], b: Parser[B]) extends Parser[Either[A,B]]
|
||||
{
|
||||
def derive(c: Char) = (a derive c) | (b derive c)
|
||||
def derive(c: Char) = (a derive c) || (b derive c)
|
||||
lazy val resultEmpty = a.resultEmpty.map(Left(_)) orElse b.resultEmpty.map(Right(_))
|
||||
lazy val completions = a.completions ++ b.completions
|
||||
}
|
||||
private final class BindParser[A,B](a: Parser[A], f: A => Parser[B]) extends Parser[B]
|
||||
{
|
||||
lazy val resultEmpty = a.resultEmpty match { case None => None; case Some(av) => f(av).resultEmpty }
|
||||
lazy val completions =
|
||||
a.completions flatMap { c =>
|
||||
apply(a)(c.append).resultEmpty match {
|
||||
case None => Completions.empty
|
||||
case Some(av) => c x f(av).completions
|
||||
}
|
||||
}
|
||||
def derive(c: Char) = a derive c flatMap f
|
||||
}
|
||||
private final class MapParser[A,B](a: Parser[A], f: A => B) extends Parser[B]
|
||||
{
|
||||
lazy val resultEmpty = a.resultEmpty map f
|
||||
|
|
@ -239,6 +287,12 @@ private final class MapParser[A,B](a: Parser[A], f: A => B) extends Parser[B]
|
|||
def completions = a.completions
|
||||
override def isTokenStart = a.isTokenStart
|
||||
}
|
||||
private final class Filter[T](p: Parser[T], f: T => Boolean) extends Parser[T]
|
||||
{
|
||||
lazy val resultEmpty = p.resultEmpty filter f
|
||||
def derive(c: Char) = (p derive c) filter f
|
||||
lazy val completions = p.completions filterS { s => apply(p)(s).resultEmpty.filter(f).isDefined }
|
||||
}
|
||||
private final class TokenStart[T](delegate: Parser[T], seen: String) extends Parser[T]
|
||||
{
|
||||
def derive(c: Char) = tokenStart( delegate derive c, seen + c )
|
||||
|
|
@ -250,9 +304,22 @@ private final class TokenStart[T](delegate: Parser[T], seen: String) extends Par
|
|||
def resultEmpty = delegate.resultEmpty
|
||||
override def isTokenStart = true
|
||||
}
|
||||
private final class And[T](a: Parser[T], b: Parser[_]) extends Parser[T]
|
||||
{
|
||||
def derive(c: Char) = (a derive c) & (b derive c)
|
||||
lazy val completions = a.completions.filterS(s => apply(b)(s).resultEmpty.isDefined )
|
||||
lazy val resultEmpty = if(b.resultEmpty.isDefined) a.resultEmpty else None
|
||||
}
|
||||
|
||||
private final class Not(delegate: Parser[_]) extends Parser[Unit]
|
||||
{
|
||||
def derive(c: Char) = if(delegate.valid) not(delegate derive c) else this
|
||||
def completions = Completions.empty
|
||||
lazy val resultEmpty = if(delegate.resultEmpty.isDefined) None else Some(())
|
||||
}
|
||||
private final class Examples[T](delegate: Parser[T], fixed: Set[String]) extends Parser[T]
|
||||
{
|
||||
def derive(c: Char) = examples(delegate.derive(c), fixed.collect { case x if x.length > 0 && x(0) == c => x.tail })
|
||||
def derive(c: Char) = examples(delegate derive c, fixed.collect { case x if x.length > 0 && x(0) == c => x.tail })
|
||||
def resultEmpty = delegate.resultEmpty
|
||||
lazy val completions = Completions(fixed map { ex => Completion.strict("",ex,false) } )
|
||||
}
|
||||
|
|
@ -287,7 +354,7 @@ private final class Repeat[T](partial: Option[Parser[T]], repeated: Parser[T], m
|
|||
val partD = repeat(Some(part derive c), repeated, min, max, accumulatedReverse)
|
||||
part.resultEmpty match
|
||||
{
|
||||
case Some(pv) => partD || repeatDerive(c, pv :: accumulatedReverse)
|
||||
case Some(pv) => partD | repeatDerive(c, pv :: accumulatedReverse)
|
||||
case None => partD
|
||||
}
|
||||
case None => repeatDerive(c, accumulatedReverse)
|
||||
|
|
@ -297,8 +364,11 @@ private final class Repeat[T](partial: Option[Parser[T]], repeated: Parser[T], m
|
|||
|
||||
lazy val completions =
|
||||
{
|
||||
def pow(comp: Completions, exp: Completions, n: Int): Completions =
|
||||
if(n == 1) comp else pow(comp x exp, exp, n - 1)
|
||||
|
||||
val repC = repeated.completions
|
||||
val fin = if(min == 0) Completion.empty +: repC else repC
|
||||
val fin = if(min == 0) Completion.empty +: repC else pow(repC, repC, min)
|
||||
partial match
|
||||
{
|
||||
case Some(p) => p.completions x fin
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ sealed trait UpperBound
|
|||
/** True if and only if this bound is zero.*/
|
||||
def isZero: Boolean
|
||||
/** If this bound is zero or Infinite, `decrement` returns this bound.
|
||||
* Otherwise, this bound is finite and nonzero, and `decrement` returns the bound that is one less than this bound.*/
|
||||
* Otherwise, this bound is finite and greater than zero and `decrement` returns the bound that is one less than this bound.*/
|
||||
def decrement: UpperBound
|
||||
/** True if and only if this is unbounded.*/
|
||||
def isInfinite: Boolean
|
||||
|
|
@ -32,12 +32,16 @@ case object Infinite extends UpperBound
|
|||
* It must positive. */
|
||||
final case class Finite(value: Int) extends UpperBound
|
||||
{
|
||||
assume(value > 0, "Maximum occurences must be positive.")
|
||||
|
||||
assume(value >= 0, "Maximum occurences must be nonnegative.")
|
||||
|
||||
def >=(min: Int) = value >= min
|
||||
def isOne = value == 1
|
||||
def isZero = value == 0
|
||||
def decrement = Finite( (value - 1) max 0 )
|
||||
def isInfinite = false
|
||||
override def toString = value.toString
|
||||
}
|
||||
object UpperBound
|
||||
{
|
||||
implicit def intToFinite(i: Int): Finite = Finite(i)
|
||||
}
|
||||
|
|
@ -23,4 +23,22 @@ object ParserExample
|
|||
println(apply(t)("test").resultEmpty)
|
||||
println(apply(t)("test w").resultEmpty)
|
||||
println(apply(t)("test was were").resultEmpty)
|
||||
}
|
||||
|
||||
def run(n: Int)
|
||||
{
|
||||
val a = 'a'.id
|
||||
val aq = a.?
|
||||
val aqn = repeat(aq, min = n, max = n)
|
||||
val an = repeat(a, min = n, max = n)
|
||||
val ann = aqn ~ an
|
||||
|
||||
def r = apply(ann)("a"*(n*2)).resultEmpty
|
||||
println(r.isDefined)
|
||||
}
|
||||
def run2(n: Int)
|
||||
{
|
||||
val ab = "ab".?.*
|
||||
val r = apply(ab)("a"*n).resultEmpty
|
||||
println(r)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue