diff options
author | Denys Shabalin <denys.shabalin@typesafe.com> | 2014-01-22 19:47:01 +0100 |
---|---|---|
committer | Denys Shabalin <denys.shabalin@typesafe.com> | 2014-01-23 13:51:48 +0100 |
commit | 01f9ab52111712a6f4bc677415e9ad7a3bfe41a2 (patch) | |
tree | 10864e589185f380d0979e3ac3f3fc842dd31555 /src/compiler | |
parent | ef118b8c86944d6fe3520782335835b9ec96590a (diff) | |
download | scala-01f9ab52111712a6f4bc677415e9ad7a3bfe41a2.tar.gz scala-01f9ab52111712a6f4bc677415e9ad7a3bfe41a2.tar.bz2 scala-01f9ab52111712a6f4bc677415e9ad7a3bfe41a2.zip |
Refactor reification of high-cardinality holes
New approach makes iterated function much more clear through aggressive
code reuse, recursion and large descriptive comment on top of it.
Diffstat (limited to 'src/compiler')
-rw-r--r-- | src/compiler/scala/tools/reflect/quasiquotes/Holes.scala | 67 |
1 files changed, 51 insertions, 16 deletions
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala index 8a54519401..6e1dfcd5e7 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala @@ -41,12 +41,14 @@ trait Holes { self: Quasiquotes => (tpe <:< flagsType) || (tpe <:< symbolType) protected def isBottomType(tpe: Type) = tpe <:< NothingClass.tpe || tpe <:< NullClass.tpe + protected def extractIterableTParam(tpe: Type) = + IterableTParam.asSeenFrom(tpe, IterableClass) protected def stripIterable(tpe: Type, limit: Option[Cardinality] = None): (Cardinality, Type) = if (limit.map { _ == NoDot }.getOrElse { false }) (NoDot, tpe) else if (tpe != null && !isIterableType(tpe)) (NoDot, tpe) else if (isBottomType(tpe)) (NoDot, tpe) else { - val targ = IterableTParam.asSeenFrom(tpe, IterableClass) + val targ = extractIterableTParam(tpe) val (card, innerTpe) = stripIterable(targ, limit.map { _.pred }) (card.succ, innerTpe) } @@ -88,7 +90,7 @@ trait Holes { self: Quasiquotes => else if (isLiftableType(itpe)) lifted(itpe)(tree) else global.abort("unreachable") if (card == NoDot) inner(strippedTpe)(splicee) - else iterated(card, strippedTpe, inner(strippedTpe))(splicee) + else iterated(card, splicee, splicee.tpe) } val pos = splicee.pos @@ -118,21 +120,54 @@ trait Holes { self: Quasiquotes => atPos(tree.pos)(lifted) } - protected def iterated(card: Cardinality, tpe: Type, elementTransform: Tree => Tree = identity)(tree: Tree): Tree = { - assert(card != NoDot) - def reifyIterable(tree: Tree, n: Cardinality): Tree = { - def loop(tree: Tree, n: Cardinality): Tree = - if (n == NoDot) elementTransform(tree) - else { - val x: TermName = c.freshName() - val wrapped = reifyIterable(Ident(x), n.pred) - val xToWrapped = Function(List(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree)), wrapped) - Select(Apply(Select(tree, nme.map), List(xToWrapped)), nme.toList) - } - if (tree.tpe != null && (tree.tpe <:< listTreeType || tree.tpe <:< listListTreeType)) tree - else atPos(tree.pos)(loop(tree, n)) + protected def toList(tree: Tree, tpe: Type): Tree = + if (isListType(tpe)) tree + else Select(tree, nme.toList) + + protected def mapF(tree: Tree, f: Tree => Tree): Tree = + if (f(Ident(TermName("x"))) equalsStructure Ident(TermName("x"))) tree + else { + val x: TermName = c.freshName() + // q"$tree.map { $x => ${f(Ident(x))} }" + Apply(Select(tree, nme.map), + Function(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree) :: Nil, + f(Ident(x))) :: Nil) } - reifyIterable(tree, card) + + protected object IterableType { + def unapply(tpe: Type): Option[Type] = + if (isIterableType(tpe)) Some(extractIterableTParam(tpe)) else None + } + + protected object LiftedType { + def unapply(tpe: Type): Option[Tree => Tree] = + if (tpe <:< treeType) Some(t => t) + else if (isLiftableType(tpe)) Some(lifted(tpe)(_)) + else None + } + + /** Map high-cardinality splice onto an expression that eveluates as a list of given cardinality. + * + * All possible combinations of representations are given in the table below: + * + * input output for T <: Tree output for T: Liftable + * + * ..${x: List[T]} x x.map(lift) + * ..${x: Iterable[T]} x.toList x.toList.map(lift) + * + * ...${x: List[List[T]]} x x.map { _.map(lift) } } + * ...${x: List[Iterable[T]} x.map { _.toList } x.map { _.toList.map(lift) } } + * ...${x: Iterable[List[T]]} x.toList x.toList.map { _.map(lift) } + * ...${x: Iterable[Iterable[T]]} x.toList { _.toList } x.toList.map { _.toList.map(lift) } + * + * As you can see table is quite repetetive. Middle column is equivalent to the right one with + * lift function equal to identity. Cases with List are equivalent to Iterated ones (due to + * the fact that toList method call is just an identity we can omit it altogether.) + */ + protected def iterated(card: Cardinality, tree: Tree, tpe: Type): Tree = (card, tpe) match { + case (DotDot, tpe @ IterableType(LiftedType(lift))) => mapF(toList(tree, tpe), lift) + case (DotDotDot, tpe @ IterableType(inner)) => mapF(toList(tree, tpe), t => iterated(DotDot, t, inner)) + case _ => global.abort("unreachable") } } |