From 05195ecc7ca65dc93939fd68cf13ea738cac2919 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Wed, 8 Dec 2010 22:16:12 -0500 Subject: [PATCH] fixes and additions to completion combinators filter,map,flatMap remove incorrect reductions --- util/complete/Completions.scala | 15 +- util/complete/Parser.scala | 194 ++++++++++++------ util/complete/UpperBound.scala | 10 +- util/complete/src/test/scala/ParserTest.scala | 20 +- 4 files changed, 170 insertions(+), 69 deletions(-) diff --git a/util/complete/Completions.scala b/util/complete/Completions.scala index db7b628e2..7a5f22444 100644 --- a/util/complete/Completions.scala +++ b/util/complete/Completions.scala @@ -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 { diff --git a/util/complete/Parser.scala b/util/complete/Parser.scala index 7bd1b455f..459f7680a 100644 --- a/util/complete/Parser.scala +++ b/util/complete/Parser.scala @@ -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 diff --git a/util/complete/UpperBound.scala b/util/complete/UpperBound.scala index 1bdc1592c..9427070f7 100644 --- a/util/complete/UpperBound.scala +++ b/util/complete/UpperBound.scala @@ -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) } \ No newline at end of file diff --git a/util/complete/src/test/scala/ParserTest.scala b/util/complete/src/test/scala/ParserTest.scala index 5331cd27b..0fda3c6e3 100644 --- a/util/complete/src/test/scala/ParserTest.scala +++ b/util/complete/src/test/scala/ParserTest.scala @@ -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) + } +} \ No newline at end of file