Format in-sourced util modules

This commit is contained in:
Dale Wijnand 2017-07-07 10:43:52 +01:00
parent 9494967e05
commit dc2d4d613f
No known key found for this signature in database
GPG Key ID: 4F256E3D151DF5EF
38 changed files with 1975 additions and 1239 deletions

View File

@ -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))
} }

View File

@ -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)
} }

View File

@ -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

View File

@ -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]

View File

@ -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))
} }

View File

@ -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))
} }
} }

View File

@ -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
} }
} }

View File

@ -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)))
} }

View File

@ -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)
} }

View File

@ -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 &lt;:&lt; Option[String] and None &lt;: Option[Int]. * because None &lt;:&lt; Option[String] and None &lt;: 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

View File

@ -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)
} }

View File

@ -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); () }
} }
} }

View File

@ -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
} }

View File

@ -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)
} }

View File

@ -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 =>

View File

@ -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"))

View File

@ -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._

View File

@ -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) {

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)))
} }

View File

@ -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 }
} }

View File

@ -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)
} }

View File

@ -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))

View File

@ -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 + ")"
} }

View File

@ -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)
} }

View File

@ -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 ""
} }

View File

@ -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)))
} }
} }

View File

@ -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
} }
} }

View File

@ -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)
} }

View File

@ -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

View File

@ -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, _))
} }
} }

View File

@ -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"))
} }

View File

@ -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)

View File

@ -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.