in scalding-quotation/src/main/scala/com/twitter/scalding/quotation/ProjectionMacro.scala [9:116]
def projections(params: List[Tree]): Tree = {
def typeName(t: Tree) =
TypeName(t.symbol.typeSignature.typeSymbol.fullName)
def accessor(m: TermName) =
Accessor(m.decodedName.toString)
def typeReference(tpe: Type) =
TypeReference(TypeName(tpe.typeSymbol.fullName))
def isFunction(t: Tree) =
Option(t.symbol)
.map {
_.typeSignature.erasure.typeSymbol.fullName
.contains("scala.Function")
}
.getOrElse(false)
def functionBodyProjections(param: Tree, inputs: List[Tree], body: Tree): List[Tree] = {
val inputSymbols = inputs.map(_.symbol).toSet
object ProjectionExtractor {
def unapply(t: Tree): Option[Tree] =
t match {
case q"$v.$m(..$params)" => unapply(v)
case q"$v.$m" if t.symbol.isMethod =>
if (inputSymbols.contains(v.symbol)) {
val p =
TypeReference(typeName(v))
.andThen(accessor(m), typeName(t))
Some(q"$p")
} else
unapply(v).map { n =>
q"$n.andThen(${accessor(m)}, ${typeName(t)})"
}
case t if inputSymbols.contains(t.symbol) =>
Some(q"${TypeReference(typeName(t))}")
case _ => None
}
}
def functionCall(func: Tree, params: List[Tree]): Tree = {
val paramProjections = params.flatMap(ProjectionExtractor.unapply)
q"""
$func match {
case f: _root_.com.twitter.scalding.quotation.QuotedFunction =>
f.quoted.projections.basedOn($paramProjections.toSet)
case _ =>
_root_.com.twitter.scalding.quotation.Projections(Set(..$paramProjections))
}
"""
}
collect(body) {
case q"$func.apply[..$t](..$params)" =>
functionCall(func, params)
case q"$func(..$params)" if isFunction(func) =>
functionCall(func, params)
case t @ ProjectionExtractor(p) =>
q"_root_.com.twitter.scalding.quotation.Projections(Set($p))"
}
}
def functionInstanceProjections(func: Tree): List[Tree] = {
val paramProjections =
func.symbol.typeSignature.typeArgs
.dropRight(1)
.map(typeReference)
q"""
$func match {
case f: _root_.com.twitter.scalding.quotation.QuotedFunction =>
f.quoted.projections
case _ =>
_root_.com.twitter.scalding.quotation.Projections(Set(..$paramProjections))
}
""" :: Nil
}
def methodProjections(method: Tree): List[Tree] = {
val paramRefs =
method.symbol.asMethod.paramLists.flatten
.map(param => typeReference(param.typeSignature))
q"${Projections(paramRefs.toSet)}" :: Nil
}
val nestedList =
params.flatMap {
case param @ q"(..$inputs) => $body" =>
functionBodyProjections(param, inputs, body)
case func if isFunction(func) =>
functionInstanceProjections(func)
case method if method.symbol != null && method.symbol.isMethod =>
methodProjections(method)
case other =>
Nil
}
q"_root_.com.twitter.scalding.quotation.Projections.flatten($nestedList)"
}