-
-
Save paulp/5301321 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package tuple | |
import language.experimental.macros | |
import language.dynamics | |
import scala.reflect.macros.Context | |
sealed trait ~[+A, +B] | |
private[tuple] object TupleMacros { | |
def apply(c: Context)(exprs: c.Expr[Any]*): c.Expr[Tuple[_]] = { | |
import c.universe._ | |
val termList = exprs.map(_.tree).toList | |
val typeList = termList.map(_.tpe) | |
val tildedList = { | |
val tildeType = typeOf[~[_,_]].typeSymbol | |
val tildeIdent = Ident(tildeType) | |
val trees = typeList.map(t => TypeTree(t)) | |
def tildeTogether(t1: Tree, t2: Tree): Tree = AppliedTypeTree(tildeIdent, List(t1, t2)) | |
trees.tail.foldLeft[Tree](trees.head)((t1: Tree, t2: Tree) => tildeTogether(t1, t2)) | |
} | |
def extractType[T: TypeTag](l: List[Tree]) = l.partition(_.tpe <:< typeOf[T]) | |
val arrayIdent = Ident(weakTypeOf[Array[_]].typeSymbol) | |
val arrayCompanionIdent = Ident(typeOf[Array[_]].typeSymbol.companionSymbol) | |
def typeIdent[T: TypeTag] = Ident(typeOf[T].typeSymbol) | |
def arrayType[T: TypeTag] = AppliedTypeTree(arrayIdent, List(typeIdent[T])) | |
def cast(casted: Tree, targetType: Tree) = TypeApply(Select(casted, newTermName("asInstanceOf")), List(targetType)) | |
def applyToArray(args: List[Tree]) = Apply(Select(arrayCompanionIdent, newTermName("apply")), args) | |
def nullLiteral = Literal(Constant(null)) | |
def arrayOrNull[T: TypeTag](args: List[Tree]) = | |
if (args.isEmpty) cast(nullLiteral, arrayType[T]) | |
else applyToArray(args) | |
def buildArgument[T: TypeTag](input: List[Tree]): (Tree, List[Tree]) = { | |
val (matching, nonMatching) = extractType[T](input) | |
(arrayOrNull[T](matching), nonMatching) | |
} | |
val (unitArrayTree, r1) = buildArgument[Unit](termList) | |
val (booleanArrayTree, r2) = buildArgument[Boolean](r1) | |
val (byteArrayTree, r3) = buildArgument[Byte](r2) | |
val (charArrayTree, r4) = buildArgument[Char](r3) | |
val (shortArrayTree, r5) = buildArgument[Short](r4) | |
val (intArrayTree, r6) = buildArgument[Int](r5) | |
val (longArrayTree, r7) = buildArgument[Long](r6) | |
val (floatArrayTree, r8) = buildArgument[Float](r7) | |
val (doubleArrayTree, r9) = buildArgument[Double](r8) | |
val refArrayTree = arrayOrNull[AnyRef](r9) | |
val tupleIdent = Ident(weakTypeOf[Tuple[_]].typeSymbol) | |
val objectCreationTree = Apply( | |
Select(New(AppliedTypeTree(tupleIdent, List(tildedList))), nme.CONSTRUCTOR), | |
List( | |
unitArrayTree, | |
booleanArrayTree, | |
byteArrayTree, | |
charArrayTree, | |
shortArrayTree, | |
intArrayTree, | |
longArrayTree, | |
floatArrayTree, | |
doubleArrayTree, | |
refArrayTree | |
) | |
) | |
c.Expr[Tuple[_]](objectCreationTree) | |
} | |
def get[T: c.WeakTypeTag](c: Context { type PrefixType <: Tuple[_] })(nameExpr: c.Expr[String]) = { | |
import c.universe._ | |
val Literal(Constant(name: String)) = nameExpr.tree | |
val tildeType = weakTypeOf[~[_, _]].typeConstructor | |
def isTildeType(tpe: Type) = tpe.erasure =:= tildeType.erasure | |
def listify(tpe: Type, current: List[Type]): List[Type] = tpe match { | |
case TypeRef(_, _, List(first, second)) if isTildeType(tpe) => listify(first, second :: current) | |
case end => end :: current | |
} | |
val typeList = listify(weakTypeOf[T], Nil) | |
if (name.head != '_') throw new IllegalArgumentException() | |
val index = name.tail.toInt - 1 | |
if (index >= typeList.size) throw new IllegalArgumentException() | |
if (index < 0) throw new IllegalArgumentException() | |
val tpe = typeList(index) | |
val indexInBin = | |
if (tpe <:< typeOf[AnyRef]) typeList.take(index).count(_ <:< typeOf[AnyRef]) | |
else typeList.take(index).count(_.erasure <:< tpe.erasure) | |
val indexExpr = c.Expr[Int](Literal(Constant(indexInBin))) | |
def cast(casted: Tree, targetType: Tree) = TypeApply(Select(casted, newTermName("asInstanceOf")), List(targetType)) | |
if (tpe <:< typeOf[Unit]) reify { c.prefix.splice.units(indexExpr.splice) } | |
else if (tpe <:< typeOf[Boolean]) reify { c.prefix.splice.booleans(indexExpr.splice) } | |
else if (tpe <:< typeOf[Byte]) reify { c.prefix.splice.bytes(indexExpr.splice) } | |
else if (tpe <:< typeOf[Char]) reify { c.prefix.splice.chars(indexExpr.splice) } | |
else if (tpe <:< typeOf[Short]) reify { c.prefix.splice.shorts(indexExpr.splice) } | |
else if (tpe <:< typeOf[Int]) reify { c.prefix.splice.ints(indexExpr.splice) } | |
else if (tpe <:< typeOf[Float]) reify { c.prefix.splice.floats(indexExpr.splice) } | |
else if (tpe <:< typeOf[Double]) reify { c.prefix.splice.doubles(indexExpr.splice) } | |
else c.Expr[AnyRef](cast(reify(c.prefix.splice.refs(indexExpr.splice)).tree, TypeTree(tpe))) | |
} | |
} | |
class Tuple[+T]( | |
val units: Array[Unit], | |
val booleans: Array[Boolean], | |
val bytes: Array[Byte], | |
val chars: Array[Char], | |
val shorts: Array[Short], | |
val ints: Array[Int], | |
val longs: Array[Long], | |
val floats: Array[Float], | |
val doubles: Array[Double], | |
val refs: Array[AnyRef] | |
) extends Dynamic { | |
def selectDynamic(nameExpr: String) = macro TupleMacros.get[T] | |
} | |
object Tuple { | |
def apply(exprs: Any *) = macro TupleMacros.apply | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment