mirror of https://github.com/sbt/sbt.git
Format in-sourced util modules
This commit is contained in:
parent
9494967e05
commit
dc2d4d613f
|
|
@ -14,11 +14,14 @@ trait AList[K[L[x]]] {
|
||||||
def foldr[M[_], A](value: K[M], f: (M[_], A) => A, init: A): A
|
def foldr[M[_], A](value: K[M], f: (M[_], A) => A, init: A): A
|
||||||
|
|
||||||
def toList[M[_]](value: K[M]): List[M[_]] = foldr[M, List[M[_]]](value, _ :: _, Nil)
|
def toList[M[_]](value: K[M]): List[M[_]] = foldr[M, List[M[_]]](value, _ :: _, Nil)
|
||||||
|
|
||||||
def apply[M[_], C](value: K[M], f: K[Id] => C)(implicit a: Applicative[M]): M[C] =
|
def apply[M[_], C](value: K[M], f: K[Id] => C)(implicit a: Applicative[M]): M[C] =
|
||||||
a.map(f, traverse[M, M, Id](value, idK[M])(a))
|
a.map(f, traverse[M, M, Id](value, idK[M])(a))
|
||||||
}
|
}
|
||||||
|
|
||||||
object AList {
|
object AList {
|
||||||
type Empty = AList[({ type l[L[x]] = Unit })#l]
|
type Empty = AList[({ type l[L[x]] = Unit })#l]
|
||||||
|
|
||||||
/** AList for Unit, which represents a sequence that is always empty.*/
|
/** AList for Unit, which represents a sequence that is always empty.*/
|
||||||
val empty: Empty = new Empty {
|
val empty: Empty = new Empty {
|
||||||
def transform[M[_], N[_]](in: Unit, f: M ~> N) = ()
|
def transform[M[_], N[_]](in: Unit, f: M ~> N) = ()
|
||||||
|
|
@ -28,12 +31,13 @@ object AList {
|
||||||
}
|
}
|
||||||
|
|
||||||
type SeqList[T] = AList[({ type l[L[x]] = List[L[T]] })#l]
|
type SeqList[T] = AList[({ type l[L[x]] = List[L[T]] })#l]
|
||||||
|
|
||||||
/** AList for a homogeneous sequence. */
|
/** AList for a homogeneous sequence. */
|
||||||
def seq[T]: SeqList[T] = new SeqList[T] {
|
def seq[T]: SeqList[T] = new SeqList[T] {
|
||||||
def transform[M[_], N[_]](s: List[M[T]], f: M ~> N) = s.map(f.fn[T])
|
def transform[M[_], N[_]](s: List[M[T]], f: M ~> N) = s.map(f.fn[T])
|
||||||
def foldr[M[_], A](s: List[M[T]], f: (M[_], A) => A, init: A): A = (init /: s.reverse)((t, m) => f(m, t))
|
def foldr[M[_], A](s: List[M[T]], f: (M[_], A) => A, init: A): A = (init /: s.reverse)((t, m) => f(m, t))
|
||||||
override def apply[M[_], C](s: List[M[T]], f: List[T] => C)(implicit ap: Applicative[M]): M[C] =
|
|
||||||
{
|
override def apply[M[_], C](s: List[M[T]], f: List[T] => C)(implicit ap: Applicative[M]): M[C] = {
|
||||||
def loop[V](in: List[M[T]], g: List[T] => V): M[V] =
|
def loop[V](in: List[M[T]], g: List[T] => V): M[V] =
|
||||||
in match {
|
in match {
|
||||||
case Nil => ap.pure(g(Nil))
|
case Nil => ap.pure(g(Nil))
|
||||||
|
|
@ -43,6 +47,7 @@ object AList {
|
||||||
}
|
}
|
||||||
loop(s, f)
|
loop(s, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
def traverse[M[_], N[_], P[_]](s: List[M[T]], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[List[P[T]]] = ???
|
def traverse[M[_], N[_], P[_]](s: List[M[T]], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[List[P[T]]] = ???
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,8 +60,9 @@ object AList {
|
||||||
override def toList[M[_]](k: KL[M]) = k.toList
|
override def toList[M[_]](k: KL[M]) = k.toList
|
||||||
}
|
}
|
||||||
|
|
||||||
/** AList for a single value. */
|
|
||||||
type Single[A] = AList[({ type l[L[x]] = L[A] })#l]
|
type Single[A] = AList[({ type l[L[x]] = L[A] })#l]
|
||||||
|
|
||||||
|
/** AList for a single value. */
|
||||||
def single[A]: Single[A] = new Single[A] {
|
def single[A]: Single[A] = new Single[A] {
|
||||||
def transform[M[_], N[_]](a: M[A], f: M ~> N) = f(a)
|
def transform[M[_], N[_]](a: M[A], f: M ~> N) = f(a)
|
||||||
def foldr[M[_], T](a: M[A], f: (M[_], T) => T, init: T): T = f(a, init)
|
def foldr[M[_], T](a: M[A], f: (M[_], T) => T, init: T): T = f(a, init)
|
||||||
|
|
@ -64,14 +70,15 @@ object AList {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ASplit[K[L[x]], B[x]] = AList[({ type l[L[x]] = K[(L ∙ B)#l] })#l]
|
type ASplit[K[L[x]], B[x]] = AList[({ type l[L[x]] = K[(L ∙ B)#l] })#l]
|
||||||
|
|
||||||
/** AList that operates on the outer type constructor `A` of a composition `[x] A[B[x]]` for type constructors `A` and `B`*/
|
/** AList that operates on the outer type constructor `A` of a composition `[x] A[B[x]]` for type constructors `A` and `B`*/
|
||||||
def asplit[K[L[x]], B[x]](base: AList[K]): ASplit[K, B] = new ASplit[K, B] {
|
def asplit[K[L[x]], B[x]](base: AList[K]): ASplit[K, B] = new ASplit[K, B] {
|
||||||
type Split[L[x]] = K[(L ∙ B)#l]
|
type Split[L[x]] = K[(L ∙ B)#l]
|
||||||
|
|
||||||
def transform[M[_], N[_]](value: Split[M], f: M ~> N): Split[N] =
|
def transform[M[_], N[_]](value: Split[M], f: M ~> N): Split[N] =
|
||||||
base.transform[(M ∙ B)#l, (N ∙ B)#l](value, nestCon[M, N, B](f))
|
base.transform[(M ∙ B)#l, (N ∙ B)#l](value, nestCon[M, N, B](f))
|
||||||
|
|
||||||
def traverse[M[_], N[_], P[_]](value: Split[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Split[P]] =
|
def traverse[M[_], N[_], P[_]](value: Split[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Split[P]] = {
|
||||||
{
|
|
||||||
val g = nestCon[M, (N ∙ P)#l, B](f)
|
val g = nestCon[M, (N ∙ P)#l, B](f)
|
||||||
base.traverse[(M ∙ B)#l, N, (P ∙ B)#l](value, g)(np)
|
base.traverse[(M ∙ B)#l, N, (P ∙ B)#l](value, g)(np)
|
||||||
}
|
}
|
||||||
|
|
@ -87,8 +94,7 @@ object AList {
|
||||||
type T2[M[_]] = (M[A], M[B])
|
type T2[M[_]] = (M[A], M[B])
|
||||||
def transform[M[_], N[_]](t: T2[M], f: M ~> N): T2[N] = (f(t._1), f(t._2))
|
def transform[M[_], N[_]](t: T2[M], f: M ~> N): T2[N] = (f(t._1), f(t._2))
|
||||||
def foldr[M[_], T](t: T2[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, init))
|
def foldr[M[_], T](t: T2[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, init))
|
||||||
def traverse[M[_], N[_], P[_]](t: T2[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T2[P]] =
|
def traverse[M[_], N[_], P[_]](t: T2[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T2[P]] = {
|
||||||
{
|
|
||||||
val g = (Tuple2.apply[P[A], P[B]] _).curried
|
val g = (Tuple2.apply[P[A], P[B]] _).curried
|
||||||
np.apply(np.map(g, f(t._1)), f(t._2))
|
np.apply(np.map(g, f(t._1)), f(t._2))
|
||||||
}
|
}
|
||||||
|
|
@ -100,8 +106,7 @@ object AList {
|
||||||
type T3[M[_]] = (M[A], M[B], M[C])
|
type T3[M[_]] = (M[A], M[B], M[C])
|
||||||
def transform[M[_], N[_]](t: T3[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3))
|
def transform[M[_], N[_]](t: T3[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3))
|
||||||
def foldr[M[_], T](t: T3[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, init)))
|
def foldr[M[_], T](t: T3[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, init)))
|
||||||
def traverse[M[_], N[_], P[_]](t: T3[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T3[P]] =
|
def traverse[M[_], N[_], P[_]](t: T3[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T3[P]] = {
|
||||||
{
|
|
||||||
val g = (Tuple3.apply[P[A], P[B], P[C]] _).curried
|
val g = (Tuple3.apply[P[A], P[B], P[C]] _).curried
|
||||||
np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3))
|
np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3))
|
||||||
}
|
}
|
||||||
|
|
@ -113,8 +118,7 @@ object AList {
|
||||||
type T4[M[_]] = (M[A], M[B], M[C], M[D])
|
type T4[M[_]] = (M[A], M[B], M[C], M[D])
|
||||||
def transform[M[_], N[_]](t: T4[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4))
|
def transform[M[_], N[_]](t: T4[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4))
|
||||||
def foldr[M[_], T](t: T4[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, init))))
|
def foldr[M[_], T](t: T4[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, init))))
|
||||||
def traverse[M[_], N[_], P[_]](t: T4[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T4[P]] =
|
def traverse[M[_], N[_], P[_]](t: T4[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T4[P]] = {
|
||||||
{
|
|
||||||
val g = (Tuple4.apply[P[A], P[B], P[C], P[D]] _).curried
|
val g = (Tuple4.apply[P[A], P[B], P[C], P[D]] _).curried
|
||||||
np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4))
|
np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4))
|
||||||
}
|
}
|
||||||
|
|
@ -126,8 +130,7 @@ object AList {
|
||||||
type T5[M[_]] = (M[A], M[B], M[C], M[D], M[E])
|
type T5[M[_]] = (M[A], M[B], M[C], M[D], M[E])
|
||||||
def transform[M[_], N[_]](t: T5[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5))
|
def transform[M[_], N[_]](t: T5[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5))
|
||||||
def foldr[M[_], T](t: T5[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, init)))))
|
def foldr[M[_], T](t: T5[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, init)))))
|
||||||
def traverse[M[_], N[_], P[_]](t: T5[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T5[P]] =
|
def traverse[M[_], N[_], P[_]](t: T5[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T5[P]] = {
|
||||||
{
|
|
||||||
val g = (Tuple5.apply[P[A], P[B], P[C], P[D], P[E]] _).curried
|
val g = (Tuple5.apply[P[A], P[B], P[C], P[D], P[E]] _).curried
|
||||||
np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5))
|
np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5))
|
||||||
}
|
}
|
||||||
|
|
@ -139,8 +142,7 @@ object AList {
|
||||||
type T6[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F])
|
type T6[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F])
|
||||||
def transform[M[_], N[_]](t: T6[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6))
|
def transform[M[_], N[_]](t: T6[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6))
|
||||||
def foldr[M[_], T](t: T6[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, init))))))
|
def foldr[M[_], T](t: T6[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, init))))))
|
||||||
def traverse[M[_], N[_], P[_]](t: T6[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T6[P]] =
|
def traverse[M[_], N[_], P[_]](t: T6[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T6[P]] = {
|
||||||
{
|
|
||||||
val g = (Tuple6.apply[P[A], P[B], P[C], P[D], P[E], P[F]] _).curried
|
val g = (Tuple6.apply[P[A], P[B], P[C], P[D], P[E], P[F]] _).curried
|
||||||
np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6))
|
np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6))
|
||||||
}
|
}
|
||||||
|
|
@ -152,20 +154,19 @@ object AList {
|
||||||
type T7[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G])
|
type T7[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G])
|
||||||
def transform[M[_], N[_]](t: T7[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7))
|
def transform[M[_], N[_]](t: T7[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7))
|
||||||
def foldr[M[_], T](t: T7[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, init)))))))
|
def foldr[M[_], T](t: T7[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, init)))))))
|
||||||
def traverse[M[_], N[_], P[_]](t: T7[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T7[P]] =
|
def traverse[M[_], N[_], P[_]](t: T7[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T7[P]] = {
|
||||||
{
|
|
||||||
val g = (Tuple7.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G]] _).curried
|
val g = (Tuple7.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G]] _).curried
|
||||||
np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7))
|
np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait T8K[A, B, C, D, E, F, G, H] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H]) }
|
sealed trait T8K[A, B, C, D, E, F, G, H] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H]) }
|
||||||
type T8List[A, B, C, D, E, F, G, H] = AList[T8K[A, B, C, D, E, F, G, H]#l]
|
type T8List[A, B, C, D, E, F, G, H] = AList[T8K[A, B, C, D, E, F, G, H]#l]
|
||||||
def tuple8[A, B, C, D, E, F, G, H]: T8List[A, B, C, D, E, F, G, H] = new T8List[A, B, C, D, E, F, G, H] {
|
def tuple8[A, B, C, D, E, F, G, H]: T8List[A, B, C, D, E, F, G, H] = new T8List[A, B, C, D, E, F, G, H] {
|
||||||
type T8[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H])
|
type T8[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H])
|
||||||
def transform[M[_], N[_]](t: T8[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8))
|
def transform[M[_], N[_]](t: T8[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8))
|
||||||
def foldr[M[_], T](t: T8[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, init))))))))
|
def foldr[M[_], T](t: T8[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, init))))))))
|
||||||
def traverse[M[_], N[_], P[_]](t: T8[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T8[P]] =
|
def traverse[M[_], N[_], P[_]](t: T8[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T8[P]] = {
|
||||||
{
|
|
||||||
val g = (Tuple8.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H]] _).curried
|
val g = (Tuple8.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H]] _).curried
|
||||||
np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8))
|
np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8))
|
||||||
}
|
}
|
||||||
|
|
@ -177,8 +178,7 @@ object AList {
|
||||||
type T9[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I])
|
type T9[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I])
|
||||||
def transform[M[_], N[_]](t: T9[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9))
|
def transform[M[_], N[_]](t: T9[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9))
|
||||||
def foldr[M[_], T](t: T9[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, init)))))))))
|
def foldr[M[_], T](t: T9[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, init)))))))))
|
||||||
def traverse[M[_], N[_], P[_]](t: T9[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T9[P]] =
|
def traverse[M[_], N[_], P[_]](t: T9[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T9[P]] = {
|
||||||
{
|
|
||||||
val g = (Tuple9.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I]] _).curried
|
val g = (Tuple9.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I]] _).curried
|
||||||
np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9))
|
np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9))
|
||||||
}
|
}
|
||||||
|
|
@ -190,8 +190,7 @@ object AList {
|
||||||
type T10[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J])
|
type T10[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J])
|
||||||
def transform[M[_], N[_]](t: T10[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10))
|
def transform[M[_], N[_]](t: T10[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10))
|
||||||
def foldr[M[_], T](t: T10[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, init))))))))))
|
def foldr[M[_], T](t: T10[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, init))))))))))
|
||||||
def traverse[M[_], N[_], P[_]](t: T10[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T10[P]] =
|
def traverse[M[_], N[_], P[_]](t: T10[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T10[P]] = {
|
||||||
{
|
|
||||||
val g = (Tuple10.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J]] _).curried
|
val g = (Tuple10.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J]] _).curried
|
||||||
np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10))
|
np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10))
|
||||||
}
|
}
|
||||||
|
|
@ -203,8 +202,7 @@ object AList {
|
||||||
type T11[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J], M[K])
|
type T11[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J], M[K])
|
||||||
def transform[M[_], N[_]](t: T11[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10), f(t._11))
|
def transform[M[_], N[_]](t: T11[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10), f(t._11))
|
||||||
def foldr[M[_], T](t: T11[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, f(t._11, init)))))))))))
|
def foldr[M[_], T](t: T11[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, f(t._11, init)))))))))))
|
||||||
def traverse[M[_], N[_], P[_]](t: T11[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T11[P]] =
|
def traverse[M[_], N[_], P[_]](t: T11[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T11[P]] = {
|
||||||
{
|
|
||||||
val g = (Tuple11.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J], P[K]] _).curried
|
val g = (Tuple11.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J], P[K]] _).curried
|
||||||
np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)), f(t._11))
|
np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)), f(t._11))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,36 +42,53 @@ sealed trait AttributeKey[T] {
|
||||||
def rank: Int
|
def rank: Int
|
||||||
|
|
||||||
def optJsonWriter: OptJsonWriter[T]
|
def optJsonWriter: OptJsonWriter[T]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] abstract class SharedAttributeKey[T] extends AttributeKey[T] {
|
private[sbt] abstract class SharedAttributeKey[T] extends AttributeKey[T] {
|
||||||
override final def toString = label
|
override final def toString = label
|
||||||
override final def hashCode = label.hashCode
|
override final def hashCode = label.hashCode
|
||||||
override final def equals(o: Any) = (this eq o.asInstanceOf[AnyRef]) || (o match {
|
override final def equals(o: Any) =
|
||||||
|
(this eq o.asInstanceOf[AnyRef]) || (o match {
|
||||||
case a: SharedAttributeKey[t] => a.label == this.label && a.manifest == this.manifest
|
case a: SharedAttributeKey[t] => a.label == this.label && a.manifest == this.manifest
|
||||||
case _ => false
|
case _ => false
|
||||||
})
|
})
|
||||||
final def isLocal: Boolean = false
|
final def isLocal: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
object AttributeKey {
|
object AttributeKey {
|
||||||
def apply[T](name: String)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] =
|
def apply[T: Manifest: OptJsonWriter](name: String): AttributeKey[T] =
|
||||||
make(name, None, Nil, Int.MaxValue)
|
make(name, None, Nil, Int.MaxValue)
|
||||||
|
|
||||||
def apply[T](name: String, rank: Int)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] =
|
def apply[T: Manifest: OptJsonWriter](name: String, rank: Int): AttributeKey[T] =
|
||||||
make(name, None, Nil, rank)
|
make(name, None, Nil, rank)
|
||||||
|
|
||||||
def apply[T](name: String, description: String)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] =
|
def apply[T: Manifest: OptJsonWriter](name: String, description: String): AttributeKey[T] =
|
||||||
apply(name, description, Nil)
|
apply(name, description, Nil)
|
||||||
|
|
||||||
def apply[T](name: String, description: String, rank: Int)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] =
|
def apply[T: Manifest: OptJsonWriter](name: String,
|
||||||
|
description: String,
|
||||||
|
rank: Int): AttributeKey[T] =
|
||||||
apply(name, description, Nil, rank)
|
apply(name, description, Nil, rank)
|
||||||
|
|
||||||
def apply[T](name: String, description: String, extend: Seq[AttributeKey[_]])(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] =
|
def apply[T: Manifest: OptJsonWriter](name: String,
|
||||||
|
description: String,
|
||||||
|
extend: Seq[AttributeKey[_]]): AttributeKey[T] =
|
||||||
apply(name, description, extend, Int.MaxValue)
|
apply(name, description, extend, Int.MaxValue)
|
||||||
|
|
||||||
def apply[T](name: String, description: String, extend: Seq[AttributeKey[_]], rank: Int)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] =
|
def apply[T: Manifest: OptJsonWriter](name: String,
|
||||||
|
description: String,
|
||||||
|
extend: Seq[AttributeKey[_]],
|
||||||
|
rank: Int): AttributeKey[T] =
|
||||||
make(name, Some(description), extend, rank)
|
make(name, Some(description), extend, rank)
|
||||||
|
|
||||||
private[this] def make[T](name: String, description0: Option[String], extend0: Seq[AttributeKey[_]], rank0: Int)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = new SharedAttributeKey[T] {
|
private[this] def make[T](
|
||||||
|
name: String,
|
||||||
|
description0: Option[String],
|
||||||
|
extend0: Seq[AttributeKey[_]],
|
||||||
|
rank0: Int
|
||||||
|
)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] =
|
||||||
|
new SharedAttributeKey[T] {
|
||||||
def manifest = mf
|
def manifest = mf
|
||||||
val label = Util.hyphenToCamel(name)
|
val label = Util.hyphenToCamel(name)
|
||||||
def description = description0
|
def description = description0
|
||||||
|
|
@ -79,7 +96,9 @@ object AttributeKey {
|
||||||
def rank = rank0
|
def rank = rank0
|
||||||
def optJsonWriter = ojw
|
def optJsonWriter = ojw
|
||||||
}
|
}
|
||||||
private[sbt] def local[T](implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = new AttributeKey[T] {
|
|
||||||
|
private[sbt] def local[T](implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] =
|
||||||
|
new AttributeKey[T] {
|
||||||
def manifest = mf
|
def manifest = mf
|
||||||
def label = LocalLabel
|
def label = LocalLabel
|
||||||
def description = None
|
def description = None
|
||||||
|
|
@ -89,7 +108,9 @@ object AttributeKey {
|
||||||
def rank = Int.MaxValue
|
def rank = Int.MaxValue
|
||||||
val optJsonWriter = ojw
|
val optJsonWriter = ojw
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] final val LocalLabel = "$" + "local"
|
private[sbt] final val LocalLabel = "$" + "local"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -98,6 +119,7 @@ object AttributeKey {
|
||||||
* Excluding this possibility is the responsibility of the client if desired.
|
* Excluding this possibility is the responsibility of the client if desired.
|
||||||
*/
|
*/
|
||||||
trait AttributeMap {
|
trait AttributeMap {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of type `T` associated with the key `k`.
|
* Gets the value of type `T` associated with the key `k`.
|
||||||
* If a key with the same label but different type is defined, this method will fail.
|
* If a key with the same label but different type is defined, this method will fail.
|
||||||
|
|
@ -142,8 +164,11 @@ trait AttributeMap {
|
||||||
|
|
||||||
/** `true` if there are no mappings in this map, `false` if there are. */
|
/** `true` if there are no mappings in this map, `false` if there are. */
|
||||||
def isEmpty: Boolean
|
def isEmpty: Boolean
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object AttributeMap {
|
object AttributeMap {
|
||||||
|
|
||||||
/** An [[AttributeMap]] without any mappings. */
|
/** An [[AttributeMap]] without any mappings. */
|
||||||
val empty: AttributeMap = new BasicAttributeMap(Map.empty)
|
val empty: AttributeMap = new BasicAttributeMap(Map.empty)
|
||||||
|
|
||||||
|
|
@ -157,27 +182,38 @@ object AttributeMap {
|
||||||
implicit def toNatTrans(map: AttributeMap): AttributeKey ~> Id = new (AttributeKey ~> Id) {
|
implicit def toNatTrans(map: AttributeMap): AttributeKey ~> Id = new (AttributeKey ~> Id) {
|
||||||
def apply[T](key: AttributeKey[T]): T = map(key)
|
def apply[T](key: AttributeKey[T]): T = map(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any]) extends AttributeMap {
|
private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any])
|
||||||
|
extends AttributeMap {
|
||||||
|
|
||||||
def isEmpty: Boolean = backing.isEmpty
|
def isEmpty: Boolean = backing.isEmpty
|
||||||
def apply[T](k: AttributeKey[T]) = backing(k).asInstanceOf[T]
|
def apply[T](k: AttributeKey[T]) = backing(k).asInstanceOf[T]
|
||||||
def get[T](k: AttributeKey[T]) = backing.get(k).asInstanceOf[Option[T]]
|
def get[T](k: AttributeKey[T]) = backing.get(k).asInstanceOf[Option[T]]
|
||||||
def remove[T](k: AttributeKey[T]): AttributeMap = new BasicAttributeMap(backing - k)
|
def remove[T](k: AttributeKey[T]): AttributeMap = new BasicAttributeMap(backing - k)
|
||||||
def contains[T](k: AttributeKey[T]) = backing.contains(k)
|
def contains[T](k: AttributeKey[T]) = backing.contains(k)
|
||||||
def put[T](k: AttributeKey[T], value: T): AttributeMap = new BasicAttributeMap(backing.updated(k, value))
|
|
||||||
|
def put[T](k: AttributeKey[T], value: T): AttributeMap =
|
||||||
|
new BasicAttributeMap(backing.updated(k, value))
|
||||||
|
|
||||||
def keys: Iterable[AttributeKey[_]] = backing.keys
|
def keys: Iterable[AttributeKey[_]] = backing.keys
|
||||||
def ++(o: Iterable[AttributeEntry[_]]): AttributeMap =
|
|
||||||
{
|
def ++(o: Iterable[AttributeEntry[_]]): AttributeMap = {
|
||||||
val newBacking = (backing /: o) { case (b, AttributeEntry(key, value)) => b.updated(key, value) }
|
val newBacking = (backing /: o) {
|
||||||
|
case (b, AttributeEntry(key, value)) => b.updated(key, value)
|
||||||
|
}
|
||||||
new BasicAttributeMap(newBacking)
|
new BasicAttributeMap(newBacking)
|
||||||
}
|
}
|
||||||
|
|
||||||
def ++(o: AttributeMap): AttributeMap =
|
def ++(o: AttributeMap): AttributeMap =
|
||||||
o match {
|
o match {
|
||||||
case bam: BasicAttributeMap => new BasicAttributeMap(backing ++ bam.backing)
|
case bam: BasicAttributeMap => new BasicAttributeMap(backing ++ bam.backing)
|
||||||
case _ => o ++ this
|
case _ => o ++ this
|
||||||
}
|
}
|
||||||
|
|
||||||
def entries: Iterable[AttributeEntry[_]] =
|
def entries: Iterable[AttributeEntry[_]] =
|
||||||
for ((k: AttributeKey[kt], v) <- backing) yield AttributeEntry(k, v.asInstanceOf[kt])
|
for ((k: AttributeKey[kt], v) <- backing) yield AttributeEntry(k, v.asInstanceOf[kt])
|
||||||
|
|
||||||
override def toString = entries.mkString("(", ", ", ")")
|
override def toString = entries.mkString("(", ", ", ")")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,16 +225,21 @@ final case class AttributeEntry[T](key: AttributeKey[T], value: T) {
|
||||||
|
|
||||||
/** Associates a `metadata` map with `data`. */
|
/** Associates a `metadata` map with `data`. */
|
||||||
final case class Attributed[D](data: D)(val metadata: AttributeMap) {
|
final case class Attributed[D](data: D)(val metadata: AttributeMap) {
|
||||||
|
|
||||||
/** Retrieves the associated value of `key` from the metadata. */
|
/** Retrieves the associated value of `key` from the metadata. */
|
||||||
def get[T](key: AttributeKey[T]): Option[T] = metadata.get(key)
|
def get[T](key: AttributeKey[T]): Option[T] = metadata.get(key)
|
||||||
|
|
||||||
/** Defines a mapping `key -> value` in the metadata. */
|
/** Defines a mapping `key -> value` in the metadata. */
|
||||||
def put[T](key: AttributeKey[T], value: T): Attributed[D] = Attributed(data)(metadata.put(key, value))
|
def put[T](key: AttributeKey[T], value: T): Attributed[D] =
|
||||||
|
Attributed(data)(metadata.put(key, value))
|
||||||
|
|
||||||
/** Transforms the data by applying `f`. */
|
/** Transforms the data by applying `f`. */
|
||||||
def map[T](f: D => T): Attributed[T] = Attributed(f(data))(metadata)
|
def map[T](f: D => T): Attributed[T] = Attributed(f(data))(metadata)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Attributed {
|
object Attributed {
|
||||||
|
|
||||||
/** Extracts the underlying data from the sequence `in`. */
|
/** Extracts the underlying data from the sequence `in`. */
|
||||||
def data[T](in: Seq[Attributed[T]]): Seq[T] = in.map(_.data)
|
def data[T](in: Seq[Attributed[T]]): Seq[T] = in.map(_.data)
|
||||||
|
|
||||||
|
|
@ -207,4 +248,5 @@ object Attributed {
|
||||||
|
|
||||||
/** Associates an empty metadata map with `data`. */
|
/** Associates an empty metadata map with `data`. */
|
||||||
def blank[T](data: T): Attributed[T] = Attributed(data)(AttributeMap.empty)
|
def blank[T](data: T): Attributed[T] = Attributed(data)(AttributeMap.empty)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,22 @@ object Classes {
|
||||||
def pure[S](s: => S): M[S]
|
def pure[S](s: => S): M[S]
|
||||||
def map[S, T](f: S => T, v: M[S]): M[T]
|
def map[S, T](f: S => T, v: M[S]): M[T]
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Monad[M[_]] extends Applicative[M] {
|
trait Monad[M[_]] extends Applicative[M] {
|
||||||
def flatten[T](m: M[M[T]]): M[T]
|
def flatten[T](m: M[M[T]]): M[T]
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit val optionMonad: Monad[Option] = new Monad[Option] {
|
implicit val optionMonad: Monad[Option] = new Monad[Option] {
|
||||||
def apply[S, T](f: Option[S => T], v: Option[S]) = (f, v) match { case (Some(fv), Some(vv)) => Some(fv(vv)); case _ => None }
|
def apply[S, T](f: Option[S => T], v: Option[S]) = (f, v) match {
|
||||||
|
case (Some(fv), Some(vv)) => Some(fv(vv))
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
|
||||||
def pure[S](s: => S) = Some(s)
|
def pure[S](s: => S) = Some(s)
|
||||||
def map[S, T](f: S => T, v: Option[S]) = v map f
|
def map[S, T](f: S => T, v: Option[S]) = v map f
|
||||||
def flatten[T](m: Option[Option[T]]): Option[T] = m.flatten
|
def flatten[T](m: Option[Option[T]]): Option[T] = m.flatten
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit val listMonad: Monad[List] = new Monad[List] {
|
implicit val listMonad: Monad[List] = new Monad[List] {
|
||||||
def apply[S, T](f: List[S => T], v: List[S]) = for (fv <- f; vv <- v) yield fv(vv)
|
def apply[S, T](f: List[S => T], v: List[S]) = for (fv <- f; vv <- v) yield fv(vv)
|
||||||
def pure[S](s: => S) = s :: Nil
|
def pure[S](s: => S) = s :: Nil
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,7 @@
|
||||||
*/
|
*/
|
||||||
package sbt.internal.util
|
package sbt.internal.util
|
||||||
|
|
||||||
trait Dag[Node <: Dag[Node]] {
|
trait Dag[Node <: Dag[Node]] { self: Node =>
|
||||||
self: Node =>
|
|
||||||
|
|
||||||
def dependencies: Iterable[Node]
|
def dependencies: Iterable[Node]
|
||||||
def topologicalSort = Dag.topologicalSort(self)(_.dependencies)
|
def topologicalSort = Dag.topologicalSort(self)(_.dependencies)
|
||||||
|
|
@ -13,10 +12,10 @@ object Dag {
|
||||||
import scala.collection.{ mutable, JavaConverters }
|
import scala.collection.{ mutable, JavaConverters }
|
||||||
import JavaConverters.asScalaSetConverter
|
import JavaConverters.asScalaSetConverter
|
||||||
|
|
||||||
def topologicalSort[T](root: T)(dependencies: T => Iterable[T]): List[T] = topologicalSort(root :: Nil)(dependencies)
|
def topologicalSort[T](root: T)(dependencies: T => Iterable[T]): List[T] =
|
||||||
|
topologicalSort(root :: Nil)(dependencies)
|
||||||
|
|
||||||
def topologicalSort[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): List[T] =
|
def topologicalSort[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): List[T] = {
|
||||||
{
|
|
||||||
val discovered = new mutable.HashSet[T]
|
val discovered = new mutable.HashSet[T]
|
||||||
val finished = (new java.util.LinkedHashSet[T]).asScala
|
val finished = (new java.util.LinkedHashSet[T]).asScala
|
||||||
|
|
||||||
|
|
@ -35,11 +34,12 @@ object Dag {
|
||||||
|
|
||||||
finished.toList
|
finished.toList
|
||||||
}
|
}
|
||||||
// doesn't check for cycles
|
|
||||||
def topologicalSortUnchecked[T](node: T)(dependencies: T => Iterable[T]): List[T] = topologicalSortUnchecked(node :: Nil)(dependencies)
|
|
||||||
|
|
||||||
def topologicalSortUnchecked[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): List[T] =
|
// doesn't check for cycles
|
||||||
{
|
def topologicalSortUnchecked[T](node: T)(dependencies: T => Iterable[T]): List[T] =
|
||||||
|
topologicalSortUnchecked(node :: Nil)(dependencies)
|
||||||
|
|
||||||
|
def topologicalSortUnchecked[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): List[T] = {
|
||||||
val discovered = new mutable.HashSet[T]
|
val discovered = new mutable.HashSet[T]
|
||||||
var finished: List[T] = Nil
|
var finished: List[T] = Nil
|
||||||
|
|
||||||
|
|
@ -55,11 +55,15 @@ object Dag {
|
||||||
visitAll(nodes);
|
visitAll(nodes);
|
||||||
finished;
|
finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Cyclic(val value: Any, val all: List[Any], val complete: Boolean)
|
final class Cyclic(val value: Any, val all: List[Any], val complete: Boolean)
|
||||||
extends Exception("Cyclic reference involving " +
|
extends Exception(
|
||||||
(if (complete) all.mkString("\n ", "\n ", "") else value)) {
|
"Cyclic reference involving " +
|
||||||
|
(if (complete) all.mkString("\n ", "\n ", "") else value)
|
||||||
|
) {
|
||||||
def this(value: Any) = this(value, value :: Nil, false)
|
def this(value: Any) = this(value, value :: Nil, false)
|
||||||
override def toString = getMessage
|
override def toString = getMessage
|
||||||
|
|
||||||
def ::(a: Any): Cyclic =
|
def ::(a: Any): Cyclic =
|
||||||
if (complete)
|
if (complete)
|
||||||
this
|
this
|
||||||
|
|
@ -71,19 +75,25 @@ object Dag {
|
||||||
|
|
||||||
/** A directed graph with edges labeled positive or negative. */
|
/** A directed graph with edges labeled positive or negative. */
|
||||||
private[sbt] trait DirectedSignedGraph[Node] {
|
private[sbt] trait DirectedSignedGraph[Node] {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directed edge type that tracks the sign and target (head) vertex.
|
* Directed edge type that tracks the sign and target (head) vertex.
|
||||||
* The sign can be obtained via [[isNegative]] and the target vertex via [[head]].
|
* The sign can be obtained via [[isNegative]] and the target vertex via [[head]].
|
||||||
*/
|
*/
|
||||||
type Arrow
|
type Arrow
|
||||||
|
|
||||||
/** List of initial nodes. */
|
/** List of initial nodes. */
|
||||||
def nodes: List[Arrow]
|
def nodes: List[Arrow]
|
||||||
|
|
||||||
/** Outgoing edges for `n`. */
|
/** Outgoing edges for `n`. */
|
||||||
def dependencies(n: Node): List[Arrow]
|
def dependencies(n: Node): List[Arrow]
|
||||||
|
|
||||||
/** `true` if the edge `a` is "negative", false if it is "positive". */
|
/** `true` if the edge `a` is "negative", false if it is "positive". */
|
||||||
def isNegative(a: Arrow): Boolean
|
def isNegative(a: Arrow): Boolean
|
||||||
|
|
||||||
/** The target of the directed edge `a`. */
|
/** The target of the directed edge `a`. */
|
||||||
def head(a: Arrow): Node
|
def head(a: Arrow): Node
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -92,8 +102,7 @@ object Dag {
|
||||||
* If a cycle containing a "negative" edge is detected, its member edges are returned in order.
|
* If a cycle containing a "negative" edge is detected, its member edges are returned in order.
|
||||||
* Otherwise, the empty list is returned.
|
* Otherwise, the empty list is returned.
|
||||||
*/
|
*/
|
||||||
private[sbt] def findNegativeCycle[Node](graph: DirectedSignedGraph[Node]): List[graph.Arrow] =
|
private[sbt] def findNegativeCycle[Node](graph: DirectedSignedGraph[Node]): List[graph.Arrow] = {
|
||||||
{
|
|
||||||
import graph._
|
import graph._
|
||||||
val finished = new mutable.HashSet[Node]
|
val finished = new mutable.HashSet[Node]
|
||||||
val visited = new mutable.HashSet[Node]
|
val visited = new mutable.HashSet[Node]
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,16 @@ import Types._
|
||||||
sealed trait HList {
|
sealed trait HList {
|
||||||
type Wrap[M[_]] <: HList
|
type Wrap[M[_]] <: HList
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait HNil extends HList {
|
sealed trait HNil extends HList {
|
||||||
type Wrap[M[_]] = HNil
|
type Wrap[M[_]] = HNil
|
||||||
def :+:[G](g: G): G :+: HNil = HCons(g, this)
|
def :+:[G](g: G): G :+: HNil = HCons(g, this)
|
||||||
|
|
||||||
override def toString = "HNil"
|
override def toString = "HNil"
|
||||||
}
|
}
|
||||||
|
|
||||||
object HNil extends HNil
|
object HNil extends HNil
|
||||||
|
|
||||||
final case class HCons[H, T <: HList](head: H, tail: T) extends HList {
|
final case class HCons[H, T <: HList](head: H, tail: T) extends HList {
|
||||||
type Wrap[M[_]] = M[H] :+: T#Wrap[M]
|
type Wrap[M[_]] = M[H] :+: T#Wrap[M]
|
||||||
def :+:[G](g: G): G :+: H :+: T = HCons(g, this)
|
def :+:[G](g: G): G :+: H :+: T = HCons(g, this)
|
||||||
|
|
@ -28,5 +31,6 @@ final case class HCons[H, T <: HList](head: H, tail: T) extends HList {
|
||||||
|
|
||||||
object HList {
|
object HList {
|
||||||
// contains no type information: not even A
|
// contains no type information: not even A
|
||||||
implicit def fromList[A](list: Traversable[A]): HList = ((HNil: HList) /: list)((hl, v) => HCons(v, hl))
|
implicit def fromList[A](list: Traversable[A]): HList =
|
||||||
|
((HNil: HList) /: list)((hl, v) => HCons(v, hl))
|
||||||
}
|
}
|
||||||
|
|
@ -21,7 +21,10 @@ trait HListFormats {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit def hconsFormat[H, T <: HList](implicit hf: JsonFormat[H], tf: HListJF[T]): JsonFormat[H :+: T] =
|
implicit def hconsFormat[H, T <: HList](
|
||||||
|
implicit hf: JsonFormat[H],
|
||||||
|
tf: HListJF[T]
|
||||||
|
): JsonFormat[H :+: T] =
|
||||||
new JsonFormat[H :+: T] {
|
new JsonFormat[H :+: T] {
|
||||||
def write[J](hcons: H :+: T, builder: Builder[J]) = {
|
def write[J](hcons: H :+: T, builder: Builder[J]) = {
|
||||||
builder.beginArray()
|
builder.beginArray()
|
||||||
|
|
@ -34,7 +37,8 @@ trait HListFormats {
|
||||||
case None => HCons(hf.read(None, unbuilder), tf.read(None, unbuilder))
|
case None => HCons(hf.read(None, unbuilder), tf.read(None, unbuilder))
|
||||||
case Some(js) =>
|
case Some(js) =>
|
||||||
unbuilder.beginArray(js)
|
unbuilder.beginArray(js)
|
||||||
val hcons = HCons(hf.read(Some(unbuilder.nextElement), unbuilder), tf.read(Some(js), unbuilder))
|
val hcons =
|
||||||
|
HCons(hf.read(Some(unbuilder.nextElement), unbuilder), tf.read(Some(js), unbuilder))
|
||||||
unbuilder.endArray()
|
unbuilder.endArray()
|
||||||
hcons
|
hcons
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +49,10 @@ trait HListFormats {
|
||||||
def write[J](obj: A, builder: Builder[J]): Unit
|
def write[J](obj: A, builder: Builder[J]): Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit def hconsHListJF[H, T <: HList](implicit hf: JsonFormat[H], tf: HListJF[T]): HListJF[H :+: T] =
|
implicit def hconsHListJF[H, T <: HList](
|
||||||
|
implicit hf: JsonFormat[H],
|
||||||
|
tf: HListJF[T]
|
||||||
|
): HListJF[H :+: T] =
|
||||||
new HListJF[H :+: T] {
|
new HListJF[H :+: T] {
|
||||||
def write[J](hcons: H :+: T, builder: Builder[J]) = {
|
def write[J](hcons: H :+: T, builder: Builder[J]) = {
|
||||||
hf.write(hcons.head, builder)
|
hf.write(hcons.head, builder)
|
||||||
|
|
@ -54,7 +61,8 @@ trait HListFormats {
|
||||||
|
|
||||||
def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]) = jsOpt match {
|
def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]) = jsOpt match {
|
||||||
case None => HCons(hf.read(None, unbuilder), tf.read(None, unbuilder))
|
case None => HCons(hf.read(None, unbuilder), tf.read(None, unbuilder))
|
||||||
case Some(js) => HCons(hf.read(Some(unbuilder.nextElement), unbuilder), tf.read(Some(js), unbuilder))
|
case Some(js) =>
|
||||||
|
HCons(hf.read(Some(unbuilder.nextElement), unbuilder), tf.read(Some(js), unbuilder))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,13 @@ trait IDSet[T] {
|
||||||
object IDSet {
|
object IDSet {
|
||||||
implicit def toTraversable[T]: IDSet[T] => Traversable[T] = _.all
|
implicit def toTraversable[T]: IDSet[T] => Traversable[T] = _.all
|
||||||
def apply[T](values: T*): IDSet[T] = apply(values)
|
def apply[T](values: T*): IDSet[T] = apply(values)
|
||||||
def apply[T](values: Iterable[T]): IDSet[T] =
|
|
||||||
{
|
def apply[T](values: Iterable[T]): IDSet[T] = {
|
||||||
val s = create[T]
|
val s = create[T]
|
||||||
s ++= values
|
s ++= values
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
def create[T]: IDSet[T] = new IDSet[T] {
|
def create[T]: IDSet[T] = new IDSet[T] {
|
||||||
private[this] val backing = new java.util.IdentityHashMap[T, AnyRef]
|
private[this] val backing = new java.util.IdentityHashMap[T, AnyRef]
|
||||||
private[this] val Dummy: AnyRef = ""
|
private[this] val Dummy: AnyRef = ""
|
||||||
|
|
@ -39,7 +40,10 @@ object IDSet {
|
||||||
def all = collection.JavaConversions.collectionAsScalaIterable(backing.keySet)
|
def all = collection.JavaConversions.collectionAsScalaIterable(backing.keySet)
|
||||||
def toList = all.toList
|
def toList = all.toList
|
||||||
def isEmpty = backing.isEmpty
|
def isEmpty = backing.isEmpty
|
||||||
def process[S](t: T)(ifSeen: S)(ifNew: => S) = if (contains(t)) ifSeen else { this += t; ifNew }
|
|
||||||
|
def process[S](t: T)(ifSeen: S)(ifNew: => S) =
|
||||||
|
if (contains(t)) ifSeen else { this += t; ifNew }
|
||||||
|
|
||||||
override def toString = backing.toString
|
override def toString = backing.toString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ object EvaluationState extends Enumeration {
|
||||||
abstract class EvaluateSettings[Scope] {
|
abstract class EvaluateSettings[Scope] {
|
||||||
protected val init: Init[Scope]
|
protected val init: Init[Scope]
|
||||||
import init._
|
import init._
|
||||||
|
|
||||||
protected def executor: Executor
|
protected def executor: Executor
|
||||||
protected def compiledSettings: Seq[Compiled[_]]
|
protected def compiledSettings: Seq[Compiled[_]]
|
||||||
|
|
||||||
|
|
@ -20,23 +21,33 @@ abstract class EvaluateSettings[Scope] {
|
||||||
private[this] val complete = new LinkedBlockingQueue[Option[Throwable]]
|
private[this] val complete = new LinkedBlockingQueue[Option[Throwable]]
|
||||||
private[this] val static = PMap.empty[ScopedKey, INode]
|
private[this] val static = PMap.empty[ScopedKey, INode]
|
||||||
private[this] val allScopes: Set[Scope] = compiledSettings.map(_.key.scope).toSet
|
private[this] val allScopes: Set[Scope] = compiledSettings.map(_.key.scope).toSet
|
||||||
private[this] def getStatic[T](key: ScopedKey[T]): INode[T] = static get key getOrElse sys.error("Illegal reference to key " + key)
|
|
||||||
|
private[this] def getStatic[T](key: ScopedKey[T]): INode[T] =
|
||||||
|
static get key getOrElse sys.error("Illegal reference to key " + key)
|
||||||
|
|
||||||
private[this] val transform: Initialize ~> INode = new (Initialize ~> INode) {
|
private[this] val transform: Initialize ~> INode = new (Initialize ~> INode) {
|
||||||
def apply[T](i: Initialize[T]): INode[T] = i match {
|
def apply[T](i: Initialize[T]): INode[T] = i match {
|
||||||
case k: Keyed[s, T] @unchecked => single(getStatic(k.scopedKey), k.transform)
|
case k: Keyed[s, T] @unchecked => single(getStatic(k.scopedKey), k.transform)
|
||||||
case a: Apply[k, T] @unchecked => new MixedNode[k, T](a.alist.transform[Initialize, INode](a.inputs, transform), a.f, a.alist)
|
case a: Apply[k, T] @unchecked =>
|
||||||
|
new MixedNode[k, T](
|
||||||
|
a.alist.transform[Initialize, INode](a.inputs, transform),
|
||||||
|
a.f,
|
||||||
|
a.alist
|
||||||
|
)
|
||||||
case b: Bind[s, T] @unchecked => new BindNode[s, T](transform(b.in), x => transform(b.f(x)))
|
case b: Bind[s, T] @unchecked => new BindNode[s, T](transform(b.in), x => transform(b.f(x)))
|
||||||
case v: Value[T] @unchecked => constant(v.value)
|
case v: Value[T] @unchecked => constant(v.value)
|
||||||
case v: ValidationCapture[T] @unchecked => strictConstant(v.key)
|
case v: ValidationCapture[T] @unchecked => strictConstant(v.key)
|
||||||
case t: TransformCapture => strictConstant(t.f)
|
case t: TransformCapture => strictConstant(t.f)
|
||||||
case o: Optional[s, T] @unchecked => o.a match {
|
case o: Optional[s, T] @unchecked =>
|
||||||
|
o.a match {
|
||||||
case None => constant(() => o.f(None))
|
case None => constant(() => o.f(None))
|
||||||
case Some(i) => single[s, T](transform(i), x => o.f(Some(x)))
|
case Some(i) => single[s, T](transform(i), x => o.f(Some(x)))
|
||||||
}
|
}
|
||||||
case x if x == StaticScopes => strictConstant(allScopes.asInstanceOf[T]) // can't convince scalac that StaticScopes => T == Set[Scope]
|
case x if x == StaticScopes =>
|
||||||
|
strictConstant(allScopes.asInstanceOf[T]) // can't convince scalac that StaticScopes => T == Set[Scope]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] lazy val roots: Seq[INode[_]] = compiledSettings flatMap { cs =>
|
private[this] lazy val roots: Seq[INode[_]] = compiledSettings flatMap { cs =>
|
||||||
(cs.settings map { s =>
|
(cs.settings map { s =>
|
||||||
val t = transform(s.init)
|
val t = transform(s.init)
|
||||||
|
|
@ -44,11 +55,11 @@ abstract class EvaluateSettings[Scope] {
|
||||||
t
|
t
|
||||||
}): Seq[INode[_]]
|
}): Seq[INode[_]]
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] var running = new AtomicInteger
|
private[this] var running = new AtomicInteger
|
||||||
private[this] var cancel = new AtomicBoolean(false)
|
private[this] var cancel = new AtomicBoolean(false)
|
||||||
|
|
||||||
def run(implicit delegates: Scope => Seq[Scope]): Settings[Scope] =
|
def run(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = {
|
||||||
{
|
|
||||||
assert(running.get() == 0, "Already running")
|
assert(running.get() == 0, "Already running")
|
||||||
startWork()
|
startWork()
|
||||||
roots.foreach(_.registerIfNew())
|
roots.foreach(_.registerIfNew())
|
||||||
|
|
@ -59,27 +70,32 @@ abstract class EvaluateSettings[Scope] {
|
||||||
}
|
}
|
||||||
getResults(delegates)
|
getResults(delegates)
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def getResults(implicit delegates: Scope => Seq[Scope]) =
|
private[this] def getResults(implicit delegates: Scope => Seq[Scope]) =
|
||||||
(empty /: static.toTypedSeq) {
|
(empty /: static.toTypedSeq) {
|
||||||
case (ss, static.TPair(key, node)) =>
|
case (ss, static.TPair(key, node)) =>
|
||||||
if (key.key.isLocal) ss else ss.set(key.scope, key.key, node.get)
|
if (key.key.isLocal) ss else ss.set(key.scope, key.key, node.get)
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] val getValue = new (INode ~> Id) { def apply[T](node: INode[T]) = node.get }
|
private[this] val getValue = new (INode ~> Id) { def apply[T](node: INode[T]) = node.get }
|
||||||
|
|
||||||
private[this] def submitEvaluate(node: INode[_]) = submit(node.evaluate())
|
private[this] def submitEvaluate(node: INode[_]) = submit(node.evaluate())
|
||||||
private[this] def submitCallComplete[T](node: BindNode[_, T], value: T) = submit(node.callComplete(value))
|
|
||||||
private[this] def submit(work: => Unit): Unit =
|
private[this] def submitCallComplete[T](node: BindNode[_, T], value: T) =
|
||||||
{
|
submit(node.callComplete(value))
|
||||||
|
|
||||||
|
private[this] def submit(work: => Unit): Unit = {
|
||||||
startWork()
|
startWork()
|
||||||
executor.execute(new Runnable { def run = if (!cancel.get()) run0(work) })
|
executor.execute(new Runnable { def run = if (!cancel.get()) run0(work) })
|
||||||
}
|
}
|
||||||
private[this] def run0(work: => Unit): Unit =
|
|
||||||
{
|
private[this] def run0(work: => Unit): Unit = {
|
||||||
try { work } catch { case e: Throwable => complete.put(Some(e)) }
|
try { work } catch { case e: Throwable => complete.put(Some(e)) }
|
||||||
workComplete()
|
workComplete()
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def startWork(): Unit = { running.incrementAndGet(); () }
|
private[this] def startWork(): Unit = { running.incrementAndGet(); () }
|
||||||
|
|
||||||
private[this] def workComplete(): Unit =
|
private[this] def workComplete(): Unit =
|
||||||
if (running.decrementAndGet() == 0)
|
if (running.decrementAndGet() == 0)
|
||||||
complete.put(None)
|
complete.put(None)
|
||||||
|
|
@ -91,26 +107,32 @@ abstract class EvaluateSettings[Scope] {
|
||||||
private[this] var blockedOn: Int = 0
|
private[this] var blockedOn: Int = 0
|
||||||
private[this] val calledBy = new collection.mutable.ListBuffer[BindNode[_, T]]
|
private[this] val calledBy = new collection.mutable.ListBuffer[BindNode[_, T]]
|
||||||
|
|
||||||
override def toString = getClass.getName + " (state=" + state + ",blockedOn=" + blockedOn + ",calledBy=" + calledBy.size + ",blocking=" + blocking.size + "): " +
|
override def toString =
|
||||||
|
getClass.getName + " (state=" + state + ",blockedOn=" + blockedOn + ",calledBy=" + calledBy.size + ",blocking=" + blocking.size + "): " +
|
||||||
keyString
|
keyString
|
||||||
|
|
||||||
private[this] def keyString =
|
private[this] def keyString =
|
||||||
(static.toSeq.flatMap { case (key, value) => if (value eq this) init.showFullKey.show(key) :: Nil else Nil }).headOption getOrElse "non-static"
|
(static.toSeq.flatMap {
|
||||||
|
case (key, value) => if (value eq this) init.showFullKey.show(key) :: Nil else Nil
|
||||||
|
}).headOption getOrElse "non-static"
|
||||||
|
|
||||||
final def get: T = synchronized {
|
final def get: T = synchronized {
|
||||||
assert(value != null, toString + " not evaluated")
|
assert(value != null, toString + " not evaluated")
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
final def doneOrBlock(from: INode[_]): Boolean = synchronized {
|
final def doneOrBlock(from: INode[_]): Boolean = synchronized {
|
||||||
val ready = state == Evaluated
|
val ready = state == Evaluated
|
||||||
if (!ready) blocking += from
|
if (!ready) blocking += from
|
||||||
registerIfNew()
|
registerIfNew()
|
||||||
ready
|
ready
|
||||||
}
|
}
|
||||||
|
|
||||||
final def isDone: Boolean = synchronized { state == Evaluated }
|
final def isDone: Boolean = synchronized { state == Evaluated }
|
||||||
final def isNew: Boolean = synchronized { state == New }
|
final def isNew: Boolean = synchronized { state == New }
|
||||||
final def isCalling: Boolean = synchronized { state == Calling }
|
final def isCalling: Boolean = synchronized { state == Calling }
|
||||||
final def registerIfNew(): Unit = synchronized { if (state == New) register() }
|
final def registerIfNew(): Unit = synchronized { if (state == New) register() }
|
||||||
|
|
||||||
private[this] def register(): Unit = {
|
private[this] def register(): Unit = {
|
||||||
assert(state == New, "Already registered and: " + toString)
|
assert(state == New, "Already registered and: " + toString)
|
||||||
val deps = dependsOn
|
val deps = dependsOn
|
||||||
|
|
@ -126,28 +148,36 @@ abstract class EvaluateSettings[Scope] {
|
||||||
state = Ready
|
state = Ready
|
||||||
submitEvaluate(this)
|
submitEvaluate(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
final def unblocked(): Unit = synchronized {
|
final def unblocked(): Unit = synchronized {
|
||||||
assert(state == Blocked, "Invalid state for unblocked() call: " + toString)
|
assert(state == Blocked, "Invalid state for unblocked() call: " + toString)
|
||||||
blockedOn -= 1
|
blockedOn -= 1
|
||||||
assert(blockedOn >= 0, "Negative blockedOn: " + blockedOn + " for " + toString)
|
assert(blockedOn >= 0, "Negative blockedOn: " + blockedOn + " for " + toString)
|
||||||
if (blockedOn == 0) schedule()
|
if (blockedOn == 0) schedule()
|
||||||
}
|
}
|
||||||
|
|
||||||
final def evaluate(): Unit = synchronized { evaluate0() }
|
final def evaluate(): Unit = synchronized { evaluate0() }
|
||||||
|
|
||||||
protected final def makeCall(source: BindNode[_, T], target: INode[T]): Unit = {
|
protected final def makeCall(source: BindNode[_, T], target: INode[T]): Unit = {
|
||||||
assert(state == Ready, "Invalid state for call to makeCall: " + toString)
|
assert(state == Ready, "Invalid state for call to makeCall: " + toString)
|
||||||
state = Calling
|
state = Calling
|
||||||
target.call(source)
|
target.call(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final def setValue(v: T): Unit = {
|
protected final def setValue(v: T): Unit = {
|
||||||
assert(state != Evaluated, "Already evaluated (trying to set value to " + v + "): " + toString)
|
assert(state != Evaluated,
|
||||||
|
"Already evaluated (trying to set value to " + v + "): " + toString)
|
||||||
if (v == null) sys.error("Setting value cannot be null: " + keyString)
|
if (v == null) sys.error("Setting value cannot be null: " + keyString)
|
||||||
value = v
|
value = v
|
||||||
state = Evaluated
|
state = Evaluated
|
||||||
blocking foreach { _.unblocked() }
|
blocking foreach { _.unblocked() }
|
||||||
blocking.clear()
|
blocking.clear()
|
||||||
calledBy foreach { node => submitCallComplete(node, value) }
|
calledBy foreach { node =>
|
||||||
|
submitCallComplete(node, value)
|
||||||
|
}
|
||||||
calledBy.clear()
|
calledBy.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
final def call(by: BindNode[_, T]): Unit = synchronized {
|
final def call(by: BindNode[_, T]): Unit = synchronized {
|
||||||
registerIfNew()
|
registerIfNew()
|
||||||
state match {
|
state match {
|
||||||
|
|
@ -156,13 +186,19 @@ abstract class EvaluateSettings[Scope] {
|
||||||
}
|
}
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def dependsOn: Seq[INode[_]]
|
protected def dependsOn: Seq[INode[_]]
|
||||||
protected def evaluate0(): Unit
|
protected def evaluate0(): Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def strictConstant[T](v: T): INode[T] = constant(() => v)
|
private[this] def strictConstant[T](v: T): INode[T] = constant(() => v)
|
||||||
private[this] def constant[T](f: () => T): INode[T] = new MixedNode[ConstK[Unit]#l, T]((), _ => f(), AList.empty)
|
|
||||||
private[this] def single[S, T](in: INode[S], f: S => T): INode[T] = new MixedNode[({ type l[L[x]] = L[S] })#l, T](in, f, AList.single[S])
|
private[this] def constant[T](f: () => T): INode[T] =
|
||||||
|
new MixedNode[ConstK[Unit]#l, T]((), _ => f(), AList.empty)
|
||||||
|
|
||||||
|
private[this] def single[S, T](in: INode[S], f: S => T): INode[T] =
|
||||||
|
new MixedNode[({ type l[L[x]] = L[S] })#l, T](in, f, AList.single[S])
|
||||||
|
|
||||||
private[this] final class BindNode[S, T](in: INode[S], f: S => INode[T]) extends INode[T] {
|
private[this] final class BindNode[S, T](in: INode[S], f: S => INode[T]) extends INode[T] {
|
||||||
protected def dependsOn = in :: Nil
|
protected def dependsOn = in :: Nil
|
||||||
protected def evaluate0(): Unit = makeCall(this, f(in.get))
|
protected def evaluate0(): Unit = makeCall(this, f(in.get))
|
||||||
|
|
@ -171,7 +207,9 @@ abstract class EvaluateSettings[Scope] {
|
||||||
setValue(value)
|
setValue(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private[this] final class MixedNode[K[L[x]], T](in: K[INode], f: K[Id] => T, alist: AList[K]) extends INode[T] {
|
|
||||||
|
private[this] final class MixedNode[K[L[x]], T](in: K[INode], f: K[Id] => T, alist: AList[K])
|
||||||
|
extends INode[T] {
|
||||||
protected def dependsOn = alist.toList(in)
|
protected def dependsOn = alist.toList(in)
|
||||||
protected def evaluate0(): Unit = setValue(f(alist.transform(in, getValue)))
|
protected def evaluate0(): Unit = setValue(f(alist.transform(in, getValue)))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,32 +22,38 @@ sealed trait KList[+M[_]] {
|
||||||
/** Discards the heterogeneous type information and constructs a plain List from this KList's elements. */
|
/** Discards the heterogeneous type information and constructs a plain List from this KList's elements. */
|
||||||
def toList: List[M[_]]
|
def toList: List[M[_]]
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class KCons[H, +T <: KList[M], +M[_]](head: M[H], tail: T) extends KList[M] {
|
final case class KCons[H, +T <: KList[M], +M[_]](head: M[H], tail: T) extends KList[M] {
|
||||||
final type Transform[N[_]] = KCons[H, tail.Transform[N], N]
|
final type Transform[N[_]] = KCons[H, tail.Transform[N], N]
|
||||||
|
|
||||||
def transform[N[_]](f: M ~> N) = KCons(f(head), tail.transform(f))
|
def transform[N[_]](f: M ~> N) = KCons(f(head), tail.transform(f))
|
||||||
def toList: List[M[_]] = head :: tail.toList
|
def toList: List[M[_]] = head :: tail.toList
|
||||||
def apply[N[x] >: M[x], Z](f: Transform[Id] => Z)(implicit ap: Applicative[N]): N[Z] =
|
|
||||||
{
|
def apply[N[x] >: M[x], Z](f: Transform[Id] => Z)(implicit ap: Applicative[N]): N[Z] = {
|
||||||
val g = (t: tail.Transform[Id]) => (h: H) => f(KCons[H, tail.Transform[Id], Id](h, t))
|
val g = (t: tail.Transform[Id]) => (h: H) => f(KCons[H, tail.Transform[Id], Id](h, t))
|
||||||
ap.apply(tail.apply[N, H => Z](g), head)
|
ap.apply(tail.apply[N, H => Z](g), head)
|
||||||
}
|
}
|
||||||
def traverse[N[_], P[_]](f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Transform[P]] =
|
|
||||||
{
|
def traverse[N[_], P[_]](f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Transform[P]] = {
|
||||||
val tt: N[tail.Transform[P]] = tail.traverse[N, P](f)
|
val tt: N[tail.Transform[P]] = tail.traverse[N, P](f)
|
||||||
val g = (t: tail.Transform[P]) => (h: P[H]) => KCons(h, t)
|
val g = (t: tail.Transform[P]) => (h: P[H]) => KCons(h, t)
|
||||||
np.apply(np.map(g, tt), f(head))
|
np.apply(np.map(g, tt), f(head))
|
||||||
}
|
}
|
||||||
|
|
||||||
def :^:[A, N[x] >: M[x]](h: N[A]) = KCons(h, this)
|
def :^:[A, N[x] >: M[x]](h: N[A]) = KCons(h, this)
|
||||||
override def foldr[B](f: (M[_], B) => B, init: B): B = f(head, tail.foldr(f, init))
|
override def foldr[B](f: (M[_], B) => B, init: B): B = f(head, tail.foldr(f, init))
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed abstract class KNil extends KList[Nothing] {
|
sealed abstract class KNil extends KList[Nothing] {
|
||||||
final type Transform[N[_]] = KNil
|
final type Transform[N[_]] = KNil
|
||||||
final def transform[N[_]](f: Nothing ~> N): Transform[N] = KNil
|
final def transform[N[_]](f: Nothing ~> N): Transform[N] = KNil
|
||||||
final def toList = Nil
|
final def toList = Nil
|
||||||
final def apply[N[x], Z](f: KNil => Z)(implicit ap: Applicative[N]): N[Z] = ap.pure(f(KNil))
|
final def apply[N[x], Z](f: KNil => Z)(implicit ap: Applicative[N]): N[Z] = ap.pure(f(KNil))
|
||||||
final def traverse[N[_], P[_]](f: Nothing ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[KNil] = np.pure(KNil)
|
|
||||||
|
final def traverse[N[_], P[_]](f: Nothing ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[KNil] =
|
||||||
|
np.pure(KNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
case object KNil extends KNil {
|
case object KNil extends KNil {
|
||||||
def :^:[M[_], H](h: M[H]): KCons[H, KNil, M] = KCons(h, this)
|
def :^:[M[_], H](h: M[H]): KCons[H, KNil, M] = KCons(h, this)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,11 @@ trait RMap[K[_], V[_]] {
|
||||||
def get[T](k: K[T]): Option[V[T]]
|
def get[T](k: K[T]): Option[V[T]]
|
||||||
def contains[T](k: K[T]): Boolean
|
def contains[T](k: K[T]): Boolean
|
||||||
def toSeq: Seq[(K[_], V[_])]
|
def toSeq: Seq[(K[_], V[_])]
|
||||||
def toTypedSeq: Seq[TPair[_]] = toSeq.map { case (k: K[t], v) => TPair[t](k, v.asInstanceOf[V[t]]) }
|
|
||||||
|
def toTypedSeq: Seq[TPair[_]] = toSeq.map {
|
||||||
|
case (k: K[t], v) => TPair[t](k, v.asInstanceOf[V[t]])
|
||||||
|
}
|
||||||
|
|
||||||
def keys: Iterable[K[_]]
|
def keys: Iterable[K[_]]
|
||||||
def values: Iterable[V[_]]
|
def values: Iterable[V[_]]
|
||||||
def isEmpty: Boolean
|
def isEmpty: Boolean
|
||||||
|
|
@ -23,19 +27,24 @@ trait IMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] {
|
||||||
def remove[T](k: K[T]): IMap[K, V]
|
def remove[T](k: K[T]): IMap[K, V]
|
||||||
def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): IMap[K, V]
|
def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): IMap[K, V]
|
||||||
def mapValues[V2[_]](f: V ~> V2): IMap[K, V2]
|
def mapValues[V2[_]](f: V ~> V2): IMap[K, V2]
|
||||||
def mapSeparate[VL[_], VR[_]](f: V ~> ({ type l[T] = Either[VL[T], VR[T]] })#l): (IMap[K, VL], IMap[K, VR])
|
def mapSeparate[VL[_], VR[_]](f: V ~> ({ type l[T] = Either[VL[T], VR[T]] })#l)
|
||||||
|
: (IMap[K, VL], IMap[K, VR])
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] {
|
trait PMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] {
|
||||||
def update[T](k: K[T], v: V[T]): Unit
|
def update[T](k: K[T], v: V[T]): Unit
|
||||||
def remove[T](k: K[T]): Option[V[T]]
|
def remove[T](k: K[T]): Option[V[T]]
|
||||||
def getOrUpdate[T](k: K[T], make: => V[T]): V[T]
|
def getOrUpdate[T](k: K[T], make: => V[T]): V[T]
|
||||||
def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T]
|
def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T]
|
||||||
}
|
}
|
||||||
|
|
||||||
object PMap {
|
object PMap {
|
||||||
implicit def toFunction[K[_], V[_]](map: PMap[K, V]): K[_] => V[_] = k => map(k)
|
implicit def toFunction[K[_], V[_]](map: PMap[K, V]): K[_] => V[_] = k => map(k)
|
||||||
def empty[K[_], V[_]]: PMap[K, V] = new DelegatingPMap[K, V](new mutable.HashMap)
|
def empty[K[_], V[_]]: PMap[K, V] = new DelegatingPMap[K, V](new mutable.HashMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
object IMap {
|
object IMap {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only suitable for K that is invariant in its type parameter.
|
* Only suitable for K that is invariant in its type parameter.
|
||||||
* Option and List keys are not suitable, for example,
|
* Option and List keys are not suitable, for example,
|
||||||
|
|
@ -43,7 +52,9 @@ object IMap {
|
||||||
*/
|
*/
|
||||||
def empty[K[_], V[_]]: IMap[K, V] = new IMap0[K, V](Map.empty)
|
def empty[K[_], V[_]]: IMap[K, V] = new IMap0[K, V](Map.empty)
|
||||||
|
|
||||||
private[this] class IMap0[K[_], V[_]](backing: Map[K[_], V[_]]) extends AbstractRMap[K, V] with IMap[K, V] {
|
private[this] class IMap0[K[_], V[_]](backing: Map[K[_], V[_]])
|
||||||
|
extends AbstractRMap[K, V]
|
||||||
|
with IMap[K, V] {
|
||||||
def get[T](k: K[T]): Option[V[T]] = (backing get k).asInstanceOf[Option[V[T]]]
|
def get[T](k: K[T]): Option[V[T]] = (backing get k).asInstanceOf[Option[V[T]]]
|
||||||
def put[T](k: K[T], v: V[T]) = new IMap0[K, V](backing.updated(k, v))
|
def put[T](k: K[T], v: V[T]) = new IMap0[K, V](backing.updated(k, v))
|
||||||
def remove[T](k: K[T]) = new IMap0[K, V](backing - k)
|
def remove[T](k: K[T]) = new IMap0[K, V](backing - k)
|
||||||
|
|
@ -54,10 +65,10 @@ object IMap {
|
||||||
def mapValues[V2[_]](f: V ~> V2) =
|
def mapValues[V2[_]](f: V ~> V2) =
|
||||||
new IMap0[K, V2](backing.mapValues(x => f(x)))
|
new IMap0[K, V2](backing.mapValues(x => f(x)))
|
||||||
|
|
||||||
def mapSeparate[VL[_], VR[_]](f: V ~> ({ type l[T] = Either[VL[T], VR[T]] })#l) =
|
def mapSeparate[VL[_], VR[_]](f: V ~> ({ type l[T] = Either[VL[T], VR[T]] })#l) = {
|
||||||
{
|
|
||||||
val mapped = backing.iterator.map {
|
val mapped = backing.iterator.map {
|
||||||
case (k, v) => f(v) match {
|
case (k, v) =>
|
||||||
|
f(v) match {
|
||||||
case Left(l) => Left((k, l))
|
case Left(l) => Left((k, l))
|
||||||
case Right(r) => Right((k, r))
|
case Right(r) => Right((k, r))
|
||||||
}
|
}
|
||||||
|
|
@ -85,17 +96,20 @@ abstract class AbstractRMap[K[_], V[_]] extends RMap[K, V] {
|
||||||
* Option and List keys are not suitable, for example,
|
* Option and List keys are not suitable, for example,
|
||||||
* because None <:< Option[String] and None <: Option[Int].
|
* because None <:< Option[String] and None <: Option[Int].
|
||||||
*/
|
*/
|
||||||
class DelegatingPMap[K[_], V[_]](backing: mutable.Map[K[_], V[_]]) extends AbstractRMap[K, V] with PMap[K, V] {
|
class DelegatingPMap[K[_], V[_]](backing: mutable.Map[K[_], V[_]])
|
||||||
|
extends AbstractRMap[K, V]
|
||||||
|
with PMap[K, V] {
|
||||||
def get[T](k: K[T]): Option[V[T]] = cast[T](backing.get(k))
|
def get[T](k: K[T]): Option[V[T]] = cast[T](backing.get(k))
|
||||||
def update[T](k: K[T], v: V[T]): Unit = { backing(k) = v }
|
def update[T](k: K[T], v: V[T]): Unit = { backing(k) = v }
|
||||||
def remove[T](k: K[T]) = cast(backing.remove(k))
|
def remove[T](k: K[T]) = cast(backing.remove(k))
|
||||||
def getOrUpdate[T](k: K[T], make: => V[T]) = cast[T](backing.getOrElseUpdate(k, make))
|
def getOrUpdate[T](k: K[T], make: => V[T]) = cast[T](backing.getOrElseUpdate(k, make))
|
||||||
def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] =
|
|
||||||
{
|
def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] = {
|
||||||
val v = f(this get k getOrElse init)
|
val v = f(this get k getOrElse init)
|
||||||
update(k, v)
|
update(k, v)
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
def toSeq = backing.toSeq
|
def toSeq = backing.toSeq
|
||||||
def keys = backing.keys
|
def keys = backing.keys
|
||||||
def values = backing.values
|
def values = backing.values
|
||||||
|
|
|
||||||
|
|
@ -19,30 +19,40 @@ sealed trait Settings[Scope] {
|
||||||
def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope]
|
def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope]
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class Settings0[Scope](val data: Map[Scope, AttributeMap], val delegates: Scope => Seq[Scope]) extends Settings[Scope] {
|
private final class Settings0[Scope](
|
||||||
|
val data: Map[Scope, AttributeMap],
|
||||||
|
val delegates: Scope => Seq[Scope]
|
||||||
|
) extends Settings[Scope] {
|
||||||
|
|
||||||
def scopes: Set[Scope] = data.keySet
|
def scopes: Set[Scope] = data.keySet
|
||||||
def keys(scope: Scope) = data(scope).keys.toSet
|
def keys(scope: Scope) = data(scope).keys.toSet
|
||||||
def allKeys[T](f: (Scope, AttributeKey[_]) => T): Seq[T] = data.flatMap { case (scope, map) => map.keys.map(k => f(scope, k)) }.toSeq
|
|
||||||
|
def allKeys[T](f: (Scope, AttributeKey[_]) => T): Seq[T] =
|
||||||
|
data.flatMap { case (scope, map) => map.keys.map(k => f(scope, k)) }.toSeq
|
||||||
|
|
||||||
def get[T](scope: Scope, key: AttributeKey[T]): Option[T] =
|
def get[T](scope: Scope, key: AttributeKey[T]): Option[T] =
|
||||||
delegates(scope).toStream.flatMap(sc => getDirect(sc, key)).headOption
|
delegates(scope).toStream.flatMap(sc => getDirect(sc, key)).headOption
|
||||||
|
|
||||||
def definingScope(scope: Scope, key: AttributeKey[_]): Option[Scope] =
|
def definingScope(scope: Scope, key: AttributeKey[_]): Option[Scope] =
|
||||||
delegates(scope).toStream.find(sc => getDirect(sc, key).isDefined)
|
delegates(scope).toStream.find(sc => getDirect(sc, key).isDefined)
|
||||||
|
|
||||||
def getDirect[T](scope: Scope, key: AttributeKey[T]): Option[T] =
|
def getDirect[T](scope: Scope, key: AttributeKey[T]): Option[T] =
|
||||||
(data get scope).flatMap(_ get key)
|
(data get scope).flatMap(_ get key)
|
||||||
|
|
||||||
def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope] =
|
def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope] = {
|
||||||
{
|
|
||||||
val map = data getOrElse (scope, AttributeMap.empty)
|
val map = data getOrElse (scope, AttributeMap.empty)
|
||||||
val newData = data.updated(scope, map.put(key, value))
|
val newData = data.updated(scope, map.put(key, value))
|
||||||
new Settings0(newData, delegates)
|
new Settings0(newData, delegates)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// delegates should contain the input Scope as the first entry
|
// delegates should contain the input Scope as the first entry
|
||||||
// this trait is intended to be mixed into an object
|
// this trait is intended to be mixed into an object
|
||||||
trait Init[Scope] {
|
trait Init[Scope] {
|
||||||
/** The Show instance used when a detailed String needs to be generated. It is typically used when no context is available.*/
|
|
||||||
|
/** The Show instance used when a detailed String needs to be generated.
|
||||||
|
* It is typically used when no context is available.
|
||||||
|
*/
|
||||||
def showFullKey: Show[ScopedKey[_]]
|
def showFullKey: Show[ScopedKey[_]]
|
||||||
|
|
||||||
sealed case class ScopedKey[T](scope: Scope, key: AttributeKey[T]) extends KeyedInitialize[T] {
|
sealed case class ScopedKey[T](scope: Scope, key: AttributeKey[T]) extends KeyedInitialize[T] {
|
||||||
|
|
@ -67,17 +77,32 @@ trait Init[Scope] {
|
||||||
* The result of this initialization is the composition of applied transformations.
|
* The result of this initialization is the composition of applied transformations.
|
||||||
* This can be useful when dealing with dynamic Initialize values.
|
* This can be useful when dealing with dynamic Initialize values.
|
||||||
*/
|
*/
|
||||||
lazy val capturedTransformations: Initialize[Initialize ~> Initialize] = new TransformCapture(idK[Initialize])
|
lazy val capturedTransformations: Initialize[Initialize ~> Initialize] =
|
||||||
|
new TransformCapture(idK[Initialize])
|
||||||
|
|
||||||
|
def setting[T](
|
||||||
|
key: ScopedKey[T],
|
||||||
|
init: Initialize[T],
|
||||||
|
pos: SourcePosition = NoPosition
|
||||||
|
): Setting[T] = new Setting[T](key, init, pos)
|
||||||
|
|
||||||
def setting[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition = NoPosition): Setting[T] = new Setting[T](key, init, pos)
|
|
||||||
def valueStrict[T](value: T): Initialize[T] = pure(() => value)
|
def valueStrict[T](value: T): Initialize[T] = pure(() => value)
|
||||||
def value[T](value: => T): Initialize[T] = pure(value _)
|
def value[T](value: => T): Initialize[T] = pure(value _)
|
||||||
def pure[T](value: () => T): Initialize[T] = new Value(value)
|
def pure[T](value: () => T): Initialize[T] = new Value(value)
|
||||||
def optional[T, U](i: Initialize[T])(f: Option[T] => U): Initialize[U] = new Optional(Some(i), f)
|
def optional[T, U](i: Initialize[T])(f: Option[T] => U): Initialize[U] = new Optional(Some(i), f)
|
||||||
def update[T](key: ScopedKey[T])(f: T => T): Setting[T] = setting[T](key, map(key)(f), NoPosition)
|
|
||||||
|
def update[T](key: ScopedKey[T])(f: T => T): Setting[T] =
|
||||||
|
setting[T](key, map(key)(f), NoPosition)
|
||||||
|
|
||||||
def bind[S, T](in: Initialize[S])(f: S => Initialize[T]): Initialize[T] = new Bind(f, in)
|
def bind[S, T](in: Initialize[S])(f: S => Initialize[T]): Initialize[T] = new Bind(f, in)
|
||||||
def map[S, T](in: Initialize[S])(f: S => T): Initialize[T] = new Apply[({ type l[L[x]] = L[S] })#l, T](f, in, AList.single[S])
|
|
||||||
def app[K[L[x]], T](inputs: K[Initialize])(f: K[Id] => T)(implicit alist: AList[K]): Initialize[T] = new Apply[K, T](f, inputs, alist)
|
def map[S, T](in: Initialize[S])(f: S => T): Initialize[T] =
|
||||||
|
new Apply[({ type l[L[x]] = L[S] })#l, T](f, in, AList.single[S])
|
||||||
|
|
||||||
|
def app[K[L[x]], T](inputs: K[Initialize])(f: K[Id] => T)(
|
||||||
|
implicit alist: AList[K]
|
||||||
|
): Initialize[T] = new Apply[K, T](f, inputs, alist)
|
||||||
|
|
||||||
def uniform[S, T](inputs: Seq[Initialize[S]])(f: Seq[S] => T): Initialize[T] =
|
def uniform[S, T](inputs: Seq[Initialize[S]])(f: Seq[S] => T): Initialize[T] =
|
||||||
new Apply[({ type l[L[x]] = List[L[S]] })#l, T](f, inputs.toList, AList.seq[S])
|
new Apply[({ type l[L[x]] = List[L[S]] })#l, T](f, inputs.toList, AList.seq[S])
|
||||||
|
|
||||||
|
|
@ -86,7 +111,11 @@ trait Init[Scope] {
|
||||||
* No dependency is introduced on `key`. If `selfRefOk` is true, validation will not fail if the key is referenced by a definition of `key`.
|
* No dependency is introduced on `key`. If `selfRefOk` is true, validation will not fail if the key is referenced by a definition of `key`.
|
||||||
* That is, key := f(validated(key).value) is allowed only if `selfRefOk == true`.
|
* That is, key := f(validated(key).value) is allowed only if `selfRefOk == true`.
|
||||||
*/
|
*/
|
||||||
private[sbt] final def validated[T](key: ScopedKey[T], selfRefOk: Boolean): ValidationCapture[T] = new ValidationCapture(key, selfRefOk)
|
private[sbt] final def validated[T](
|
||||||
|
key: ScopedKey[T],
|
||||||
|
selfRefOk: Boolean
|
||||||
|
): ValidationCapture[T] =
|
||||||
|
new ValidationCapture(key, selfRefOk)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a derived setting that will be automatically defined in every scope where one of its dependencies
|
* Constructs a derived setting that will be automatically defined in every scope where one of its dependencies
|
||||||
|
|
@ -94,7 +123,13 @@ trait Init[Scope] {
|
||||||
* A setting initialized with dynamic dependencies is only allowed if `allowDynamic` is true.
|
* A setting initialized with dynamic dependencies is only allowed if `allowDynamic` is true.
|
||||||
* Only the static dependencies are tracked, however. Dependencies on previous values do not introduce a derived setting either.
|
* Only the static dependencies are tracked, however. Dependencies on previous values do not introduce a derived setting either.
|
||||||
*/
|
*/
|
||||||
final def derive[T](s: Setting[T], allowDynamic: Boolean = false, filter: Scope => Boolean = const(true), trigger: AttributeKey[_] => Boolean = const(true), default: Boolean = false): Setting[T] = {
|
final def derive[T](
|
||||||
|
s: Setting[T],
|
||||||
|
allowDynamic: Boolean = false,
|
||||||
|
filter: Scope => Boolean = const(true),
|
||||||
|
trigger: AttributeKey[_] => Boolean = const(true),
|
||||||
|
default: Boolean = false
|
||||||
|
): Setting[T] = {
|
||||||
deriveAllowed(s, allowDynamic) foreach sys.error
|
deriveAllowed(s, allowDynamic) foreach sys.error
|
||||||
val d = new DerivedSetting[T](s.key, s.init, s.pos, filter, trigger)
|
val d = new DerivedSetting[T](s.key, s.init, s.pos, filter, trigger)
|
||||||
if (default) d.default() else d
|
if (default) d.default() else d
|
||||||
|
|
@ -107,29 +142,46 @@ trait Init[Scope] {
|
||||||
|
|
||||||
// id is used for equality
|
// id is used for equality
|
||||||
private[sbt] final def defaultSetting[T](s: Setting[T]): Setting[T] = s.default()
|
private[sbt] final def defaultSetting[T](s: Setting[T]): Setting[T] = s.default()
|
||||||
private[sbt] def defaultSettings(ss: Seq[Setting[_]]): Seq[Setting[_]] = ss.map(s => defaultSetting(s))
|
|
||||||
|
private[sbt] def defaultSettings(ss: Seq[Setting[_]]): Seq[Setting[_]] =
|
||||||
|
ss.map(s => defaultSetting(s))
|
||||||
|
|
||||||
private[this] final val nextID = new java.util.concurrent.atomic.AtomicLong
|
private[this] final val nextID = new java.util.concurrent.atomic.AtomicLong
|
||||||
private[this] final def nextDefaultID(): Long = nextID.incrementAndGet()
|
private[this] final def nextDefaultID(): Long = nextID.incrementAndGet()
|
||||||
|
|
||||||
def empty(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = new Settings0(Map.empty, delegates)
|
def empty(implicit delegates: Scope => Seq[Scope]): Settings[Scope] =
|
||||||
|
new Settings0(Map.empty, delegates)
|
||||||
|
|
||||||
def asTransform(s: Settings[Scope]): ScopedKey ~> Id = new (ScopedKey ~> Id) {
|
def asTransform(s: Settings[Scope]): ScopedKey ~> Id = new (ScopedKey ~> Id) {
|
||||||
def apply[T](k: ScopedKey[T]): T = getValue(s, k)
|
def apply[T](k: ScopedKey[T]): T = getValue(s, k)
|
||||||
}
|
}
|
||||||
def getValue[T](s: Settings[Scope], k: ScopedKey[T]) = s.get(k.scope, k.key) getOrElse (throw new InvalidReference(k))
|
|
||||||
|
def getValue[T](s: Settings[Scope], k: ScopedKey[T]) =
|
||||||
|
s.get(k.scope, k.key) getOrElse (throw new InvalidReference(k))
|
||||||
|
|
||||||
def asFunction[T](s: Settings[Scope]): ScopedKey[T] => T = k => getValue(s, k)
|
def asFunction[T](s: Settings[Scope]): ScopedKey[T] => T = k => getValue(s, k)
|
||||||
|
|
||||||
def mapScope(f: Scope => Scope): MapScoped = new MapScoped {
|
def mapScope(f: Scope => Scope): MapScoped = new MapScoped {
|
||||||
def apply[T](k: ScopedKey[T]): ScopedKey[T] = k.copy(scope = f(k.scope))
|
def apply[T](k: ScopedKey[T]): ScopedKey[T] = k.copy(scope = f(k.scope))
|
||||||
}
|
}
|
||||||
private final class InvalidReference(val key: ScopedKey[_]) extends RuntimeException("Internal settings error: invalid reference to " + showFullKey.show(key))
|
|
||||||
|
|
||||||
private[this] def applyDefaults(ss: Seq[Setting[_]]): Seq[Setting[_]] =
|
private final class InvalidReference(val key: ScopedKey[_])
|
||||||
{
|
extends RuntimeException(
|
||||||
val (defaults, others) = Util.separate[Setting[_], DefaultSetting[_], Setting[_]](ss) { case u: DefaultSetting[_] => Left(u); case s => Right(s) }
|
"Internal settings error: invalid reference to " + showFullKey.show(key)
|
||||||
|
)
|
||||||
|
|
||||||
|
private[this] def applyDefaults(ss: Seq[Setting[_]]): Seq[Setting[_]] = {
|
||||||
|
val (defaults, others) = Util.separate[Setting[_], DefaultSetting[_], Setting[_]](ss) {
|
||||||
|
case u: DefaultSetting[_] => Left(u); case s => Right(s)
|
||||||
|
}
|
||||||
defaults.distinct ++ others
|
defaults.distinct ++ others
|
||||||
}
|
}
|
||||||
|
|
||||||
def compiled(init: Seq[Setting[_]], actual: Boolean = true)(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal, display: Show[ScopedKey[_]]): CompiledMap =
|
def compiled(init: Seq[Setting[_]], actual: Boolean = true)(
|
||||||
{
|
implicit delegates: Scope => Seq[Scope],
|
||||||
|
scopeLocal: ScopeLocal,
|
||||||
|
display: Show[ScopedKey[_]]
|
||||||
|
): CompiledMap = {
|
||||||
val initDefaults = applyDefaults(init)
|
val initDefaults = applyDefaults(init)
|
||||||
// inject derived settings into scopes where their dependencies are directly defined
|
// inject derived settings into scopes where their dependencies are directly defined
|
||||||
// and prepend per-scope settings
|
// and prepend per-scope settings
|
||||||
|
|
@ -141,15 +193,22 @@ trait Init[Scope] {
|
||||||
// merge Seq[Setting[_]] into Compiled
|
// merge Seq[Setting[_]] into Compiled
|
||||||
compile(dMap)
|
compile(dMap)
|
||||||
}
|
}
|
||||||
def make(init: Seq[Setting[_]])(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal, display: Show[ScopedKey[_]]): Settings[Scope] =
|
|
||||||
{
|
def make(init: Seq[Setting[_]])(
|
||||||
|
implicit delegates: Scope => Seq[Scope],
|
||||||
|
scopeLocal: ScopeLocal,
|
||||||
|
display: Show[ScopedKey[_]]
|
||||||
|
): Settings[Scope] = {
|
||||||
val cMap = compiled(init)(delegates, scopeLocal, display)
|
val cMap = compiled(init)(delegates, scopeLocal, display)
|
||||||
// order the initializations. cyclic references are detected here.
|
// order the initializations. cyclic references are detected here.
|
||||||
val ordered: Seq[Compiled[_]] = sort(cMap)
|
val ordered: Seq[Compiled[_]] = sort(cMap)
|
||||||
// evaluation: apply the initializations.
|
// evaluation: apply the initializations.
|
||||||
try { applyInits(ordered) }
|
try { applyInits(ordered) } catch {
|
||||||
catch { case rru: RuntimeUndefined => throw Uninitialized(cMap.keys.toSeq, delegates, rru.undefined, true) }
|
case rru: RuntimeUndefined =>
|
||||||
|
throw Uninitialized(cMap.keys.toSeq, delegates, rru.undefined, true)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def sort(cMap: CompiledMap): Seq[Compiled[_]] =
|
def sort(cMap: CompiledMap): Seq[Compiled[_]] =
|
||||||
Dag.topologicalSort(cMap.values)(_.dependencies.map(cMap))
|
Dag.topologicalSort(cMap.values)(_.dependencies.map(cMap))
|
||||||
|
|
||||||
|
|
@ -172,36 +231,52 @@ trait Init[Scope] {
|
||||||
def addLocal(init: Seq[Setting[_]])(implicit scopeLocal: ScopeLocal): Seq[Setting[_]] =
|
def addLocal(init: Seq[Setting[_]])(implicit scopeLocal: ScopeLocal): Seq[Setting[_]] =
|
||||||
init.flatMap(_.dependencies flatMap scopeLocal) ++ init
|
init.flatMap(_.dependencies flatMap scopeLocal) ++ init
|
||||||
|
|
||||||
def delegate(sMap: ScopedMap)(implicit delegates: Scope => Seq[Scope], display: Show[ScopedKey[_]]): ScopedMap =
|
def delegate(sMap: ScopedMap)(
|
||||||
{
|
implicit delegates: Scope => Seq[Scope],
|
||||||
|
display: Show[ScopedKey[_]]
|
||||||
|
): ScopedMap = {
|
||||||
def refMap(ref: Setting[_], isFirst: Boolean) = new ValidateKeyRef {
|
def refMap(ref: Setting[_], isFirst: Boolean) = new ValidateKeyRef {
|
||||||
def apply[T](k: ScopedKey[T], selfRefOk: Boolean) =
|
def apply[T](k: ScopedKey[T], selfRefOk: Boolean) =
|
||||||
delegateForKey(sMap, k, delegates(k.scope), ref, selfRefOk || !isFirst)
|
delegateForKey(sMap, k, delegates(k.scope), ref, selfRefOk || !isFirst)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidatedSettings[T] = Either[Seq[Undefined], SettingSeq[T]]
|
type ValidatedSettings[T] = Either[Seq[Undefined], SettingSeq[T]]
|
||||||
|
|
||||||
val f = new (SettingSeq ~> ValidatedSettings) {
|
val f = new (SettingSeq ~> ValidatedSettings) {
|
||||||
def apply[T](ks: Seq[Setting[T]]) = {
|
def apply[T](ks: Seq[Setting[T]]) = {
|
||||||
val (undefs, valid) = Util.separate(ks.zipWithIndex) { case (s, i) => s validateKeyReferenced refMap(s, i == 0) }
|
val (undefs, valid) = Util.separate(ks.zipWithIndex) {
|
||||||
|
case (s, i) => s validateKeyReferenced refMap(s, i == 0)
|
||||||
|
}
|
||||||
if (undefs.isEmpty) Right(valid) else Left(undefs.flatten)
|
if (undefs.isEmpty) Right(valid) else Left(undefs.flatten)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Undefs[_] = Seq[Undefined]
|
type Undefs[_] = Seq[Undefined]
|
||||||
val (undefineds, result) = sMap.mapSeparate[Undefs, SettingSeq](f)
|
val (undefineds, result) = sMap.mapSeparate[Undefs, SettingSeq](f)
|
||||||
|
|
||||||
if (undefineds.isEmpty)
|
if (undefineds.isEmpty)
|
||||||
result
|
result
|
||||||
else
|
else
|
||||||
throw Uninitialized(sMap.keys.toSeq, delegates, undefineds.values.flatten.toList, false)
|
throw Uninitialized(sMap.keys.toSeq, delegates, undefineds.values.flatten.toList, false)
|
||||||
}
|
}
|
||||||
private[this] def delegateForKey[T](sMap: ScopedMap, k: ScopedKey[T], scopes: Seq[Scope], ref: Setting[_], selfRefOk: Boolean): Either[Undefined, ScopedKey[T]] =
|
|
||||||
{
|
private[this] def delegateForKey[T](
|
||||||
|
sMap: ScopedMap,
|
||||||
|
k: ScopedKey[T],
|
||||||
|
scopes: Seq[Scope],
|
||||||
|
ref: Setting[_],
|
||||||
|
selfRefOk: Boolean
|
||||||
|
): Either[Undefined, ScopedKey[T]] = {
|
||||||
val skeys = scopes.iterator.map(x => ScopedKey(x, k.key))
|
val skeys = scopes.iterator.map(x => ScopedKey(x, k.key))
|
||||||
val definedAt = skeys.find(sk => (selfRefOk || ref.key != sk) && (sMap contains sk))
|
val definedAt = skeys.find(sk => (selfRefOk || ref.key != sk) && (sMap contains sk))
|
||||||
definedAt.toRight(Undefined(ref, k))
|
definedAt.toRight(Undefined(ref, k))
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def applyInits(ordered: Seq[Compiled[_]])(implicit delegates: Scope => Seq[Scope]): Settings[Scope] =
|
private[this] def applyInits(ordered: Seq[Compiled[_]])(
|
||||||
{
|
implicit delegates: Scope => Seq[Scope]
|
||||||
val x = java.util.concurrent.Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors)
|
): Settings[Scope] = {
|
||||||
|
val x =
|
||||||
|
java.util.concurrent.Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors)
|
||||||
try {
|
try {
|
||||||
val eval: EvaluateSettings[Scope] = new EvaluateSettings[Scope] {
|
val eval: EvaluateSettings[Scope] = new EvaluateSettings[Scope] {
|
||||||
override val init: Init.this.type = Init.this
|
override val init: Init.this.type = Init.this
|
||||||
|
|
@ -212,63 +287,110 @@ trait Init[Scope] {
|
||||||
} finally { x.shutdown() }
|
} finally { x.shutdown() }
|
||||||
}
|
}
|
||||||
|
|
||||||
def showUndefined(u: Undefined, validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope])(implicit display: Show[ScopedKey[_]]): String =
|
def showUndefined(u: Undefined, validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope])(
|
||||||
{
|
implicit display: Show[ScopedKey[_]]
|
||||||
|
): String = {
|
||||||
val guessed = guessIntendedScope(validKeys, delegates, u.referencedKey)
|
val guessed = guessIntendedScope(validKeys, delegates, u.referencedKey)
|
||||||
val derived = u.defining.isDerived
|
val derived = u.defining.isDerived
|
||||||
val refString = display.show(u.defining.key)
|
val refString = display.show(u.defining.key)
|
||||||
val sourceString = if (derived) "" else parenPosString(u.defining)
|
val sourceString = if (derived) "" else parenPosString(u.defining)
|
||||||
val guessedString = if (derived) "" else guessed.map(g => "\n Did you mean " + display.show(g) + " ?").toList.mkString
|
val guessedString =
|
||||||
val derivedString = if (derived) ", which is a derived setting that needs this key to be defined in this scope." else ""
|
if (derived) ""
|
||||||
|
else guessed.map(g => "\n Did you mean " + display.show(g) + " ?").toList.mkString
|
||||||
|
val derivedString =
|
||||||
|
if (derived) ", which is a derived setting that needs this key to be defined in this scope."
|
||||||
|
else ""
|
||||||
display.show(u.referencedKey) + " from " + refString + sourceString + derivedString + guessedString
|
display.show(u.referencedKey) + " from " + refString + sourceString + derivedString + guessedString
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def parenPosString(s: Setting[_]): String =
|
private[this] def parenPosString(s: Setting[_]): String =
|
||||||
s.positionString match { case None => ""; case Some(s) => " (" + s + ")" }
|
s.positionString match { case None => ""; case Some(s) => " (" + s + ")" }
|
||||||
|
|
||||||
def guessIntendedScope(validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope], key: ScopedKey[_]): Option[ScopedKey[_]] =
|
def guessIntendedScope(
|
||||||
{
|
validKeys: Seq[ScopedKey[_]],
|
||||||
val distances = validKeys.flatMap { validKey => refinedDistance(delegates, validKey, key).map(dist => (dist, validKey)) }
|
delegates: Scope => Seq[Scope],
|
||||||
|
key: ScopedKey[_]
|
||||||
|
): Option[ScopedKey[_]] = {
|
||||||
|
val distances = validKeys.flatMap { validKey =>
|
||||||
|
refinedDistance(delegates, validKey, key).map(dist => (dist, validKey))
|
||||||
|
}
|
||||||
distances.sortBy(_._1).map(_._2).headOption
|
distances.sortBy(_._1).map(_._2).headOption
|
||||||
}
|
}
|
||||||
def refinedDistance(delegates: Scope => Seq[Scope], a: ScopedKey[_], b: ScopedKey[_]): Option[Int] =
|
|
||||||
|
def refinedDistance(
|
||||||
|
delegates: Scope => Seq[Scope],
|
||||||
|
a: ScopedKey[_],
|
||||||
|
b: ScopedKey[_]
|
||||||
|
): Option[Int] =
|
||||||
if (a.key != b.key || a == b) None
|
if (a.key != b.key || a == b) None
|
||||||
else {
|
else {
|
||||||
val dist = delegates(a.scope).indexOf(b.scope)
|
val dist = delegates(a.scope).indexOf(b.scope)
|
||||||
if (dist < 0) None else Some(dist)
|
if (dist < 0) None else Some(dist)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Uninitialized(val undefined: Seq[Undefined], override val toString: String) extends Exception(toString)
|
final class Uninitialized(val undefined: Seq[Undefined], override val toString: String)
|
||||||
|
extends Exception(toString)
|
||||||
|
|
||||||
final class Undefined private[sbt] (val defining: Setting[_], val referencedKey: ScopedKey[_])
|
final class Undefined private[sbt] (val defining: Setting[_], val referencedKey: ScopedKey[_])
|
||||||
final class RuntimeUndefined(val undefined: Seq[Undefined]) extends RuntimeException("References to undefined settings at runtime.") {
|
|
||||||
|
final class RuntimeUndefined(val undefined: Seq[Undefined])
|
||||||
|
extends RuntimeException("References to undefined settings at runtime.") {
|
||||||
override def getMessage =
|
override def getMessage =
|
||||||
super.getMessage + undefined.map { u =>
|
super.getMessage + undefined.map { u =>
|
||||||
"\n" + u.defining + " referenced from " + u.referencedKey
|
"\n" + u.defining + " referenced from " + u.referencedKey
|
||||||
}.mkString
|
}.mkString
|
||||||
}
|
}
|
||||||
|
|
||||||
def Undefined(defining: Setting[_], referencedKey: ScopedKey[_]): Undefined = new Undefined(defining, referencedKey)
|
def Undefined(defining: Setting[_], referencedKey: ScopedKey[_]): Undefined =
|
||||||
def Uninitialized(validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope], keys: Seq[Undefined], runtime: Boolean)(implicit display: Show[ScopedKey[_]]): Uninitialized =
|
new Undefined(defining, referencedKey)
|
||||||
{
|
|
||||||
|
def Uninitialized(
|
||||||
|
validKeys: Seq[ScopedKey[_]],
|
||||||
|
delegates: Scope => Seq[Scope],
|
||||||
|
keys: Seq[Undefined],
|
||||||
|
runtime: Boolean
|
||||||
|
)(implicit display: Show[ScopedKey[_]]): Uninitialized = {
|
||||||
assert(keys.nonEmpty)
|
assert(keys.nonEmpty)
|
||||||
val suffix = if (keys.length > 1) "s" else ""
|
val suffix = if (keys.length > 1) "s" else ""
|
||||||
val prefix = if (runtime) "Runtime reference" else "Reference"
|
val prefix = if (runtime) "Runtime reference" else "Reference"
|
||||||
val keysString = keys.map(u => showUndefined(u, validKeys, delegates)).mkString("\n\n ", "\n\n ", "")
|
val keysString =
|
||||||
new Uninitialized(keys, prefix + suffix + " to undefined setting" + suffix + ": " + keysString + "\n ")
|
keys.map(u => showUndefined(u, validKeys, delegates)).mkString("\n\n ", "\n\n ", "")
|
||||||
|
new Uninitialized(
|
||||||
|
keys,
|
||||||
|
prefix + suffix + " to undefined setting" + suffix + ": " + keysString + "\n ")
|
||||||
}
|
}
|
||||||
final class Compiled[T](val key: ScopedKey[T], val dependencies: Iterable[ScopedKey[_]], val settings: Seq[Setting[T]]) {
|
|
||||||
|
final class Compiled[T](
|
||||||
|
val key: ScopedKey[T],
|
||||||
|
val dependencies: Iterable[ScopedKey[_]],
|
||||||
|
val settings: Seq[Setting[T]]
|
||||||
|
) {
|
||||||
override def toString = showFullKey.show(key)
|
override def toString = showFullKey.show(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Flattened(val key: ScopedKey[_], val dependencies: Iterable[ScopedKey[_]])
|
final class Flattened(val key: ScopedKey[_], val dependencies: Iterable[ScopedKey[_]])
|
||||||
|
|
||||||
def flattenLocals(compiled: CompiledMap): Map[ScopedKey[_], Flattened] =
|
def flattenLocals(compiled: CompiledMap): Map[ScopedKey[_], Flattened] = {
|
||||||
{
|
val locals = compiled flatMap {
|
||||||
val locals = compiled flatMap { case (key, comp) => if (key.key.isLocal) Seq[Compiled[_]](comp) else Nil }
|
case (key, comp) => if (key.key.isLocal) Seq[Compiled[_]](comp) else Nil
|
||||||
val ordered = Dag.topologicalSort(locals)(_.dependencies.flatMap(dep => if (dep.key.isLocal) Seq[Compiled[_]](compiled(dep)) else Nil))
|
}
|
||||||
def flatten(cmap: Map[ScopedKey[_], Flattened], key: ScopedKey[_], deps: Iterable[ScopedKey[_]]): Flattened =
|
val ordered = Dag.topologicalSort(locals)(_.dependencies.flatMap(dep =>
|
||||||
new Flattened(key, deps.flatMap(dep => if (dep.key.isLocal) cmap(dep).dependencies else dep :: Nil))
|
if (dep.key.isLocal) Seq[Compiled[_]](compiled(dep)) else Nil))
|
||||||
|
def flatten(
|
||||||
|
cmap: Map[ScopedKey[_], Flattened],
|
||||||
|
key: ScopedKey[_],
|
||||||
|
deps: Iterable[ScopedKey[_]]
|
||||||
|
): Flattened =
|
||||||
|
new Flattened(
|
||||||
|
key,
|
||||||
|
deps.flatMap(dep => if (dep.key.isLocal) cmap(dep).dependencies else dep :: Nil))
|
||||||
|
|
||||||
val empty = Map.empty[ScopedKey[_], Flattened]
|
val empty = Map.empty[ScopedKey[_], Flattened]
|
||||||
val flattenedLocals = (empty /: ordered) { (cmap, c) => cmap.updated(c.key, flatten(cmap, c.key, c.dependencies)) }
|
|
||||||
|
val flattenedLocals = (empty /: ordered) { (cmap, c) =>
|
||||||
|
cmap.updated(c.key, flatten(cmap, c.key, c.dependencies))
|
||||||
|
}
|
||||||
|
|
||||||
compiled flatMap {
|
compiled flatMap {
|
||||||
case (key, comp) =>
|
case (key, comp) =>
|
||||||
if (key.key.isLocal)
|
if (key.key.isLocal)
|
||||||
|
|
@ -278,11 +400,12 @@ trait Init[Scope] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def definedAtString(settings: Seq[Setting[_]]): String =
|
def definedAtString(settings: Seq[Setting[_]]): String = {
|
||||||
{
|
|
||||||
val posDefined = settings.flatMap(_.positionString.toList)
|
val posDefined = settings.flatMap(_.positionString.toList)
|
||||||
if (posDefined.nonEmpty) {
|
if (posDefined.nonEmpty) {
|
||||||
val header = if (posDefined.size == settings.size) "defined at:" else
|
val header =
|
||||||
|
if (posDefined.size == settings.size) "defined at:"
|
||||||
|
else
|
||||||
"some of the defining occurrences:"
|
"some of the defining occurrences:"
|
||||||
header + (posDefined.distinct mkString ("\n\t", "\n\t", "\n"))
|
header + (posDefined.distinct mkString ("\n\t", "\n\t", "\n"))
|
||||||
} else ""
|
} else ""
|
||||||
|
|
@ -291,13 +414,16 @@ trait Init[Scope] {
|
||||||
/**
|
/**
|
||||||
* Intersects two scopes, returning the more specific one if they intersect, or None otherwise.
|
* Intersects two scopes, returning the more specific one if they intersect, or None otherwise.
|
||||||
*/
|
*/
|
||||||
private[sbt] def intersect(s1: Scope, s2: Scope)(implicit delegates: Scope => Seq[Scope]): Option[Scope] =
|
private[sbt] def intersect(s1: Scope, s2: Scope)(
|
||||||
|
implicit delegates: Scope => Seq[Scope]): Option[Scope] =
|
||||||
if (delegates(s1).contains(s2)) Some(s1) // s1 is more specific
|
if (delegates(s1).contains(s2)) Some(s1) // s1 is more specific
|
||||||
else if (delegates(s2).contains(s1)) Some(s2) // s2 is more specific
|
else if (delegates(s2).contains(s1)) Some(s2) // s2 is more specific
|
||||||
else None
|
else None
|
||||||
|
|
||||||
private[this] def deriveAndLocal(init: Seq[Setting[_]])(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal): Seq[Setting[_]] =
|
private[this] def deriveAndLocal(init: Seq[Setting[_]])(
|
||||||
{
|
implicit delegates: Scope => Seq[Scope],
|
||||||
|
scopeLocal: ScopeLocal
|
||||||
|
): Seq[Setting[_]] = {
|
||||||
import collection.mutable
|
import collection.mutable
|
||||||
|
|
||||||
final class Derived(val setting: DerivedSetting[_]) {
|
final class Derived(val setting: DerivedSetting[_]) {
|
||||||
|
|
@ -306,14 +432,18 @@ trait Init[Scope] {
|
||||||
val inScopes = new mutable.HashSet[Scope]
|
val inScopes = new mutable.HashSet[Scope]
|
||||||
val outputs = new mutable.ListBuffer[Setting[_]]
|
val outputs = new mutable.ListBuffer[Setting[_]]
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Deriveds(val key: AttributeKey[_], val settings: mutable.ListBuffer[Derived]) {
|
final class Deriveds(val key: AttributeKey[_], val settings: mutable.ListBuffer[Derived]) {
|
||||||
def dependencies = settings.flatMap(_.dependencies)
|
def dependencies = settings.flatMap(_.dependencies)
|
||||||
// This is mainly for use in the cyclic reference error message
|
// This is mainly for use in the cyclic reference error message
|
||||||
override def toString = s"Derived settings for ${key.label}, ${definedAtString(settings.map(_.setting))}"
|
override def toString =
|
||||||
|
s"Derived settings for ${key.label}, ${definedAtString(settings.map(_.setting))}"
|
||||||
}
|
}
|
||||||
|
|
||||||
// separate `derived` settings from normal settings (`defs`)
|
// separate `derived` settings from normal settings (`defs`)
|
||||||
val (derived, rawDefs) = Util.separate[Setting[_], Derived, Setting[_]](init) { case d: DerivedSetting[_] => Left(new Derived(d)); case s => Right(s) }
|
val (derived, rawDefs) = Util.separate[Setting[_], Derived, Setting[_]](init) {
|
||||||
|
case d: DerivedSetting[_] => Left(new Derived(d)); case s => Right(s)
|
||||||
|
}
|
||||||
val defs = addLocal(rawDefs)(scopeLocal)
|
val defs = addLocal(rawDefs)(scopeLocal)
|
||||||
|
|
||||||
// group derived settings by the key they define
|
// group derived settings by the key they define
|
||||||
|
|
@ -330,7 +460,9 @@ trait Init[Scope] {
|
||||||
|
|
||||||
// Map a DerivedSetting[_] to the `Derived` struct wrapping it. Used to ultimately replace a DerivedSetting with
|
// Map a DerivedSetting[_] to the `Derived` struct wrapping it. Used to ultimately replace a DerivedSetting with
|
||||||
// the `Setting`s that were actually derived from it: `Derived.outputs`
|
// the `Setting`s that were actually derived from it: `Derived.outputs`
|
||||||
val derivedToStruct: Map[DerivedSetting[_], Derived] = (derived map { s => s.setting -> s }).toMap
|
val derivedToStruct: Map[DerivedSetting[_], Derived] = (derived map { s =>
|
||||||
|
s.setting -> s
|
||||||
|
}).toMap
|
||||||
|
|
||||||
// set of defined scoped keys, used to ensure a derived setting is only added if all dependencies are present
|
// set of defined scoped keys, used to ensure a derived setting is only added if all dependencies are present
|
||||||
val defined = new mutable.HashSet[ScopedKey[_]]
|
val defined = new mutable.HashSet[ScopedKey[_]]
|
||||||
|
|
@ -392,7 +524,10 @@ trait Init[Scope] {
|
||||||
// Take all the original defs and DerivedSettings along with locals, replace each DerivedSetting with the actual
|
// Take all the original defs and DerivedSettings along with locals, replace each DerivedSetting with the actual
|
||||||
// settings that were derived.
|
// settings that were derived.
|
||||||
val allDefs = addLocal(init)(scopeLocal)
|
val allDefs = addLocal(init)(scopeLocal)
|
||||||
allDefs flatMap { case d: DerivedSetting[_] => (derivedToStruct get d map (_.outputs)).toStream.flatten; case s => Stream(s) }
|
allDefs flatMap {
|
||||||
|
case d: DerivedSetting[_] => (derivedToStruct get d map (_.outputs)).toStream.flatten;
|
||||||
|
case s => Stream(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait Initialize[T] {
|
sealed trait Initialize[T] {
|
||||||
|
|
@ -401,8 +536,11 @@ trait Init[Scope] {
|
||||||
|
|
||||||
private[sbt] def mapReferenced(g: MapScoped): Initialize[T]
|
private[sbt] def mapReferenced(g: MapScoped): Initialize[T]
|
||||||
private[sbt] def mapConstant(g: MapConstant): Initialize[T]
|
private[sbt] def mapConstant(g: MapConstant): Initialize[T]
|
||||||
|
|
||||||
private[sbt] def validateReferenced(g: ValidateRef): ValidatedInit[T] =
|
private[sbt] def validateReferenced(g: ValidateRef): ValidatedInit[T] =
|
||||||
validateKeyReferenced(new ValidateKeyRef { def apply[B](key: ScopedKey[B], selfRefOk: Boolean) = g(key) })
|
validateKeyReferenced(new ValidateKeyRef {
|
||||||
|
def apply[B](key: ScopedKey[B], selfRefOk: Boolean) = g(key)
|
||||||
|
})
|
||||||
|
|
||||||
private[sbt] def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T]
|
private[sbt] def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T]
|
||||||
|
|
||||||
|
|
@ -411,33 +549,50 @@ trait Init[Scope] {
|
||||||
def zipWith[S, U](o: Initialize[S])(f: (T, S) => U): Initialize[U] = zipTupled(o)(f.tupled)
|
def zipWith[S, U](o: Initialize[S])(f: (T, S) => U): Initialize[U] = zipTupled(o)(f.tupled)
|
||||||
private[this] def zipTupled[S, U](o: Initialize[S])(f: ((T, S)) => U): Initialize[U] =
|
private[this] def zipTupled[S, U](o: Initialize[S])(f: ((T, S)) => U): Initialize[U] =
|
||||||
new Apply[({ type l[L[x]] = (L[T], L[S]) })#l, U](f, (this, o), AList.tuple2[T, S])
|
new Apply[({ type l[L[x]] = (L[T], L[S]) })#l, U](f, (this, o), AList.tuple2[T, S])
|
||||||
|
|
||||||
/** A fold on the static attributes of this and nested Initializes. */
|
/** A fold on the static attributes of this and nested Initializes. */
|
||||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S
|
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S
|
||||||
}
|
}
|
||||||
|
|
||||||
object Initialize {
|
object Initialize {
|
||||||
implicit def joinInitialize[T](s: Seq[Initialize[T]]): JoinInitSeq[T] = new JoinInitSeq(s)
|
implicit def joinInitialize[T](s: Seq[Initialize[T]]): JoinInitSeq[T] = new JoinInitSeq(s)
|
||||||
|
|
||||||
final class JoinInitSeq[T](s: Seq[Initialize[T]]) {
|
final class JoinInitSeq[T](s: Seq[Initialize[T]]) {
|
||||||
def joinWith[S](f: Seq[T] => S): Initialize[S] = uniform(s)(f)
|
def joinWith[S](f: Seq[T] => S): Initialize[S] = uniform(s)(f)
|
||||||
def join: Initialize[Seq[T]] = uniform(s)(idFun)
|
def join: Initialize[Seq[T]] = uniform(s)(idFun)
|
||||||
}
|
}
|
||||||
|
|
||||||
def join[T](inits: Seq[Initialize[T]]): Initialize[Seq[T]] = uniform(inits)(idFun)
|
def join[T](inits: Seq[Initialize[T]]): Initialize[Seq[T]] = uniform(inits)(idFun)
|
||||||
|
|
||||||
def joinAny[M[_]](inits: Seq[Initialize[M[T]] forSome { type T }]): Initialize[Seq[M[_]]] =
|
def joinAny[M[_]](inits: Seq[Initialize[M[T]] forSome { type T }]): Initialize[Seq[M[_]]] =
|
||||||
join(inits.asInstanceOf[Seq[Initialize[M[Any]]]]).asInstanceOf[Initialize[Seq[M[T] forSome { type T }]]]
|
join(inits.asInstanceOf[Seq[Initialize[M[Any]]]])
|
||||||
|
.asInstanceOf[Initialize[Seq[M[T] forSome { type T }]]]
|
||||||
}
|
}
|
||||||
|
|
||||||
object SettingsDefinition {
|
object SettingsDefinition {
|
||||||
implicit def unwrapSettingsDefinition(d: SettingsDefinition): Seq[Setting[_]] = d.settings
|
implicit def unwrapSettingsDefinition(d: SettingsDefinition): Seq[Setting[_]] = d.settings
|
||||||
implicit def wrapSettingsDefinition(ss: Seq[Setting[_]]): SettingsDefinition = new SettingList(ss)
|
implicit def wrapSettingsDefinition(ss: Seq[Setting[_]]): SettingsDefinition =
|
||||||
|
new SettingList(ss)
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait SettingsDefinition {
|
sealed trait SettingsDefinition {
|
||||||
def settings: Seq[Setting[_]]
|
def settings: Seq[Setting[_]]
|
||||||
}
|
}
|
||||||
|
|
||||||
final class SettingList(val settings: Seq[Setting[_]]) extends SettingsDefinition
|
final class SettingList(val settings: Seq[Setting[_]]) extends SettingsDefinition
|
||||||
sealed class Setting[T] private[Init] (val key: ScopedKey[T], val init: Initialize[T], val pos: SourcePosition) extends SettingsDefinition {
|
|
||||||
|
sealed class Setting[T] private[Init] (
|
||||||
|
val key: ScopedKey[T],
|
||||||
|
val init: Initialize[T],
|
||||||
|
val pos: SourcePosition
|
||||||
|
) extends SettingsDefinition {
|
||||||
def settings = this :: Nil
|
def settings = this :: Nil
|
||||||
def definitive: Boolean = !init.dependencies.contains(key)
|
def definitive: Boolean = !init.dependencies.contains(key)
|
||||||
def dependencies: Seq[ScopedKey[_]] = remove(init.dependencies, key)
|
def dependencies: Seq[ScopedKey[_]] = remove(init.dependencies, key)
|
||||||
def mapReferenced(g: MapScoped): Setting[T] = make(key, init mapReferenced g, pos)
|
def mapReferenced(g: MapScoped): Setting[T] = make(key, init mapReferenced g, pos)
|
||||||
def validateReferenced(g: ValidateRef): Either[Seq[Undefined], Setting[T]] = (init validateReferenced g).right.map(newI => make(key, newI, pos))
|
|
||||||
|
def validateReferenced(g: ValidateRef): Either[Seq[Undefined], Setting[T]] =
|
||||||
|
(init validateReferenced g).right.map(newI => make(key, newI, pos))
|
||||||
|
|
||||||
private[sbt] def validateKeyReferenced(g: ValidateKeyRef): Either[Seq[Undefined], Setting[T]] =
|
private[sbt] def validateKeyReferenced(g: ValidateKeyRef): Either[Seq[Undefined], Setting[T]] =
|
||||||
(init validateKeyReferenced g).right.map(newI => make(key, newI, pos))
|
(init validateKeyReferenced g).right.map(newI => make(key, newI, pos))
|
||||||
|
|
@ -446,38 +601,72 @@ trait Init[Scope] {
|
||||||
def mapInit(f: (ScopedKey[T], T) => T): Setting[T] = make(key, init(t => f(key, t)), pos)
|
def mapInit(f: (ScopedKey[T], T) => T): Setting[T] = make(key, init(t => f(key, t)), pos)
|
||||||
def mapConstant(g: MapConstant): Setting[T] = make(key, init mapConstant g, pos)
|
def mapConstant(g: MapConstant): Setting[T] = make(key, init mapConstant g, pos)
|
||||||
def withPos(pos: SourcePosition) = make(key, init, pos)
|
def withPos(pos: SourcePosition) = make(key, init, pos)
|
||||||
|
|
||||||
def positionString: Option[String] = pos match {
|
def positionString: Option[String] = pos match {
|
||||||
case pos: FilePosition => Some(pos.path + ":" + pos.startLine)
|
case pos: FilePosition => Some(pos.path + ":" + pos.startLine)
|
||||||
case NoPosition => None
|
case NoPosition => None
|
||||||
}
|
}
|
||||||
private[sbt] def mapInitialize(f: Initialize[T] => Initialize[T]): Setting[T] = make(key, f(init), pos)
|
|
||||||
|
private[sbt] def mapInitialize(f: Initialize[T] => Initialize[T]): Setting[T] =
|
||||||
|
make(key, f(init), pos)
|
||||||
|
|
||||||
override def toString = "setting(" + key + ") at " + pos
|
override def toString = "setting(" + key + ") at " + pos
|
||||||
|
|
||||||
protected[this] def make[B](key: ScopedKey[B], init: Initialize[B], pos: SourcePosition): Setting[B] = new Setting[B](key, init, pos)
|
protected[this] def make[B](
|
||||||
|
key: ScopedKey[B],
|
||||||
|
init: Initialize[B],
|
||||||
|
pos: SourcePosition
|
||||||
|
): Setting[B] = new Setting[B](key, init, pos)
|
||||||
|
|
||||||
protected[sbt] def isDerived: Boolean = false
|
protected[sbt] def isDerived: Boolean = false
|
||||||
private[sbt] def setScope(s: Scope): Setting[T] = make(key.copy(scope = s), init.mapReferenced(mapScope(const(s))), pos)
|
private[sbt] def setScope(s: Scope): Setting[T] =
|
||||||
|
make(key.copy(scope = s), init.mapReferenced(mapScope(const(s))), pos)
|
||||||
|
|
||||||
/** Turn this setting into a `DefaultSetting` if it's not already, otherwise returns `this` */
|
/** Turn this setting into a `DefaultSetting` if it's not already, otherwise returns `this` */
|
||||||
private[sbt] def default(id: => Long = nextDefaultID()): DefaultSetting[T] = DefaultSetting(key, init, pos, id)
|
private[sbt] def default(id: => Long = nextDefaultID()): DefaultSetting[T] =
|
||||||
|
DefaultSetting(key, init, pos, id)
|
||||||
}
|
}
|
||||||
private[Init] sealed class DerivedSetting[T](sk: ScopedKey[T], i: Initialize[T], p: SourcePosition, val filter: Scope => Boolean, val trigger: AttributeKey[_] => Boolean) extends Setting[T](sk, i, p) {
|
|
||||||
override def make[B](key: ScopedKey[B], init: Initialize[B], pos: SourcePosition): Setting[B] = new DerivedSetting[B](key, init, pos, filter, trigger)
|
private[Init] sealed class DerivedSetting[T](
|
||||||
|
sk: ScopedKey[T],
|
||||||
|
i: Initialize[T],
|
||||||
|
p: SourcePosition,
|
||||||
|
val filter: Scope => Boolean,
|
||||||
|
val trigger: AttributeKey[_] => Boolean
|
||||||
|
) extends Setting[T](sk, i, p) {
|
||||||
|
|
||||||
|
override def make[B](key: ScopedKey[B], init: Initialize[B], pos: SourcePosition): Setting[B] =
|
||||||
|
new DerivedSetting[B](key, init, pos, filter, trigger)
|
||||||
|
|
||||||
protected[sbt] override def isDerived: Boolean = true
|
protected[sbt] override def isDerived: Boolean = true
|
||||||
override def default(_id: => Long): DefaultSetting[T] = new DerivedSetting[T](sk, i, p, filter, trigger) with DefaultSetting[T] { val id = _id }
|
|
||||||
|
override def default(_id: => Long): DefaultSetting[T] =
|
||||||
|
new DerivedSetting[T](sk, i, p, filter, trigger) with DefaultSetting[T] { val id = _id }
|
||||||
|
|
||||||
override def toString = "derived " + super.toString
|
override def toString = "derived " + super.toString
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only keep the first occurrence of this setting and move it to the front so that it has lower precedence than non-defaults.
|
// Only keep the first occurrence of this setting and move it to the front so that it has lower precedence than non-defaults.
|
||||||
// This is intended for internal sbt use only, where alternatives like Plugin.globalSettings are not available.
|
// This is intended for internal sbt use only, where alternatives like Plugin.globalSettings are not available.
|
||||||
private[Init] sealed trait DefaultSetting[T] extends Setting[T] {
|
private[Init] sealed trait DefaultSetting[T] extends Setting[T] {
|
||||||
val id: Long
|
val id: Long
|
||||||
override def make[B](key: ScopedKey[B], init: Initialize[B], pos: SourcePosition): Setting[B] = super.make(key, init, pos) default id
|
|
||||||
|
override def make[B](key: ScopedKey[B], init: Initialize[B], pos: SourcePosition): Setting[B] =
|
||||||
|
super.make(key, init, pos) default id
|
||||||
|
|
||||||
override final def hashCode = id.hashCode
|
override final def hashCode = id.hashCode
|
||||||
override final def equals(o: Any): Boolean = o match { case d: DefaultSetting[_] => d.id == id; case _ => false }
|
|
||||||
|
override final def equals(o: Any): Boolean = o match {
|
||||||
|
case d: DefaultSetting[_] => d.id == id; case _ => false
|
||||||
|
}
|
||||||
|
|
||||||
override def toString = s"default($id) " + super.toString
|
override def toString = s"default($id) " + super.toString
|
||||||
override def default(id: => Long) = this
|
override def default(id: => Long) = this
|
||||||
}
|
}
|
||||||
|
|
||||||
object DefaultSetting {
|
object DefaultSetting {
|
||||||
def apply[T](sk: ScopedKey[T], i: Initialize[T], p: SourcePosition, _id: Long) = new Setting[T](sk, i, p) with DefaultSetting[T] { val id = _id }
|
def apply[T](sk: ScopedKey[T], i: Initialize[T], p: SourcePosition, _id: Long) =
|
||||||
|
new Setting[T](sk, i, p) with DefaultSetting[T] { val id = _id }
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def handleUndefined[T](vr: ValidatedInit[T]): Initialize[T] = vr match {
|
private[this] def handleUndefined[T](vr: ValidatedInit[T]): Initialize[T] = vr match {
|
||||||
|
|
@ -490,7 +679,9 @@ trait Init[Scope] {
|
||||||
|
|
||||||
// mainly for reducing generated class count
|
// mainly for reducing generated class count
|
||||||
private[this] def validateKeyReferencedT(g: ValidateKeyRef) =
|
private[this] def validateKeyReferencedT(g: ValidateKeyRef) =
|
||||||
new (Initialize ~> ValidatedInit) { def apply[T](i: Initialize[T]) = i validateKeyReferenced g }
|
new (Initialize ~> ValidatedInit) {
|
||||||
|
def apply[T](i: Initialize[T]) = i validateKeyReferenced g
|
||||||
|
}
|
||||||
|
|
||||||
private[this] def mapReferencedT(g: MapScoped) =
|
private[this] def mapReferencedT(g: MapScoped) =
|
||||||
new (Initialize ~> Initialize) { def apply[T](i: Initialize[T]) = i mapReferenced g }
|
new (Initialize ~> Initialize) { def apply[T](i: Initialize[T]) = i mapReferenced g }
|
||||||
|
|
@ -506,40 +697,55 @@ trait Init[Scope] {
|
||||||
sealed trait Keyed[S, T] extends Initialize[T] {
|
sealed trait Keyed[S, T] extends Initialize[T] {
|
||||||
def scopedKey: ScopedKey[S]
|
def scopedKey: ScopedKey[S]
|
||||||
def transform: S => T
|
def transform: S => T
|
||||||
|
|
||||||
final def dependencies = scopedKey :: Nil
|
final def dependencies = scopedKey :: Nil
|
||||||
final def apply[Z](g: T => Z): Initialize[Z] = new GetValue(scopedKey, g compose transform)
|
final def apply[Z](g: T => Z): Initialize[Z] = new GetValue(scopedKey, g compose transform)
|
||||||
final def evaluate(ss: Settings[Scope]): T = transform(getValue(ss, scopedKey))
|
final def evaluate(ss: Settings[Scope]): T = transform(getValue(ss, scopedKey))
|
||||||
final def mapReferenced(g: MapScoped): Initialize[T] = new GetValue(g(scopedKey), transform)
|
final def mapReferenced(g: MapScoped): Initialize[T] = new GetValue(g(scopedKey), transform)
|
||||||
private[sbt] final def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T] = g(scopedKey, false) match {
|
|
||||||
|
private[sbt] final def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T] =
|
||||||
|
g(scopedKey, false) match {
|
||||||
case Left(un) => Left(un :: Nil)
|
case Left(un) => Left(un :: Nil)
|
||||||
case Right(nk) => Right(new GetValue(nk, transform))
|
case Right(nk) => Right(new GetValue(nk, transform))
|
||||||
}
|
}
|
||||||
|
|
||||||
final def mapConstant(g: MapConstant): Initialize[T] = g(scopedKey) match {
|
final def mapConstant(g: MapConstant): Initialize[T] = g(scopedKey) match {
|
||||||
case None => this
|
case None => this
|
||||||
case Some(const) => new Value(() => transform(const))
|
case Some(const) => new Value(() => transform(const))
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] def processAttributes[B](init: B)(f: (B, AttributeMap) => B): B = init
|
private[sbt] def processAttributes[B](init: B)(f: (B, AttributeMap) => B): B = init
|
||||||
}
|
}
|
||||||
private[this] final class GetValue[S, T](val scopedKey: ScopedKey[S], val transform: S => T) extends Keyed[S, T]
|
|
||||||
|
private[this] final class GetValue[S, T](val scopedKey: ScopedKey[S], val transform: S => T)
|
||||||
|
extends Keyed[S, T]
|
||||||
|
|
||||||
trait KeyedInitialize[T] extends Keyed[T, T] {
|
trait KeyedInitialize[T] extends Keyed[T, T] {
|
||||||
final val transform = idFun[T]
|
final val transform = idFun[T]
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] final class TransformCapture(val f: Initialize ~> Initialize) extends Initialize[Initialize ~> Initialize] {
|
private[sbt] final class TransformCapture(val f: Initialize ~> Initialize)
|
||||||
|
extends Initialize[Initialize ~> Initialize] {
|
||||||
def dependencies = Nil
|
def dependencies = Nil
|
||||||
def apply[Z](g2: (Initialize ~> Initialize) => Z): Initialize[Z] = map(this)(g2)
|
def apply[Z](g2: (Initialize ~> Initialize) => Z): Initialize[Z] = map(this)(g2)
|
||||||
def evaluate(ss: Settings[Scope]): Initialize ~> Initialize = f
|
def evaluate(ss: Settings[Scope]): Initialize ~> Initialize = f
|
||||||
def mapReferenced(g: MapScoped) = new TransformCapture(mapReferencedT(g) ∙ f)
|
def mapReferenced(g: MapScoped) = new TransformCapture(mapReferencedT(g) ∙ f)
|
||||||
def mapConstant(g: MapConstant) = new TransformCapture(mapConstantT(g) ∙ f)
|
def mapConstant(g: MapConstant) = new TransformCapture(mapConstantT(g) ∙ f)
|
||||||
def validateKeyReferenced(g: ValidateKeyRef) = Right(new TransformCapture(getValidated ∙ validateKeyReferencedT(g) ∙ f))
|
|
||||||
|
def validateKeyReferenced(g: ValidateKeyRef) =
|
||||||
|
Right(new TransformCapture(getValidated ∙ validateKeyReferencedT(g) ∙ f))
|
||||||
|
|
||||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init
|
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init
|
||||||
}
|
}
|
||||||
private[sbt] final class ValidationCapture[T](val key: ScopedKey[T], val selfRefOk: Boolean) extends Initialize[ScopedKey[T]] {
|
|
||||||
|
private[sbt] final class ValidationCapture[T](val key: ScopedKey[T], val selfRefOk: Boolean)
|
||||||
|
extends Initialize[ScopedKey[T]] {
|
||||||
def dependencies = Nil
|
def dependencies = Nil
|
||||||
def apply[Z](g2: ScopedKey[T] => Z): Initialize[Z] = map(this)(g2)
|
def apply[Z](g2: ScopedKey[T] => Z): Initialize[Z] = map(this)(g2)
|
||||||
def evaluate(ss: Settings[Scope]) = key
|
def evaluate(ss: Settings[Scope]) = key
|
||||||
def mapReferenced(g: MapScoped) = new ValidationCapture(g(key), selfRefOk)
|
def mapReferenced(g: MapScoped) = new ValidationCapture(g(key), selfRefOk)
|
||||||
def mapConstant(g: MapConstant) = this
|
def mapConstant(g: MapConstant) = this
|
||||||
|
|
||||||
def validateKeyReferenced(g: ValidateKeyRef) = g(key, selfRefOk) match {
|
def validateKeyReferenced(g: ValidateKeyRef) = g(key, selfRefOk) match {
|
||||||
case Left(un) => Left(un :: Nil)
|
case Left(un) => Left(un :: Nil)
|
||||||
case Right(k) => Right(new ValidationCapture(k, selfRefOk))
|
case Right(k) => Right(new ValidationCapture(k, selfRefOk))
|
||||||
|
|
@ -547,34 +753,50 @@ trait Init[Scope] {
|
||||||
|
|
||||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init
|
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init
|
||||||
}
|
}
|
||||||
private[sbt] final class Bind[S, T](val f: S => Initialize[T], val in: Initialize[S]) extends Initialize[T] {
|
|
||||||
|
private[sbt] final class Bind[S, T](val f: S => Initialize[T], val in: Initialize[S])
|
||||||
|
extends Initialize[T] {
|
||||||
def dependencies = in.dependencies
|
def dependencies = in.dependencies
|
||||||
def apply[Z](g: T => Z): Initialize[Z] = new Bind[S, Z](s => f(s)(g), in)
|
def apply[Z](g: T => Z): Initialize[Z] = new Bind[S, Z](s => f(s)(g), in)
|
||||||
def evaluate(ss: Settings[Scope]): T = f(in evaluate ss) evaluate ss
|
def evaluate(ss: Settings[Scope]): T = f(in evaluate ss) evaluate ss
|
||||||
def mapReferenced(g: MapScoped) = new Bind[S, T](s => f(s) mapReferenced g, in mapReferenced g)
|
def mapReferenced(g: MapScoped) = new Bind[S, T](s => f(s) mapReferenced g, in mapReferenced g)
|
||||||
def validateKeyReferenced(g: ValidateKeyRef) = (in validateKeyReferenced g).right.map { validIn =>
|
|
||||||
|
def validateKeyReferenced(g: ValidateKeyRef) = (in validateKeyReferenced g).right.map {
|
||||||
|
validIn =>
|
||||||
new Bind[S, T](s => handleUndefined(f(s) validateKeyReferenced g), validIn)
|
new Bind[S, T](s => handleUndefined(f(s) validateKeyReferenced g), validIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
def mapConstant(g: MapConstant) = new Bind[S, T](s => f(s) mapConstant g, in mapConstant g)
|
def mapConstant(g: MapConstant) = new Bind[S, T](s => f(s) mapConstant g, in mapConstant g)
|
||||||
private[sbt] def processAttributes[B](init: B)(f: (B, AttributeMap) => B): B = in.processAttributes(init)(f)
|
|
||||||
|
private[sbt] def processAttributes[B](init: B)(f: (B, AttributeMap) => B): B =
|
||||||
|
in.processAttributes(init)(f)
|
||||||
}
|
}
|
||||||
private[sbt] final class Optional[S, T](val a: Option[Initialize[S]], val f: Option[S] => T) extends Initialize[T] {
|
|
||||||
|
private[sbt] final class Optional[S, T](val a: Option[Initialize[S]], val f: Option[S] => T)
|
||||||
|
extends Initialize[T] {
|
||||||
def dependencies = deps(a.toList)
|
def dependencies = deps(a.toList)
|
||||||
def apply[Z](g: T => Z): Initialize[Z] = new Optional[S, Z](a, g compose f)
|
def apply[Z](g: T => Z): Initialize[Z] = new Optional[S, Z](a, g compose f)
|
||||||
def mapReferenced(g: MapScoped) = new Optional(a map mapReferencedT(g).fn, f)
|
def mapReferenced(g: MapScoped) = new Optional(a map mapReferencedT(g).fn, f)
|
||||||
|
|
||||||
def validateKeyReferenced(g: ValidateKeyRef) = a match {
|
def validateKeyReferenced(g: ValidateKeyRef) = a match {
|
||||||
case None => Right(this)
|
case None => Right(this)
|
||||||
case Some(i) => Right(new Optional(i.validateKeyReferenced(g).right.toOption, f))
|
case Some(i) => Right(new Optional(i.validateKeyReferenced(g).right.toOption, f))
|
||||||
}
|
}
|
||||||
|
|
||||||
def mapConstant(g: MapConstant): Initialize[T] = new Optional(a map mapConstantT(g).fn, f)
|
def mapConstant(g: MapConstant): Initialize[T] = new Optional(a map mapConstantT(g).fn, f)
|
||||||
def evaluate(ss: Settings[Scope]): T = f(a.flatMap(i => trapBadRef(evaluateT(ss)(i))))
|
def evaluate(ss: Settings[Scope]): T = f(a.flatMap(i => trapBadRef(evaluateT(ss)(i))))
|
||||||
|
|
||||||
// proper solution is for evaluate to be deprecated or for external use only and a new internal method returning Either be used
|
// proper solution is for evaluate to be deprecated or for external use only and a new internal method returning Either be used
|
||||||
private[this] def trapBadRef[A](run: => A): Option[A] = try Some(run) catch { case e: InvalidReference => None }
|
private[this] def trapBadRef[A](run: => A): Option[A] =
|
||||||
|
try Some(run)
|
||||||
|
catch { case e: InvalidReference => None }
|
||||||
|
|
||||||
private[sbt] def processAttributes[B](init: B)(f: (B, AttributeMap) => B): B = a match {
|
private[sbt] def processAttributes[B](init: B)(f: (B, AttributeMap) => B): B = a match {
|
||||||
case None => init
|
case None => init
|
||||||
case Some(i) => i.processAttributes(init)(f)
|
case Some(i) => i.processAttributes(init)(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] final class Value[T](val value: () => T) extends Initialize[T] {
|
private[sbt] final class Value[T](val value: () => T) extends Initialize[T] {
|
||||||
def dependencies = Nil
|
def dependencies = Nil
|
||||||
def mapReferenced(g: MapScoped) = this
|
def mapReferenced(g: MapScoped) = this
|
||||||
|
|
@ -584,6 +806,7 @@ trait Init[Scope] {
|
||||||
def evaluate(map: Settings[Scope]): T = value()
|
def evaluate(map: Settings[Scope]): T = value()
|
||||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init
|
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] final object StaticScopes extends Initialize[Set[Scope]] {
|
private[sbt] final object StaticScopes extends Initialize[Set[Scope]] {
|
||||||
def dependencies = Nil
|
def dependencies = Nil
|
||||||
def mapReferenced(g: MapScoped) = this
|
def mapReferenced(g: MapScoped) = this
|
||||||
|
|
@ -593,23 +816,35 @@ trait Init[Scope] {
|
||||||
def evaluate(map: Settings[Scope]) = map.scopes
|
def evaluate(map: Settings[Scope]) = map.scopes
|
||||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init
|
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init
|
||||||
}
|
}
|
||||||
private[sbt] final class Apply[K[L[x]], T](val f: K[Id] => T, val inputs: K[Initialize], val alist: AList[K]) extends Initialize[T] {
|
|
||||||
|
private[sbt] final class Apply[K[L[x]], T](
|
||||||
|
val f: K[Id] => T,
|
||||||
|
val inputs: K[Initialize],
|
||||||
|
val alist: AList[K]
|
||||||
|
) extends Initialize[T] {
|
||||||
def dependencies = deps(alist.toList(inputs))
|
def dependencies = deps(alist.toList(inputs))
|
||||||
def mapReferenced(g: MapScoped) = mapInputs(mapReferencedT(g))
|
def mapReferenced(g: MapScoped) = mapInputs(mapReferencedT(g))
|
||||||
def apply[S](g: T => S) = new Apply(g compose f, inputs, alist)
|
def apply[S](g: T => S) = new Apply(g compose f, inputs, alist)
|
||||||
def mapConstant(g: MapConstant) = mapInputs(mapConstantT(g))
|
def mapConstant(g: MapConstant) = mapInputs(mapConstantT(g))
|
||||||
def mapInputs(g: Initialize ~> Initialize): Initialize[T] = new Apply(f, alist.transform(inputs, g), alist)
|
|
||||||
|
def mapInputs(g: Initialize ~> Initialize): Initialize[T] =
|
||||||
|
new Apply(f, alist.transform(inputs, g), alist)
|
||||||
|
|
||||||
def evaluate(ss: Settings[Scope]) = f(alist.transform(inputs, evaluateT(ss)))
|
def evaluate(ss: Settings[Scope]) = f(alist.transform(inputs, evaluateT(ss)))
|
||||||
def validateKeyReferenced(g: ValidateKeyRef) =
|
|
||||||
{
|
def validateKeyReferenced(g: ValidateKeyRef) = {
|
||||||
val tx = alist.transform(inputs, validateKeyReferencedT(g))
|
val tx = alist.transform(inputs, validateKeyReferencedT(g))
|
||||||
val undefs = alist.toList(tx).flatMap(_.left.toSeq.flatten)
|
val undefs = alist.toList(tx).flatMap(_.left.toSeq.flatten)
|
||||||
val get = new (ValidatedInit ~> Initialize) { def apply[B](vr: ValidatedInit[B]) = vr.right.get }
|
val get = new (ValidatedInit ~> Initialize) {
|
||||||
|
def apply[B](vr: ValidatedInit[B]) = vr.right.get
|
||||||
|
}
|
||||||
if (undefs.isEmpty) Right(new Apply(f, alist.transform(tx, get), alist)) else Left(undefs)
|
if (undefs.isEmpty) Right(new Apply(f, alist.transform(tx, get), alist)) else Left(undefs)
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S =
|
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S =
|
||||||
(init /: alist.toList(inputs)) { (v, i) => i.processAttributes(v)(f) }
|
(init /: alist.toList(inputs)) { (v, i) =>
|
||||||
|
i.processAttributes(v)(f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private def remove[T](s: Seq[T], v: T) = s filterNot (_ == v)
|
private def remove[T](s: Seq[T], v: T) = s filterNot (_ == v)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ package sbt.internal.util
|
||||||
object Signals {
|
object Signals {
|
||||||
val CONT = "CONT"
|
val CONT = "CONT"
|
||||||
val INT = "INT"
|
val INT = "INT"
|
||||||
def withHandler[T](handler: () => Unit, signal: String = INT)(action: () => T): T =
|
|
||||||
{
|
def withHandler[T](handler: () => Unit, signal: String = INT)(action: () => T): T = {
|
||||||
val result =
|
val result =
|
||||||
try {
|
try {
|
||||||
val signals = new Signals0
|
val signals = new Signals0
|
||||||
|
|
@ -21,6 +21,7 @@ object Signals {
|
||||||
sealed trait Registration {
|
sealed trait Registration {
|
||||||
def remove(): Unit
|
def remove(): Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a signal handler that can be removed later.
|
* Register a signal handler that can be removed later.
|
||||||
* NOTE: Does not stack with other signal handlers!!!!
|
* NOTE: Does not stack with other signal handlers!!!!
|
||||||
|
|
@ -60,17 +61,14 @@ object Signals {
|
||||||
// try { } catch { case e: LinkageError => ... }
|
// try { } catch { case e: LinkageError => ... }
|
||||||
// block to
|
// block to
|
||||||
private final class Signals0 {
|
private final class Signals0 {
|
||||||
def supported(signal: String): Boolean =
|
def supported(signal: String): Boolean = {
|
||||||
{
|
|
||||||
import sun.misc.Signal
|
import sun.misc.Signal
|
||||||
try { new Signal(signal); true }
|
try { new Signal(signal); true } catch { case e: IllegalArgumentException => false }
|
||||||
catch { case e: IllegalArgumentException => false }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns a LinkageError in `action` as Left(t) in order to avoid it being
|
// returns a LinkageError in `action` as Left(t) in order to avoid it being
|
||||||
// incorrectly swallowed as missing Signal/SignalHandler
|
// incorrectly swallowed as missing Signal/SignalHandler
|
||||||
def withHandler[T](signal: String, handler: () => Unit, action: () => T): Either[Throwable, T] =
|
def withHandler[T](signal: String, handler: () => Unit, action: () => T): Either[Throwable, T] = {
|
||||||
{
|
|
||||||
import sun.misc.{ Signal, SignalHandler }
|
import sun.misc.{ Signal, SignalHandler }
|
||||||
val intSignal = new Signal(signal)
|
val intSignal = new Signal(signal)
|
||||||
val newHandler = new SignalHandler {
|
val newHandler = new SignalHandler {
|
||||||
|
|
@ -80,7 +78,6 @@ private final class Signals0 {
|
||||||
val oldHandler = Signal.handle(intSignal, newHandler)
|
val oldHandler = Signal.handle(intSignal, newHandler)
|
||||||
|
|
||||||
try Right(action())
|
try Right(action())
|
||||||
catch { case e: LinkageError => Left(e) }
|
catch { case e: LinkageError => Left(e) } finally { Signal.handle(intSignal, oldHandler); () }
|
||||||
finally { Signal.handle(intSignal, oldHandler); () }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,10 @@ trait TypeFunctions {
|
||||||
|
|
||||||
def nestCon[M[_], N[_], G[_]](f: M ~> N): (M ∙ G)#l ~> (N ∙ G)#l =
|
def nestCon[M[_], N[_], G[_]](f: M ~> N): (M ∙ G)#l ~> (N ∙ G)#l =
|
||||||
f.asInstanceOf[(M ∙ G)#l ~> (N ∙ G)#l] // implemented with a cast to avoid extra object+method call. castless version:
|
f.asInstanceOf[(M ∙ G)#l ~> (N ∙ G)#l] // implemented with a cast to avoid extra object+method call. castless version:
|
||||||
|
|
||||||
/* new ( (M ∙ G)#l ~> (N ∙ G)#l ) {
|
/* new ( (M ∙ G)#l ~> (N ∙ G)#l ) {
|
||||||
def apply[T](mg: M[G[T]]): N[G[T]] = f(mg)
|
def apply[T](mg: M[G[T]]): N[G[T]] = f(mg)
|
||||||
}*/
|
} */
|
||||||
|
|
||||||
implicit def toFn1[A, B](f: A => B): Fn1[A, B] = new Fn1[A, B] {
|
implicit def toFn1[A, B](f: A => B): Fn1[A, B] = new Fn1[A, B] {
|
||||||
def ∙[C](g: C => A) = f compose g
|
def ∙[C](g: C => A) = f compose g
|
||||||
|
|
@ -31,6 +32,7 @@ trait TypeFunctions {
|
||||||
type Endo[T] = T => T
|
type Endo[T] = T => T
|
||||||
type ~>|[A[_], B[_]] = A ~> Compose[Option, B]#Apply
|
type ~>|[A[_], B[_]] = A ~> Compose[Option, B]#Apply
|
||||||
}
|
}
|
||||||
|
|
||||||
object TypeFunctions extends TypeFunctions
|
object TypeFunctions extends TypeFunctions
|
||||||
|
|
||||||
trait ~>[-A[_], +B[_]] { outer =>
|
trait ~>[-A[_], +B[_]] { outer =>
|
||||||
|
|
@ -40,11 +42,13 @@ trait ~>[-A[_], +B[_]] { outer =>
|
||||||
final def ∙[C, D](g: C => D)(implicit ev: D <:< A[D]): C => B[D] = i => apply(ev(g(i)))
|
final def ∙[C, D](g: C => D)(implicit ev: D <:< A[D]): C => B[D] = i => apply(ev(g(i)))
|
||||||
final def fn[T] = (t: A[T]) => apply[T](t)
|
final def fn[T] = (t: A[T]) => apply[T](t)
|
||||||
}
|
}
|
||||||
|
|
||||||
object ~> {
|
object ~> {
|
||||||
import TypeFunctions._
|
import TypeFunctions._
|
||||||
val Id: Id ~> Id = new (Id ~> Id) { def apply[T](a: T): T = a }
|
val Id: Id ~> Id = new (Id ~> Id) { def apply[T](a: T): T = a }
|
||||||
implicit def tcIdEquals: (Id ~> Id) = Id
|
implicit def tcIdEquals: (Id ~> Id) = Id
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Fn1[A, B] {
|
trait Fn1[A, B] {
|
||||||
def ∙[C](g: C => A): C => B
|
def ∙[C](g: C => A): C => B
|
||||||
}
|
}
|
||||||
|
|
@ -11,8 +11,7 @@ object Util {
|
||||||
def separateE[A, B](ps: Seq[Either[A, B]]): (Seq[A], Seq[B]) =
|
def separateE[A, B](ps: Seq[Either[A, B]]): (Seq[A], Seq[B]) =
|
||||||
separate(ps)(Types.idFun)
|
separate(ps)(Types.idFun)
|
||||||
|
|
||||||
def separate[T, A, B](ps: Seq[T])(f: T => Either[A, B]): (Seq[A], Seq[B]) =
|
def separate[T, A, B](ps: Seq[T])(f: T => Either[A, B]): (Seq[A], Seq[B]) = {
|
||||||
{
|
|
||||||
val (a, b) = ((Nil: Seq[A], Nil: Seq[B]) /: ps)((xs, y) => prependEither(xs, f(y)))
|
val (a, b) = ((Nil: Seq[A], Nil: Seq[B]) /: ps)((xs, y) => prependEither(xs, f(y)))
|
||||||
(a.reverse, b.reverse)
|
(a.reverse, b.reverse)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,16 +9,20 @@ import Prop._
|
||||||
import scala.collection.mutable.HashSet
|
import scala.collection.mutable.HashSet
|
||||||
|
|
||||||
object DagSpecification extends Properties("Dag") {
|
object DagSpecification extends Properties("Dag") {
|
||||||
property("No repeated nodes") = forAll { (dag: TestDag) => isSet(dag.topologicalSort) }
|
property("No repeated nodes") = forAll { (dag: TestDag) =>
|
||||||
property("Sort contains node") = forAll { (dag: TestDag) => dag.topologicalSort.contains(dag) }
|
isSet(dag.topologicalSort)
|
||||||
property("Dependencies precede node") = forAll { (dag: TestDag) => dependenciesPrecedeNodes(dag.topologicalSort) }
|
}
|
||||||
|
property("Sort contains node") = forAll { (dag: TestDag) =>
|
||||||
|
dag.topologicalSort.contains(dag)
|
||||||
|
}
|
||||||
|
property("Dependencies precede node") = forAll { (dag: TestDag) =>
|
||||||
|
dependenciesPrecedeNodes(dag.topologicalSort)
|
||||||
|
}
|
||||||
|
|
||||||
implicit lazy val arbTestDag: Arbitrary[TestDag] = Arbitrary(Gen.sized(dagGen))
|
implicit lazy val arbTestDag: Arbitrary[TestDag] = Arbitrary(Gen.sized(dagGen))
|
||||||
private def dagGen(nodeCount: Int): Gen[TestDag] =
|
private def dagGen(nodeCount: Int): Gen[TestDag] = {
|
||||||
{
|
|
||||||
val nodes = new HashSet[TestDag]
|
val nodes = new HashSet[TestDag]
|
||||||
def nonterminalGen(p: Gen.Parameters): Gen[TestDag] =
|
def nonterminalGen(p: Gen.Parameters): Gen[TestDag] = {
|
||||||
{
|
|
||||||
val seed = rng.Seed.random()
|
val seed = rng.Seed.random()
|
||||||
for {
|
for {
|
||||||
i <- 0 until nodeCount
|
i <- 0 until nodeCount
|
||||||
|
|
@ -30,11 +34,9 @@ object DagSpecification extends Properties("Dag") {
|
||||||
}
|
}
|
||||||
|
|
||||||
private def isSet[T](c: Seq[T]) = Set(c: _*).size == c.size
|
private def isSet[T](c: Seq[T]) = Set(c: _*).size == c.size
|
||||||
private def dependenciesPrecedeNodes(sort: List[TestDag]) =
|
private def dependenciesPrecedeNodes(sort: List[TestDag]) = {
|
||||||
{
|
|
||||||
val seen = new HashSet[TestDag]
|
val seen = new HashSet[TestDag]
|
||||||
def iterate(remaining: List[TestDag]): Boolean =
|
def iterate(remaining: List[TestDag]): Boolean = {
|
||||||
{
|
|
||||||
remaining match {
|
remaining match {
|
||||||
case Nil => true
|
case Nil => true
|
||||||
case node :: tail =>
|
case node :: tail =>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ object LiteralTest {
|
||||||
def x[A[_], B[_]](f: A ~> B) = f
|
def x[A[_], B[_]](f: A ~> B) = f
|
||||||
|
|
||||||
import Param._
|
import Param._
|
||||||
val f = x { (p: Param[Option, List]) => p.ret(p.in.toList) }
|
val f = x { (p: Param[Option, List]) =>
|
||||||
|
p.ret(p.in.toList)
|
||||||
|
}
|
||||||
|
|
||||||
val a: List[Int] = f(Some(3))
|
val a: List[Int] = f(Some(3))
|
||||||
val b: List[String] = f(Some("aa"))
|
val b: List[String] = f(Some("aa"))
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package sbt.internal.util
|
||||||
import sbt.util.Show
|
import sbt.util.Show
|
||||||
|
|
||||||
/** Define our settings system */
|
/** Define our settings system */
|
||||||
|
|
||||||
// A basic scope indexed by an integer.
|
// A basic scope indexed by an integer.
|
||||||
final case class Scope(nestIndex: Int, idAtIndex: Int = 0)
|
final case class Scope(nestIndex: Int, idAtIndex: Int = 0)
|
||||||
|
|
||||||
|
|
@ -14,15 +13,15 @@ final case class Scope(nestIndex: Int, idAtIndex: Int = 0)
|
||||||
// That would be a general pain.)
|
// That would be a general pain.)
|
||||||
case class SettingsExample() extends Init[Scope] {
|
case class SettingsExample() extends Init[Scope] {
|
||||||
// Provides a way of showing a Scope+AttributeKey[_]
|
// Provides a way of showing a Scope+AttributeKey[_]
|
||||||
val showFullKey: Show[ScopedKey[_]] = Show[ScopedKey[_]]((key: ScopedKey[_]) =>
|
val showFullKey: Show[ScopedKey[_]] = Show[ScopedKey[_]]((key: ScopedKey[_]) => {
|
||||||
{
|
|
||||||
s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}"
|
s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}"
|
||||||
})
|
})
|
||||||
|
|
||||||
// A sample delegation function that delegates to a Scope with a lower index.
|
// A sample delegation function that delegates to a Scope with a lower index.
|
||||||
val delegates: Scope => Seq[Scope] = {
|
val delegates: Scope => Seq[Scope] = {
|
||||||
case s @ Scope(index, proj) =>
|
case s @ Scope(index, proj) =>
|
||||||
s +: (if (index <= 0) Nil else { (if (proj > 0) List(Scope(index)) else Nil) ++: delegates(Scope(index - 1)) })
|
s +: (if (index <= 0) Nil
|
||||||
|
else { (if (proj > 0) List(Scope(index)) else Nil) ++: delegates(Scope(index - 1)) })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not using this feature in this example.
|
// Not using this feature in this example.
|
||||||
|
|
@ -32,7 +31,6 @@ case class SettingsExample() extends Init[Scope] {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Usage Example **/
|
/** Usage Example **/
|
||||||
|
|
||||||
case class SettingsUsage(val settingsExample: SettingsExample) {
|
case class SettingsUsage(val settingsExample: SettingsExample) {
|
||||||
import settingsExample._
|
import settingsExample._
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,7 @@ object SettingsTest extends Properties("settings") {
|
||||||
}
|
}
|
||||||
|
|
||||||
property("Allows references to completed settings") = forAllNoShrink(30) { allowedReference }
|
property("Allows references to completed settings") = forAllNoShrink(30) { allowedReference }
|
||||||
final def allowedReference(intermediate: Int): Prop =
|
final def allowedReference(intermediate: Int): Prop = {
|
||||||
{
|
|
||||||
val top = value(intermediate)
|
val top = value(intermediate)
|
||||||
def iterate(init: Initialize[Int]): Initialize[Int] =
|
def iterate(init: Initialize[Int]): Initialize[Int] =
|
||||||
bind(init) { t =>
|
bind(init) { t =>
|
||||||
|
|
@ -39,9 +38,9 @@ object SettingsTest extends Properties("settings") {
|
||||||
evaluate(setting(chk, iterate(top)) :: Nil); true
|
evaluate(setting(chk, iterate(top)) :: Nil); true
|
||||||
}
|
}
|
||||||
|
|
||||||
property("Derived setting chain depending on (prev derived, normal setting)") = forAllNoShrink(Gen.choose(1, 100).label("numSettings")) { derivedSettings }
|
property("Derived setting chain depending on (prev derived, normal setting)") =
|
||||||
final def derivedSettings(nr: Int): Prop =
|
forAllNoShrink(Gen.choose(1, 100).label("numSettings")) { derivedSettings }
|
||||||
{
|
final def derivedSettings(nr: Int): Prop = {
|
||||||
val genScopedKeys = {
|
val genScopedKeys = {
|
||||||
// We wan
|
// We wan
|
||||||
// t to generate lists of keys that DO NOT inclue the "ch" key we use to check things.
|
// t to generate lists of keys that DO NOT inclue the "ch" key we use to check things.
|
||||||
|
|
@ -56,7 +55,10 @@ object SettingsTest extends Properties("settings") {
|
||||||
for {
|
for {
|
||||||
List(scoped0, scoped1) <- chk :: scopedKeys sliding 2
|
List(scoped0, scoped1) <- chk :: scopedKeys sliding 2
|
||||||
nextInit = if (scoped0 == chk) chk
|
nextInit = if (scoped0 == chk) chk
|
||||||
else (scoped0 zipWith chk) { (p, _) => p + 1 }
|
else
|
||||||
|
(scoped0 zipWith chk) { (p, _) =>
|
||||||
|
p + 1
|
||||||
|
}
|
||||||
} yield derive(setting(scoped1, nextInit))
|
} yield derive(setting(scoped1, nextInit))
|
||||||
).toSeq
|
).toSeq
|
||||||
|
|
||||||
|
|
@ -75,8 +77,7 @@ object SettingsTest extends Properties("settings") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def mkAttrKeys[T](nr: Int)(implicit mf: Manifest[T]): Gen[List[AttributeKey[T]]] =
|
private def mkAttrKeys[T](nr: Int)(implicit mf: Manifest[T]): Gen[List[AttributeKey[T]]] = {
|
||||||
{
|
|
||||||
import Gen._
|
import Gen._
|
||||||
val nonEmptyAlphaStr =
|
val nonEmptyAlphaStr =
|
||||||
nonEmptyListOf(alphaChar).map(_.mkString).suchThat(_.forall(_.isLetter))
|
nonEmptyListOf(alphaChar).map(_.mkString).suchThat(_.forall(_.isLetter))
|
||||||
|
|
@ -87,9 +88,9 @@ object SettingsTest extends Properties("settings") {
|
||||||
} yield AttributeKey[T](item)).label(s"mkAttrKeys($nr)")
|
} yield AttributeKey[T](item)).label(s"mkAttrKeys($nr)")
|
||||||
}
|
}
|
||||||
|
|
||||||
property("Derived setting(s) replace DerivedSetting in the Seq[Setting[_]]") = derivedKeepsPosition
|
property("Derived setting(s) replace DerivedSetting in the Seq[Setting[_]]") =
|
||||||
final def derivedKeepsPosition: Prop =
|
derivedKeepsPosition
|
||||||
{
|
final def derivedKeepsPosition: Prop = {
|
||||||
val a: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("a"))
|
val a: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("a"))
|
||||||
val b: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("b"))
|
val b: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("b"))
|
||||||
val prop1 = {
|
val prop1 = {
|
||||||
|
|
@ -116,19 +117,22 @@ object SettingsTest extends Properties("settings") {
|
||||||
prop1 && prop2
|
prop1 && prop2
|
||||||
}
|
}
|
||||||
|
|
||||||
property("DerivedSetting in ThisBuild scopes derived settings under projects thus allowing safe +=") = forAllNoShrink(Gen.choose(1, 100)) { derivedSettingsScope }
|
property(
|
||||||
final def derivedSettingsScope(nrProjects: Int): Prop =
|
"DerivedSetting in ThisBuild scopes derived settings under projects thus allowing safe +="
|
||||||
{
|
) = forAllNoShrink(Gen.choose(1, 100)) { derivedSettingsScope }
|
||||||
|
final def derivedSettingsScope(nrProjects: Int): Prop = {
|
||||||
forAll(mkAttrKeys[Int](2)) {
|
forAll(mkAttrKeys[Int](2)) {
|
||||||
case List(key, derivedKey) =>
|
case List(key, derivedKey) =>
|
||||||
val projectKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), key)
|
val projectKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), key)
|
||||||
val projectDerivedKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), derivedKey)
|
val projectDerivedKeys = for { proj <- 1 to nrProjects } yield
|
||||||
|
ScopedKey(Scope(1, proj), derivedKey)
|
||||||
val globalKey = ScopedKey(Scope(0), key)
|
val globalKey = ScopedKey(Scope(0), key)
|
||||||
val globalDerivedKey = ScopedKey(Scope(0), derivedKey)
|
val globalDerivedKey = ScopedKey(Scope(0), derivedKey)
|
||||||
// Each project defines an initial value, but the update is defined in globalKey.
|
// Each project defines an initial value, but the update is defined in globalKey.
|
||||||
// However, the derived Settings that come from this should be scoped in each project.
|
// However, the derived Settings that come from this should be scoped in each project.
|
||||||
val settings: Seq[Setting[_]] =
|
val settings: Seq[Setting[_]] =
|
||||||
derive(setting(globalDerivedKey, settingsExample.map(globalKey)(_ + 1))) +: projectKeys.map(pk => setting(pk, value(0)))
|
derive(setting(globalDerivedKey, settingsExample.map(globalKey)(_ + 1))) +: projectKeys
|
||||||
|
.map(pk => setting(pk, value(0)))
|
||||||
val ev = evaluate(settings)
|
val ev = evaluate(settings)
|
||||||
// Also check that the key has no value at the "global" scope
|
// Also check that the key has no value at the "global" scope
|
||||||
val props = for { pk <- projectDerivedKeys } yield checkKey(pk, Some(1), ev)
|
val props = for { pk <- projectDerivedKeys } yield checkKey(pk, Some(1), ev)
|
||||||
|
|
@ -141,11 +145,11 @@ object SettingsTest extends Properties("settings") {
|
||||||
// but it may be necessary to provide an option to detect them (with a performance hit)
|
// but it may be necessary to provide an option to detect them (with a performance hit)
|
||||||
// This would test that cycle detection.
|
// This would test that cycle detection.
|
||||||
// property("Catches circular references") = forAll(chainLengthGen) { checkCircularReferences _ }
|
// property("Catches circular references") = forAll(chainLengthGen) { checkCircularReferences _ }
|
||||||
final def checkCircularReferences(intermediate: Int): Prop =
|
final def checkCircularReferences(intermediate: Int): Prop = {
|
||||||
{
|
|
||||||
val ccr = new CCR(intermediate)
|
val ccr = new CCR(intermediate)
|
||||||
try { evaluate(setting(chk, ccr.top) :: Nil); false }
|
try { evaluate(setting(chk, ccr.top) :: Nil); false } catch {
|
||||||
catch { case e: java.lang.Exception => true }
|
case e: java.lang.Exception => true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def tests =
|
def tests =
|
||||||
|
|
@ -154,7 +158,8 @@ object SettingsTest extends Properties("settings") {
|
||||||
checkKey[Int](ScopedKey(Scope(i), k), expected, applied)
|
checkKey[Int](ScopedKey(Scope(i), k), expected, applied)
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val expectedValues = None :: None :: None :: None :: None :: None :: Some(3) :: None :: Some(3) :: Some(9) :: Some(4) :: Some(9) :: Nil
|
lazy val expectedValues = None :: None :: None :: None :: None :: None :: Some(3) :: None ::
|
||||||
|
Some(3) :: Some(9) :: Some(4) :: Some(9) :: Nil
|
||||||
|
|
||||||
lazy val ch = AttributeKey[Int]("ch")
|
lazy val ch = AttributeKey[Int]("ch")
|
||||||
lazy val chk = ScopedKey(Scope(0), ch)
|
lazy val chk = ScopedKey(Scope(0), ch)
|
||||||
|
|
@ -165,14 +170,12 @@ object SettingsTest extends Properties("settings") {
|
||||||
bind(prev) { v =>
|
bind(prev) { v =>
|
||||||
if (v <= 0) prev else chainBind(value(v - 1))
|
if (v <= 0) prev else chainBind(value(v - 1))
|
||||||
}
|
}
|
||||||
def singleIntTest(i: Initialize[Int], expected: Int) =
|
def singleIntTest(i: Initialize[Int], expected: Int) = {
|
||||||
{
|
|
||||||
val eval = evaluate(setting(chk, i) :: Nil)
|
val eval = evaluate(setting(chk, i) :: Nil)
|
||||||
checkKey(chk, Some(expected), eval)
|
checkKey(chk, Some(expected), eval)
|
||||||
}
|
}
|
||||||
|
|
||||||
def checkKey[T](key: ScopedKey[T], expected: Option[T], settings: Settings[Scope]) =
|
def checkKey[T](key: ScopedKey[T], expected: Option[T], settings: Settings[Scope]) = {
|
||||||
{
|
|
||||||
val value = settings.get(key.scope, key.key)
|
val value = settings.get(key.scope, key.key)
|
||||||
("Key: " + key) |:
|
("Key: " + key) |:
|
||||||
("Value: " + value) |:
|
("Value: " + value) |:
|
||||||
|
|
@ -181,8 +184,9 @@ object SettingsTest extends Properties("settings") {
|
||||||
}
|
}
|
||||||
|
|
||||||
def evaluate(settings: Seq[Setting[_]]): Settings[Scope] =
|
def evaluate(settings: Seq[Setting[_]]): Settings[Scope] =
|
||||||
try { make(settings)(delegates, scopeLocal, showFullKey) }
|
try { make(settings)(delegates, scopeLocal, showFullKey) } catch {
|
||||||
catch { case e: Throwable => e.printStackTrace; throw e }
|
case e: Throwable => e.printStackTrace; throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// This setup is a workaround for module synchronization issues
|
// This setup is a workaround for module synchronization issues
|
||||||
final class CCR(intermediate: Int) {
|
final class CCR(intermediate: Int) {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,10 @@ abstract class JLine extends LineReader {
|
||||||
protected[this] def reader: ConsoleReader
|
protected[this] def reader: ConsoleReader
|
||||||
protected[this] def injectThreadSleep: Boolean
|
protected[this] def injectThreadSleep: Boolean
|
||||||
protected[this] val in: InputStream = JLine.makeInputStream(injectThreadSleep)
|
protected[this] val in: InputStream = JLine.makeInputStream(injectThreadSleep)
|
||||||
def readLine(prompt: String, mask: Option[Char] = None) = JLine.withJLine { unsynchronizedReadLine(prompt, mask) }
|
|
||||||
|
def readLine(prompt: String, mask: Option[Char] = None) = JLine.withJLine {
|
||||||
|
unsynchronizedReadLine(prompt, mask)
|
||||||
|
}
|
||||||
|
|
||||||
private[this] def unsynchronizedReadLine(prompt: String, mask: Option[Char]): Option[String] =
|
private[this] def unsynchronizedReadLine(prompt: String, mask: Option[Char]): Option[String] =
|
||||||
readLineWithHistory(prompt, mask) map { x =>
|
readLineWithHistory(prompt, mask) map { x =>
|
||||||
|
|
@ -25,18 +28,19 @@ abstract class JLine extends LineReader {
|
||||||
private[this] def readLineWithHistory(prompt: String, mask: Option[Char]): Option[String] =
|
private[this] def readLineWithHistory(prompt: String, mask: Option[Char]): Option[String] =
|
||||||
reader.getHistory match {
|
reader.getHistory match {
|
||||||
case fh: FileHistory =>
|
case fh: FileHistory =>
|
||||||
try { readLineDirect(prompt, mask) }
|
try readLineDirect(prompt, mask)
|
||||||
finally { fh.flush() }
|
finally fh.flush()
|
||||||
case _ => readLineDirect(prompt, mask)
|
case _ => readLineDirect(prompt, mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def readLineDirect(prompt: String, mask: Option[Char]): Option[String] =
|
private[this] def readLineDirect(prompt: String, mask: Option[Char]): Option[String] =
|
||||||
if (handleCONT)
|
if (handleCONT)
|
||||||
Signals.withHandler(() => resume(), signal = Signals.CONT)(() => readLineDirectRaw(prompt, mask))
|
Signals.withHandler(() => resume(), signal = Signals.CONT)(() =>
|
||||||
|
readLineDirectRaw(prompt, mask))
|
||||||
else
|
else
|
||||||
readLineDirectRaw(prompt, mask)
|
readLineDirectRaw(prompt, mask)
|
||||||
private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): Option[String] =
|
|
||||||
{
|
private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): Option[String] = {
|
||||||
val newprompt = handleMultilinePrompt(prompt)
|
val newprompt = handleMultilinePrompt(prompt)
|
||||||
try {
|
try {
|
||||||
mask match {
|
mask match {
|
||||||
|
|
@ -66,6 +70,7 @@ abstract class JLine extends LineReader {
|
||||||
reader.flush()
|
reader.flush()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] object JLine {
|
private[sbt] object JLine {
|
||||||
private[this] val TerminalProperty = "jline.terminal"
|
private[this] val TerminalProperty = "jline.terminal"
|
||||||
|
|
||||||
|
|
@ -87,6 +92,7 @@ private[sbt] object JLine {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected[this] val originalIn = new FileInputStream(FileDescriptor.in)
|
protected[this] val originalIn = new FileInputStream(FileDescriptor.in)
|
||||||
|
|
||||||
private[sbt] def makeInputStream(injectThreadSleep: Boolean): InputStream =
|
private[sbt] def makeInputStream(injectThreadSleep: Boolean): InputStream =
|
||||||
if (injectThreadSleep) new InputStreamWrapper(originalIn, Duration("50 ms"))
|
if (injectThreadSleep) new InputStreamWrapper(originalIn, Duration("50 ms"))
|
||||||
else originalIn
|
else originalIn
|
||||||
|
|
@ -94,11 +100,13 @@ private[sbt] object JLine {
|
||||||
// When calling this, ensure that enableEcho has been or will be called.
|
// When calling this, ensure that enableEcho has been or will be called.
|
||||||
// TerminalFactory.get will initialize the terminal to disable echo.
|
// TerminalFactory.get will initialize the terminal to disable echo.
|
||||||
private def terminal = jline.TerminalFactory.get
|
private def terminal = jline.TerminalFactory.get
|
||||||
|
|
||||||
private def withTerminal[T](f: jline.Terminal => T): T =
|
private def withTerminal[T](f: jline.Terminal => T): T =
|
||||||
synchronized {
|
synchronized {
|
||||||
val t = terminal
|
val t = terminal
|
||||||
t.synchronized { f(t) }
|
t.synchronized { f(t) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For accessing the JLine Terminal object.
|
* For accessing the JLine Terminal object.
|
||||||
* This ensures synchronized access as well as re-enabling echo after getting the Terminal.
|
* This ensures synchronized access as well as re-enabling echo after getting the Terminal.
|
||||||
|
|
@ -108,7 +116,9 @@ private[sbt] object JLine {
|
||||||
t.restore
|
t.restore
|
||||||
f(t)
|
f(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
def createReader(): ConsoleReader = createReader(None, JLine.makeInputStream(true))
|
def createReader(): ConsoleReader = createReader(None, JLine.makeInputStream(true))
|
||||||
|
|
||||||
def createReader(historyPath: Option[File], in: InputStream): ConsoleReader =
|
def createReader(historyPath: Option[File], in: InputStream): ConsoleReader =
|
||||||
usingTerminal { t =>
|
usingTerminal { t =>
|
||||||
val cr = new ConsoleReader(in, System.out)
|
val cr = new ConsoleReader(in, System.out)
|
||||||
|
|
@ -122,11 +132,11 @@ private[sbt] object JLine {
|
||||||
cr.setHistory(h)
|
cr.setHistory(h)
|
||||||
cr
|
cr
|
||||||
}
|
}
|
||||||
|
|
||||||
def withJLine[T](action: => T): T =
|
def withJLine[T](action: => T): T =
|
||||||
withTerminal { t =>
|
withTerminal { t =>
|
||||||
t.init
|
t.init
|
||||||
try { action }
|
try { action } finally { t.restore }
|
||||||
finally { t.restore }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def simple(
|
def simple(
|
||||||
|
|
@ -134,29 +144,30 @@ private[sbt] object JLine {
|
||||||
handleCONT: Boolean = HandleCONT,
|
handleCONT: Boolean = HandleCONT,
|
||||||
injectThreadSleep: Boolean = false
|
injectThreadSleep: Boolean = false
|
||||||
): SimpleReader = new SimpleReader(historyPath, handleCONT, injectThreadSleep)
|
): SimpleReader = new SimpleReader(historyPath, handleCONT, injectThreadSleep)
|
||||||
|
|
||||||
val MaxHistorySize = 500
|
val MaxHistorySize = 500
|
||||||
val HandleCONT = !java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT)
|
|
||||||
|
val HandleCONT =
|
||||||
|
!java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT)
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] class InputStreamWrapper(is: InputStream, val poll: Duration) extends FilterInputStream(is) {
|
private[sbt] class InputStreamWrapper(is: InputStream, val poll: Duration)
|
||||||
@tailrec
|
extends FilterInputStream(is) {
|
||||||
final override def read(): Int =
|
@tailrec final override def read(): Int =
|
||||||
if (is.available() != 0) is.read()
|
if (is.available() != 0) is.read()
|
||||||
else {
|
else {
|
||||||
Thread.sleep(poll.toMillis)
|
Thread.sleep(poll.toMillis)
|
||||||
read()
|
read()
|
||||||
}
|
}
|
||||||
|
|
||||||
@tailrec
|
@tailrec final override def read(b: Array[Byte]): Int =
|
||||||
final override def read(b: Array[Byte]): Int =
|
|
||||||
if (is.available() != 0) is.read(b)
|
if (is.available() != 0) is.read(b)
|
||||||
else {
|
else {
|
||||||
Thread.sleep(poll.toMillis)
|
Thread.sleep(poll.toMillis)
|
||||||
read(b)
|
read(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
@tailrec
|
@tailrec final override def read(b: Array[Byte], off: Int, len: Int): Int =
|
||||||
final override def read(b: Array[Byte], off: Int, len: Int): Int =
|
|
||||||
if (is.available() != 0) is.read(b, off, len)
|
if (is.available() != 0) is.read(b, off, len)
|
||||||
else {
|
else {
|
||||||
Thread.sleep(poll.toMillis)
|
Thread.sleep(poll.toMillis)
|
||||||
|
|
@ -167,23 +178,26 @@ private[sbt] class InputStreamWrapper(is: InputStream, val poll: Duration) exten
|
||||||
trait LineReader {
|
trait LineReader {
|
||||||
def readLine(prompt: String, mask: Option[Char] = None): Option[String]
|
def readLine(prompt: String, mask: Option[Char] = None): Option[String]
|
||||||
}
|
}
|
||||||
|
|
||||||
final class FullReader(
|
final class FullReader(
|
||||||
historyPath: Option[File],
|
historyPath: Option[File],
|
||||||
complete: Parser[_],
|
complete: Parser[_],
|
||||||
val handleCONT: Boolean = JLine.HandleCONT,
|
val handleCONT: Boolean = JLine.HandleCONT,
|
||||||
val injectThreadSleep: Boolean = false
|
val injectThreadSleep: Boolean = false
|
||||||
) extends JLine {
|
) extends JLine {
|
||||||
protected[this] val reader =
|
protected[this] val reader = {
|
||||||
{
|
|
||||||
val cr = JLine.createReader(historyPath, in)
|
val cr = JLine.createReader(historyPath, in)
|
||||||
sbt.internal.util.complete.JLineCompletion.installCustomCompletor(cr, complete)
|
sbt.internal.util.complete.JLineCompletion.installCustomCompletor(cr, complete)
|
||||||
cr
|
cr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimpleReader private[sbt] (historyPath: Option[File], val handleCONT: Boolean, val injectThreadSleep: Boolean) extends JLine {
|
class SimpleReader private[sbt] (
|
||||||
|
historyPath: Option[File],
|
||||||
|
val handleCONT: Boolean,
|
||||||
|
val injectThreadSleep: Boolean
|
||||||
|
) extends JLine {
|
||||||
protected[this] val reader = JLine.createReader(historyPath, in)
|
protected[this] val reader = JLine.createReader(historyPath, in)
|
||||||
|
|
||||||
}
|
}
|
||||||
object SimpleReader extends SimpleReader(None, JLine.HandleCONT, false)
|
|
||||||
|
|
||||||
|
object SimpleReader extends SimpleReader(None, JLine.HandleCONT, false)
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,28 @@ package complete
|
||||||
*/
|
*/
|
||||||
sealed trait Completions {
|
sealed trait Completions {
|
||||||
def get: Set[Completion]
|
def get: Set[Completion]
|
||||||
|
|
||||||
final def x(o: Completions): Completions = flatMap(_ x o)
|
final def x(o: Completions): Completions = flatMap(_ x o)
|
||||||
final def ++(o: Completions): Completions = Completions(get ++ o.get)
|
final def ++(o: Completions): Completions = Completions(get ++ o.get)
|
||||||
final def +:(o: Completion): Completions = Completions(get + o)
|
final def +:(o: Completion): Completions = Completions(get + o)
|
||||||
final def filter(f: Completion => Boolean): Completions = Completions(get filter f)
|
final def filter(f: Completion => Boolean): Completions = Completions(get filter f)
|
||||||
final def filterS(f: String => Boolean): Completions = filter(c => f(c.append))
|
final def filterS(f: String => Boolean): Completions = filter(c => f(c.append))
|
||||||
|
|
||||||
override def toString = get.mkString("Completions(", ",", ")")
|
override def toString = get.mkString("Completions(", ",", ")")
|
||||||
final def flatMap(f: Completion => Completions): Completions = Completions(get.flatMap(c => f(c).get))
|
|
||||||
|
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)
|
final def map(f: Completion => Completion): Completions = Completions(get map f)
|
||||||
|
|
||||||
override final def hashCode = get.hashCode
|
override final def hashCode = get.hashCode
|
||||||
override final def equals(o: Any) = o match { case c: Completions => get == c.get; case _ => false }
|
override final def equals(o: Any) = o match {
|
||||||
|
case c: Completions => get == c.get; case _ => false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Completions {
|
object Completions {
|
||||||
|
|
||||||
/** Returns a lazy Completions instance using the provided Completion Set. */
|
/** Returns a lazy Completions instance using the provided Completion Set. */
|
||||||
def apply(cs: => Set[Completion]): Completions = new Completions {
|
def apply(cs: => Set[Completion]): Completions = new Completions {
|
||||||
lazy val get = cs
|
lazy val get = cs
|
||||||
|
|
@ -45,6 +55,7 @@ object Completions {
|
||||||
|
|
||||||
/** Returns a strict Completions instance containing only the provided Completion.*/
|
/** Returns a strict Completions instance containing only the provided Completion.*/
|
||||||
def single(c: Completion): Completions = strict(Set.empty + c)
|
def single(c: Completion): Completions = strict(Set.empty + c)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -63,33 +74,45 @@ object Completions {
|
||||||
* 2) the full token being completed, which is useful for presenting a user with choices to select
|
* 2) the full token being completed, which is useful for presenting a user with choices to select
|
||||||
*/
|
*/
|
||||||
sealed trait Completion {
|
sealed trait Completion {
|
||||||
|
|
||||||
/** The proposed suffix to append to the existing input to complete the last token in the input.*/
|
/** The proposed suffix to append to the existing input to complete the last token in the input.*/
|
||||||
def append: String
|
def append: String
|
||||||
|
|
||||||
/** The string to present to the user to represent the full token being suggested.*/
|
/** The string to present to the user to represent the full token being suggested.*/
|
||||||
def display: String
|
def display: String
|
||||||
|
|
||||||
/** True if this Completion is suggesting the empty string.*/
|
/** True if this Completion is suggesting the empty string.*/
|
||||||
def isEmpty: Boolean
|
def isEmpty: Boolean
|
||||||
|
|
||||||
/** Appends the completions in `o` with the completions in this Completion.*/
|
/** Appends the completions in `o` with the completions in this Completion.*/
|
||||||
def ++(o: Completion): Completion = Completion.concat(this, o)
|
def ++(o: Completion): Completion = Completion.concat(this, o)
|
||||||
final def x(o: Completions): Completions = if (Completion evaluatesRight this) o.map(this ++ _) else Completions.strict(Set.empty + this)
|
|
||||||
|
final def x(o: Completions): Completions =
|
||||||
|
if (Completion evaluatesRight this) o.map(this ++ _) else Completions.strict(Set.empty + this)
|
||||||
|
|
||||||
override final lazy val hashCode = Completion.hashCode(this)
|
override final lazy val hashCode = Completion.hashCode(this)
|
||||||
override final def equals(o: Any) = o match { case c: Completion => Completion.equal(this, c); case _ => false }
|
override final def equals(o: Any) = o match {
|
||||||
|
case c: Completion => Completion.equal(this, c); case _ => false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class DisplayOnly(val display: String) extends Completion {
|
final class DisplayOnly(val display: String) extends Completion {
|
||||||
def isEmpty = display.isEmpty
|
def isEmpty = display.isEmpty
|
||||||
def append = ""
|
def append = ""
|
||||||
override def toString = "{" + display + "}"
|
override def toString = "{" + display + "}"
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Token(val display: String, val append: String) extends Completion {
|
final class Token(val display: String, val append: String) extends Completion {
|
||||||
def isEmpty = display.isEmpty && append.isEmpty
|
def isEmpty = display.isEmpty && append.isEmpty
|
||||||
override final def toString = "[" + display + "]++" + append
|
override final def toString = "[" + display + "]++" + append
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Suggestion(val append: String) extends Completion {
|
final class Suggestion(val append: String) extends Completion {
|
||||||
def isEmpty = append.isEmpty
|
def isEmpty = append.isEmpty
|
||||||
def display = append
|
def display = append
|
||||||
override def toString = append
|
override def toString = append
|
||||||
}
|
}
|
||||||
|
|
||||||
object Completion {
|
object Completion {
|
||||||
def concat(a: Completion, b: Completion): Completion =
|
def concat(a: Completion, b: Completion): Completion =
|
||||||
(a, b) match {
|
(a, b) match {
|
||||||
|
|
@ -98,6 +121,7 @@ object Completion {
|
||||||
case _ if a.isEmpty => b
|
case _ if a.isEmpty => b
|
||||||
case _ => a
|
case _ => a
|
||||||
}
|
}
|
||||||
|
|
||||||
def evaluatesRight(a: Completion): Boolean =
|
def evaluatesRight(a: Completion): Boolean =
|
||||||
a match {
|
a match {
|
||||||
case _: Suggestion => true
|
case _: Suggestion => true
|
||||||
|
|
@ -127,7 +151,8 @@ object Completion {
|
||||||
def displayOnly(value: => String): Completion = new DisplayOnly(value)
|
def displayOnly(value: => String): Completion = new DisplayOnly(value)
|
||||||
|
|
||||||
// TODO: make strict in 0.13.0 to match Token
|
// TODO: make strict in 0.13.0 to match Token
|
||||||
def token(prepend: => String, append: => String): Completion = new Token(prepend + append, append)
|
def token(prepend: => String, append: => String): Completion =
|
||||||
|
new Token(prepend + append, append)
|
||||||
|
|
||||||
/** @since 0.12.1 */
|
/** @since 0.12.1 */
|
||||||
def tokenDisplay(append: String, display: String): Completion = new Token(display, append)
|
def tokenDisplay(append: String, display: String): Completion = new Token(display, append)
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,23 @@ import java.lang.Character.{ toLowerCase => lower }
|
||||||
|
|
||||||
/** @author Paul Phillips*/
|
/** @author Paul Phillips*/
|
||||||
object EditDistance {
|
object EditDistance {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translated from the java version at
|
* Translated from the java version at
|
||||||
* http://www.merriampark.com/ld.htm
|
* http://www.merriampark.com/ld.htm
|
||||||
* which is declared to be public domain.
|
* which is declared to be public domain.
|
||||||
*/
|
*/
|
||||||
def levenshtein(s: String, t: String, insertCost: Int = 1, deleteCost: Int = 1, subCost: Int = 1, transposeCost: Int = 1, matchCost: Int = 0, caseCost: Int = 1, transpositions: Boolean = false): Int = {
|
def levenshtein(
|
||||||
|
s: String,
|
||||||
|
t: String,
|
||||||
|
insertCost: Int = 1,
|
||||||
|
deleteCost: Int = 1,
|
||||||
|
subCost: Int = 1,
|
||||||
|
transposeCost: Int = 1,
|
||||||
|
matchCost: Int = 0,
|
||||||
|
caseCost: Int = 1,
|
||||||
|
transpositions: Boolean = false
|
||||||
|
): Int = {
|
||||||
val n = s.length
|
val n = s.length
|
||||||
val m = t.length
|
val m = t.length
|
||||||
if (n == 0) return m
|
if (n == 0) return m
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import sbt.io.IO
|
||||||
* TAB key in the console.
|
* TAB key in the console.
|
||||||
*/
|
*/
|
||||||
trait ExampleSource {
|
trait ExampleSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a (possibly lazy) list of completion example strings. These strings are continuations of user's input. The
|
* @return a (possibly lazy) list of completion example strings. These strings are continuations of user's input. The
|
||||||
* user's input is incremented with calls to [[withAddedPrefix]].
|
* user's input is incremented with calls to [[withAddedPrefix]].
|
||||||
|
|
@ -22,6 +23,7 @@ trait ExampleSource {
|
||||||
* the just added prefix).
|
* the just added prefix).
|
||||||
*/
|
*/
|
||||||
def withAddedPrefix(addedPrefix: String): ExampleSource
|
def withAddedPrefix(addedPrefix: String): ExampleSource
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -29,7 +31,8 @@ trait ExampleSource {
|
||||||
* @param examples the examples that will be displayed to the user when they press the TAB key.
|
* @param examples the examples that will be displayed to the user when they press the TAB key.
|
||||||
*/
|
*/
|
||||||
sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSource {
|
sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSource {
|
||||||
override def withAddedPrefix(addedPrefix: String): ExampleSource = FixedSetExamples(examplesWithRemovedPrefix(addedPrefix))
|
override def withAddedPrefix(addedPrefix: String): ExampleSource =
|
||||||
|
FixedSetExamples(examplesWithRemovedPrefix(addedPrefix))
|
||||||
|
|
||||||
override def apply(): Iterable[String] = examples
|
override def apply(): Iterable[String] = examples
|
||||||
|
|
||||||
|
|
@ -46,12 +49,17 @@ sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSo
|
||||||
class FileExamples(base: File, prefix: String = "") extends ExampleSource {
|
class FileExamples(base: File, prefix: String = "") extends ExampleSource {
|
||||||
override def apply(): Stream[String] = files(base).map(_ substring prefix.length)
|
override def apply(): Stream[String] = files(base).map(_ substring prefix.length)
|
||||||
|
|
||||||
override def withAddedPrefix(addedPrefix: String): FileExamples = new FileExamples(base, prefix + addedPrefix)
|
override def withAddedPrefix(addedPrefix: String): FileExamples =
|
||||||
|
new FileExamples(base, prefix + addedPrefix)
|
||||||
|
|
||||||
protected def files(directory: File): Stream[String] = {
|
protected def files(directory: File): Stream[String] = {
|
||||||
val childPaths = IO.listFiles(directory).toStream
|
val childPaths = IO.listFiles(directory).toStream
|
||||||
val prefixedDirectChildPaths = childPaths map { IO.relativize(base, _).get } filter { _ startsWith prefix }
|
val prefixedDirectChildPaths = childPaths map { IO.relativize(base, _).get } filter {
|
||||||
val dirsToRecurseInto = childPaths filter { _.isDirectory } map { IO.relativize(base, _).get } filter { dirStartsWithPrefix }
|
_ startsWith prefix
|
||||||
|
}
|
||||||
|
val dirsToRecurseInto = childPaths filter { _.isDirectory } map { IO.relativize(base, _).get } filter {
|
||||||
|
dirStartsWithPrefix
|
||||||
|
}
|
||||||
prefixedDirectChildPaths append dirsToRecurseInto.flatMap(dir => files(new File(base, dir)))
|
prefixedDirectChildPaths append dirsToRecurseInto.flatMap(dir => files(new File(base, dir)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,20 @@ package complete
|
||||||
import History.number
|
import History.number
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
final class History private (val lines: IndexedSeq[String], val path: Option[File], error: String => Unit) {
|
final class History private (
|
||||||
|
val lines: IndexedSeq[String],
|
||||||
|
val path: Option[File],
|
||||||
|
error: String => Unit
|
||||||
|
) {
|
||||||
private def reversed = lines.reverse
|
private def reversed = lines.reverse
|
||||||
|
|
||||||
def all: Seq[String] = lines
|
def all: Seq[String] = lines
|
||||||
def size = lines.length
|
def size = lines.length
|
||||||
def !! : Option[String] = !-(1)
|
def !! : Option[String] = !-(1)
|
||||||
def apply(i: Int): Option[String] = if (0 <= i && i < size) Some(lines(i)) else { sys.error("Invalid history index: " + i) }
|
|
||||||
|
def apply(i: Int): Option[String] =
|
||||||
|
if (0 <= i && i < size) Some(lines(i)) else { sys.error("Invalid history index: " + i) }
|
||||||
|
|
||||||
def !(i: Int): Option[String] = apply(i)
|
def !(i: Int): Option[String] = apply(i)
|
||||||
|
|
||||||
def !(s: String): Option[String] =
|
def !(s: String): Option[String] =
|
||||||
|
|
@ -21,6 +28,7 @@ final class History private (val lines: IndexedSeq[String], val path: Option[Fil
|
||||||
case Some(n) => if (n < 0) !-(-n) else apply(n)
|
case Some(n) => if (n < 0) !-(-n) else apply(n)
|
||||||
case None => nonEmpty(s) { reversed.find(_.startsWith(s)) }
|
case None => nonEmpty(s) { reversed.find(_.startsWith(s)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
def !-(n: Int): Option[String] = apply(size - n - 1)
|
def !-(n: Int): Option[String] = apply(size - n - 1)
|
||||||
|
|
||||||
def !?(s: String): Option[String] = nonEmpty(s) { reversed.drop(1).find(_.contains(s)) }
|
def !?(s: String): Option[String] = nonEmpty(s) { reversed.drop(1).find(_.contains(s)) }
|
||||||
|
|
@ -32,13 +40,17 @@ final class History private (val lines: IndexedSeq[String], val path: Option[Fil
|
||||||
act
|
act
|
||||||
|
|
||||||
def list(historySize: Int, show: Int): Seq[String] =
|
def list(historySize: Int, show: Int): Seq[String] =
|
||||||
lines.toList.drop(scala.math.max(0, lines.size - historySize)).zipWithIndex.map { case (line, number) => " " + number + " " + line }.takeRight(show max 1)
|
lines.toList
|
||||||
|
.drop(scala.math.max(0, lines.size - historySize))
|
||||||
|
.zipWithIndex
|
||||||
|
.map { case (line, number) => " " + number + " " + line }
|
||||||
|
.takeRight(show max 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
object History {
|
object History {
|
||||||
def apply(lines: Seq[String], path: Option[File], error: String => Unit): History = new History(lines.toIndexedSeq, path, sys.error)
|
def apply(lines: Seq[String], path: Option[File], error: String => Unit): History =
|
||||||
|
new History(lines.toIndexedSeq, path, sys.error)
|
||||||
|
|
||||||
def number(s: String): Option[Int] =
|
def number(s: String): Option[Int] =
|
||||||
try { Some(s.toInt) }
|
try { Some(s.toInt) } catch { case e: NumberFormatException => None }
|
||||||
catch { case e: NumberFormatException => None }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,14 @@ object HistoryCommands {
|
||||||
StartsWithString -> "Execute the most recent command starting with 'string'",
|
StartsWithString -> "Execute the most recent command starting with 'string'",
|
||||||
ContainsString -> "Execute the most recent command containing 'string'"
|
ContainsString -> "Execute the most recent command containing 'string'"
|
||||||
)
|
)
|
||||||
def helpString = "History commands:\n " + (descriptions.map { case (c, d) => c + " " + d }).mkString("\n ")
|
|
||||||
def printHelp(): Unit =
|
def helpString =
|
||||||
println(helpString)
|
"History commands:\n " + (descriptions
|
||||||
|
.map { case (c, d) => c + " " + d })
|
||||||
|
.mkString("\n ")
|
||||||
|
|
||||||
|
def printHelp(): Unit = println(helpString)
|
||||||
|
|
||||||
def printHistory(history: complete.History, historySize: Int, show: Int): Unit =
|
def printHistory(history: complete.History, historySize: Int, show: Int): Unit =
|
||||||
history.list(historySize, show).foreach(println)
|
history.list(historySize, show).foreach(println)
|
||||||
|
|
||||||
|
|
@ -46,24 +51,30 @@ object HistoryCommands {
|
||||||
val MaxLines = 500
|
val MaxLines = 500
|
||||||
lazy val num = token(NatBasic, "<integer>")
|
lazy val num = token(NatBasic, "<integer>")
|
||||||
lazy val last = Last ^^^ { execute(_.!!) }
|
lazy val last = Last ^^^ { execute(_.!!) }
|
||||||
lazy val list = ListCommands ~> (num ?? Int.MaxValue) map { show => (h: History) => { printHistory(h, MaxLines, show); Some(Nil) }
|
|
||||||
|
lazy val list = ListCommands ~> (num ?? Int.MaxValue) map { show => (h: History) =>
|
||||||
|
{ printHistory(h, MaxLines, show); Some(Nil) }
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val execStr = flag('?') ~ token(any.+.string, "<string>") map {
|
lazy val execStr = flag('?') ~ token(any.+.string, "<string>") map {
|
||||||
case (contains, str) =>
|
case (contains, str) =>
|
||||||
execute(h => if (contains) h !? str else h ! str)
|
execute(h => if (contains) h !? str else h ! str)
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val execInt = flag('-') ~ num map {
|
lazy val execInt = flag('-') ~ num map {
|
||||||
case (neg, value) =>
|
case (neg, value) =>
|
||||||
execute(h => if (neg) h !- value else h ! value)
|
execute(h => if (neg) h !- value else h ! value)
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val help = success((h: History) => { printHelp(); Some(Nil) })
|
lazy val help = success((h: History) => { printHelp(); Some(Nil) })
|
||||||
|
|
||||||
def execute(f: History => Option[String]): History => Option[List[String]] = (h: History) =>
|
def execute(f: History => Option[String]): History => Option[List[String]] = (h: History) => {
|
||||||
{
|
|
||||||
val command = f(h).filterNot(_.startsWith(Start))
|
val command = f(h).filterNot(_.startsWith(Start))
|
||||||
val lines = h.lines.toArray
|
val lines = h.lines.toArray
|
||||||
command.foreach(lines(lines.length - 1) = _)
|
command.foreach(lines(lines.length - 1) = _)
|
||||||
h.path foreach { h => IO.writeLines(h, lines) }
|
h.path foreach { h =>
|
||||||
|
IO.writeLines(h, lines)
|
||||||
|
}
|
||||||
Some(command.toList)
|
Some(command.toList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,19 +12,31 @@ import collection.JavaConversions
|
||||||
object JLineCompletion {
|
object JLineCompletion {
|
||||||
def installCustomCompletor(reader: ConsoleReader, parser: Parser[_]): Unit =
|
def installCustomCompletor(reader: ConsoleReader, parser: Parser[_]): Unit =
|
||||||
installCustomCompletor(reader)(parserAsCompletor(parser))
|
installCustomCompletor(reader)(parserAsCompletor(parser))
|
||||||
def installCustomCompletor(reader: ConsoleReader)(complete: (String, Int) => (Seq[String], Seq[String])): Unit =
|
|
||||||
|
def installCustomCompletor(reader: ConsoleReader)(
|
||||||
|
complete: (String, Int) => (Seq[String], Seq[String])
|
||||||
|
): Unit =
|
||||||
installCustomCompletor(customCompletor(complete), reader)
|
installCustomCompletor(customCompletor(complete), reader)
|
||||||
def installCustomCompletor(complete: (ConsoleReader, Int) => Boolean, reader: ConsoleReader): Unit =
|
|
||||||
{
|
def installCustomCompletor(
|
||||||
|
complete: (ConsoleReader, Int) => Boolean,
|
||||||
|
reader: ConsoleReader
|
||||||
|
): Unit = {
|
||||||
reader.removeCompleter(DummyCompletor)
|
reader.removeCompleter(DummyCompletor)
|
||||||
reader.addCompleter(DummyCompletor)
|
reader.addCompleter(DummyCompletor)
|
||||||
reader.setCompletionHandler(new CustomHandler(complete))
|
reader.setCompletionHandler(new CustomHandler(complete))
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] final class CustomHandler(completeImpl: (ConsoleReader, Int) => Boolean) extends CompletionHandler {
|
private[this] final class CustomHandler(completeImpl: (ConsoleReader, Int) => Boolean)
|
||||||
|
extends CompletionHandler {
|
||||||
private[this] var previous: Option[(String, Int)] = None
|
private[this] var previous: Option[(String, Int)] = None
|
||||||
private[this] var level: Int = 1
|
private[this] var level: Int = 1
|
||||||
override def complete(reader: ConsoleReader, candidates: java.util.List[CharSequence], position: Int) = {
|
|
||||||
|
override def complete(
|
||||||
|
reader: ConsoleReader,
|
||||||
|
candidates: java.util.List[CharSequence],
|
||||||
|
position: Int
|
||||||
|
) = {
|
||||||
val current = Some(bufferSnapshot(reader))
|
val current = Some(bufferSnapshot(reader))
|
||||||
level = if (current == previous) level + 1 else 1
|
level = if (current == previous) level + 1 else 1
|
||||||
previous = current
|
previous = current
|
||||||
|
|
@ -42,8 +54,11 @@ object JLineCompletion {
|
||||||
// (ConsoleReader doesn't call the handler if there aren't any completions)
|
// (ConsoleReader doesn't call the handler if there aren't any completions)
|
||||||
// the custom handler will then throw away the candidates and call the custom function
|
// the custom handler will then throw away the candidates and call the custom function
|
||||||
private[this] final object DummyCompletor extends Completer {
|
private[this] final object DummyCompletor extends Completer {
|
||||||
override def complete(buffer: String, cursor: Int, candidates: java.util.List[CharSequence]): Int =
|
override def complete(
|
||||||
{
|
buffer: String,
|
||||||
|
cursor: Int,
|
||||||
|
candidates: java.util.List[CharSequence]
|
||||||
|
): Int = {
|
||||||
candidates.asInstanceOf[java.util.List[String]] add "dummy"
|
candidates.asInstanceOf[java.util.List[String]] add "dummy"
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
@ -52,16 +67,15 @@ object JLineCompletion {
|
||||||
def parserAsCompletor(p: Parser[_]): (String, Int) => (Seq[String], Seq[String]) =
|
def parserAsCompletor(p: Parser[_]): (String, Int) => (Seq[String], Seq[String]) =
|
||||||
(str, level) => convertCompletions(Parser.completions(p, str, level))
|
(str, level) => convertCompletions(Parser.completions(p, str, level))
|
||||||
|
|
||||||
def convertCompletions(c: Completions): (Seq[String], Seq[String]) =
|
def convertCompletions(c: Completions): (Seq[String], Seq[String]) = {
|
||||||
{
|
|
||||||
val cs = c.get
|
val cs = c.get
|
||||||
if (cs.isEmpty)
|
if (cs.isEmpty)
|
||||||
(Nil, "{invalid input}" :: Nil)
|
(Nil, "{invalid input}" :: Nil)
|
||||||
else
|
else
|
||||||
convertCompletions(cs)
|
convertCompletions(cs)
|
||||||
}
|
}
|
||||||
def convertCompletions(cs: Set[Completion]): (Seq[String], Seq[String]) =
|
|
||||||
{
|
def convertCompletions(cs: Set[Completion]): (Seq[String], Seq[String]) = {
|
||||||
val (insert, display) =
|
val (insert, display) =
|
||||||
((Set.empty[String], Set.empty[String]) /: cs) {
|
((Set.empty[String], Set.empty[String]) /: cs) {
|
||||||
case (t @ (insert, display), comp) =>
|
case (t @ (insert, display), comp) =>
|
||||||
|
|
@ -69,29 +83,33 @@ object JLineCompletion {
|
||||||
}
|
}
|
||||||
(insert.toSeq, display.toSeq.sorted)
|
(insert.toSeq, display.toSeq.sorted)
|
||||||
}
|
}
|
||||||
|
|
||||||
def appendNonEmpty(set: Set[String], add: String) = if (add.trim.isEmpty) set else set + add
|
def appendNonEmpty(set: Set[String], add: String) = if (add.trim.isEmpty) set else set + add
|
||||||
|
|
||||||
def customCompletor(f: (String, Int) => (Seq[String], Seq[String])): (ConsoleReader, Int) => Boolean =
|
def customCompletor(
|
||||||
|
f: (String, Int) => (Seq[String], Seq[String])): (ConsoleReader, Int) => Boolean =
|
||||||
(reader, level) => {
|
(reader, level) => {
|
||||||
val success = complete(beforeCursor(reader), reader => f(reader, level), reader)
|
val success = complete(beforeCursor(reader), reader => f(reader, level), reader)
|
||||||
reader.flush()
|
reader.flush()
|
||||||
success
|
success
|
||||||
}
|
}
|
||||||
|
|
||||||
def bufferSnapshot(reader: ConsoleReader): (String, Int) =
|
def bufferSnapshot(reader: ConsoleReader): (String, Int) = {
|
||||||
{
|
|
||||||
val b = reader.getCursorBuffer
|
val b = reader.getCursorBuffer
|
||||||
(b.buffer.toString, b.cursor)
|
(b.buffer.toString, b.cursor)
|
||||||
}
|
}
|
||||||
def beforeCursor(reader: ConsoleReader): String =
|
|
||||||
{
|
def beforeCursor(reader: ConsoleReader): String = {
|
||||||
val b = reader.getCursorBuffer
|
val b = reader.getCursorBuffer
|
||||||
b.buffer.substring(0, b.cursor)
|
b.buffer.substring(0, b.cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns false if there was nothing to insert and nothing to display
|
// returns false if there was nothing to insert and nothing to display
|
||||||
def complete(beforeCursor: String, completions: String => (Seq[String], Seq[String]), reader: ConsoleReader): Boolean =
|
def complete(
|
||||||
{
|
beforeCursor: String,
|
||||||
|
completions: String => (Seq[String], Seq[String]),
|
||||||
|
reader: ConsoleReader
|
||||||
|
): Boolean = {
|
||||||
val (insert, display) = completions(beforeCursor)
|
val (insert, display) = completions(beforeCursor)
|
||||||
val common = commonPrefix(insert)
|
val common = commonPrefix(insert)
|
||||||
if (common.isEmpty)
|
if (common.isEmpty)
|
||||||
|
|
@ -118,11 +136,13 @@ object JLineCompletion {
|
||||||
printCompletions(display, reader)
|
printCompletions(display, reader)
|
||||||
reader.drawLine()
|
reader.drawLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
def printCompletions(cs: Seq[String], reader: ConsoleReader): Unit = {
|
def printCompletions(cs: Seq[String], reader: ConsoleReader): Unit = {
|
||||||
val print = shouldPrint(cs, reader)
|
val print = shouldPrint(cs, reader)
|
||||||
reader.println()
|
reader.println()
|
||||||
if (print) printLinesAndColumns(cs, reader)
|
if (print) printLinesAndColumns(cs, reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
def printLinesAndColumns(cs: Seq[String], reader: ConsoleReader): Unit = {
|
def printLinesAndColumns(cs: Seq[String], reader: ConsoleReader): Unit = {
|
||||||
val (lines, columns) = cs partition hasNewline
|
val (lines, columns) = cs partition hasNewline
|
||||||
for (line <- lines) {
|
for (line <- lines) {
|
||||||
|
|
@ -132,15 +152,16 @@ object JLineCompletion {
|
||||||
}
|
}
|
||||||
reader.printColumns(JavaConversions.seqAsJavaList(columns.map(_.trim)))
|
reader.printColumns(JavaConversions.seqAsJavaList(columns.map(_.trim)))
|
||||||
}
|
}
|
||||||
|
|
||||||
def hasNewline(s: String): Boolean = s.indexOf('\n') >= 0
|
def hasNewline(s: String): Boolean = s.indexOf('\n') >= 0
|
||||||
def shouldPrint(cs: Seq[String], reader: ConsoleReader): Boolean =
|
|
||||||
{
|
def shouldPrint(cs: Seq[String], reader: ConsoleReader): Boolean = {
|
||||||
val size = cs.size
|
val size = cs.size
|
||||||
(size <= reader.getAutoprintThreshold) ||
|
(size <= reader.getAutoprintThreshold) ||
|
||||||
confirm("Display all %d possibilities? (y or n) ".format(size), 'y', 'n', reader)
|
confirm("Display all %d possibilities? (y or n) ".format(size), 'y', 'n', reader)
|
||||||
}
|
}
|
||||||
def confirm(prompt: String, trueC: Char, falseC: Char, reader: ConsoleReader): Boolean =
|
|
||||||
{
|
def confirm(prompt: String, trueC: Char, falseC: Char, reader: ConsoleReader): Boolean = {
|
||||||
reader.println()
|
reader.println()
|
||||||
reader.print(prompt)
|
reader.print(prompt)
|
||||||
reader.flush()
|
reader.flush()
|
||||||
|
|
@ -148,8 +169,8 @@ object JLineCompletion {
|
||||||
}
|
}
|
||||||
|
|
||||||
def commonPrefix(s: Seq[String]): String = if (s.isEmpty) "" else s reduceLeft commonPrefix
|
def commonPrefix(s: Seq[String]): String = if (s.isEmpty) "" else s reduceLeft commonPrefix
|
||||||
def commonPrefix(a: String, b: String): String =
|
|
||||||
{
|
def commonPrefix(a: String, b: String): String = {
|
||||||
val len = scala.math.min(a.length, b.length)
|
val len = scala.math.min(a.length, b.length)
|
||||||
@tailrec def loop(i: Int): Int = if (i >= len) len else if (a(i) != b(i)) i else loop(i + 1)
|
@tailrec def loop(i: Int): Int = if (i >= len) len else if (a(i) != b(i)) i else loop(i + 1)
|
||||||
a.substring(0, loop(0))
|
a.substring(0, loop(0))
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,9 @@ sealed trait Parser[+T] {
|
||||||
def ifValid[S](p: => Parser[S]): Parser[S]
|
def ifValid[S](p: => Parser[S]): Parser[S]
|
||||||
def valid: Boolean
|
def valid: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait RichParser[A] {
|
sealed trait RichParser[A] {
|
||||||
|
|
||||||
/** Apply the original Parser and then apply `next` (in order). The result of both is provides as a pair. */
|
/** Apply the original Parser and then apply `next` (in order). The result of both is provides as a pair. */
|
||||||
def ~[B](next: Parser[B]): Parser[(A, B)]
|
def ~[B](next: Parser[B]): Parser[(A, B)]
|
||||||
|
|
||||||
|
|
@ -100,14 +102,19 @@ sealed trait RichParser[A] {
|
||||||
* be displayed.
|
* be displayed.
|
||||||
* @return a new parser with a new source of completions.
|
* @return a new parser with a new source of completions.
|
||||||
*/
|
*/
|
||||||
def examples(exampleSource: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean): Parser[A]
|
def examples(
|
||||||
|
exampleSource: ExampleSource,
|
||||||
|
maxNumberOfExamples: Int,
|
||||||
|
removeInvalidExamples: Boolean
|
||||||
|
): Parser[A]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param exampleSource the source of examples when displaying completions to the user.
|
* @param exampleSource the source of examples when displaying completions to the user.
|
||||||
* @return a new parser with a new source of completions. It displays at most 25 completion examples and does not
|
* @return a new parser with a new source of completions. It displays at most 25 completion examples and does not
|
||||||
* remove invalid examples.
|
* remove invalid examples.
|
||||||
*/
|
*/
|
||||||
def examples(exampleSource: ExampleSource): Parser[A] = examples(exampleSource, maxNumberOfExamples = 25, removeInvalidExamples = false)
|
def examples(exampleSource: ExampleSource): Parser[A] =
|
||||||
|
examples(exampleSource, maxNumberOfExamples = 25, removeInvalidExamples = false)
|
||||||
|
|
||||||
/** Converts a Parser returning a Char sequence to a Parser returning a String.*/
|
/** Converts a Parser returning a Char sequence to a Parser returning a String.*/
|
||||||
def string(implicit ev: A <:< Seq[Char]): Parser[String]
|
def string(implicit ev: A <:< Seq[Char]): Parser[String]
|
||||||
|
|
@ -139,14 +146,17 @@ object Parser extends ParserMain {
|
||||||
def app[B, C](b: => Result[B])(f: (T, B) => C): Result[C]
|
def app[B, C](b: => Result[B])(f: (T, B) => C): Result[C]
|
||||||
def toEither: Either[() => Seq[String], T]
|
def toEither: Either[() => Seq[String], T]
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class Value[+T](value: T) extends Result[T] {
|
final case class Value[+T](value: T) extends Result[T] {
|
||||||
def isFailure = false
|
def isFailure = false
|
||||||
def isValid: Boolean = true
|
def isValid: Boolean = true
|
||||||
def errors = Nil
|
def errors = Nil
|
||||||
|
|
||||||
def app[B, C](b: => Result[B])(f: (T, B) => C): Result[C] = b match {
|
def app[B, C](b: => Result[B])(f: (T, B) => C): Result[C] = b match {
|
||||||
case fail: Failure => fail
|
case fail: Failure => fail
|
||||||
case Value(bv) => Value(f(value, bv))
|
case Value(bv) => Value(f(value, bv))
|
||||||
}
|
}
|
||||||
|
|
||||||
def &&(b: => Result[_]): Result[T] = b match { case f: Failure => f; case _ => this }
|
def &&(b: => Result[_]): Result[T] = b match { case f: Failure => f; case _ => this }
|
||||||
def or[B >: T](b: => Result[B]): Result[B] = 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 either[B](b: => Result[B]): Result[Either[T, B]] = Value(Left(value))
|
||||||
|
|
@ -155,20 +165,25 @@ object Parser extends ParserMain {
|
||||||
def filter(f: T => Boolean, msg: => String): Result[T] = if (f(value)) this else mkFailure(msg)
|
def filter(f: T => Boolean, msg: => String): Result[T] = if (f(value)) this else mkFailure(msg)
|
||||||
def toEither = Right(value)
|
def toEither = Right(value)
|
||||||
}
|
}
|
||||||
final class Failure private[sbt] (mkErrors: => Seq[String], val definitive: Boolean) extends Result[Nothing] {
|
|
||||||
|
final class Failure private[sbt] (mkErrors: => Seq[String], val definitive: Boolean)
|
||||||
|
extends Result[Nothing] {
|
||||||
lazy val errors: Seq[String] = mkErrors
|
lazy val errors: Seq[String] = mkErrors
|
||||||
def isFailure = true
|
def isFailure = true
|
||||||
def isValid = false
|
def isValid = false
|
||||||
def map[B](f: Nothing => B) = this
|
def map[B](f: Nothing => B) = this
|
||||||
def flatMap[B](f: Nothing => Result[B]) = this
|
def flatMap[B](f: Nothing => Result[B]) = this
|
||||||
|
|
||||||
def or[B](b: => Result[B]): Result[B] = b match {
|
def or[B](b: => Result[B]): Result[B] = b match {
|
||||||
case v: Value[B] => v
|
case v: Value[B] => v
|
||||||
case f: Failure => if (definitive) this else this ++ f
|
case f: Failure => if (definitive) this else this ++ f
|
||||||
}
|
}
|
||||||
|
|
||||||
def either[B](b: => Result[B]): Result[Either[Nothing, B]] = b match {
|
def either[B](b: => Result[B]): Result[Either[Nothing, B]] = b match {
|
||||||
case Value(v) => Value(Right(v))
|
case Value(v) => Value(Right(v))
|
||||||
case f: Failure => if (definitive) this else this ++ f
|
case f: Failure => if (definitive) this else this ++ f
|
||||||
}
|
}
|
||||||
|
|
||||||
def filter(f: Nothing => Boolean, msg: => String) = this
|
def filter(f: Nothing => Boolean, msg: => String) = this
|
||||||
def app[B, C](b: => Result[B])(f: (Nothing, B) => C): Result[C] = this
|
def app[B, C](b: => Result[B])(f: (Nothing, B) => C): Result[C] = this
|
||||||
def &&(b: => Result[_]) = this
|
def &&(b: => Result[_]) = this
|
||||||
|
|
@ -176,8 +191,12 @@ object Parser extends ParserMain {
|
||||||
|
|
||||||
private[sbt] def ++(f: Failure) = mkFailures(errors ++ f.errors)
|
private[sbt] def ++(f: Failure) = mkFailures(errors ++ f.errors)
|
||||||
}
|
}
|
||||||
def mkFailures(errors: => Seq[String], definitive: Boolean = false): Failure = new Failure(errors.distinct, definitive)
|
|
||||||
def mkFailure(error: => String, definitive: Boolean = false): Failure = new Failure(error :: Nil, definitive)
|
def mkFailures(errors: => Seq[String], definitive: Boolean = false): Failure =
|
||||||
|
new Failure(errors.distinct, definitive)
|
||||||
|
|
||||||
|
def mkFailure(error: => String, definitive: Boolean = false): Failure =
|
||||||
|
new Failure(error :: Nil, definitive)
|
||||||
|
|
||||||
def tuple[A, B](a: Option[A], b: Option[B]): Option[(A, B)] =
|
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 }
|
(a, b) match { case (Some(av), Some(bv)) => Some((av, bv)); case _ => None }
|
||||||
|
|
@ -198,7 +217,12 @@ object Parser extends ParserMain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def filterParser[T](a: Parser[T], f: T => Boolean, seen: String, msg: String => String): Parser[T] =
|
def filterParser[T](
|
||||||
|
a: Parser[T],
|
||||||
|
f: T => Boolean,
|
||||||
|
seen: String,
|
||||||
|
msg: String => String
|
||||||
|
): Parser[T] =
|
||||||
a.ifValid {
|
a.ifValid {
|
||||||
a.result match {
|
a.result match {
|
||||||
case Some(av) if f(av) => success(av)
|
case Some(av) if f(av) => success(av)
|
||||||
|
|
@ -211,8 +235,8 @@ object Parser extends ParserMain {
|
||||||
b.ifValid {
|
b.ifValid {
|
||||||
(a.result, b.result) match {
|
(a.result, b.result) match {
|
||||||
case (Some(av), Some(bv)) => success((av, bv))
|
case (Some(av), Some(bv)) => success((av, bv))
|
||||||
case (Some(av), None) => b map { bv => (av, bv) }
|
case (Some(av), None) => b map (bv => (av, bv))
|
||||||
case (None, Some(bv)) => a map { av => (av, bv) }
|
case (None, Some(bv)) => a map (av => (av, bv))
|
||||||
case (None, None) => new SeqParser(a, b)
|
case (None, None) => new SeqParser(a, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -229,6 +253,7 @@ object Parser extends ParserMain {
|
||||||
|
|
||||||
def onFailure[T](delegate: Parser[T], msg: String): Parser[T] =
|
def onFailure[T](delegate: Parser[T], msg: String): Parser[T] =
|
||||||
if (delegate.valid) new OnFailure(delegate, msg) else failure(msg)
|
if (delegate.valid) new OnFailure(delegate, msg) else failure(msg)
|
||||||
|
|
||||||
def trapAndFail[T](delegate: Parser[T]): Parser[T] =
|
def trapAndFail[T](delegate: Parser[T]): Parser[T] =
|
||||||
delegate.ifValid(new TrapAndFail(delegate))
|
delegate.ifValid(new TrapAndFail(delegate))
|
||||||
|
|
||||||
|
|
@ -237,10 +262,17 @@ object Parser extends ParserMain {
|
||||||
|
|
||||||
def repeat[T](p: Parser[T], min: Int = 0, max: UpperBound = Infinite): Parser[Seq[T]] =
|
def repeat[T](p: Parser[T], min: Int = 0, max: UpperBound = Infinite): Parser[Seq[T]] =
|
||||||
repeat(None, p, min, max, Nil)
|
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]] =
|
|
||||||
{
|
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(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 + ")")
|
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]] =
|
def checkRepeated(invalidButOptional: => Parser[Seq[T]]): Parser[Seq[T]] =
|
||||||
repeated match {
|
repeated match {
|
||||||
|
|
@ -248,8 +280,11 @@ object Parser extends ParserMain {
|
||||||
case i: Invalid => i
|
case i: Invalid => i
|
||||||
case _ =>
|
case _ =>
|
||||||
repeated.result match {
|
repeated.result match {
|
||||||
case Some(value) => success(revAcc reverse_::: value :: Nil) // revAcc should be Nil here
|
case Some(value) =>
|
||||||
case None => if (max.isZero) success(revAcc.reverse) else new Repeat(partial, repeated, min, max, revAcc)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -267,7 +302,9 @@ object Parser extends ParserMain {
|
||||||
|
|
||||||
def and[T](a: Parser[T], b: Parser[_]): Parser[T] = a.ifValid(b.ifValid(new And(a, b)))
|
def and[T](a: Parser[T], b: Parser[_]): Parser[T] = a.ifValid(b.ifValid(new And(a, b)))
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ParserMain {
|
trait ParserMain {
|
||||||
|
|
||||||
/** Provides combinators for Parsers.*/
|
/** Provides combinators for Parsers.*/
|
||||||
implicit def richParser[A](a: Parser[A]): RichParser[A] = new RichParser[A] {
|
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]) = seqParser(a, b)
|
||||||
|
|
@ -279,7 +316,7 @@ trait ParserMain {
|
||||||
def map[B](f: A => B) = mapParser(a, f)
|
def map[B](f: A => B) = mapParser(a, f)
|
||||||
def id = a
|
def id = a
|
||||||
|
|
||||||
def ^^^[B](value: B): Parser[B] = a map { _ => value }
|
def ^^^[B](value: B): Parser[B] = a map (_ => value)
|
||||||
def ??[B >: A](alt: B): Parser[B] = a.? map { _ getOrElse alt }
|
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[A] = (a ~ b) map { case av ~ _ => av }
|
||||||
def ~>[B](b: Parser[B]): Parser[B] = (a ~ b) map { case _ ~ bv => bv }
|
def ~>[B](b: Parser[B]): Parser[B] = (a ~ b) map { case _ ~ bv => bv }
|
||||||
|
|
@ -290,8 +327,17 @@ trait ParserMain {
|
||||||
def &(o: Parser[_]) = and(a, o)
|
def &(o: Parser[_]) = and(a, o)
|
||||||
def -(o: Parser[_]) = and(a, not(o, "Unexpected: " + o))
|
def -(o: Parser[_]) = and(a, not(o, "Unexpected: " + o))
|
||||||
def examples(s: String*): Parser[A] = examples(s.toSet)
|
def examples(s: String*): Parser[A] = examples(s.toSet)
|
||||||
def examples(s: Set[String], check: Boolean = false): Parser[A] = examples(new FixedSetExamples(s), s.size, check)
|
|
||||||
def examples(s: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean): Parser[A] = Parser.examples(a, s, maxNumberOfExamples, removeInvalidExamples)
|
def examples(s: Set[String], check: Boolean = false): Parser[A] =
|
||||||
|
examples(new FixedSetExamples(s), s.size, check)
|
||||||
|
|
||||||
|
def examples(
|
||||||
|
s: ExampleSource,
|
||||||
|
maxNumberOfExamples: Int,
|
||||||
|
removeInvalidExamples: Boolean
|
||||||
|
): Parser[A] =
|
||||||
|
Parser.examples(a, s, maxNumberOfExamples, removeInvalidExamples)
|
||||||
|
|
||||||
def filter(f: A => Boolean, msg: String => String): Parser[A] = filterParser(a, f, "", msg)
|
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 string(implicit ev: A <:< Seq[Char]): Parser[String] = map(_.mkString)
|
||||||
def flatMap[B](f: A => Parser[B]) = bindParser(a, f)
|
def flatMap[B](f: A => Parser[B]) = bindParser(a, f)
|
||||||
|
|
@ -313,13 +359,15 @@ trait ParserMain {
|
||||||
* Defines a parser that always fails on any input with messages `msgs`.
|
* Defines a parser that always fails on any input with messages `msgs`.
|
||||||
* If `definitive` is `true`, any failures by later alternatives are discarded.
|
* If `definitive` is `true`, any failures by later alternatives are discarded.
|
||||||
*/
|
*/
|
||||||
def invalid(msgs: => Seq[String], definitive: Boolean = false): Parser[Nothing] = Invalid(mkFailures(msgs, definitive))
|
def invalid(msgs: => Seq[String], definitive: Boolean = false): Parser[Nothing] =
|
||||||
|
Invalid(mkFailures(msgs, definitive))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a parser that always fails on any input with message `msg`.
|
* Defines a parser that always fails on any input with message `msg`.
|
||||||
* If `definitive` is `true`, any failures by later alternatives are discarded.
|
* If `definitive` is `true`, any failures by later alternatives are discarded.
|
||||||
*/
|
*/
|
||||||
def failure(msg: => String, definitive: Boolean = false): Parser[Nothing] = invalid(msg :: Nil, definitive)
|
def failure(msg: => String, definitive: Boolean = false): Parser[Nothing] =
|
||||||
|
invalid(msg :: Nil, definitive)
|
||||||
|
|
||||||
/** Defines a parser that always succeeds on empty input with the result `value`.*/
|
/** Defines a parser that always succeeds on empty input with the result `value`.*/
|
||||||
def success[T](value: T): Parser[T] = new ValidParser[T] {
|
def success[T](value: T): Parser[T] = new ValidParser[T] {
|
||||||
|
|
@ -335,8 +383,7 @@ trait ParserMain {
|
||||||
charClass(r contains _).examples(r.map(_.toString): _*)
|
charClass(r contains _).examples(r.map(_.toString): _*)
|
||||||
|
|
||||||
/** Defines a Parser that parses a single character only if it is contained in `legal`.*/
|
/** Defines a Parser that parses a single character only if it is contained in `legal`.*/
|
||||||
def chars(legal: String): Parser[Char] =
|
def chars(legal: String): Parser[Char] = {
|
||||||
{
|
|
||||||
val set = legal.toSet
|
val set = legal.toSet
|
||||||
charClass(set, "character in '" + legal + "'") examples (set.map(_.toString))
|
charClass(set, "character in '" + legal + "'") examples (set.map(_.toString))
|
||||||
}
|
}
|
||||||
|
|
@ -345,7 +392,8 @@ trait ParserMain {
|
||||||
* Defines a Parser that parses a single character only if the predicate `f` returns true for that character.
|
* Defines a Parser that parses a single character only if the predicate `f` returns true for that character.
|
||||||
* If this parser fails, `label` is used as the failure message.
|
* If this parser fails, `label` is used as the failure message.
|
||||||
*/
|
*/
|
||||||
def charClass(f: Char => Boolean, label: String = "<unspecified>"): Parser[Char] = new CharacterClass(f, label)
|
def charClass(f: Char => Boolean, label: String = "<unspecified>"): Parser[Char] =
|
||||||
|
new CharacterClass(f, label)
|
||||||
|
|
||||||
/** Presents a single Char `ch` as a Parser that only parses that exact character. */
|
/** Presents a single Char `ch` as a Parser that only parses that exact character. */
|
||||||
implicit def literal(ch: Char): Parser[Char] = new ValidParser[Char] {
|
implicit def literal(ch: Char): Parser[Char] = new ValidParser[Char] {
|
||||||
|
|
@ -355,13 +403,16 @@ trait ParserMain {
|
||||||
def completions(level: Int) = Completions.single(Completion.suggestion(ch.toString))
|
def completions(level: Int) = Completions.single(Completion.suggestion(ch.toString))
|
||||||
override def toString = "'" + ch + "'"
|
override def toString = "'" + ch + "'"
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Presents a literal String `s` as a Parser that only parses that exact text and provides it as the result.*/
|
/** Presents a literal String `s` as a Parser that only parses that exact text and provides it as the result.*/
|
||||||
implicit def literal(s: String): Parser[String] = stringLiteral(s, 0)
|
implicit def literal(s: String): Parser[String] = stringLiteral(s, 0)
|
||||||
|
|
||||||
/** See [[unapply]]. */
|
/** See [[unapply]]. */
|
||||||
object ~ {
|
object ~ {
|
||||||
|
|
||||||
/** Convenience for destructuring a tuple that mirrors the `~` combinator.*/
|
/** Convenience for destructuring a tuple that mirrors the `~` combinator.*/
|
||||||
def unapply[A, B](t: (A, B)): Some[(A, B)] = Some(t)
|
def unapply[A, B](t: (A, B)): Some[(A, B)] = Some(t)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Parses input `str` using `parser`. If successful, the result is provided wrapped in `Right`. If unsuccessful, an error message is provided in `Left`.*/
|
/** Parses input `str` using `parser`. If successful, the result is provided wrapped in `Right`. If unsuccessful, an error message is provided in `Left`.*/
|
||||||
|
|
@ -403,8 +454,7 @@ trait ParserMain {
|
||||||
Parser.completions(parser, str, level).get foreach println
|
Parser.completions(parser, str, level).get foreach println
|
||||||
|
|
||||||
// intended to be temporary pending proper error feedback
|
// intended to be temporary pending proper error feedback
|
||||||
def result[T](p: Parser[T], s: String): Either[() => (Seq[String], Int), T] =
|
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] =
|
def loop(i: Int, a: Parser[T]): Either[() => (Seq[String], Int), T] =
|
||||||
a match {
|
a match {
|
||||||
case Invalid(f) => Left(() => (f.errors, i))
|
case Invalid(f) => Left(() => (f.errors, i))
|
||||||
|
|
@ -415,8 +465,7 @@ trait ParserMain {
|
||||||
val msgs = msgs0()
|
val msgs = msgs0()
|
||||||
val nonEmpty = if (msgs.isEmpty) "Unexpected end of input" :: Nil else msgs
|
val nonEmpty = if (msgs.isEmpty) "Unexpected end of input" :: Nil else msgs
|
||||||
(nonEmpty, ci)
|
(nonEmpty, ci)
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
loop(ci, a derive s(ci))
|
loop(ci, a derive s(ci))
|
||||||
}
|
}
|
||||||
loop(-1, p)
|
loop(-1, p)
|
||||||
|
|
@ -454,7 +503,12 @@ trait ParserMain {
|
||||||
* @tparam A the type of values that are returned by the parser.
|
* @tparam A the type of values that are returned by the parser.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
def examples[A](a: Parser[A], completions: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean): Parser[A] =
|
def examples[A](
|
||||||
|
a: Parser[A],
|
||||||
|
completions: ExampleSource,
|
||||||
|
maxNumberOfExamples: Int,
|
||||||
|
removeInvalidExamples: Boolean
|
||||||
|
): Parser[A] =
|
||||||
if (a.valid) {
|
if (a.valid) {
|
||||||
a.result match {
|
a.result match {
|
||||||
case Some(av) => success(av)
|
case Some(av) => success(av)
|
||||||
|
|
@ -463,7 +517,11 @@ trait ParserMain {
|
||||||
}
|
}
|
||||||
} else a
|
} else a
|
||||||
|
|
||||||
def matched(t: Parser[_], seen: Vector[Char] = Vector.empty, partial: Boolean = false): Parser[String] =
|
def matched(
|
||||||
|
t: Parser[_],
|
||||||
|
seen: Vector[Char] = Vector.empty,
|
||||||
|
partial: Boolean = false
|
||||||
|
): Parser[String] =
|
||||||
t match {
|
t match {
|
||||||
case i: Invalid => if (partial && seen.nonEmpty) success(seen.mkString) else i
|
case i: Invalid => if (partial && seen.nonEmpty) success(seen.mkString) else i
|
||||||
case _ =>
|
case _ =>
|
||||||
|
|
@ -485,13 +543,15 @@ trait ParserMain {
|
||||||
* When tab completion of part of this token is requested, no completions are returned if `hide` returns true for the current tab completion level.
|
* 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.
|
* 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))
|
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.
|
* 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.
|
* 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))
|
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.
|
* Establishes delegate parser `t` as a single token of tab completion.
|
||||||
|
|
@ -524,24 +584,29 @@ trait ParserMain {
|
||||||
|
|
||||||
def oneOf[T](p: Seq[Parser[T]]): Parser[T] = p.reduceLeft(_ | _)
|
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 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]] =
|
|
||||||
{
|
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) }
|
val (newErrors, valid) = separate(p) {
|
||||||
|
case Invalid(f) => Left(f.errors _); case ok => Right(ok)
|
||||||
|
}
|
||||||
def combinedErrors = errors ++ newErrors.flatMap(_())
|
def combinedErrors = errors ++ newErrors.flatMap(_())
|
||||||
if (valid.isEmpty) invalid(combinedErrors) else new ParserSeq(valid, combinedErrors)
|
if (valid.isEmpty) invalid(combinedErrors) else new ParserSeq(valid, combinedErrors)
|
||||||
}
|
}
|
||||||
|
|
||||||
def stringLiteral(s: String, start: Int): Parser[String] =
|
def stringLiteral(s: String, start: Int): Parser[String] = {
|
||||||
{
|
|
||||||
val len = s.length
|
val len = s.length
|
||||||
if (len == 0) sys.error("String literal cannot be empty") else if (start >= len) success(s) else new StringLiteral(s, start)
|
if (len == 0) sys.error("String literal cannot be empty")
|
||||||
|
else if (start >= len) success(s)
|
||||||
|
else new StringLiteral(s, start)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait ValidParser[T] extends Parser[T] {
|
sealed trait ValidParser[T] extends Parser[T] {
|
||||||
final def valid = true
|
final def valid = true
|
||||||
final def failure = None
|
final def failure = None
|
||||||
final def ifValid[S](p: => Parser[S]): Parser[S] = p
|
final def ifValid[S](p: => Parser[S]): Parser[S] = p
|
||||||
}
|
}
|
||||||
|
|
||||||
private final case class Invalid(fail: Failure) extends Parser[Nothing] {
|
private final case class Invalid(fail: Failure) extends Parser[Nothing] {
|
||||||
def failure = Some(fail)
|
def failure = Some(fail)
|
||||||
def result = None
|
def result = None
|
||||||
|
|
@ -564,8 +629,15 @@ private final case class SoftInvalid(fail: Failure) extends ValidParser[Nothing]
|
||||||
private final class TrapAndFail[A](a: Parser[A]) extends ValidParser[A] {
|
private final class TrapAndFail[A](a: Parser[A]) extends ValidParser[A] {
|
||||||
def result = try { a.result } catch { case e: Exception => None }
|
def result = try { a.result } catch { case e: Exception => None }
|
||||||
def resultEmpty = try { a.resultEmpty } catch { case e: Exception => fail(e) }
|
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 }
|
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 toString = "trap(" + a + ")"
|
||||||
override def isTokenStart = a.isTokenStart
|
override def isTokenStart = a.isTokenStart
|
||||||
private[this] def fail(e: Exception): Failure = mkFailure(e.toString)
|
private[this] def fail(e: Exception): Failure = mkFailure(e.toString)
|
||||||
|
|
@ -573,23 +645,29 @@ private final class TrapAndFail[A](a: Parser[A]) extends ValidParser[A] {
|
||||||
|
|
||||||
private final class OnFailure[A](a: Parser[A], message: String) extends ValidParser[A] {
|
private final class OnFailure[A](a: Parser[A], message: String) extends ValidParser[A] {
|
||||||
def result = a.result
|
def result = a.result
|
||||||
def resultEmpty = a.resultEmpty match { case f: Failure => mkFailure(message); case v: Value[A] => v }
|
|
||||||
|
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 derive(c: Char) = onFailure(a derive c, message)
|
||||||
def completions(level: Int) = a.completions(level)
|
def completions(level: Int) = a.completions(level)
|
||||||
override def toString = "(" + a + " !!! \"" + message + "\" )"
|
override def toString = "(" + a + " !!! \"" + message + "\" )"
|
||||||
override def isTokenStart = a.isTokenStart
|
override def isTokenStart = a.isTokenStart
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class SeqParser[A, B](a: Parser[A], b: Parser[B]) extends ValidParser[(A, B)] {
|
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 result = tuple(a.result, b.result)
|
||||||
lazy val resultEmpty = a.resultEmpty seq b.resultEmpty
|
lazy val resultEmpty = a.resultEmpty seq b.resultEmpty
|
||||||
def derive(c: Char) =
|
|
||||||
{
|
def derive(c: Char) = {
|
||||||
val common = a.derive(c) ~ b
|
val common = a.derive(c) ~ b
|
||||||
a.resultEmpty match {
|
a.resultEmpty match {
|
||||||
case Value(av) => common | b.derive(c).map(br => (av, br))
|
case Value(av) => common | b.derive(c).map(br => (av, br))
|
||||||
case _: Failure => common
|
case _: Failure => common
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def completions(level: Int) = a.completions(level) x b.completions(level)
|
def completions(level: Int) = a.completions(level) x b.completions(level)
|
||||||
override def toString = "(" + a + " ~ " + b + ")"
|
override def toString = "(" + a + " ~ " + b + ")"
|
||||||
}
|
}
|
||||||
|
|
@ -601,6 +679,7 @@ private final class HomParser[A](a: Parser[A], b: Parser[A]) extends ValidParser
|
||||||
def completions(level: Int) = a.completions(level) ++ b.completions(level)
|
def completions(level: Int) = a.completions(level) ++ b.completions(level)
|
||||||
override def toString = "(" + a + " | " + b + ")"
|
override def toString = "(" + a + " | " + b + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class HetParser[A, B](a: Parser[A], b: Parser[B]) extends ValidParser[Either[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) }
|
lazy val result = tuple(a.result, b.result) map { case (a, b) => Left(a) }
|
||||||
def derive(c: Char) = (a derive c) || (b derive c)
|
def derive(c: Char) = (a derive c) || (b derive c)
|
||||||
|
|
@ -608,27 +687,33 @@ private final class HetParser[A, B](a: Parser[A], b: Parser[B]) extends ValidPar
|
||||||
def completions(level: Int) = a.completions(level) ++ b.completions(level)
|
def completions(level: Int) = a.completions(level) ++ b.completions(level)
|
||||||
override def toString = "(" + a + " || " + b + ")"
|
override def toString = "(" + a + " || " + b + ")"
|
||||||
}
|
}
|
||||||
private final class ParserSeq[T](a: Seq[Parser[T]], errors: => Seq[String]) extends ValidParser[Seq[T]] {
|
|
||||||
|
private final class ParserSeq[T](a: Seq[Parser[T]], errors: => Seq[String])
|
||||||
|
extends ValidParser[Seq[T]] {
|
||||||
assert(a.nonEmpty)
|
assert(a.nonEmpty)
|
||||||
lazy val resultEmpty: Result[Seq[T]] =
|
|
||||||
{
|
lazy val resultEmpty: Result[Seq[T]] = {
|
||||||
val res = a.map(_.resultEmpty)
|
val res = a.map(_.resultEmpty)
|
||||||
val (failures, values) = separate(res)(_.toEither)
|
val (failures, values) = separate(res)(_.toEither)
|
||||||
// if(failures.isEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors)
|
// if(failures.isEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors)
|
||||||
if (values.nonEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors)
|
if (values.nonEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
def result = {
|
def result = {
|
||||||
val success = a.flatMap(_.result)
|
val success = a.flatMap(_.result)
|
||||||
if (success.length == a.length) Some(success) else None
|
if (success.length == a.length) Some(success) else None
|
||||||
}
|
}
|
||||||
|
|
||||||
def completions(level: Int) = a.map(_.completions(level)).reduceLeft(_ ++ _)
|
def completions(level: Int) = a.map(_.completions(level)).reduceLeft(_ ++ _)
|
||||||
def derive(c: Char) = seq0(a.map(_ derive c), errors)
|
def derive(c: Char) = seq0(a.map(_ derive c), errors)
|
||||||
|
|
||||||
override def toString = "seq(" + a + ")"
|
override def toString = "seq(" + a + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class BindParser[A, B](a: Parser[A], f: A => Parser[B]) extends ValidParser[B] {
|
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 result = a.result flatMap (av => f(av).result)
|
||||||
lazy val resultEmpty = a.resultEmpty flatMap { av => f(av).resultEmpty }
|
lazy val resultEmpty = a.resultEmpty flatMap (av => f(av).resultEmpty)
|
||||||
|
|
||||||
def completions(level: Int) =
|
def completions(level: Int) =
|
||||||
a.completions(level) flatMap { c =>
|
a.completions(level) flatMap { c =>
|
||||||
apply(a)(c.append).resultEmpty match {
|
apply(a)(c.append).resultEmpty match {
|
||||||
|
|
@ -637,17 +722,19 @@ private final class BindParser[A, B](a: Parser[A], f: A => Parser[B]) extends Va
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def derive(c: Char) =
|
def derive(c: Char) = {
|
||||||
{
|
|
||||||
val common = a derive c flatMap f
|
val common = a derive c flatMap f
|
||||||
a.resultEmpty match {
|
a.resultEmpty match {
|
||||||
case Value(av) => common | derive1(f(av), c)
|
case Value(av) => common | derive1(f(av), c)
|
||||||
case _: Failure => common
|
case _: Failure => common
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override def isTokenStart = a.isTokenStart
|
override def isTokenStart = a.isTokenStart
|
||||||
|
|
||||||
override def toString = "bind(" + a + ")"
|
override def toString = "bind(" + a + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class MapParser[A, B](a: Parser[A], f: A => B) extends ValidParser[B] {
|
private final class MapParser[A, B](a: Parser[A], f: A => B) extends ValidParser[B] {
|
||||||
lazy val result = a.result map f
|
lazy val result = a.result map f
|
||||||
lazy val resultEmpty = a.resultEmpty map f
|
lazy val resultEmpty = a.resultEmpty map f
|
||||||
|
|
@ -656,35 +743,53 @@ private final class MapParser[A, B](a: Parser[A], f: A => B) extends ValidParser
|
||||||
override def isTokenStart = a.isTokenStart
|
override def isTokenStart = a.isTokenStart
|
||||||
override def toString = "map(" + a + ")"
|
override def toString = "map(" + a + ")"
|
||||||
}
|
}
|
||||||
private final class Filter[T](p: Parser[T], f: T => Boolean, seen: String, msg: String => String) extends ValidParser[T] {
|
|
||||||
|
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))
|
def filterResult(r: Result[T]) = r.filter(f, msg(seen))
|
||||||
lazy val result = p.result filter f
|
lazy val result = p.result filter f
|
||||||
lazy val resultEmpty = filterResult(p.resultEmpty)
|
lazy val resultEmpty = filterResult(p.resultEmpty)
|
||||||
def derive(c: Char) = filterParser(p derive c, f, seen + c, msg)
|
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 }
|
|
||||||
|
def completions(level: Int) = p.completions(level) filterS { s =>
|
||||||
|
filterResult(apply(p)(s).resultEmpty).isValid
|
||||||
|
}
|
||||||
|
|
||||||
override def toString = "filter(" + p + ")"
|
override def toString = "filter(" + p + ")"
|
||||||
override def isTokenStart = p.isTokenStart
|
override def isTokenStart = p.isTokenStart
|
||||||
}
|
}
|
||||||
private final class MatchedString(delegate: Parser[_], seenV: Vector[Char], partial: Boolean) extends ValidParser[String] {
|
|
||||||
|
private final class MatchedString(delegate: Parser[_], seenV: Vector[Char], partial: Boolean)
|
||||||
|
extends ValidParser[String] {
|
||||||
lazy val seen = seenV.mkString
|
lazy val seen = seenV.mkString
|
||||||
def derive(c: Char) = matched(delegate derive c, seenV :+ c, partial)
|
def derive(c: Char) = matched(delegate derive c, seenV :+ c, partial)
|
||||||
def completions(level: Int) = delegate.completions(level)
|
def completions(level: Int) = delegate.completions(level)
|
||||||
def result = if (delegate.result.isDefined) Some(seen) else None
|
def result = if (delegate.result.isDefined) Some(seen) else None
|
||||||
def resultEmpty = delegate.resultEmpty match { case f: Failure if !partial => f; case _ => Value(seen) }
|
|
||||||
|
def resultEmpty = delegate.resultEmpty match {
|
||||||
|
case f: Failure if !partial => f; case _ => Value(seen)
|
||||||
|
}
|
||||||
|
|
||||||
override def isTokenStart = delegate.isTokenStart
|
override def isTokenStart = delegate.isTokenStart
|
||||||
override def toString = "matched(" + partial + ", " + seen + ", " + delegate + ")"
|
override def toString = "matched(" + partial + ", " + seen + ", " + delegate + ")"
|
||||||
}
|
}
|
||||||
private final class TokenStart[T](delegate: Parser[T], seen: String, complete: TokenCompletions) extends ValidParser[T] {
|
|
||||||
|
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 derive(c: Char) = mkToken(delegate derive c, seen + c, complete)
|
||||||
|
|
||||||
def completions(level: Int) = complete match {
|
def completions(level: Int) = complete match {
|
||||||
case dc: TokenCompletions.Delegating => dc.completions(seen, level, delegate.completions(level))
|
case dc: TokenCompletions.Delegating =>
|
||||||
|
dc.completions(seen, level, delegate.completions(level))
|
||||||
case fc: TokenCompletions.Fixed => fc.completions(seen, level)
|
case fc: TokenCompletions.Fixed => fc.completions(seen, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
def result = delegate.result
|
def result = delegate.result
|
||||||
def resultEmpty = delegate.resultEmpty
|
def resultEmpty = delegate.resultEmpty
|
||||||
override def isTokenStart = true
|
override def isTokenStart = true
|
||||||
override def toString = "token('" + complete + ", " + delegate + ")"
|
override def toString = "token('" + complete + ", " + delegate + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class And[T](a: Parser[T], b: Parser[_]) extends ValidParser[T] {
|
private final class And[T](a: Parser[T], b: Parser[_]) extends ValidParser[T] {
|
||||||
lazy val result = tuple(a.result, b.result) map { _._1 }
|
lazy val result = tuple(a.result, b.result) map { _._1 }
|
||||||
def derive(c: Char) = (a derive c) & (b derive c)
|
def derive(c: Char) = (a derive c) & (b derive c)
|
||||||
|
|
@ -697,10 +802,12 @@ private final class Not(delegate: Parser[_], failMessage: String) extends ValidP
|
||||||
def derive(c: Char) = if (delegate.valid) not(delegate derive c, failMessage) else this
|
def derive(c: Char) = if (delegate.valid) not(delegate derive c, failMessage) else this
|
||||||
def completions(level: Int) = Completions.empty
|
def completions(level: Int) = Completions.empty
|
||||||
def result = None
|
def result = None
|
||||||
|
|
||||||
lazy val resultEmpty = delegate.resultEmpty match {
|
lazy val resultEmpty = delegate.resultEmpty match {
|
||||||
case f: Failure => Value(())
|
case f: Failure => Value(())
|
||||||
case v: Value[_] => mkFailure(failMessage)
|
case v: Value[_] => mkFailure(failMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def toString = " -(%s)".format(delegate)
|
override def toString = " -(%s)".format(delegate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -719,9 +826,18 @@ private final class Not(delegate: Parser[_], failMessage: String) extends ValidP
|
||||||
* @param removeInvalidExamples indicates whether to remove examples that are deemed invalid by the delegate parser.
|
* @param removeInvalidExamples indicates whether to remove examples that are deemed invalid by the delegate parser.
|
||||||
* @tparam T the type of value produced by the parser.
|
* @tparam T the type of value produced by the parser.
|
||||||
*/
|
*/
|
||||||
private final class ParserWithExamples[T](delegate: Parser[T], exampleSource: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean) extends ValidParser[T] {
|
private final class ParserWithExamples[T](
|
||||||
|
delegate: Parser[T],
|
||||||
|
exampleSource: ExampleSource,
|
||||||
|
maxNumberOfExamples: Int,
|
||||||
|
removeInvalidExamples: Boolean
|
||||||
|
) extends ValidParser[T] {
|
||||||
|
|
||||||
def derive(c: Char) =
|
def derive(c: Char) =
|
||||||
examples(delegate derive c, exampleSource.withAddedPrefix(c.toString), maxNumberOfExamples, removeInvalidExamples)
|
examples(delegate derive c,
|
||||||
|
exampleSource.withAddedPrefix(c.toString),
|
||||||
|
maxNumberOfExamples,
|
||||||
|
removeInvalidExamples)
|
||||||
|
|
||||||
def result = delegate.result
|
def result = delegate.result
|
||||||
|
|
||||||
|
|
@ -749,15 +865,21 @@ private final class ParserWithExamples[T](delegate: Parser[T], exampleSource: Ex
|
||||||
apply(delegate)(example).resultEmpty.isValid
|
apply(delegate)(example).resultEmpty.isValid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class StringLiteral(str: String, start: Int) extends ValidParser[String] {
|
private final class StringLiteral(str: String, start: Int) extends ValidParser[String] {
|
||||||
assert(0 <= start && start < str.length)
|
assert(0 <= start && start < str.length)
|
||||||
|
|
||||||
def failMsg = "Expected '" + str + "'"
|
def failMsg = "Expected '" + str + "'"
|
||||||
def resultEmpty = mkFailure(failMsg)
|
def resultEmpty = mkFailure(failMsg)
|
||||||
def result = None
|
def result = None
|
||||||
def derive(c: Char) = if (str.charAt(start) == c) stringLiteral(str, start + 1) else new Invalid(resultEmpty)
|
|
||||||
|
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)))
|
def completions(level: Int) = Completions.single(Completion.suggestion(str.substring(start)))
|
||||||
override def toString = '"' + str + '"'
|
override def toString = '"' + str + '"'
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class CharacterClass(f: Char => Boolean, label: String) extends ValidParser[Char] {
|
private final class CharacterClass(f: Char => Boolean, label: String) extends ValidParser[Char] {
|
||||||
def result = None
|
def result = None
|
||||||
def resultEmpty = mkFailure("Expected " + label)
|
def resultEmpty = mkFailure("Expected " + label)
|
||||||
|
|
@ -765,6 +887,7 @@ private final class CharacterClass(f: Char => Boolean, label: String) extends Va
|
||||||
def completions(level: Int) = Completions.empty
|
def completions(level: Int) = Completions.empty
|
||||||
override def toString = "class(" + label + ")"
|
override def toString = "class(" + label + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class Optional[T](delegate: Parser[T]) extends ValidParser[Option[T]] {
|
private final class Optional[T](delegate: Parser[T]) extends ValidParser[Option[T]] {
|
||||||
def result = delegate.result map some.fn
|
def result = delegate.result map some.fn
|
||||||
def resultEmpty = Value(None)
|
def resultEmpty = Value(None)
|
||||||
|
|
@ -772,7 +895,14 @@ private final class Optional[T](delegate: Parser[T]) extends ValidParser[Option[
|
||||||
def completions(level: Int) = Completion.empty +: delegate.completions(level)
|
def completions(level: Int) = Completion.empty +: delegate.completions(level)
|
||||||
override def toString = delegate.toString + "?"
|
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]] {
|
|
||||||
|
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(0 <= min, "Minimum occurences must be non-negative")
|
||||||
assume(max >= min, "Minimum occurences must be less than the maximum occurences")
|
assume(max >= min, "Minimum occurences must be less than the maximum occurences")
|
||||||
|
|
||||||
|
|
@ -787,10 +917,10 @@ private final class Repeat[T](partial: Option[Parser[T]], repeated: Parser[T], m
|
||||||
case None => repeatDerive(c, accumulatedReverse)
|
case None => repeatDerive(c, accumulatedReverse)
|
||||||
}
|
}
|
||||||
|
|
||||||
def repeatDerive(c: Char, accRev: List[T]): Parser[Seq[T]] = repeat(Some(repeated derive c), repeated, scala.math.max(0, min - 1), max.decrement, accRev)
|
def repeatDerive(c: Char, accRev: List[T]): Parser[Seq[T]] =
|
||||||
|
repeat(Some(repeated derive c), repeated, scala.math.max(0, min - 1), max.decrement, accRev)
|
||||||
|
|
||||||
def completions(level: Int) =
|
def completions(level: Int) = {
|
||||||
{
|
|
||||||
def pow(comp: Completions, exp: Completions, n: Int): Completions =
|
def pow(comp: Completions, exp: Completions, n: Int): Completions =
|
||||||
if (n == 1) comp else pow(comp x exp, exp, n - 1)
|
if (n == 1) comp else pow(comp x exp, exp, n - 1)
|
||||||
|
|
||||||
|
|
@ -801,9 +931,10 @@ private final class Repeat[T](partial: Option[Parser[T]], repeated: Parser[T], m
|
||||||
case None => fin
|
case None => fin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def result = None
|
def result = None
|
||||||
lazy val resultEmpty: Result[Seq[T]] =
|
|
||||||
{
|
lazy val resultEmpty: Result[Seq[T]] = {
|
||||||
val partialAccumulatedOption =
|
val partialAccumulatedOption =
|
||||||
partial match {
|
partial match {
|
||||||
case None => Value(accumulatedReverse)
|
case None => Value(accumulatedReverse)
|
||||||
|
|
@ -811,13 +942,14 @@ private final class Repeat[T](partial: Option[Parser[T]], repeated: Parser[T], m
|
||||||
}
|
}
|
||||||
(partialAccumulatedOption app repeatedParseEmpty)(_ reverse_::: _)
|
(partialAccumulatedOption app repeatedParseEmpty)(_ reverse_::: _)
|
||||||
}
|
}
|
||||||
private def repeatedParseEmpty: Result[List[T]] =
|
|
||||||
{
|
private def repeatedParseEmpty: Result[List[T]] = {
|
||||||
if (min == 0)
|
if (min == 0)
|
||||||
Value(Nil)
|
Value(Nil)
|
||||||
else
|
else
|
||||||
// forced determinism
|
// forced determinism
|
||||||
for (value <- repeated.resultEmpty) yield makeList(min, value)
|
for (value <- repeated.resultEmpty) yield makeList(min, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def toString = "repeat(" + min + "," + max + "," + partial + "," + repeated + ")"
|
override def toString = "repeat(" + min + "," + max + "," + partial + "," + repeated + ")"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,19 @@ package complete
|
||||||
import Parser._
|
import Parser._
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.lang.Character.{ getType, MATH_SYMBOL, OTHER_SYMBOL, DASH_PUNCTUATION, OTHER_PUNCTUATION, MODIFIER_SYMBOL, CURRENCY_SYMBOL }
|
import java.lang.Character.{
|
||||||
|
getType,
|
||||||
|
MATH_SYMBOL,
|
||||||
|
OTHER_SYMBOL,
|
||||||
|
DASH_PUNCTUATION,
|
||||||
|
OTHER_PUNCTUATION,
|
||||||
|
MODIFIER_SYMBOL,
|
||||||
|
CURRENCY_SYMBOL
|
||||||
|
}
|
||||||
|
|
||||||
/** Provides standard implementations of commonly useful [[Parser]]s. */
|
/** Provides standard implementations of commonly useful [[Parser]]s. */
|
||||||
trait Parsers {
|
trait Parsers {
|
||||||
|
|
||||||
/** Matches the end of input, providing no useful result on success. */
|
/** Matches the end of input, providing no useful result on success. */
|
||||||
lazy val EOF = not(any, "Expected EOF")
|
lazy val EOF = not(any, "Expected EOF")
|
||||||
|
|
||||||
|
|
@ -24,10 +33,12 @@ trait Parsers {
|
||||||
lazy val Digit = charClass(_.isDigit, "digit") examples DigitSet
|
lazy val Digit = charClass(_.isDigit, "digit") examples DigitSet
|
||||||
|
|
||||||
/** Set containing Chars for hexadecimal digits 0-9 and A-F (but not a-f). */
|
/** Set containing Chars for hexadecimal digits 0-9 and A-F (but not a-f). */
|
||||||
lazy val HexDigitSet = Set('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')
|
lazy val HexDigitSet =
|
||||||
|
Set('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')
|
||||||
|
|
||||||
/** Parses a single hexadecimal digit (0-9, a-f, A-F). */
|
/** Parses a single hexadecimal digit (0-9, a-f, A-F). */
|
||||||
lazy val HexDigit = charClass(c => HexDigitSet(c.toUpper), "hex digit") examples HexDigitSet.map(_.toString)
|
lazy val HexDigit = charClass(c => HexDigitSet(c.toUpper), "hex digit") examples HexDigitSet.map(
|
||||||
|
_.toString)
|
||||||
|
|
||||||
/** Parses a single letter, according to Char.isLetter, into a Char. */
|
/** Parses a single letter, according to Char.isLetter, into a Char. */
|
||||||
lazy val Letter = charClass(_.isLetter, "letter")
|
lazy val Letter = charClass(_.isLetter, "letter")
|
||||||
|
|
@ -70,14 +81,22 @@ trait Parsers {
|
||||||
|
|
||||||
/** Returns true if `c` an operator character. */
|
/** Returns true if `c` an operator character. */
|
||||||
def isOpChar(c: Char) = !isDelimiter(c) && isOpType(getType(c))
|
def isOpChar(c: Char) = !isDelimiter(c) && isOpType(getType(c))
|
||||||
def isOpType(cat: Int) = cat match { case MATH_SYMBOL | OTHER_SYMBOL | DASH_PUNCTUATION | OTHER_PUNCTUATION | MODIFIER_SYMBOL | CURRENCY_SYMBOL => true; case _ => false }
|
|
||||||
|
def isOpType(cat: Int) = cat match {
|
||||||
|
case MATH_SYMBOL | OTHER_SYMBOL | DASH_PUNCTUATION | OTHER_PUNCTUATION | MODIFIER_SYMBOL |
|
||||||
|
CURRENCY_SYMBOL =>
|
||||||
|
true; case _ => false
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns true if `c` is a dash `-`, a letter, digit, or an underscore `_`. */
|
/** Returns true if `c` is a dash `-`, a letter, digit, or an underscore `_`. */
|
||||||
def isIDChar(c: Char) = isScalaIDChar(c) || c == '-'
|
def isIDChar(c: Char) = isScalaIDChar(c) || c == '-'
|
||||||
|
|
||||||
/** Returns true if `c` is a letter, digit, or an underscore `_`. */
|
/** Returns true if `c` is a letter, digit, or an underscore `_`. */
|
||||||
def isScalaIDChar(c: Char) = c.isLetterOrDigit || c == '_'
|
def isScalaIDChar(c: Char) = c.isLetterOrDigit || c == '_'
|
||||||
|
|
||||||
def isDelimiter(c: Char) = c match { case '`' | '\'' | '\"' | /*';' | */ ',' | '.' => true; case _ => false }
|
def isDelimiter(c: Char) = c match {
|
||||||
|
case '`' | '\'' | '\"' | /*';' | */ ',' | '.' => true; case _ => false
|
||||||
|
}
|
||||||
|
|
||||||
/** Matches a single character that is not a whitespace character. */
|
/** Matches a single character that is not a whitespace character. */
|
||||||
lazy val NotSpaceClass = charClass(!_.isWhitespace, "non-whitespace character")
|
lazy val NotSpaceClass = charClass(!_.isWhitespace, "non-whitespace character")
|
||||||
|
|
@ -120,17 +139,22 @@ trait Parsers {
|
||||||
|
|
||||||
/** Matches any character except a double quote or whitespace. */
|
/** Matches any character except a double quote or whitespace. */
|
||||||
lazy val NotDQuoteSpaceClass =
|
lazy val NotDQuoteSpaceClass =
|
||||||
charClass({ c: Char => (c != DQuoteChar) && !c.isWhitespace }, "non-double-quote-space character")
|
charClass({ c: Char =>
|
||||||
|
(c != DQuoteChar) && !c.isWhitespace
|
||||||
|
}, "non-double-quote-space character")
|
||||||
|
|
||||||
/** Matches any character except a double quote or backslash. */
|
/** Matches any character except a double quote or backslash. */
|
||||||
lazy val NotDQuoteBackslashClass =
|
lazy val NotDQuoteBackslashClass =
|
||||||
charClass({ c: Char => (c != DQuoteChar) && (c != BackslashChar) }, "non-double-quote-backslash character")
|
charClass({ c: Char =>
|
||||||
|
(c != DQuoteChar) && (c != BackslashChar)
|
||||||
|
}, "non-double-quote-backslash character")
|
||||||
|
|
||||||
/** Matches a single character that is valid somewhere in a URI. */
|
/** Matches a single character that is valid somewhere in a URI. */
|
||||||
lazy val URIChar = charClass(alphanum) | chars("_-!.~'()*,;:$&+=?/[]@%#")
|
lazy val URIChar = charClass(alphanum) | chars("_-!.~'()*,;:$&+=?/[]@%#")
|
||||||
|
|
||||||
/** Returns true if `c` is an ASCII letter or digit. */
|
/** Returns true if `c` is an ASCII letter or digit. */
|
||||||
def alphanum(c: Char) = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')
|
def alphanum(c: Char) =
|
||||||
|
('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param base the directory used for completion proposals (when the user presses the TAB key). Only paths under this
|
* @param base the directory used for completion proposals (when the user presses the TAB key). Only paths under this
|
||||||
|
|
@ -192,7 +216,9 @@ trait Parsers {
|
||||||
* A unicode escape begins with a backslash, followed by a `u` and 4 hexadecimal digits representing the unicode value.
|
* A unicode escape begins with a backslash, followed by a `u` and 4 hexadecimal digits representing the unicode value.
|
||||||
*/
|
*/
|
||||||
lazy val UnicodeEscape: Parser[Char] =
|
lazy val UnicodeEscape: Parser[Char] =
|
||||||
("u" ~> repeat(HexDigit, 4, 4)) map { seq => Integer.parseInt(seq.mkString, 16).toChar }
|
("u" ~> repeat(HexDigit, 4, 4)) map { seq =>
|
||||||
|
Integer.parseInt(seq.mkString, 16).toChar
|
||||||
|
}
|
||||||
|
|
||||||
/** Parses an unquoted, non-empty String value that cannot start with a double quote and cannot contain whitespace.*/
|
/** Parses an unquoted, non-empty String value that cannot start with a double quote and cannot contain whitespace.*/
|
||||||
lazy val NotQuoted = (NotDQuoteSpaceClass ~ OptNotSpace) map { case (c, s) => c.toString + s }
|
lazy val NotQuoted = (NotDQuoteSpaceClass ~ OptNotSpace) map { case (c, s) => c.toString + s }
|
||||||
|
|
@ -212,20 +238,25 @@ trait Parsers {
|
||||||
(rep ~ (sep ~> rep).*).map { case (x ~ xs) => x +: xs }
|
(rep ~ (sep ~> rep).*).map { case (x ~ xs) => x +: xs }
|
||||||
|
|
||||||
/** Wraps the result of `p` in `Some`.*/
|
/** Wraps the result of `p` in `Some`.*/
|
||||||
def some[T](p: Parser[T]): Parser[Option[T]] = p map { v => Some(v) }
|
def some[T](p: Parser[T]): Parser[Option[T]] = p map { v =>
|
||||||
|
Some(v)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies `f` to the result of `p`, transforming any exception when evaluating
|
* Applies `f` to the result of `p`, transforming any exception when evaluating
|
||||||
* `f` into a parse failure with the exception `toString` as the message.
|
* `f` into a parse failure with the exception `toString` as the message.
|
||||||
*/
|
*/
|
||||||
def mapOrFail[S, T](p: Parser[S])(f: S => T): Parser[T] =
|
def mapOrFail[S, T](p: Parser[S])(f: S => T): Parser[T] =
|
||||||
p flatMap { s => try { success(f(s)) } catch { case e: Exception => failure(e.toString) } }
|
p flatMap { s =>
|
||||||
|
try { success(f(s)) } catch { case e: Exception => failure(e.toString) }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a space-delimited, possibly empty sequence of arguments.
|
* Parses a space-delimited, possibly empty sequence of arguments.
|
||||||
* The arguments may use quotes and escapes according to [[StringBasic]].
|
* The arguments may use quotes and escapes according to [[StringBasic]].
|
||||||
*/
|
*/
|
||||||
def spaceDelimited(display: String): Parser[Seq[String]] = (token(Space) ~> token(StringBasic, display)).* <~ SpaceClass.*
|
def spaceDelimited(display: String): Parser[Seq[String]] =
|
||||||
|
(token(Space) ~> token(StringBasic, display)).* <~ SpaceClass.*
|
||||||
|
|
||||||
/** Applies `p` and uses `true` as the result if it succeeds and turns failure into a result of `false`. */
|
/** Applies `p` and uses `true` as the result if it succeeds and turns failure into a result of `false`. */
|
||||||
def flag[T](p: Parser[T]): Parser[Boolean] = (p ^^^ true) ?? false
|
def flag[T](p: Parser[T]): Parser[Boolean] = (p ^^^ true) ?? false
|
||||||
|
|
@ -236,13 +267,16 @@ trait Parsers {
|
||||||
* The parsers obtained in this way are separated by `sep`, whose result is discarded and only the sequence
|
* The parsers obtained in this way are separated by `sep`, whose result is discarded and only the sequence
|
||||||
* of values from the parsers returned by `p` is used for the result.
|
* of values from the parsers returned by `p` is used for the result.
|
||||||
*/
|
*/
|
||||||
def repeatDep[A](p: Seq[A] => Parser[A], sep: Parser[Any]): Parser[Seq[A]] =
|
def repeatDep[A](p: Seq[A] => Parser[A], sep: Parser[Any]): Parser[Seq[A]] = {
|
||||||
{
|
|
||||||
def loop(acc: Seq[A]): Parser[Seq[A]] = {
|
def loop(acc: Seq[A]): Parser[Seq[A]] = {
|
||||||
val next = (sep ~> p(acc)) flatMap { result => loop(acc :+ result) }
|
val next = (sep ~> p(acc)) flatMap { result =>
|
||||||
|
loop(acc :+ result)
|
||||||
|
}
|
||||||
next ?? acc
|
next ?? acc
|
||||||
}
|
}
|
||||||
p(Vector()) flatMap { first => loop(Seq(first)) }
|
p(Vector()) flatMap { first =>
|
||||||
|
loop(Seq(first))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Applies String.trim to the result of `p`. */
|
/** Applies String.trim to the result of `p`. */
|
||||||
|
|
@ -260,10 +294,12 @@ object Parsers extends Parsers
|
||||||
|
|
||||||
/** Provides common [[Parser]] implementations and helper methods.*/
|
/** Provides common [[Parser]] implementations and helper methods.*/
|
||||||
object DefaultParsers extends Parsers with ParserMain {
|
object DefaultParsers extends Parsers with ParserMain {
|
||||||
|
|
||||||
/** Applies parser `p` to input `s` and returns `true` if the parse was successful. */
|
/** Applies parser `p` to input `s` and returns `true` if the parse was successful. */
|
||||||
def matches(p: Parser[_], s: String): Boolean =
|
def matches(p: Parser[_], s: String): Boolean =
|
||||||
apply(p)(s).resultEmpty.isValid
|
apply(p)(s).resultEmpty.isValid
|
||||||
|
|
||||||
/** Returns `true` if `s` parses successfully according to [[ID]].*/
|
/** Returns `true` if `s` parses successfully according to [[ID]].*/
|
||||||
def validID(s: String): Boolean = matches(ID, s)
|
def validID(s: String): Boolean = matches(ID, s)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,20 @@ package sbt.internal.util
|
||||||
package complete
|
package complete
|
||||||
|
|
||||||
object ProcessError {
|
object ProcessError {
|
||||||
def apply(command: String, msgs: Seq[String], index: Int): String =
|
def apply(command: String, msgs: Seq[String], index: Int): String = {
|
||||||
{
|
|
||||||
val (line, modIndex) = extractLine(command, index)
|
val (line, modIndex) = extractLine(command, index)
|
||||||
val point = pointerSpace(command, modIndex)
|
val point = pointerSpace(command, modIndex)
|
||||||
msgs.mkString("\n") + "\n" + line + "\n" + point + "^"
|
msgs.mkString("\n") + "\n" + line + "\n" + point + "^"
|
||||||
}
|
}
|
||||||
def extractLine(s: String, i: Int): (String, Int) =
|
|
||||||
{
|
def extractLine(s: String, i: Int): (String, Int) = {
|
||||||
val notNewline = (c: Char) => c != '\n' && c != '\r'
|
val notNewline = (c: Char) => c != '\n' && c != '\r'
|
||||||
val left = takeRightWhile(s.substring(0, i))(notNewline)
|
val left = takeRightWhile(s.substring(0, i))(notNewline)
|
||||||
val right = s substring i takeWhile notNewline
|
val right = s substring i takeWhile notNewline
|
||||||
(left + right, left.length)
|
(left + right, left.length)
|
||||||
}
|
}
|
||||||
def takeRightWhile(s: String)(pred: Char => Boolean): String =
|
|
||||||
{
|
def takeRightWhile(s: String)(pred: Char => Boolean): String = {
|
||||||
def loop(i: Int): String =
|
def loop(i: Int): String =
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
s
|
s
|
||||||
|
|
@ -26,5 +25,7 @@ object ProcessError {
|
||||||
s.substring(i + 1)
|
s.substring(i + 1)
|
||||||
loop(s.length - 1)
|
loop(s.length - 1)
|
||||||
}
|
}
|
||||||
def pointerSpace(s: String, i: Int): String = (s take i) map { case '\t' => '\t'; case _ => ' ' } mkString ""
|
|
||||||
|
def pointerSpace(s: String, i: Int): String =
|
||||||
|
(s take i) map { case '\t' => '\t'; case _ => ' ' } mkString ""
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import Completion.{ token => ctoken, tokenDisplay }
|
||||||
sealed trait TokenCompletions {
|
sealed trait TokenCompletions {
|
||||||
def hideWhen(f: Int => Boolean): TokenCompletions
|
def hideWhen(f: Int => Boolean): TokenCompletions
|
||||||
}
|
}
|
||||||
|
|
||||||
object TokenCompletions {
|
object TokenCompletions {
|
||||||
private[sbt] abstract class Delegating extends TokenCompletions { outer =>
|
private[sbt] abstract class Delegating extends TokenCompletions { outer =>
|
||||||
def completions(seen: String, level: Int, delegate: Completions): Completions
|
def completions(seen: String, level: Int, delegate: Completions): Completions
|
||||||
|
|
@ -14,6 +15,7 @@ object TokenCompletions {
|
||||||
if (hide(level)) Completions.nil else outer.completions(seen, level, delegate)
|
if (hide(level)) Completions.nil else outer.completions(seen, level, delegate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] abstract class Fixed extends TokenCompletions { outer =>
|
private[sbt] abstract class Fixed extends TokenCompletions { outer =>
|
||||||
def completions(seen: String, level: Int): Completions
|
def completions(seen: String, level: Int): Completions
|
||||||
final def hideWhen(hide: Int => Boolean): TokenCompletions = new Fixed {
|
final def hideWhen(hide: Int => Boolean): TokenCompletions = new Fixed {
|
||||||
|
|
@ -22,17 +24,23 @@ object TokenCompletions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val default: TokenCompletions = mapDelegateCompletions((seen, level, c) => ctoken(seen, c.append))
|
val default: TokenCompletions = mapDelegateCompletions(
|
||||||
|
(seen, level, c) => ctoken(seen, c.append))
|
||||||
|
|
||||||
def displayOnly(msg: String): TokenCompletions = new Fixed {
|
def displayOnly(msg: String): TokenCompletions = new Fixed {
|
||||||
def completions(seen: String, level: Int) = Completions.single(Completion.displayOnly(msg))
|
def completions(seen: String, level: Int) = Completions.single(Completion.displayOnly(msg))
|
||||||
}
|
}
|
||||||
def overrideDisplay(msg: String): TokenCompletions = mapDelegateCompletions((seen, level, c) => tokenDisplay(display = msg, append = c.append))
|
|
||||||
|
def overrideDisplay(msg: String): TokenCompletions =
|
||||||
|
mapDelegateCompletions((seen, level, c) => tokenDisplay(display = msg, append = c.append))
|
||||||
|
|
||||||
def fixed(f: (String, Int) => Completions): TokenCompletions = new Fixed {
|
def fixed(f: (String, Int) => Completions): TokenCompletions = new Fixed {
|
||||||
def completions(seen: String, level: Int) = f(seen, level)
|
def completions(seen: String, level: Int) = f(seen, level)
|
||||||
}
|
}
|
||||||
def mapDelegateCompletions(f: (String, Int, Completion) => Completion): TokenCompletions = new Delegating {
|
|
||||||
def completions(seen: String, level: Int, delegate: Completions) = Completions(delegate.get.map(c => f(seen, level, c)))
|
def mapDelegateCompletions(f: (String, Int, Completion) => Completion): TokenCompletions =
|
||||||
|
new Delegating {
|
||||||
|
def completions(seen: String, level: Int, delegate: Completions) =
|
||||||
|
Completions(delegate.get.map(c => f(seen, level, c)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ private[sbt] final class TypeString(val base: String, val args: List[TypeString]
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] object TypeString {
|
private[sbt] object TypeString {
|
||||||
|
|
||||||
/** Makes the string representation of a type as returned by Manifest.toString more readable.*/
|
/** Makes the string representation of a type as returned by Manifest.toString more readable.*/
|
||||||
def cleanup(typeString: String): String =
|
def cleanup(typeString: String): String =
|
||||||
parse(typeString, typeStringParser) match {
|
parse(typeString, typeStringParser) match {
|
||||||
|
|
@ -59,6 +60,7 @@ private[sbt] object TypeString {
|
||||||
final val JavaPrefix = "java.lang."
|
final val JavaPrefix = "java.lang."
|
||||||
/* scala.collection.X -> X */
|
/* scala.collection.X -> X */
|
||||||
val ShortenCollection = Set("Seq", "List", "Set", "Map", "Iterable")
|
val ShortenCollection = Set("Seq", "List", "Set", "Map", "Iterable")
|
||||||
|
|
||||||
val TypeMap = Map(
|
val TypeMap = Map(
|
||||||
"java.io.File" -> "File",
|
"java.io.File" -> "File",
|
||||||
"java.net.URL" -> "URL",
|
"java.net.URL" -> "URL",
|
||||||
|
|
@ -69,12 +71,13 @@ private[sbt] object TypeString {
|
||||||
* A Parser that extracts basic structure from the string representation of a type from Manifest.toString.
|
* A Parser that extracts basic structure from the string representation of a type from Manifest.toString.
|
||||||
* This is rudimentary and essentially only decomposes the string into names and arguments for parameterized types.
|
* This is rudimentary and essentially only decomposes the string into names and arguments for parameterized types.
|
||||||
*/
|
*/
|
||||||
lazy val typeStringParser: Parser[TypeString] =
|
lazy val typeStringParser: Parser[TypeString] = {
|
||||||
{
|
|
||||||
def isFullScalaIDChar(c: Char) = isScalaIDChar(c) || c == '.' || c == '$'
|
def isFullScalaIDChar(c: Char) = isScalaIDChar(c) || c == '.' || c == '$'
|
||||||
lazy val fullScalaID = identifier(IDStart, charClass(isFullScalaIDChar, "Scala identifier character"))
|
lazy val fullScalaID =
|
||||||
|
identifier(IDStart, charClass(isFullScalaIDChar, "Scala identifier character"))
|
||||||
lazy val tpe: Parser[TypeString] =
|
lazy val tpe: Parser[TypeString] =
|
||||||
for (id <- fullScalaID; args <- ('[' ~> rep1sep(tpe, ',') <~ ']').?) yield new TypeString(id, args.toList.flatten)
|
for (id <- fullScalaID; args <- ('[' ~> rep1sep(tpe, ',') <~ ']').?)
|
||||||
|
yield new TypeString(id, args.toList.flatten)
|
||||||
tpe
|
tpe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,30 +5,41 @@ package sbt.internal.util
|
||||||
package complete
|
package complete
|
||||||
|
|
||||||
sealed trait UpperBound {
|
sealed trait UpperBound {
|
||||||
|
|
||||||
/** True if and only if the given value meets this bound.*/
|
/** True if and only if the given value meets this bound.*/
|
||||||
def >=(min: Int): Boolean
|
def >=(min: Int): Boolean
|
||||||
|
|
||||||
/** True if and only if this bound is one.*/
|
/** True if and only if this bound is one.*/
|
||||||
def isOne: Boolean
|
def isOne: Boolean
|
||||||
|
|
||||||
/** True if and only if this bound is zero.*/
|
/** True if and only if this bound is zero.*/
|
||||||
def isZero: Boolean
|
def isZero: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If this bound is zero or Infinite, `decrement` returns this bound.
|
* If this bound is zero or Infinite, `decrement` returns this bound.
|
||||||
* Otherwise, this bound is finite and greater than zero 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
|
def decrement: UpperBound
|
||||||
|
|
||||||
/** True if and only if this is unbounded.*/
|
/** True if and only if this is unbounded.*/
|
||||||
def isInfinite: Boolean
|
def isInfinite: Boolean
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents unbounded. */
|
/** Represents unbounded. */
|
||||||
case object Infinite extends UpperBound {
|
case object Infinite extends UpperBound {
|
||||||
|
|
||||||
/** All finite numbers meet this bound. */
|
/** All finite numbers meet this bound. */
|
||||||
def >=(min: Int) = true
|
def >=(min: Int) = true
|
||||||
|
|
||||||
def isOne = false
|
def isOne = false
|
||||||
def isZero = false
|
def isZero = false
|
||||||
def decrement = this
|
def decrement = this
|
||||||
def isInfinite = true
|
def isInfinite = true
|
||||||
|
|
||||||
override def toString = "Infinity"
|
override def toString = "Infinity"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a finite upper bound. The maximum allowed value is 'value', inclusive.
|
* Represents a finite upper bound. The maximum allowed value is 'value', inclusive.
|
||||||
* It must positive.
|
* It must positive.
|
||||||
|
|
@ -43,6 +54,7 @@ final case class Finite(value: Int) extends UpperBound {
|
||||||
def isInfinite = false
|
def isInfinite = false
|
||||||
override def toString = value.toString
|
override def toString = value.toString
|
||||||
}
|
}
|
||||||
|
|
||||||
object UpperBound {
|
object UpperBound {
|
||||||
implicit def intToFinite(i: Int): Finite = Finite(i)
|
implicit def intToFinite(i: Int): Finite = Finite(i)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,7 @@ object ParserTest extends Properties("Completing Parser") {
|
||||||
def checkOne(in: String, parser: Parser[_], expect: Completion): Prop =
|
def checkOne(in: String, parser: Parser[_], expect: Completion): Prop =
|
||||||
completions(parser, in, 1) == Completions.single(expect)
|
completions(parser, in, 1) == Completions.single(expect)
|
||||||
|
|
||||||
def checkAll(in: String, parser: Parser[_], expect: Completions): Prop =
|
def checkAll(in: String, parser: Parser[_], expect: Completions): Prop = {
|
||||||
{
|
|
||||||
val cs = completions(parser, in, 1)
|
val cs = completions(parser, in, 1)
|
||||||
("completions: " + cs) |: ("Expected: " + expect) |: (cs == expect: Prop)
|
("completions: " + cs) |: ("Expected: " + expect) |: (cs == expect: Prop)
|
||||||
}
|
}
|
||||||
|
|
@ -66,17 +65,20 @@ object ParserTest extends Properties("Completing Parser") {
|
||||||
(("token '" + in + "'") |: checkInv(in, nested)) &&
|
(("token '" + in + "'") |: checkInv(in, nested)) &&
|
||||||
(("display '" + in + "'") |: checkInv(in, nestedDisplay))
|
(("display '" + in + "'") |: checkInv(in, nestedDisplay))
|
||||||
|
|
||||||
def checkInv(in: String, parser: Parser[_]): Prop =
|
def checkInv(in: String, parser: Parser[_]): Prop = {
|
||||||
{
|
|
||||||
val cs = completions(parser, in, 1)
|
val cs = completions(parser, in, 1)
|
||||||
("completions: " + cs) |: (cs == Completions.nil: Prop)
|
("completions: " + cs) |: (cs == Completions.nil: Prop)
|
||||||
}
|
}
|
||||||
|
|
||||||
property("nested tokens a") = checkSingle("", Completion.token("", "a1"))(Completion.displayOnly("<a1>"))
|
property("nested tokens a") =
|
||||||
property("nested tokens a1") = checkSingle("a", Completion.token("a", "1"))(Completion.displayOnly("<a1>"))
|
checkSingle("", Completion.token("", "a1"))(Completion.displayOnly("<a1>"))
|
||||||
|
property("nested tokens a1") =
|
||||||
|
checkSingle("a", Completion.token("a", "1"))(Completion.displayOnly("<a1>"))
|
||||||
property("nested tokens a inv") = checkInvalid("b")
|
property("nested tokens a inv") = checkInvalid("b")
|
||||||
property("nested tokens b") = checkSingle("a1", Completion.token("", "b2"))(Completion.displayOnly("<b2>"))
|
property("nested tokens b") =
|
||||||
property("nested tokens b2") = checkSingle("a1b", Completion.token("b", "2"))(Completion.displayOnly("<b2>"))
|
checkSingle("a1", Completion.token("", "b2"))(Completion.displayOnly("<b2>"))
|
||||||
|
property("nested tokens b2") =
|
||||||
|
checkSingle("a1b", Completion.token("b", "2"))(Completion.displayOnly("<b2>"))
|
||||||
property("nested tokens b inv") = checkInvalid("a1a")
|
property("nested tokens b inv") = checkInvalid("a1a")
|
||||||
property("nested tokens c") = checkSingle("a1b2", Completion.suggestion("c3"))()
|
property("nested tokens c") = checkSingle("a1b2", Completion.suggestion("c3"))()
|
||||||
property("nested tokens c3") = checkSingle("a1b2c", Completion.suggestion("3"))()
|
property("nested tokens c3") = checkSingle("a1b2c", Completion.suggestion("3"))()
|
||||||
|
|
@ -86,14 +88,16 @@ object ParserTest extends Properties("Completing Parser") {
|
||||||
property("suggest port") = checkOne(" ", spacePort, Completion.displayOnly("<port>"))
|
property("suggest port") = checkOne(" ", spacePort, Completion.displayOnly("<port>"))
|
||||||
property("no suggest at end") = checkOne("asdf", "asdf", Completion.suggestion(""))
|
property("no suggest at end") = checkOne("asdf", "asdf", Completion.suggestion(""))
|
||||||
property("no suggest at token end") = checkOne("asdf", token("asdf"), Completion.suggestion(""))
|
property("no suggest at token end") = checkOne("asdf", token("asdf"), Completion.suggestion(""))
|
||||||
property("empty suggest for examples") = checkOne("asdf", any.+.examples("asdf", "qwer"), Completion.suggestion(""))
|
property("empty suggest for examples") =
|
||||||
property("empty suggest for examples token") = checkOne("asdf", token(any.+.examples("asdf", "qwer")), Completion.suggestion(""))
|
checkOne("asdf", any.+.examples("asdf", "qwer"), Completion.suggestion(""))
|
||||||
|
property("empty suggest for examples token") =
|
||||||
|
checkOne("asdf", token(any.+.examples("asdf", "qwer")), Completion.suggestion(""))
|
||||||
|
|
||||||
val colors = Set("blue", "green", "red")
|
val colors = Set("blue", "green", "red")
|
||||||
val base = (seen: Seq[String]) => token(ID examples (colors -- seen))
|
val base = (seen: Seq[String]) => token(ID examples (colors -- seen))
|
||||||
val sep = token(Space)
|
val sep = token(Space)
|
||||||
val repeat = repeatDep(base, sep)
|
val repeat = repeatDep(base, sep)
|
||||||
def completionStrings(ss: Set[String]): Completions = Completions(ss.map { s => Completion.token("", s) })
|
def completionStrings(ss: Set[String]) = Completions(ss map (Completion.token("", _)))
|
||||||
|
|
||||||
property("repeatDep no suggestions for bad input") = checkInv(".", repeat)
|
property("repeatDep no suggestions for bad input") = checkInv(".", repeat)
|
||||||
property("repeatDep suggest all") = checkAll("", repeat, completionStrings(colors))
|
property("repeatDep suggest all") = checkAll("", repeat, completionStrings(colors))
|
||||||
|
|
@ -116,7 +120,9 @@ object ParserExample {
|
||||||
val name = token("test")
|
val name = token("test")
|
||||||
val options = (ws ~> token("quick" | "failed" | "new")).*
|
val options = (ws ~> token("quick" | "failed" | "new")).*
|
||||||
val exampleSet = Set("am", "is", "are", "was", "were")
|
val exampleSet = Set("am", "is", "are", "was", "were")
|
||||||
val include = (ws ~> token(examples(notws.string, new FixedSetExamples(exampleSet), exampleSet.size, false))).*
|
val include = (ws ~> token(
|
||||||
|
examples(notws.string, new FixedSetExamples(exampleSet), exampleSet.size, false)
|
||||||
|
)).*
|
||||||
|
|
||||||
val t = name ~ options ~ include
|
val t = name ~ options ~ include
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,14 +64,16 @@ class FileExamplesTest extends UnitSpec {
|
||||||
var nestedDirectories: List[File] = _
|
var nestedDirectories: List[File] = _
|
||||||
|
|
||||||
def allRelativizedPaths: List[String] =
|
def allRelativizedPaths: List[String] =
|
||||||
(childFiles ++ childDirectories ++ nestedFiles ++ nestedDirectories).map(relativize(baseDir, _).get)
|
(childFiles ++ childDirectories ++ nestedFiles ++ nestedDirectories)
|
||||||
|
.map(relativize(baseDir, _).get)
|
||||||
|
|
||||||
def prefixedPathsOnly: List[String] =
|
def prefixedPathsOnly: List[String] =
|
||||||
allRelativizedPaths.filter(_ startsWith withCompletionPrefix).map(_ substring withCompletionPrefix.length)
|
allRelativizedPaths
|
||||||
|
.filter(_ startsWith withCompletionPrefix)
|
||||||
|
.map(_ substring withCompletionPrefix.length)
|
||||||
|
|
||||||
override def delayedInit(testBody: => Unit): Unit = {
|
override def delayedInit(testBody: => Unit): Unit = {
|
||||||
withTemporaryDirectory {
|
withTemporaryDirectory { tempDir =>
|
||||||
tempDir =>
|
|
||||||
createSampleDirStructure(tempDir)
|
createSampleDirStructure(tempDir)
|
||||||
fileExamples = new FileExamples(baseDir, withCompletionPrefix)
|
fileExamples = new FileExamples(baseDir, withCompletionPrefix)
|
||||||
testBody
|
testBody
|
||||||
|
|
@ -90,7 +92,8 @@ class FileExamplesTest extends UnitSpec {
|
||||||
baseDir = tempDir
|
baseDir = tempDir
|
||||||
}
|
}
|
||||||
|
|
||||||
private def toChildFiles(baseDir: File, files: List[String]): List[File] = files.map(new File(baseDir, _))
|
private def toChildFiles(baseDir: File, files: List[String]): List[File] =
|
||||||
|
files.map(new File(baseDir, _))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ class FixedSetExamplesTest extends UnitSpec {
|
||||||
|
|
||||||
"adding a prefix" should "produce a smaller set of examples with the prefix removed" in {
|
"adding a prefix" should "produce a smaller set of examples with the prefix removed" in {
|
||||||
val _ = new Examples {
|
val _ = new Examples {
|
||||||
fixedSetExamples.withAddedPrefix("f")() should contain theSameElementsAs (List("oo", "ool", "u"))
|
fixedSetExamples.withAddedPrefix("f")() should contain theSameElementsAs
|
||||||
|
(List("oo", "ool", "u"))
|
||||||
fixedSetExamples.withAddedPrefix("fo")() should contain theSameElementsAs (List("o", "ol"))
|
fixedSetExamples.withAddedPrefix("fo")() should contain theSameElementsAs (List("o", "ol"))
|
||||||
fixedSetExamples.withAddedPrefix("b")() should contain theSameElementsAs (List("ar"))
|
fixedSetExamples.withAddedPrefix("b")() should contain theSameElementsAs (List("ar"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ class ParserWithExamplesTest extends UnitSpec {
|
||||||
"listing only valid completions" should
|
"listing only valid completions" should
|
||||||
"use the delegate parser to remove invalid examples" in {
|
"use the delegate parser to remove invalid examples" in {
|
||||||
val _ = new ParserWithValidExamples {
|
val _ = new ParserWithValidExamples {
|
||||||
val validCompletions = Completions(Set(
|
val validCompletions = Completions(
|
||||||
|
Set(
|
||||||
suggestion("blue"),
|
suggestion("blue"),
|
||||||
suggestion("red")
|
suggestion("red")
|
||||||
))
|
))
|
||||||
|
|
@ -27,7 +28,8 @@ class ParserWithExamplesTest extends UnitSpec {
|
||||||
"listing valid completions in a derived parser" should
|
"listing valid completions in a derived parser" should
|
||||||
"produce only valid examples that start with the character of the derivation" in {
|
"produce only valid examples that start with the character of the derivation" in {
|
||||||
val _ = new ParserWithValidExamples {
|
val _ = new ParserWithValidExamples {
|
||||||
val derivedCompletions = Completions(Set(
|
val derivedCompletions = Completions(
|
||||||
|
Set(
|
||||||
suggestion("lue")
|
suggestion("lue")
|
||||||
))
|
))
|
||||||
parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions
|
parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions
|
||||||
|
|
@ -45,7 +47,8 @@ class ParserWithExamplesTest extends UnitSpec {
|
||||||
"listing valid and invalid completions in a derived parser" should
|
"listing valid and invalid completions in a derived parser" should
|
||||||
"produce only examples that start with the character of the derivation" in {
|
"produce only examples that start with the character of the derivation" in {
|
||||||
val _ = new parserWithAllExamples {
|
val _ = new parserWithAllExamples {
|
||||||
val derivedCompletions = Completions(Set(
|
val derivedCompletions = Completions(
|
||||||
|
Set(
|
||||||
suggestion("lue"),
|
suggestion("lue"),
|
||||||
suggestion("lock")
|
suggestion("lock")
|
||||||
))
|
))
|
||||||
|
|
@ -53,7 +56,12 @@ class ParserWithExamplesTest extends UnitSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ParserWithLazyExamples extends ParserExample(GrowableSourceOfExamples(), maxNumberOfExamples = 5, removeInvalidExamples = false)
|
class ParserWithLazyExamples
|
||||||
|
extends ParserExample(
|
||||||
|
GrowableSourceOfExamples(),
|
||||||
|
maxNumberOfExamples = 5,
|
||||||
|
removeInvalidExamples = false
|
||||||
|
)
|
||||||
|
|
||||||
class ParserWithValidExamples extends ParserExample(removeInvalidExamples = true)
|
class ParserWithValidExamples extends ParserExample(removeInvalidExamples = true)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ import scala.annotation.tailrec
|
||||||
import Formula.{ And, True }
|
import Formula.{ And, True }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Defines a propositional logic with negation as failure and only allows stratified rule sets (negation must be acyclic) in order to have a unique minimal model.
|
Defines a propositional logic with negation as failure and only allows stratified rule sets
|
||||||
|
(negation must be acyclic) in order to have a unique minimal model.
|
||||||
|
|
||||||
For example, this is not allowed:
|
For example, this is not allowed:
|
||||||
+ p :- not q
|
+ p :- not q
|
||||||
|
|
@ -24,7 +25,7 @@ as is this:
|
||||||
+ https://en.wikipedia.org/wiki/Propositional_logic
|
+ https://en.wikipedia.org/wiki/Propositional_logic
|
||||||
+ https://en.wikipedia.org/wiki/Stable_model_semantics
|
+ https://en.wikipedia.org/wiki/Stable_model_semantics
|
||||||
+ http://www.w3.org/2005/rules/wg/wiki/negation
|
+ http://www.w3.org/2005/rules/wg/wiki/negation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Disjunction (or) of the list of clauses. */
|
/** Disjunction (or) of the list of clauses. */
|
||||||
final case class Clauses(clauses: List[Clause]) {
|
final case class Clauses(clauses: List[Clause]) {
|
||||||
|
|
@ -36,16 +37,21 @@ final case class Clause(body: Formula, head: Set[Atom])
|
||||||
|
|
||||||
/** A literal is an [[Atom]] or its [[negation|Negated]]. */
|
/** A literal is an [[Atom]] or its [[negation|Negated]]. */
|
||||||
sealed abstract class Literal extends Formula {
|
sealed abstract class Literal extends Formula {
|
||||||
|
|
||||||
/** The underlying (positive) atom. */
|
/** The underlying (positive) atom. */
|
||||||
def atom: Atom
|
def atom: Atom
|
||||||
|
|
||||||
/** Negates this literal.*/
|
/** Negates this literal.*/
|
||||||
def unary_! : Literal
|
def unary_! : Literal
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A variable with name `label`. */
|
/** A variable with name `label`. */
|
||||||
final case class Atom(label: String) extends Literal {
|
final case class Atom(label: String) extends Literal {
|
||||||
def atom = this
|
def atom = this
|
||||||
def unary_! : Negated = Negated(this)
|
def unary_! : Negated = Negated(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A negated atom, in the sense of negation as failure, not logical negation.
|
* A negated atom, in the sense of negation as failure, not logical negation.
|
||||||
* That is, it is true if `atom` is not known/defined.
|
* That is, it is true if `atom` is not known/defined.
|
||||||
|
|
@ -60,6 +66,7 @@ final case class Negated(atom: Atom) extends Literal {
|
||||||
* This is less convenient when defining clauses, but is not less powerful.)
|
* This is less convenient when defining clauses, but is not less powerful.)
|
||||||
*/
|
*/
|
||||||
sealed abstract class Formula {
|
sealed abstract class Formula {
|
||||||
|
|
||||||
/** Constructs a clause that proves `atoms` when this formula is true. */
|
/** Constructs a clause that proves `atoms` when this formula is true. */
|
||||||
def proves(atom: Atom, atoms: Atom*): Clause = Clause(this, (atom +: atoms).toSet)
|
def proves(atom: Atom, atoms: Atom*): Clause = Clause(this, (atom +: atoms).toSet)
|
||||||
|
|
||||||
|
|
@ -72,28 +79,34 @@ sealed abstract class Formula {
|
||||||
case (a: Literal, And(bs)) => And(bs + a)
|
case (a: Literal, And(bs)) => And(bs + a)
|
||||||
case (a: Literal, b: Literal) => And(Set(a, b))
|
case (a: Literal, b: Literal) => And(Set(a, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Formula {
|
object Formula {
|
||||||
|
|
||||||
/** A conjunction of literals. */
|
/** A conjunction of literals. */
|
||||||
final case class And(literals: Set[Literal]) extends Formula {
|
final case class And(literals: Set[Literal]) extends Formula {
|
||||||
assert(literals.nonEmpty, "'And' requires at least one literal.")
|
assert(literals.nonEmpty, "'And' requires at least one literal.")
|
||||||
}
|
}
|
||||||
|
|
||||||
final case object True extends Formula
|
final case object True extends Formula
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Logic {
|
object Logic {
|
||||||
def reduceAll(clauses: List[Clause], initialFacts: Set[Literal]): Either[LogicException, Matched] =
|
def reduceAll(
|
||||||
|
clauses: List[Clause],
|
||||||
|
initialFacts: Set[Literal]
|
||||||
|
): Either[LogicException, Matched] =
|
||||||
reduce(Clauses(clauses), initialFacts)
|
reduce(Clauses(clauses), initialFacts)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the variables in the unique stable model for the program represented by `clauses` and `initialFacts`.
|
* Computes the variables in the unique stable model for the program represented by `clauses` and
|
||||||
* `clause` may not have any negative feedback (that is, negation is acyclic)
|
* `initialFacts`. `clause` may not have any negative feedback (that is, negation is acyclic)
|
||||||
* and `initialFacts` cannot be in the head of any clauses in `clause`.
|
* and `initialFacts` cannot be in the head of any clauses in `clause`.
|
||||||
* These restrictions ensure that the logic program has a unique minimal model.
|
* These restrictions ensure that the logic program has a unique minimal model.
|
||||||
*/
|
*/
|
||||||
def reduce(clauses: Clauses, initialFacts: Set[Literal]): Either[LogicException, Matched] =
|
def reduce(clauses: Clauses, initialFacts: Set[Literal]): Either[LogicException, Matched] = {
|
||||||
{
|
|
||||||
val (posSeq, negSeq) = separate(initialFacts.toSeq)
|
val (posSeq, negSeq) = separate(initialFacts.toSeq)
|
||||||
val (pos, neg) = (posSeq.toSet, negSeq.toSet)
|
val (pos, neg) = (posSeq.toSet, negSeq.toSet)
|
||||||
|
|
||||||
|
|
@ -113,13 +126,19 @@ object Logic {
|
||||||
* This isn't necessarily a problem, but the main sbt use cases expects
|
* This isn't necessarily a problem, but the main sbt use cases expects
|
||||||
* a proven atom to have at least one clause satisfied.
|
* a proven atom to have at least one clause satisfied.
|
||||||
*/
|
*/
|
||||||
private[this] def checkOverlap(clauses: Clauses, initialFacts: Set[Atom]): Option[InitialOverlap] = {
|
private[this] def checkOverlap(
|
||||||
|
clauses: Clauses,
|
||||||
|
initialFacts: Set[Atom]
|
||||||
|
): Option[InitialOverlap] = {
|
||||||
val as = atoms(clauses)
|
val as = atoms(clauses)
|
||||||
val initialOverlap = initialFacts.filter(as.inHead)
|
val initialOverlap = initialFacts.filter(as.inHead)
|
||||||
if (initialOverlap.nonEmpty) Some(new InitialOverlap(initialOverlap)) else None
|
if (initialOverlap.nonEmpty) Some(new InitialOverlap(initialOverlap)) else None
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def checkContradictions(pos: Set[Atom], neg: Set[Atom]): Option[InitialContradictions] = {
|
private[this] def checkContradictions(
|
||||||
|
pos: Set[Atom],
|
||||||
|
neg: Set[Atom]
|
||||||
|
): Option[InitialContradictions] = {
|
||||||
val contradictions = pos intersect neg
|
val contradictions = pos intersect neg
|
||||||
if (contradictions.nonEmpty) Some(new InitialContradictions(contradictions)) else None
|
if (contradictions.nonEmpty) Some(new InitialContradictions(contradictions)) else None
|
||||||
}
|
}
|
||||||
|
|
@ -129,14 +148,17 @@ object Logic {
|
||||||
val cycle = Dag.findNegativeCycle(graph(deps))
|
val cycle = Dag.findNegativeCycle(graph(deps))
|
||||||
if (cycle.nonEmpty) Some(new CyclicNegation(cycle)) else None
|
if (cycle.nonEmpty) Some(new CyclicNegation(cycle)) else None
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def graph(deps: Map[Atom, Set[Literal]]) = new Dag.DirectedSignedGraph[Atom] {
|
private[this] def graph(deps: Map[Atom, Set[Literal]]) = new Dag.DirectedSignedGraph[Atom] {
|
||||||
type Arrow = Literal
|
type Arrow = Literal
|
||||||
def nodes = deps.keys.toList
|
def nodes = deps.keys.toList
|
||||||
def dependencies(a: Atom) = deps.getOrElse(a, Set.empty).toList
|
def dependencies(a: Atom) = deps.getOrElse(a, Set.empty).toList
|
||||||
|
|
||||||
def isNegative(b: Literal) = b match {
|
def isNegative(b: Literal) = b match {
|
||||||
case Negated(_) => true
|
case Negated(_) => true
|
||||||
case Atom(_) => false
|
case Atom(_) => false
|
||||||
}
|
}
|
||||||
|
|
||||||
def head(b: Literal) = b.atom
|
def head(b: Literal) = b.atom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,30 +166,48 @@ object Logic {
|
||||||
(Map.empty[Atom, Set[Literal]] /: clauses.clauses) {
|
(Map.empty[Atom, Set[Literal]] /: clauses.clauses) {
|
||||||
case (m, Clause(formula, heads)) =>
|
case (m, Clause(formula, heads)) =>
|
||||||
val deps = literals(formula)
|
val deps = literals(formula)
|
||||||
(m /: heads) { (n, head) => n.updated(head, n.getOrElse(head, Set.empty) ++ deps) }
|
(m /: heads) { (n, head) =>
|
||||||
|
n.updated(head, n.getOrElse(head, Set.empty) ++ deps)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed abstract class LogicException(override val toString: String)
|
sealed abstract class LogicException(override val toString: String)
|
||||||
final class InitialContradictions(val literals: Set[Atom]) extends LogicException("Initial facts cannot be both true and false:\n\t" + literals.mkString("\n\t"))
|
|
||||||
final class InitialOverlap(val literals: Set[Atom]) extends LogicException("Initial positive facts cannot be implied by any clauses:\n\t" + literals.mkString("\n\t"))
|
final class InitialContradictions(val literals: Set[Atom])
|
||||||
final class CyclicNegation(val cycle: List[Literal]) extends LogicException("Negation may not be involved in a cycle:\n\t" + cycle.mkString("\n\t"))
|
extends LogicException(
|
||||||
|
"Initial facts cannot be both true and false:\n\t" + literals.mkString("\n\t")
|
||||||
|
)
|
||||||
|
|
||||||
|
final class InitialOverlap(val literals: Set[Atom])
|
||||||
|
extends LogicException(
|
||||||
|
"Initial positive facts cannot be implied by any clauses:\n\t" + literals.mkString("\n\t")
|
||||||
|
)
|
||||||
|
|
||||||
|
final class CyclicNegation(val cycle: List[Literal])
|
||||||
|
extends LogicException(
|
||||||
|
"Negation may not be involved in a cycle:\n\t" + cycle.mkString("\n\t")
|
||||||
|
)
|
||||||
|
|
||||||
/** Tracks proven atoms in the reverse order they were proved. */
|
/** Tracks proven atoms in the reverse order they were proved. */
|
||||||
final class Matched private (val provenSet: Set[Atom], reverseOrdered: List[Atom]) {
|
final class Matched private (val provenSet: Set[Atom], reverseOrdered: List[Atom]) {
|
||||||
def add(atoms: Set[Atom]): Matched = add(atoms.toList)
|
def add(atoms: Set[Atom]): Matched = add(atoms.toList)
|
||||||
|
|
||||||
def add(atoms: List[Atom]): Matched = {
|
def add(atoms: List[Atom]): Matched = {
|
||||||
val newOnly = atoms.filterNot(provenSet)
|
val newOnly = atoms.filterNot(provenSet)
|
||||||
new Matched(provenSet ++ newOnly, newOnly ::: reverseOrdered)
|
new Matched(provenSet ++ newOnly, newOnly ::: reverseOrdered)
|
||||||
}
|
}
|
||||||
|
|
||||||
def ordered: List[Atom] = reverseOrdered.reverse
|
def ordered: List[Atom] = reverseOrdered.reverse
|
||||||
override def toString = ordered.map(_.label).mkString("Matched(", ",", ")")
|
override def toString = ordered.map(_.label).mkString("Matched(", ",", ")")
|
||||||
}
|
}
|
||||||
|
|
||||||
object Matched {
|
object Matched {
|
||||||
val empty = new Matched(Set.empty, Nil)
|
val empty = new Matched(Set.empty, Nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Separates a sequence of literals into `(pos, neg)` atom sequences. */
|
/** Separates a sequence of literals into `(pos, neg)` atom sequences. */
|
||||||
private[this] def separate(lits: Seq[Literal]): (Seq[Atom], Seq[Atom]) = Util.separate(lits) {
|
private[this] def separate(lits: Seq[Literal]): (Seq[Atom], Seq[Atom]) =
|
||||||
|
Util.separate(lits) {
|
||||||
case a: Atom => Left(a)
|
case a: Atom => Left(a)
|
||||||
case Negated(n) => Right(n)
|
case Negated(n) => Right(n)
|
||||||
}
|
}
|
||||||
|
|
@ -176,20 +216,22 @@ object Logic {
|
||||||
* Finds clauses that have no body and thus prove their head.
|
* Finds clauses that have no body and thus prove their head.
|
||||||
* Returns `(<proven atoms>, <remaining unproven clauses>)`.
|
* Returns `(<proven atoms>, <remaining unproven clauses>)`.
|
||||||
*/
|
*/
|
||||||
private[this] def findProven(c: Clauses): (Set[Atom], List[Clause]) =
|
private[this] def findProven(c: Clauses): (Set[Atom], List[Clause]) = {
|
||||||
{
|
|
||||||
val (proven, unproven) = c.clauses.partition(_.body == True)
|
val (proven, unproven) = c.clauses.partition(_.body == True)
|
||||||
(proven.flatMap(_.head).toSet, unproven)
|
(proven.flatMap(_.head).toSet, unproven)
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def keepPositive(lits: Set[Literal]): Set[Atom] =
|
private[this] def keepPositive(lits: Set[Literal]): Set[Atom] =
|
||||||
lits.collect { case a: Atom => a }.toSet
|
lits.collect { case a: Atom => a }.toSet
|
||||||
|
|
||||||
// precondition: factsToProcess contains no contradictions
|
// precondition: factsToProcess contains no contradictions
|
||||||
@tailrec
|
@tailrec private[this] def reduce0(
|
||||||
private[this] def reduce0(clauses: Clauses, factsToProcess: Set[Literal], state: Matched): Matched =
|
clauses: Clauses,
|
||||||
|
factsToProcess: Set[Literal],
|
||||||
|
state: Matched
|
||||||
|
): Matched =
|
||||||
applyAll(clauses, factsToProcess) match {
|
applyAll(clauses, factsToProcess) match {
|
||||||
case None => // all of the remaining clauses failed on the new facts
|
case None => state // all of the remaining clauses failed on the new facts
|
||||||
state
|
|
||||||
case Some(applied) =>
|
case Some(applied) =>
|
||||||
val (proven, unprovenClauses) = findProven(applied)
|
val (proven, unprovenClauses) = findProven(applied)
|
||||||
val processedFacts = state add keepPositive(factsToProcess)
|
val processedFacts = state add keepPositive(factsToProcess)
|
||||||
|
|
@ -199,7 +241,8 @@ object Logic {
|
||||||
newState // no remaining clauses, done.
|
newState // no remaining clauses, done.
|
||||||
else {
|
else {
|
||||||
val unproven = Clauses(unprovenClauses)
|
val unproven = Clauses(unprovenClauses)
|
||||||
val nextFacts: Set[Literal] = if (newlyProven.nonEmpty) newlyProven.toSet else inferFailure(unproven)
|
val nextFacts: Set[Literal] =
|
||||||
|
if (newlyProven.nonEmpty) newlyProven.toSet else inferFailure(unproven)
|
||||||
reduce0(unproven, nextFacts, newState)
|
reduce0(unproven, nextFacts, newState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -208,12 +251,16 @@ object Logic {
|
||||||
* Finds negated atoms under the negation as failure rule and returns them.
|
* Finds negated atoms under the negation as failure rule and returns them.
|
||||||
* This should be called only after there are no more known atoms to be substituted.
|
* This should be called only after there are no more known atoms to be substituted.
|
||||||
*/
|
*/
|
||||||
private[this] def inferFailure(clauses: Clauses): Set[Literal] =
|
private[this] def inferFailure(clauses: Clauses): Set[Literal] = {
|
||||||
{
|
/* At this point, there is at least one clause and one of the following is the case as the
|
||||||
/* At this point, there is at least one clause and one of the following is the case as the result of the acyclic negation rule:
|
result of the acyclic negation rule:
|
||||||
i. there is at least one variable that occurs in a clause body but not in the head of a clause
|
i. there is at least one variable that occurs in a clause body but not in the head of a
|
||||||
ii. there is at least one variable that occurs in the head of a clause and does not transitively depend on a negated variable
|
clause
|
||||||
In either case, each such variable x cannot be proven true and therefore proves 'not x' (negation as failure, !x in the code).
|
ii. there is at least one variable that occurs in the head of a clause and does not
|
||||||
|
transitively depend on a negated variable
|
||||||
|
|
||||||
|
In either case, each such variable x cannot be proven true and therefore proves 'not x'
|
||||||
|
(negation as failure, !x in the code).
|
||||||
*/
|
*/
|
||||||
val allAtoms = atoms(clauses)
|
val allAtoms = atoms(clauses)
|
||||||
val newFacts: Set[Literal] = negated(allAtoms.triviallyFalse)
|
val newFacts: Set[Literal] = negated(allAtoms.triviallyFalse)
|
||||||
|
|
@ -238,11 +285,16 @@ object Logic {
|
||||||
* d :- a
|
* d :- a
|
||||||
*/
|
*/
|
||||||
@tailrec
|
@tailrec
|
||||||
def hasNegatedDependency(clauses: Seq[Clause], posDeps: Relation[Atom, Atom], negDeps: Relation[Atom, Atom]): List[Atom] =
|
def hasNegatedDependency(
|
||||||
|
clauses: Seq[Clause],
|
||||||
|
posDeps: Relation[Atom, Atom],
|
||||||
|
negDeps: Relation[Atom, Atom]
|
||||||
|
): List[Atom] =
|
||||||
clauses match {
|
clauses match {
|
||||||
case Seq() =>
|
case Seq() =>
|
||||||
// because cycles between positive literals are allowed, this isn't strictly a topological sort
|
// because cycles between positive literals are allowed, this isn't strictly a topological sort
|
||||||
Dag.topologicalSortUnchecked(negDeps._1s)(posDeps.reverse)
|
Dag.topologicalSortUnchecked(negDeps._1s)(posDeps.reverse)
|
||||||
|
|
||||||
case Clause(formula, head) +: tail =>
|
case Clause(formula, head) +: tail =>
|
||||||
// collect direct positive and negative literals and track them in separate graphs
|
// collect direct positive and negative literals and track them in separate graphs
|
||||||
val (pos, neg) = directDeps(formula)
|
val (pos, neg) = directDeps(formula)
|
||||||
|
|
@ -259,6 +311,7 @@ object Logic {
|
||||||
case Negated(a) => Right(a)
|
case Negated(a) => Right(a)
|
||||||
case a: Atom => Left(a)
|
case a: Atom => Left(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def literals(formula: Formula): Set[Literal] = formula match {
|
private[this] def literals(formula: Formula): Set[Literal] = formula match {
|
||||||
case And(lits) => lits
|
case And(lits) => lits
|
||||||
case l: Literal => Set(l)
|
case l: Literal => Set(l)
|
||||||
|
|
@ -278,10 +331,13 @@ object Logic {
|
||||||
|
|
||||||
/** Represents the set of atoms in the heads of clauses and in the bodies (formulas) of clauses. */
|
/** Represents the set of atoms in the heads of clauses and in the bodies (formulas) of clauses. */
|
||||||
final case class Atoms(inHead: Set[Atom], inFormula: Set[Atom]) {
|
final case class Atoms(inHead: Set[Atom], inFormula: Set[Atom]) {
|
||||||
|
|
||||||
/** Concatenates this with `as`. */
|
/** Concatenates this with `as`. */
|
||||||
def ++(as: Atoms): Atoms = Atoms(inHead ++ as.inHead, inFormula ++ as.inFormula)
|
def ++(as: Atoms): Atoms = Atoms(inHead ++ as.inHead, inFormula ++ as.inFormula)
|
||||||
|
|
||||||
/** Atoms that cannot be true because they do not occur in a head. */
|
/** Atoms that cannot be true because they do not occur in a head. */
|
||||||
def triviallyFalse: Set[Atom] = inFormula -- inHead
|
def triviallyFalse: Set[Atom] = inFormula -- inHead
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -297,8 +353,7 @@ object Logic {
|
||||||
* Postcondition: no atom in `facts` is present in the result
|
* Postcondition: no atom in `facts` is present in the result
|
||||||
* Postcondition: No clauses have an empty head
|
* Postcondition: No clauses have an empty head
|
||||||
*/
|
*/
|
||||||
def applyAll(cs: Clauses, facts: Set[Literal]): Option[Clauses] =
|
def applyAll(cs: Clauses, facts: Set[Literal]): Option[Clauses] = {
|
||||||
{
|
|
||||||
val newClauses =
|
val newClauses =
|
||||||
if (facts.isEmpty)
|
if (facts.isEmpty)
|
||||||
cs.clauses.filter(_.head.nonEmpty) // still need to drop clauses with an empty head
|
cs.clauses.filter(_.head.nonEmpty) // still need to drop clauses with an empty head
|
||||||
|
|
@ -307,8 +362,7 @@ object Logic {
|
||||||
if (newClauses.isEmpty) None else Some(Clauses(newClauses))
|
if (newClauses.isEmpty) None else Some(Clauses(newClauses))
|
||||||
}
|
}
|
||||||
|
|
||||||
def applyAll(c: Clause, facts: Set[Literal]): Option[Clause] =
|
def applyAll(c: Clause, facts: Set[Literal]): Option[Clause] = {
|
||||||
{
|
|
||||||
val atoms = facts.map(_.atom)
|
val atoms = facts.map(_.atom)
|
||||||
val newHead = c.head -- atoms // 3.
|
val newHead = c.head -- atoms // 3.
|
||||||
if (newHead.isEmpty) // 4. empty head
|
if (newHead.isEmpty) // 4. empty head
|
||||||
|
|
@ -318,8 +372,7 @@ object Logic {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Derives the formula that results from substituting `facts` into `formula`. */
|
/** Derives the formula that results from substituting `facts` into `formula`. */
|
||||||
@tailrec
|
@tailrec def substitute(formula: Formula, facts: Set[Literal]): Option[Formula] = formula match {
|
||||||
def substitute(formula: Formula, facts: Set[Literal]): Option[Formula] = formula match {
|
|
||||||
case And(lits) =>
|
case And(lits) =>
|
||||||
def negated(lits: Set[Literal]): Set[Literal] = lits.map(a => !a)
|
def negated(lits: Set[Literal]): Set[Literal] = lits.map(a => !a)
|
||||||
if (lits.exists(negated(facts))) // 2.
|
if (lits.exists(negated(facts))) // 2.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue