project/Generator.scala (106 lines of code) (raw):
package bijection
object Generator {
val pkg = "package com.twitter.bijection"
/* Example of the code generated:
implicit def tuple2[A1,B1,A2,B2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2]):
Bijection[(A1,B1),(A2,B2)] = new AbstractBijection[(A1,B1),(A2,B2)] {
def apply(in: (A1,B1)) = (ba(in._1), bb(in._2))
override def invert(out: (A2,B2)) = (ba.invert(out._1), bb.invert(out._2))
}
// For Injection:
implicit def tuple2[A1,B1,A2,B2](implicit ba: Injection[A1,A2], bb: Injection[B1,B2]):
Injection[(A1,B1),(A2,B2)] = new AbstractInjection[(A1,B1),(A2,B2)] {
def apply(in: (A1,B1)) = (ba(in._1), bb(in._2))
def invert(out: (A2,B2)) =
for(a <- ba.invert(out._1);
b <- bb.invert(out._2)
) yield (a,b)
}
// Injection of Tuple* to List:
implicit def tuple2ToList[A,B,C](implicit ba: Injection[A,C], bb: Injection[B,C]):
Injection[(A,B),List[C]] = new AbstractInjection[(A,B),List[C]] {
def apply(in: (A,B)) = List(ba(in._1), bb(in._2))
def invert(out: List[C]) = out match {
case a :: b :: Nil => for(a <- ba.invert(a); b <- bb.invert(b)) yield (a,b)
case _ => InversionFailure.failedAttempt(out)
}
}
*/
val lowerLetters = ('a' to 'z').toIndexedSeq
val upperLetters = ('A' to 'Z').toIndexedSeq
def bijectionParameter(i: Int, typeStr: String = "Bijection"): String = {
val l = lowerLetters(i)
val U = upperLetters(i)
"b" + l + ": " + typeStr + "[" + U + "1," + U + "2]"
}
def singleTypeParameter(typeStr: String, i: Int): String =
typeStr + "[" + upperLetters(i) + "]"
def injectionParameter(fromIdx: Int, toIdx: Int, typeStr: String = "Injection"): String = {
val l = lowerLetters(fromIdx)
val (fromU, toU) = (upperLetters(fromIdx), upperLetters(toIdx))
"b" + l + ": " + typeStr + "[" + fromU + "," + toU + "]"
}
def typeList(cnt: Int, suffix: String) =
upperLetters.slice(0, cnt) map { l =>
l.toString + suffix
} mkString (",")
def tupleBijectionType(cnt: Int, typeStr: String = "Bijection"): String =
typeStr + "[(" + typeList(cnt, "1") + "), (" + typeList(cnt, "2") + ")]"
def applyPart(i: Int): String = "b" + lowerLetters(i) + "(in._" + (i + 1) + ")"
def invertPart(i: Int): String = "b" + lowerLetters(i) + ".invert(out._" + (i + 1) + ")"
def forInvertPart(i: Int): String = lowerLetters(i) + " <- " + invertPart(i)
def expressionTuple(cnt: Int, sep: String = ", ")(part: (Int) => String) =
(0 until cnt) map { part(_) } mkString ("(", sep, ")")
def applyMethod(cnt: Int) =
"def apply(in: (" + typeList(cnt, "1") + ")) = " + expressionTuple(cnt) { applyPart _ }
def invertMethod(cnt: Int) =
"override def invert(out: (" + typeList(cnt, "2") + ")) = " + expressionTuple(cnt) {
invertPart _
}
// Here we put it all together:
def implicitTuple(cnt: Int): String =
" implicit def tuple" + cnt + "[" + typeList(cnt, "1") + "," + typeList(cnt, "2") +
"](implicit " + ((0 until cnt) map {
bijectionParameter(_, "ImplicitBijection")
} mkString (", ")) + "):\n " +
tupleBijectionType(cnt) + " = new Abstract" + tupleBijectionType(cnt) + " {\n" +
" " + applyMethod(cnt) + "\n" +
" " + invertMethod(cnt) + "\n" +
" }"
def invertInj(cnt: Int) =
"def invert(out: (" + typeList(cnt, "2") + ")) = for" +
expressionTuple(cnt, "; ") { forInvertPart _ } + " yield (" +
lowerLetters.slice(0, cnt).mkString(",") + ")"
// For the Injections:
def implicitTupleInj(cnt: Int): String =
" implicit def tuple" + cnt + "[" + typeList(cnt, "1") + "," + typeList(cnt, "2") +
"](implicit " + ((0 until cnt) map {
bijectionParameter(_, "Injection")
} mkString (", ")) + "):\n " +
tupleBijectionType(cnt, "Injection") + " = new Abstract" + tupleBijectionType(
cnt,
"Injection"
) + " {\n" +
" " + applyMethod(cnt) + "\n" +
" " + invertInj(cnt) + "\n" +
" }"
def toListInjectionType(cnt: Int): String =
"Injection[(" + typeList(cnt, "") + ")," + singleTypeParameter("List", cnt) + "]"
def toListMethod(cnt: Int) =
"def apply(in: (" + typeList(cnt, "") + ")) = List" + expressionTuple(cnt) { applyPart(_) }
def fromListInvertPart(i: Int): String = {
val letter = lowerLetters(i)
"b" + letter + ".invert(" + letter + ")"
}
def forInvertFromListPart(i: Int): String = lowerLetters(i) + " <- " + fromListInvertPart(i)
def fromListMethod(cnt: Int) = {
val letters = lowerLetters.slice(0, cnt)
val someCase = {
" case " + letters.mkString(" :: ") + " :: Nil => " + "for" +
expressionTuple(cnt, "; ") { forInvertFromListPart(_) } + " yield (" +
letters.mkString(",") + ")\n"
}
val noneCase = " case _ => InversionFailure.failedAttempt(out)\n"
"def invert(out: " + singleTypeParameter("List", cnt) + ") = out match {\n" +
someCase + noneCase + " }"
}
def implicitTupleToCollInj(cnt: Int): String = {
val implicitParams: String = (0 until cnt).map { injectionParameter(_, cnt) }.mkString(", ")
" implicit def tuple" + cnt + "ToList[" + typeList(
cnt + 1,
""
) + "](implicit " + implicitParams + "):\n " +
toListInjectionType(cnt) + " = new Abstract" + toListInjectionType(cnt) + " {\n" +
" " + toListMethod(cnt) + "\n" +
" " + fromListMethod(cnt) + "\n" +
" }"
}
def generate = {
val b = new StringBuffer
b.append("// Autogenerated code DO NOT EDIT BY HAND\n")
b.append(pkg).append("\n")
b.append("\ntrait GeneratedTupleBijections extends LowPriorityBijections {\n")
(2 to 22).foreach { cnt => b.append(implicitTuple(cnt)).append("\n") }
b.append("}\n")
b.append("\ntrait GeneratedTupleCollectionInjections extends LowPriorityInjections {\n")
(2 to 22).foreach { cnt => b.append(implicitTupleToCollInj(cnt)).append("\n") }
b.append("}\n")
b.append("\ntrait GeneratedTupleInjections extends GeneratedTupleCollectionInjections {\n")
(2 to 22).foreach { cnt => b.append(implicitTupleInj(cnt)).append("\n") }
b.append("}\n")
b.toString
}
}