mirror of
https://github.com/sbt/sbt.git
synced 2026-10-07 18:43:31 +02:00
alias only parses the right hand side for tab completion help. The assignment should happen whether or not the parse is successful because the context may change by the time the alias is actually evaluated. In particular, the 'set' command uses the loaded project for tab completion in 0.12.1. When a .sbtrc file is processed, the project has not been loaded yet, so aliases involving set fail. Wrapping the rhs in failOnException addresses this.
682 lines
26 KiB
Scala
682 lines
26 KiB
Scala
/* sbt -- Simple Build Tool
|
|
* Copyright 2008, 2010, 2011 Mark Harrah
|
|
*/
|
|
package sbt.complete
|
|
|
|
import Parser._
|
|
import sbt.Types.{const, left, right, some}
|
|
import sbt.Util.separate
|
|
|
|
sealed trait Parser[+T]
|
|
{
|
|
def derive(i: Char): Parser[T]
|
|
def resultEmpty: Result[T]
|
|
def result: Option[T]
|
|
def completions(level: Int): Completions
|
|
def failure: Option[Failure]
|
|
def isTokenStart = false
|
|
def ifValid[S](p: => Parser[S]): Parser[S]
|
|
def valid: Boolean
|
|
}
|
|
sealed trait RichParser[A]
|
|
{
|
|
/** Produces a Parser that applies the original Parser and then applies `next` (in order).*/
|
|
def ~[B](next: Parser[B]): Parser[(A,B)]
|
|
/** Produces a Parser that applies the original Parser one or more times.*/
|
|
def + : Parser[Seq[A]]
|
|
/** Produces a Parser that applies the original Parser zero or more times.*/
|
|
def * : Parser[Seq[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 `b`.*/
|
|
def |[B >: A](b: Parser[B]): Parser[B]
|
|
/** Produces a Parser that applies either the original Parser or `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 ^^^[B](value: B): Parser[B]
|
|
def ??[B >: A](alt: B): Parser[B]
|
|
def <~[B](b: Parser[B]): Parser[A]
|
|
def ~>[B](b: Parser[B]): Parser[B]
|
|
|
|
/** Uses the specified message if the original Parser fails.*/
|
|
def !!!(msg: String): Parser[A]
|
|
/** If an exception is thrown by the original Parser,
|
|
* capture it and fail locally instead of allowing the exception to propagate up and terminate parsing.*/
|
|
def failOnException: 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], check: Boolean = false): 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, msg: String => String): Parser[A]
|
|
|
|
def flatMap[B](f: A => Parser[B]): Parser[B]
|
|
}
|
|
object Parser extends ParserMain
|
|
{
|
|
sealed abstract class Result[+T] {
|
|
def isFailure: Boolean
|
|
def isValid: Boolean
|
|
def errors: Seq[String]
|
|
def or[B >: T](b: => Result[B]): Result[B]
|
|
def either[B](b: => Result[B]): Result[Either[T,B]]
|
|
def map[B](f: T => B): Result[B]
|
|
def flatMap[B](f: T => Result[B]): Result[B]
|
|
def &&(b: => Result[_]): Result[T]
|
|
def filter(f: T => Boolean, msg: => String): Result[T]
|
|
def seq[B](b: => Result[B]): Result[(T,B)] = app(b)( (m,n) => (m,n) )
|
|
def app[B,C](b: => Result[B])(f: (T, B) => C): Result[C]
|
|
def toEither: Either[() => Seq[String], T]
|
|
}
|
|
final case class Value[+T](value: T) extends Result[T] {
|
|
def isFailure = false
|
|
def isValid: Boolean = true
|
|
def errors = Nil
|
|
def app[B,C](b: => Result[B])(f: (T, B) => C): Result[C] = b match {
|
|
case fail: Failure => fail
|
|
case Value(bv) => Value(f(value, bv))
|
|
}
|
|
def &&(b: => Result[_]): Result[T] = b match { case f: Failure => f; case _ => this }
|
|
def or[B >: T](b: => Result[B]): Result[B] = this
|
|
def either[B](b: => Result[B]): Result[Either[T,B]] = Value(Left(value))
|
|
def map[B](f: T => B): Result[B] = Value(f(value))
|
|
def flatMap[B](f: T => Result[B]): Result[B] = f(value)
|
|
def filter(f: T => Boolean, msg: => String): Result[T] = if(f(value)) this else mkFailure(msg)
|
|
def toEither = Right(value)
|
|
}
|
|
final class Failure(mkErrors: => Seq[String]) extends Result[Nothing] {
|
|
lazy val errors: Seq[String] = mkErrors
|
|
def isFailure = true
|
|
def isValid = false
|
|
def map[B](f: Nothing => B) = this
|
|
def flatMap[B](f: Nothing => Result[B]) = this
|
|
def or[B](b: => Result[B]): Result[B] = b match {
|
|
case v: Value[B] => v
|
|
case f: Failure => concatErrors(f)
|
|
}
|
|
def either[B](b: => Result[B]): Result[Either[Nothing,B]] = b match {
|
|
case Value(v) => Value(Right(v))
|
|
case f: Failure => concatErrors(f)
|
|
}
|
|
def filter(f: Nothing => Boolean, msg: => String) = this
|
|
def app[B,C](b: => Result[B])(f: (Nothing, B) => C): Result[C] = this
|
|
def &&(b: => Result[_]) = this
|
|
def toEither = Left(() => errors)
|
|
|
|
private[this] def concatErrors(f: Failure) = mkFailures(errors ++ f.errors)
|
|
}
|
|
def mkFailures(errors: => Seq[String]): Failure = new Failure(errors.distinct)
|
|
def mkFailure(error: => String): Failure = new Failure(error :: Nil)
|
|
|
|
def checkMatches(a: Parser[_], completions: Seq[String])
|
|
{
|
|
val bad = completions.filter( apply(a)(_).resultEmpty.isFailure)
|
|
if(!bad.isEmpty) error("Invalid example completions: " + bad.mkString("'", "', '", "'"))
|
|
}
|
|
def tuple[A,B](a: Option[A], b: Option[B]): Option[(A,B)] =
|
|
(a,b) match { case (Some(av), Some(bv)) => Some(av, bv); case _ => None }
|
|
|
|
def mapParser[A,B](a: Parser[A], f: A => B): Parser[B] =
|
|
a.ifValid {
|
|
a.result match
|
|
{
|
|
case Some(av) => success( f(av) )
|
|
case None => new MapParser(a, f)
|
|
}
|
|
}
|
|
|
|
def bindParser[A,B](a: Parser[A], f: A => Parser[B]): Parser[B] =
|
|
a.ifValid {
|
|
a.result match
|
|
{
|
|
case Some(av) => f(av)
|
|
case None => new BindParser(a, f)
|
|
}
|
|
}
|
|
|
|
def filterParser[T](a: Parser[T], f: T => Boolean, seen: String, msg: String => String): Parser[T] =
|
|
a.ifValid {
|
|
a.result match
|
|
{
|
|
case Some(av) if f(av) => success( av )
|
|
case _ => new Filter(a, f, seen, msg)
|
|
}
|
|
}
|
|
|
|
def seqParser[A,B](a: Parser[A], b: Parser[B]): Parser[(A,B)] =
|
|
a.ifValid { b.ifValid {
|
|
(a.result, b.result) match {
|
|
case (Some(av), Some(bv)) => success( (av, bv) )
|
|
case (Some(av), None) => b map { bv => (av, bv) }
|
|
case (None, Some(bv)) => a map { av => (av, bv) }
|
|
case (None, None) => new SeqParser(a,b)
|
|
}
|
|
}}
|
|
|
|
def choiceParser[A,B](a: Parser[A], b: Parser[B]): Parser[Either[A,B]] =
|
|
if(a.valid)
|
|
if(b.valid) new HetParser(a,b) else a.map( left.fn )
|
|
else
|
|
b.map( right.fn )
|
|
|
|
def opt[T](a: Parser[T]): Parser[Option[T]] =
|
|
if(a.valid) new Optional(a) else success(None)
|
|
|
|
def onFailure[T](delegate: Parser[T], msg: String): Parser[T] =
|
|
if(delegate.valid) new OnFailure(delegate, msg) else failure(msg)
|
|
def trapAndFail[T](delegate: Parser[T]): Parser[T] =
|
|
delegate.ifValid( new TrapAndFail(delegate) )
|
|
|
|
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)
|
|
|
|
def repeat[T](p: Parser[T], min: Int = 0, max: UpperBound = Infinite): Parser[Seq[T]] =
|
|
repeat(None, p, min, max, Nil)
|
|
private[complete] 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 (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]] =
|
|
repeated match
|
|
{
|
|
case i: Invalid if min == 0 => invalidButOptional
|
|
case i: Invalid => i
|
|
case _ =>
|
|
repeated.result match
|
|
{
|
|
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)
|
|
}
|
|
}
|
|
|
|
partial match
|
|
{
|
|
case Some(part) =>
|
|
part.ifValid {
|
|
part.result match
|
|
{
|
|
case Some(value) => repeat(None, repeated, min, max, value :: revAcc)
|
|
case None => checkRepeated(part.map(lv => (lv :: revAcc).reverse))
|
|
}
|
|
}
|
|
case None => checkRepeated(success(Nil))
|
|
}
|
|
}
|
|
|
|
def sub[T](a: Parser[T], b: Parser[_]): Parser[T] = and(a, not(b))
|
|
|
|
def and[T](a: Parser[T], b: Parser[_]): Parser[T] = a.ifValid( b.ifValid( new And(a, b) ))
|
|
}
|
|
trait ParserMain
|
|
{
|
|
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 ? = opt(a)
|
|
def * = zeroOrMore(a)
|
|
def + = oneOrMore(a)
|
|
def map[B](f: A => B) = mapParser(a, f)
|
|
def id = a
|
|
|
|
def ^^^[B](value: B): Parser[B] = a map { _ => value }
|
|
def ??[B >: A](alt: B): Parser[B] = a.? map { _ getOrElse alt }
|
|
def <~[B](b: Parser[B]): Parser[A] = (a ~ b) map { case av ~ _ => av }
|
|
def ~>[B](b: Parser[B]): Parser[B] = (a ~ b) map { case _ ~ bv => bv }
|
|
def !!!(msg: String): Parser[A] = onFailure(a, msg)
|
|
def failOnException: Parser[A] = trapAndFail(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], check: Boolean = false): Parser[A] = Parser.examples(a, s, check)
|
|
def filter(f: A => Boolean, msg: String => String): Parser[A] = filterParser(a, f, "", msg)
|
|
def string(implicit ev: A <:< Seq[Char]): Parser[String] = map(_.mkString)
|
|
def flatMap[B](f: A => Parser[B]) = bindParser(a, f)
|
|
}
|
|
implicit def literalRichCharParser(c: Char): RichParser[Char] = richParser(c)
|
|
implicit def literalRichStringParser(s: String): RichParser[String] = richParser(s)
|
|
|
|
def invalid(msgs: => Seq[String]): Parser[Nothing] = Invalid(mkFailures(msgs))
|
|
def failure(msg: => String): Parser[Nothing] = invalid(msg :: Nil)
|
|
def success[T](value: T): Parser[T] = new ValidParser[T] {
|
|
override def result = Some(value)
|
|
def resultEmpty = Value(value)
|
|
def derive(c: Char) = Parser.failure("Expected end of input.")
|
|
def completions(level: Int) = Completions.empty
|
|
override def toString = "success(" + value + ")"
|
|
}
|
|
|
|
implicit def range(r: collection.immutable.NumericRange[Char]): Parser[Char] =
|
|
charClass(r contains _).examples(r.map(_.toString) : _*)
|
|
def chars(legal: String): Parser[Char] =
|
|
{
|
|
val set = legal.toSet
|
|
charClass(set, "character in '" + legal + "'") examples(set.map(_.toString))
|
|
}
|
|
def charClass(f: Char => Boolean, label: String = "<unspecified>"): Parser[Char] = new CharacterClass(f, label)
|
|
|
|
implicit def literal(ch: Char): Parser[Char] = new ValidParser[Char] {
|
|
def result = None
|
|
def resultEmpty = mkFailure( "Expected '" + ch + "'" )
|
|
def derive(c: Char) = if(c == ch) success(ch) else new Invalid(resultEmpty)
|
|
def completions(level: Int) = Completions.single(Completion.suggestStrict(ch.toString))
|
|
override def toString = "'" + ch + "'"
|
|
}
|
|
implicit def literal(s: String): Parser[String] = stringLiteral(s, 0)
|
|
object ~ {
|
|
def unapply[A,B](t: (A,B)): Some[(A,B)] = Some(t)
|
|
}
|
|
|
|
def parse[T](str: String, parser: Parser[T]): Either[String, T] =
|
|
Parser.result(parser, str).left.map { failures =>
|
|
val (msgs,pos) = failures()
|
|
ProcessError(str, msgs, pos)
|
|
}
|
|
|
|
def sample(str: String, parser: Parser[_], completions: Boolean = false): Unit =
|
|
if(completions) sampleCompletions(str, parser) else sampleParse(str, parser)
|
|
def sampleParse(str: String, parser: Parser[_]): Unit =
|
|
parse(str, parser) match {
|
|
case Left(msg) => println(msg)
|
|
case Right(v) => println(v)
|
|
}
|
|
def sampleCompletions(str: String, parser: Parser[_], level: Int = 1): Unit =
|
|
Parser.completions(parser, str, level).get foreach println
|
|
|
|
// intended to be temporary pending proper error feedback
|
|
def result[T](p: Parser[T], s: String): Either[() => (Seq[String],Int), T] =
|
|
{
|
|
def loop(i: Int, a: Parser[T]): Either[() => (Seq[String],Int), T] =
|
|
a match
|
|
{
|
|
case Invalid(f) => Left( () => (f.errors, i) )
|
|
case _ =>
|
|
val ci = i+1
|
|
if(ci >= s.length)
|
|
a.resultEmpty.toEither.left.map { msgs0 => () =>
|
|
val msgs = msgs0()
|
|
val nonEmpty = if(msgs.isEmpty) "Unexpected end of input" :: Nil else msgs
|
|
(nonEmpty, ci)
|
|
}
|
|
else
|
|
loop(ci, a derive s(ci) )
|
|
}
|
|
loop(-1, p)
|
|
}
|
|
|
|
def apply[T](p: Parser[T])(s: String): Parser[T] =
|
|
(p /: s)(derive1)
|
|
|
|
def derive1[T](p: Parser[T], c: Char): Parser[T] =
|
|
if(p.valid) p.derive(c) else p
|
|
|
|
// The x Completions.empty removes any trailing token completions where append.isEmpty
|
|
def completions(p: Parser[_], s: String, level: Int): Completions = apply(p)(s).completions(level) x Completions.empty
|
|
|
|
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 =>
|
|
if(check) checkMatches(a, completions.toSeq)
|
|
new Examples(a, completions)
|
|
}
|
|
}
|
|
else a
|
|
|
|
def matched(t: Parser[_], seen: Vector[Char] = Vector.empty, partial: Boolean = false): Parser[String] =
|
|
t match
|
|
{
|
|
case i: Invalid => if(partial && !seen.isEmpty) success(seen.mkString) else i
|
|
case _ =>
|
|
if(t.result.isEmpty)
|
|
new MatchedString(t, seen, partial)
|
|
else
|
|
success(seen.mkString)
|
|
}
|
|
|
|
/** Establishes delegate parser `t` as a single token of tab completion.
|
|
* When tab completion of part of this token is requested, the completions provided by the delegate `t` or a later derivative are appended to
|
|
* the prefix String already seen by this parser. */
|
|
def token[T](t: Parser[T]): Parser[T] = token(t, TokenCompletions.default)
|
|
|
|
/** Establishes delegate parser `t` as a single token of tab completion.
|
|
* When tab completion of part of this token is requested, no completions are returned if `hide` returns true for the current tab completion level.
|
|
* Otherwise, the completions provided by the delegate `t` or a later derivative are appended to the prefix String already seen by this parser.*/
|
|
def token[T](t: Parser[T], hide: Int => Boolean): Parser[T] = token(t, TokenCompletions.default.hideWhen(hide))
|
|
|
|
/** Establishes delegate parser `t` as a single token of tab completion.
|
|
* When tab completion of part of this token is requested, `description` is displayed for suggestions and no completions are ever performed. */
|
|
def token[T](t: Parser[T], description: String): Parser[T] = token(t, TokenCompletions.displayOnly(description))
|
|
|
|
/** Establishes delegate parser `t` as a single token of tab completion.
|
|
* When tab completion of part of this token is requested, `display` is used as the printed suggestion, but the completions from the delegate
|
|
* parser `t` are used to complete if unambiguous. */
|
|
def tokenDisplay[T](t: Parser[T], display: String): Parser[T] =
|
|
token(t, TokenCompletions.overrideDisplay(display))
|
|
|
|
def token[T](t: Parser[T], complete: TokenCompletions): Parser[T] =
|
|
mkToken(t, "", complete)
|
|
|
|
@deprecated("Use a different `token` overload.", "0.12.1")
|
|
def token[T](t: Parser[T], seen: String, track: Boolean, hide: Int => Boolean): Parser[T] =
|
|
{
|
|
val base = if(track) TokenCompletions.default else TokenCompletions.displayOnly(seen)
|
|
token(t, base.hideWhen(hide))
|
|
}
|
|
|
|
private[sbt] def mkToken[T](t: Parser[T], seen: String, complete: TokenCompletions): Parser[T] =
|
|
if(t.valid && !t.isTokenStart)
|
|
if(t.result.isEmpty) new TokenStart(t, seen, complete) else t
|
|
else
|
|
t
|
|
|
|
def homParser[A](a: Parser[A], b: Parser[A]): Parser[A] =
|
|
if(a.valid)
|
|
if(b.valid) new HomParser(a, b) else a
|
|
else
|
|
b
|
|
|
|
def not(p: Parser[_]): Parser[Unit] = p.result match {
|
|
case None => new Not(p)
|
|
case Some(_) => failure("Excluded.")
|
|
}
|
|
|
|
def oneOf[T](p: Seq[Parser[T]]): Parser[T] = p.reduceLeft(_ | _)
|
|
def seq[T](p: Seq[Parser[T]]): Parser[Seq[T]] = seq0(p, Nil)
|
|
def seq0[T](p: Seq[Parser[T]], errors: => Seq[String]): Parser[Seq[T]] =
|
|
{
|
|
val (newErrors, valid) = separate(p) { case Invalid(f) => Left(f.errors); case ok => Right(ok) }
|
|
def combinedErrors = errors ++ newErrors.flatten
|
|
if(valid.isEmpty) invalid(combinedErrors) else new ParserSeq(valid, combinedErrors)
|
|
}
|
|
|
|
def stringLiteral(s: String, start: Int): Parser[String] =
|
|
{
|
|
val len = s.length
|
|
if(len == 0) error("String literal cannot be empty") else if(start >= len) success(s) else new StringLiteral(s, start)
|
|
}
|
|
}
|
|
sealed trait ValidParser[T] extends Parser[T]
|
|
{
|
|
final def valid = true
|
|
final def failure = None
|
|
final def ifValid[S](p: => Parser[S]): Parser[S] = p
|
|
}
|
|
private final case class Invalid(fail: Failure) extends Parser[Nothing]
|
|
{
|
|
def failure = Some(fail)
|
|
def result = None
|
|
def resultEmpty = fail
|
|
def derive(c: Char) = error("Invalid.")
|
|
def completions(level: Int) = Completions.nil
|
|
override def toString = fail.errors.mkString("; ")
|
|
def valid = false
|
|
def ifValid[S](p: => Parser[S]): Parser[S] = this
|
|
}
|
|
|
|
private final class TrapAndFail[A](a: Parser[A]) extends ValidParser[A]
|
|
{
|
|
def result = try { a.result } catch { case e: Exception => None }
|
|
def resultEmpty = try { a.resultEmpty } catch { case e: Exception => fail(e) }
|
|
def derive(c: Char) = try { trapAndFail(a derive c) } catch { case e: Exception => Invalid(fail(e)) }
|
|
def completions(level: Int) = try { a.completions(level) } catch { case e: Exception => Completions.nil }
|
|
override def toString = "trap(" + a + ")"
|
|
override def isTokenStart = a.isTokenStart
|
|
private[this] def fail(e: Exception): Failure = mkFailure(e.toString)
|
|
}
|
|
|
|
private final class OnFailure[A](a: Parser[A], message: String) extends ValidParser[A]
|
|
{
|
|
def result = a.result
|
|
def resultEmpty = a.resultEmpty match { case f: Failure => mkFailure(message); case v: Value[A] => v }
|
|
def derive(c: Char) = onFailure(a derive c, message)
|
|
def completions(level: Int) = a.completions(level)
|
|
override def toString = "(" + a + " !!! \"" + message + "\" )"
|
|
override def isTokenStart = a.isTokenStart
|
|
}
|
|
private final class SeqParser[A,B](a: Parser[A], b: Parser[B]) extends ValidParser[(A,B)]
|
|
{
|
|
lazy val result = tuple(a.result,b.result)
|
|
lazy val resultEmpty = a.resultEmpty seq b.resultEmpty
|
|
def derive(c: Char) =
|
|
{
|
|
val common = a.derive(c) ~ b
|
|
a.resultEmpty match
|
|
{
|
|
case Value(av) => common | b.derive(c).map(br => (av,br))
|
|
case _: Failure => common
|
|
}
|
|
}
|
|
def completions(level: Int) = a.completions(level) x b.completions(level)
|
|
override def toString = "(" + a + " ~ " + b + ")"
|
|
}
|
|
|
|
private final class HomParser[A](a: Parser[A], b: Parser[A]) extends ValidParser[A]
|
|
{
|
|
lazy val result = tuple(a.result, b.result) map (_._1)
|
|
def derive(c: Char) = (a derive c) | (b derive c)
|
|
lazy val resultEmpty = a.resultEmpty or b.resultEmpty
|
|
def completions(level: Int) = a.completions(level) ++ b.completions(level)
|
|
override def toString = "(" + a + " | " + b + ")"
|
|
}
|
|
private final class HetParser[A,B](a: Parser[A], b: Parser[B]) extends ValidParser[Either[A,B]]
|
|
{
|
|
lazy val result = tuple(a.result, b.result) map { case (a,b) => Left(a) }
|
|
def derive(c: Char) = (a derive c) || (b derive c)
|
|
lazy val resultEmpty = a.resultEmpty either b.resultEmpty
|
|
def completions(level: Int) = a.completions(level) ++ b.completions(level)
|
|
override def toString = "(" + a + " || " + b + ")"
|
|
}
|
|
private final class ParserSeq[T](a: Seq[Parser[T]], errors: => Seq[String]) extends ValidParser[Seq[T]]
|
|
{
|
|
assert(!a.isEmpty)
|
|
lazy val resultEmpty: Result[Seq[T]] =
|
|
{
|
|
val res = a.map(_.resultEmpty)
|
|
val (failures, values) = separate(res)(_.toEither)
|
|
// if(failures.isEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors)
|
|
if(values.nonEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors)
|
|
}
|
|
def result = {
|
|
val success = a.flatMap(_.result)
|
|
if(success.length == a.length) Some(success) else None
|
|
}
|
|
def completions(level: Int) = a.map(_.completions(level)).reduceLeft(_ ++ _)
|
|
def derive(c: Char) = seq0(a.map(_ derive c), errors)
|
|
override def toString = "seq(" + a + ")"
|
|
}
|
|
|
|
private final class BindParser[A,B](a: Parser[A], f: A => Parser[B]) extends ValidParser[B]
|
|
{
|
|
lazy val result = a.result flatMap { av => f(av).result }
|
|
lazy val resultEmpty = a.resultEmpty flatMap { av => f(av).resultEmpty }
|
|
def completions(level: Int) =
|
|
a.completions(level) flatMap { c =>
|
|
apply(a)(c.append).resultEmpty match {
|
|
case _: Failure => Completions.strict(Set.empty + c)
|
|
case Value(av) => c x f(av).completions(level)
|
|
}
|
|
}
|
|
|
|
def derive(c: Char) =
|
|
{
|
|
val common = a derive c flatMap f
|
|
a.resultEmpty match
|
|
{
|
|
case Value(av) => common | derive1(f(av), c)
|
|
case _: Failure => common
|
|
}
|
|
}
|
|
override def isTokenStart = a.isTokenStart
|
|
override def toString = "bind(" + a + ")"
|
|
}
|
|
private final class MapParser[A,B](a: Parser[A], f: A => B) extends ValidParser[B]
|
|
{
|
|
lazy val result = a.result map f
|
|
lazy val resultEmpty = a.resultEmpty map f
|
|
def derive(c: Char) = (a derive c) map f
|
|
def completions(level: Int) = a.completions(level)
|
|
override def isTokenStart = a.isTokenStart
|
|
override def toString = "map(" + a + ")"
|
|
}
|
|
private final class Filter[T](p: Parser[T], f: T => Boolean, seen: String, msg: String => String) extends ValidParser[T]
|
|
{
|
|
def filterResult(r: Result[T]) = r.filter(f, msg(seen))
|
|
lazy val result = p.result filter f
|
|
lazy val resultEmpty = filterResult(p.resultEmpty)
|
|
def derive(c: Char) = filterParser(p derive c, f, seen + c, msg)
|
|
def completions(level: Int) = p.completions(level) filterS { s => filterResult(apply(p)(s).resultEmpty).isValid }
|
|
override def toString = "filter(" + p + ")"
|
|
override def isTokenStart = p.isTokenStart
|
|
}
|
|
private final class MatchedString(delegate: Parser[_], seenV: Vector[Char], partial: Boolean) extends ValidParser[String]
|
|
{
|
|
lazy val seen = seenV.mkString
|
|
def derive(c: Char) = matched(delegate derive c, seenV :+ c, partial)
|
|
def completions(level: Int) = delegate.completions(level)
|
|
def result = if(delegate.result.isDefined) Some(seen) else None
|
|
def resultEmpty = delegate.resultEmpty match { case f: Failure if !partial => f; case _ => Value(seen) }
|
|
override def isTokenStart = delegate.isTokenStart
|
|
override def toString = "matched(" + partial + ", " + seen + ", " + delegate + ")"
|
|
}
|
|
private final class TokenStart[T](delegate: Parser[T], seen: String, complete: TokenCompletions) extends ValidParser[T]
|
|
{
|
|
def derive(c: Char) = mkToken( delegate derive c, seen + c, complete)
|
|
def completions(level: Int) = complete match {
|
|
case dc: TokenCompletions.Delegating => dc.completions(seen, level, delegate.completions(level))
|
|
case fc: TokenCompletions.Fixed => fc.completions(seen, level)
|
|
}
|
|
def result = delegate.result
|
|
def resultEmpty = delegate.resultEmpty
|
|
override def isTokenStart = true
|
|
override def toString = "token('" + complete + ", " + delegate + ")"
|
|
}
|
|
private final class And[T](a: Parser[T], b: Parser[_]) extends ValidParser[T]
|
|
{
|
|
lazy val result = tuple(a.result,b.result) map { _._1 }
|
|
def derive(c: Char) = (a derive c) & (b derive c)
|
|
def completions(level: Int) = a.completions(level).filterS(s => apply(b)(s).resultEmpty.isValid )
|
|
lazy val resultEmpty = a.resultEmpty && b.resultEmpty
|
|
}
|
|
|
|
private final class Not(delegate: Parser[_]) extends ValidParser[Unit]
|
|
{
|
|
def derive(c: Char) = if(delegate.valid) not(delegate derive c) else this
|
|
def completions(level: Int) = Completions.empty
|
|
def result = None
|
|
lazy val resultEmpty = delegate.resultEmpty match {
|
|
case f: Failure => Value(())
|
|
case v: Value[_] => mkFailure("Excluded.")
|
|
}
|
|
}
|
|
private final class Examples[T](delegate: Parser[T], fixed: Set[String]) extends ValidParser[T]
|
|
{
|
|
def derive(c: Char) = examples(delegate derive c, fixed.collect { case x if x.length > 0 && x(0) == c => x substring 1 })
|
|
def result = delegate.result
|
|
lazy val resultEmpty = delegate.resultEmpty
|
|
def completions(level: Int) =
|
|
if(fixed.isEmpty)
|
|
if(resultEmpty.isValid) Completions.nil else Completions.empty
|
|
else
|
|
Completions(fixed map(f => Completion.suggestion(f)) )
|
|
override def toString = "examples(" + delegate + ", " + fixed.take(2) + ")"
|
|
}
|
|
private final class StringLiteral(str: String, start: Int) extends ValidParser[String]
|
|
{
|
|
assert(0 <= start && start < str.length)
|
|
def failMsg = "Expected '" + str + "'"
|
|
def resultEmpty = mkFailure(failMsg)
|
|
def result = None
|
|
def derive(c: Char) = if(str.charAt(start) == c) stringLiteral(str, start+1) else new Invalid(resultEmpty)
|
|
def completions(level: Int) = Completions.single(Completion.suggestion(str.substring(start)))
|
|
override def toString = '"' + str + '"'
|
|
}
|
|
private final class CharacterClass(f: Char => Boolean, label: String) extends ValidParser[Char]
|
|
{
|
|
def result = None
|
|
def resultEmpty = mkFailure("Expected " + label)
|
|
def derive(c: Char) = if( f(c) ) success(c) else Invalid(resultEmpty)
|
|
def completions(level: Int) = Completions.empty
|
|
override def toString = "class(" + label + ")"
|
|
}
|
|
private final class Optional[T](delegate: Parser[T]) extends ValidParser[Option[T]]
|
|
{
|
|
def result = delegate.result map some.fn
|
|
def resultEmpty = Value(None)
|
|
def derive(c: Char) = (delegate derive c).map(some.fn)
|
|
def completions(level: Int) = Completion.empty +: delegate.completions(level)
|
|
override def toString = delegate.toString + "?"
|
|
}
|
|
private final class Repeat[T](partial: Option[Parser[T]], repeated: Parser[T], min: Int, max: UpperBound, accumulatedReverse: List[T]) extends ValidParser[Seq[T]]
|
|
{
|
|
assume(0 <= min, "Minimum occurences must be non-negative")
|
|
assume(max >= min, "Minimum occurences must be less than the maximum occurences")
|
|
|
|
def derive(c: Char) =
|
|
partial match
|
|
{
|
|
case Some(part) =>
|
|
val partD = repeat(Some(part derive c), repeated, min, max, accumulatedReverse)
|
|
part.resultEmpty match
|
|
{
|
|
case Value(pv) => partD | repeatDerive(c, pv :: accumulatedReverse)
|
|
case _: Failure => partD
|
|
}
|
|
case None => repeatDerive(c, accumulatedReverse)
|
|
}
|
|
|
|
def repeatDerive(c: Char, accRev: List[T]): Parser[Seq[T]] = repeat(Some(repeated derive c), repeated, (min - 1) max 0, max.decrement, accRev)
|
|
|
|
def completions(level: Int) =
|
|
{
|
|
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(level)
|
|
val fin = if(min == 0) Completion.empty +: repC else pow(repC, repC, min)
|
|
partial match
|
|
{
|
|
case Some(p) => p.completions(level) x fin
|
|
case None => fin
|
|
}
|
|
}
|
|
def result = None
|
|
lazy val resultEmpty: Result[Seq[T]] =
|
|
{
|
|
val partialAccumulatedOption =
|
|
partial match
|
|
{
|
|
case None => Value(accumulatedReverse)
|
|
case Some(partialPattern) => partialPattern.resultEmpty.map(_ :: accumulatedReverse)
|
|
}
|
|
(partialAccumulatedOption app repeatedParseEmpty)(_ reverse_::: _)
|
|
}
|
|
private def repeatedParseEmpty: Result[List[T]] =
|
|
{
|
|
if(min == 0)
|
|
Value(Nil)
|
|
else
|
|
// forced determinism
|
|
for(value <- repeated.resultEmpty) yield
|
|
List.make(min, value)
|
|
}
|
|
override def toString = "repeat(" + min + "," + max +"," + partial + "," + repeated + ")"
|
|
} |