singleton.ops.impl.GeneralMacros.scala Maven / Gradle / Ivy
The newest version!
package singleton.ops.impl
import shapeless.tag
import shapeless.tag.@@
import singleton.twoface.impl.TwoFaceAny
import scala.reflect.macros.whitebox
trait GeneralMacros {
val c: whitebox.Context
import c.universe._
val defaultAnnotatedSym : Option[TypeSymbol] =
if (c.enclosingImplicits.isEmpty) None else c.enclosingImplicits.last.pt match {
case TypeRef(_,sym,_) => Some(sym.asType)
case x => Some(x.typeSymbol.asType)
}
object funcTypes {
val Arg = symbolOf[OpId.Arg]
val AcceptNonLiteral = symbolOf[OpId.AcceptNonLiteral]
val GetArg = symbolOf[OpId.GetArg]
val GetLHSArg = symbolOf[OpId.GetLHSArg]
val ImplicitFound = symbolOf[OpId.ImplicitFound]
val EnumCount = symbolOf[OpId.EnumCount]
val Id = symbolOf[OpId.Id]
val ToNat = symbolOf[OpId.ToNat]
val ToChar = symbolOf[OpId.ToChar]
val ToInt = symbolOf[OpId.ToInt]
val ToLong = symbolOf[OpId.ToLong]
val ToFloat = symbolOf[OpId.ToFloat]
val ToDouble = symbolOf[OpId.ToDouble]
val ToString = symbolOf[OpId.ToString]
val ToSymbol = symbolOf[OpId.ToSymbol]
val IsNat = symbolOf[OpId.IsNat]
val IsChar = symbolOf[OpId.IsChar]
val IsInt = symbolOf[OpId.IsInt]
val IsLong = symbolOf[OpId.IsLong]
val IsFloat = symbolOf[OpId.IsFloat]
val IsDouble = symbolOf[OpId.IsDouble]
val IsString = symbolOf[OpId.IsString]
val IsBoolean = symbolOf[OpId.IsBoolean]
val IsSymbol = symbolOf[OpId.IsSymbol]
val Negate = symbolOf[OpId.Negate]
val Abs = symbolOf[OpId.Abs]
val NumberOfLeadingZeros = symbolOf[OpId.NumberOfLeadingZeros]
val Floor = symbolOf[OpId.Floor]
val Ceil = symbolOf[OpId.Ceil]
val Round = symbolOf[OpId.Round]
val Sin = symbolOf[OpId.Sin]
val Cos = symbolOf[OpId.Cos]
val Tan = symbolOf[OpId.Tan]
val Sqrt = symbolOf[OpId.Sqrt]
val Log = symbolOf[OpId.Log]
val Log10 = symbolOf[OpId.Log10]
val Reverse = symbolOf[OpId.Reverse]
val ! = symbolOf[OpId.!]
val Require = symbolOf[OpId.Require]
val ITE = symbolOf[OpId.ITE]
val IsNonLiteral = symbolOf[OpId.IsNonLiteral]
val GetType = symbolOf[OpId.GetType]
val ==> = symbolOf[OpId.==>]
val + = symbolOf[OpId.+]
val - = symbolOf[OpId.-]
val * = symbolOf[OpId.*]
val / = symbolOf[OpId./]
val % = symbolOf[OpId.%]
val < = symbolOf[OpId.<]
val > = symbolOf[OpId.>]
val <= = symbolOf[OpId.<=]
val >= = symbolOf[OpId.>=]
val == = symbolOf[OpId.==]
val != = symbolOf[OpId.!=]
val && = symbolOf[OpId.&&]
val || = symbolOf[OpId.||]
val Pow = symbolOf[OpId.Pow]
val Min = symbolOf[OpId.Min]
val Max = symbolOf[OpId.Max]
val Substring = symbolOf[OpId.Substring]
val CharAt = symbolOf[OpId.CharAt]
val Length = symbolOf[OpId.Length]
}
////////////////////////////////////////////////////////////////////
// Code thanks to Shapeless
// https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/lazy.scala
////////////////////////////////////////////////////////////////////
def setAnnotation(msg: String, annotatedSym : TypeSymbol): Unit = {
import c.internal._
import decorators._
val tree0 =
c.typecheck(
q"""
new _root_.scala.annotation.implicitNotFound("dummy")
""",
silent = false
)
class SubstMessage extends Transformer {
val global = c.universe.asInstanceOf[scala.tools.nsc.Global]
override def transform(tree: Tree): Tree = {
super.transform {
tree match {
case Literal(Constant("dummy")) => Literal(Constant(msg))
case t => t
}
}
}
}
val tree = new SubstMessage().transform(tree0)
annotatedSym.setAnnotations(Annotation(tree))
()
}
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Calc
////////////////////////////////////////////////////////////////////
sealed trait Calc {
type T
val tpe : Type
val name : String
}
sealed trait CalcType extends Calc
object CalcType {
sealed trait Char extends CalcType{type T = std.Char; val tpe = typeOf[scala.Char]; val name = "Char"}
sealed trait Int extends CalcType{type T = std.Int; val tpe = typeOf[scala.Int]; val name = "Int"}
sealed trait Long extends CalcType{type T = std.Long; val tpe = typeOf[scala.Long]; val name = "Long"}
sealed trait Float extends CalcType{type T = std.Float; val tpe = typeOf[scala.Float]; val name = "Float"}
sealed trait Double extends CalcType{type T = std.Double; val tpe = typeOf[scala.Double]; val name = "Double"}
sealed trait String extends CalcType{type T = std.String; val tpe = typeOf[java.lang.String]; val name = "String"}
sealed trait Boolean extends CalcType{type T = std.Boolean; val tpe = typeOf[scala.Boolean]; val name = "Boolean"}
sealed trait Symbol extends CalcType{type T = std.Symbol; val tpe = typeOf[scala.Symbol]; val name = "String"}
object Char extends Char
object Int extends Int
object Long extends Long
object Float extends Float
object Double extends Double
object String extends String
object Boolean extends Boolean
object Symbol extends Symbol
}
sealed trait CalcVal extends Calc {
val value : T
val tree : Tree
}
object CalcVal {
sealed trait Kind
object Lit extends Kind
object NLit extends Kind
implicit val lift = Liftable[CalcVal] {p => p.tree}
class Char(val value : std.Char, val tree : Tree) extends CalcVal with CalcType.Char
object Char {
def unapply(arg: Char) : Option[(arg.T, Tree)] = Some((arg.value, arg.tree))
}
class Int(val value : std.Int, val tree : Tree) extends CalcVal with CalcType.Int
object Int {
def unapply(arg: Int) : Option[(arg.T, Tree)] = Some((arg.value, arg.tree))
}
class Long(val value : std.Long, val tree : Tree) extends CalcVal with CalcType.Long
object Long {
def unapply(arg: Long) : Option[(arg.T, Tree)] = Some((arg.value, arg.tree))
}
class Float(val value : std.Float, val tree : Tree) extends CalcVal with CalcType.Float
object Float {
def unapply(arg: Float) : Option[(arg.T, Tree)] = Some((arg.value, arg.tree))
}
class Double(val value : std.Double, val tree : Tree) extends CalcVal with CalcType.Double
object Double {
def unapply(arg: Double) : Option[(arg.T, Tree)] = Some((arg.value, arg.tree))
}
class String(val value : std.String, val tree : Tree) extends CalcVal with CalcType.String
object String {
def unapply(arg: String) : Option[(arg.T, Tree)] = Some((arg.value, arg.tree))
}
class Boolean(val value : std.Boolean, val tree : Tree) extends CalcVal with CalcType.Boolean
object Boolean {
def unapply(arg: Boolean) : Option[(arg.T, Tree)] = Some((arg.value, arg.tree))
}
def apply[T](value : T, tree : Tree)(implicit kind : Kind) = kind match {
case Lit => CalcLit(value)
case NLit => CalcNLit(value, tree)
}
}
sealed trait CalcLit extends CalcVal {
override val tpe = constantTypeOf(value)
}
object CalcLit {
implicit val lift = Liftable[CalcLit] { p => p.tree }
case class Char(override val value : std.Char) extends CalcVal.Char(value, Literal(Constant(value))) with CalcLit
case class Int(override val value : std.Int) extends CalcVal.Int(value, Literal(Constant(value))) with CalcLit
case class Long(override val value : std.Long) extends CalcVal.Long(value, Literal(Constant(value))) with CalcLit
case class Float(override val value : std.Float) extends CalcVal.Float(value, Literal(Constant(value))) with CalcLit
case class Double(override val value : std.Double) extends CalcVal.Double(value, Literal(Constant(value))) with CalcLit
case class String(override val value : std.String) extends CalcVal.String(value, Literal(Constant(value))) with CalcLit
case class Boolean(override val value : std.Boolean) extends CalcVal.Boolean(value, Literal(Constant(value))) with CalcLit
def apply[T](t : T) = t match {
case t : std.Char => Char(t)
case t : std.Int => Int(t)
case t : std.Long => Long(t)
case t : std.Float => Float(t)
case t : std.Double => Double(t)
case t : std.String => String(t)
case t : std.Boolean => Boolean(t)
case t : std.Symbol => String(t.name.toString)
case _ => abort(s"Unsupported literal type: $t")
}
def unapply(arg: CalcLit) : Option[arg.T] = Some(arg.value)
}
sealed trait CalcTFType extends Calc
object CalcTFType {
object Char extends CalcTFType with CalcType.Char
object Int extends CalcTFType with CalcType.Int
object Long extends CalcTFType with CalcType.Long
object Float extends CalcTFType with CalcType.Float
object Double extends CalcTFType with CalcType.Double
object String extends CalcTFType with CalcType.String
object Boolean extends CalcTFType with CalcType.Boolean
}
sealed trait CalcUBType extends Calc
object CalcUBType {
object Char extends CalcUBType with CalcType.Char
object Int extends CalcUBType with CalcType.Int
object Long extends CalcUBType with CalcType.Long
object Float extends CalcUBType with CalcType.Float
object Double extends CalcUBType with CalcType.Double
object String extends CalcUBType with CalcType.String
object Boolean extends CalcUBType with CalcType.Boolean
def apply(calcTypeRef : Calc) : CalcUBType = {
calcTypeRef match {
case (t: CalcType.Char) => Char
case (t: CalcType.Int) => Int
case (t: CalcType.Long) => Long
case (t: CalcType.Float) => Float
case (t: CalcType.Double) => Double
case (t: CalcType.String) => String
case (t: CalcType.Boolean) => Boolean
case _ => abort("Unsupported type")
}
}
}
sealed trait CalcNLit extends CalcVal
object CalcNLit {
implicit val lift = Liftable[CalcNLit] { p => p.tree }
case class Char(override val tree : Tree, override val tpe : Type = CalcType.Char.tpe) extends CalcVal.Char('\u0001', tree) with CalcNLit
case class Int(override val tree : Tree, override val tpe : Type = CalcType.Int.tpe) extends CalcVal.Int(1, tree) with CalcNLit
case class Long(override val tree : Tree, override val tpe : Type = CalcType.Long.tpe) extends CalcVal.Long(1L, tree) with CalcNLit
case class Float(override val tree : Tree, override val tpe : Type = CalcType.Float.tpe) extends CalcVal.Float(1.0f, tree) with CalcNLit
case class Double(override val tree : Tree, override val tpe : Type = CalcType.Double.tpe) extends CalcVal.Double(1.0, tree) with CalcNLit
case class String(override val tree : Tree, override val tpe : Type = CalcType.String.tpe) extends CalcVal.String("1", tree) with CalcNLit
case class Boolean(override val tree : Tree, override val tpe : Type = CalcType.Boolean.tpe) extends CalcVal.Boolean(true, tree) with CalcNLit
def apply[T](valueRef : T, tree : Tree) : CalcNLit =
CalcNLit(CalcLit(valueRef), tree)
def apply(calcTypeRef : Calc, tree : Tree) : CalcNLit = {
calcTypeRef match {
case (t : CalcType.Char) => Char(tree)
case (t : CalcType.Int) => Int(tree)
case (t : CalcType.Long) => Long(tree)
case (t : CalcType.Float) => Float(tree)
case (t : CalcType.Double) => Double(tree)
case (t : CalcType.String) => String(tree)
case (t : CalcType.Boolean) => Boolean(tree)
case _ => abort("Unsupported type")
}
}
def applyTpe(calcTypeRef : Calc, tree : Tree, tpe : Type) : CalcNLit = {
calcTypeRef match {
case (t: CalcType.Char) => Char(tree, tpe)
case (t: CalcType.Int) => Int(tree, tpe)
case (t: CalcType.Long) => Long(tree, tpe)
case (t: CalcType.Float) => Float(tree, tpe)
case (t: CalcType.Double) => Double(tree, tpe)
case (t: CalcType.String) => String(tree, tpe)
case (t: CalcType.Boolean) => Boolean(tree, tpe)
case _ => abort("Unsupported type")
}
}
def unapply(arg: CalcNLit) : Option[Tree] = Some(arg.tree)
}
case class CalcUnknown(t: Type) extends Calc {
val tpe = t
val name = "Unknown"
}
object NonLiteralCalc {
def unapply(tpe: Type): Option[CalcType] = tpe match {
case TypeRef(_, sym, _) => sym match {
case t if t == symbolOf[Char] => Some(CalcType.Char)
case t if t == symbolOf[Int] => Some(CalcType.Int)
case t if t == symbolOf[Long] => Some(CalcType.Long)
case t if t == symbolOf[Float] => Some(CalcType.Float)
case t if t == symbolOf[Double] => Some(CalcType.Double)
case t if t == symbolOf[java.lang.String] => Some(CalcType.String)
case t if t == symbolOf[Boolean] => Some(CalcType.Boolean)
case t if t == symbolOf[scala.Symbol] => Some(CalcType.Symbol)
case _ => None
}
case _ => None
}
}
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Code thanks to Paul Phillips
// https://github.com/paulp/psply/blob/master/src/main/scala/PsplyMacros.scala
////////////////////////////////////////////////////////////////////
import scala.reflect.internal.SymbolTable
object VerboseTraversal {
private val verboseTraversal = false
private val indentSize = 2
private var indent : Int = 0
private def indentStr : String = List.fill(indent * indentSize)(' ').mkString
def incIdent : Unit = indent = indent + 1
def decIdent : Unit = indent = indent - 1
def apply(s : String) : Unit = {
if (verboseTraversal) println(indentStr + s.replaceAll("\n",s"\n$indentStr"))
}
}
/** Typecheck singleton types so as to obtain indirectly
* available known-at-compile-time values.
*/
object TypeCalc {
////////////////////////////////////////////////////////////////////////
// Calculates the integer value of Shapeless Nat
////////////////////////////////////////////////////////////////////////
object NatCalc {
def unapply(tp: Type): Option[CalcLit.Int] = {
tp match {
case TypeRef(_, sym, args) if sym == symbolOf[shapeless.Succ[_]] =>
args.head match {
case NatCalc(t) => Some(CalcLit.Int(t.value+1))
case _ => abort(s"Given Nat type is defective: $tp, raw: ${showRaw(tp)}")
}
case TypeRef(_, sym, _) if sym == symbolOf[shapeless._0] =>
Some(CalcLit.Int(0))
case TypeRef(pre, sym, Nil) =>
unapply(sym.info asSeenFrom (pre, sym.owner))
case _ =>
None
}
}
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// Calculates the TwoFace values
////////////////////////////////////////////////////////////////////////
object TwoFaceCalc {
def unapplyArg(calcTFType : Option[CalcTFType], tfArgType : Type): Option[Calc] = {
TypeCalc.unapply(tfArgType) match {
case Some(t : CalcLit) => Some(t)
case _ => calcTFType
}
}
def unapply(tp: Type) : Option[Calc] = {
val tfAnySym = symbolOf[TwoFaceAny[_,_]]
tp match {
case TypeRef(_, sym, args) if args.nonEmpty && tp.baseClasses.contains(tfAnySym) =>
VerboseTraversal(s"@@TwoFaceCalc@@\nTP: $tp\nRAW: ${showRaw(tp)}\nBaseCls:${tp.baseClasses}")
val calcTFType = sym match {
case t if tp.baseClasses.contains(symbolOf[TwoFaceAny.Char[_]]) => Some(CalcTFType.Char)
case t if tp.baseClasses.contains(symbolOf[TwoFaceAny.Int[_]]) => Some(CalcTFType.Int)
case t if tp.baseClasses.contains(symbolOf[TwoFaceAny.Long[_]]) => Some(CalcTFType.Long)
case t if tp.baseClasses.contains(symbolOf[TwoFaceAny.Float[_]]) => Some(CalcTFType.Float)
case t if tp.baseClasses.contains(symbolOf[TwoFaceAny.Double[_]]) => Some(CalcTFType.Double)
case t if tp.baseClasses.contains(symbolOf[TwoFaceAny.String[_]]) => Some(CalcTFType.String)
case t if tp.baseClasses.contains(symbolOf[TwoFaceAny.Boolean[_]]) => Some(CalcTFType.Boolean)
case _ => None
}
if (calcTFType.isDefined)
unapplyArg(calcTFType, tp.baseType(tfAnySym).typeArgs(1))
else
None
case _ => None
}
}
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// Calculates the different Op wrappers by unapplying their argument.
////////////////////////////////////////////////////////////////////////
object OpCastCalc {
def unapply(tp: Type): Option[Calc] = {
tp match {
case TypeRef(_, sym, args) =>
sym match {
case t if t == symbolOf[OpNat[_]] => Some(TypeCalc(args.head))
case t if t == symbolOf[OpChar[_]] => Some(TypeCalc(args.head))
case t if t == symbolOf[OpInt[_]] => Some(TypeCalc(args.head))
case t if t == symbolOf[OpLong[_]] => Some(TypeCalc(args.head))
case t if t == symbolOf[OpFloat[_]] => Some(TypeCalc(args.head))
case t if t == symbolOf[OpDouble[_]] => Some(TypeCalc(args.head))
case t if t == symbolOf[OpString[_]] => Some(TypeCalc(args.head))
case t if t == symbolOf[OpBoolean[_]] => Some(TypeCalc(args.head))
case t if t == symbolOf[OpSymbol[_]] => Some(TypeCalc(args.head))
case _ => None
}
case _ =>
None
}
}
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// Calculates an Op
////////////////////////////////////////////////////////////////////////
object OpCalc {
def unapply(tp: Type): Option[Calc] = {
tp match {
case TypeRef(_, sym, args) if sym == symbolOf[OpMacro[_,_,_,_]] =>
VerboseTraversal(s"@@OpCalc@@\nTP: $tp\nRAW: + ${showRaw(tp)}")
val args = tp.typeArgs
lazy val aValue = TypeCalc(args(1))
lazy val bValue = TypeCalc(args(2))
lazy val cValue = TypeCalc(args(3))
val funcType = args.head.typeSymbol.asType
if (funcType == funcTypes.GetType)
return Some(CalcUnknown(args(1)))
//If function is set/get variable we keep the original string,
//otherwise we get the variable's value
val retVal = (funcType, aValue) match {
case (funcTypes.ImplicitFound, _) =>
aValue match {
case CalcUnknown(t) => try {
c.typecheck(q"implicitly[$t]")
Some(CalcLit(true))
} catch {
case e : Throwable =>
Some(CalcLit(false))
}
case _ => Some(CalcLit(false))
}
case (funcTypes.EnumCount, _) =>
aValue match {
case CalcUnknown(t) => Some(CalcLit(t.typeSymbol.asClass.knownDirectSubclasses.size))
case _ => Some(CalcLit(0))
}
case (funcTypes.IsNat, _) =>
aValue match {
case CalcLit.Int(t) if t >= 0 => Some(CalcLit(true))
case _ => Some(CalcLit(false))
}
case (funcTypes.IsChar, _) =>
aValue match {
case t : CalcType.Char => Some(CalcLit(true))
case _ => Some(CalcLit(false))
}
case (funcTypes.IsInt, _) =>
aValue match {
case t : CalcType.Int => Some(CalcLit(true))
case _ => Some(CalcLit(false))
}
case (funcTypes.IsLong, _) =>
aValue match {
case t : CalcType.Long => Some(CalcLit(true))
case _ => Some(CalcLit(false))
}
case (funcTypes.IsFloat, _) =>
aValue match {
case t : CalcType.Float => Some(CalcLit(true))
case _ => Some(CalcLit(false))
}
case (funcTypes.IsDouble, _) =>
aValue match {
case t : CalcType.Double => Some(CalcLit(true))
case _ => Some(CalcLit(false))
}
case (funcTypes.IsString, _) =>
aValue match {
case t : CalcType.String => Some(CalcLit(true))
case _ => Some(CalcLit(false))
}
case (funcTypes.IsBoolean, _) =>
aValue match {
case t : CalcType.Boolean => Some(CalcLit(true))
case _ => Some(CalcLit(false))
}
case (funcTypes.IsSymbol, _) =>
aValue match {
case t : CalcType.String => Some(CalcLit(true))
case _ => Some(CalcLit(false))
}
case (funcTypes.IsNonLiteral, _) => //Looking for non literals
aValue match {
case t : CalcLit => Some(CalcLit(false))
case _ => Some(CalcLit(true)) //non-literal type (e.g., Int, Long,...)
}
case (funcTypes.ITE, CalcLit.Boolean(cond)) => //Special control case: ITE (If-Then-Else)
if (cond) Some(bValue) //true (then) part of the IF
else Some(cValue) //false (else) part of the IF
case (funcTypes.Arg, CalcLit.Int(argNum)) =>
bValue match { //Checking the argument type
case t : CalcLit => Some(t) //Literal argument is just a literal
case _ => //Got a type, so returning argument name
TypeCalc.unapply(args(3)) match {
case Some(t: CalcType) =>
val term = TermName(s"arg$argNum")
Some(CalcNLit(t, q"$term"))
case _ =>
None
}
}
case _ => //regular cases
opCalc(funcType, aValue, bValue, cValue) match {
case (res : CalcVal) => Some(res)
case _ => None
}
}
retVal
case _ => None
}
}
}
////////////////////////////////////////////////////////////////////////
def apply(tp: Type): Calc = {
TypeCalc.unapply(tp) match {
case Some(t : CalcVal) => t
case Some(t : CalcType.Symbol) => CalcNLit(CalcType.String, q"valueOf[$tp].name")
case Some(t : CalcUBType) => t
case Some(t : CalcTFType) => CalcNLit(t, q"valueOf[$tp].getValue")
case Some(t : CalcType) => CalcNLit(t, q"valueOf[$tp]")
case Some(t : CalcUnknown) => t
case _ =>
VerboseTraversal(s"@@Unknown@@\nTP: $tp\nRAW: + ${showRaw(tp)}")
CalcUnknown(tp)
}
}
def unapply(tp: Type): Option[Calc] = {
val g = c.universe.asInstanceOf[SymbolTable]
implicit def fixSymbolOps(sym: Symbol): g.Symbol = sym.asInstanceOf[g.Symbol]
VerboseTraversal(s"${c.enclosingPosition}\nTP: $tp\nRAW: ${showRaw(tp)}")
VerboseTraversal.incIdent
val tpCalc = tp match {
////////////////////////////////////////////////////////////////////////
// Value cases
////////////////////////////////////////////////////////////////////////
case ConstantType(Constant(t)) => Some(CalcLit(t)) //Constant
case OpCalc(t) => Some(t) // Operational Function
case OpCastCalc(t) => Some(t) //Op Cast wrappers
case TwoFaceCalc(t) => Some(t) //TwoFace values
case NonLiteralCalc(t) => Some(t)// Non-literal values
case NatCalc(t) => Some(t) //For Shapeless Nat
case SingletonSymbolType(s) => Some(CalcLit(s)) //Symbol constant
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// Tree traversal
////////////////////////////////////////////////////////////////////////
case tp @ ExistentialType(_, _) => unapply(tp.underlying)
case TypeBounds(lo, hi) => unapply(hi) match {
case Some(t : CalcLit) => Some(t)
//There can be cases, like in the following example, where we can extract a non-literal value.
// def foo2[W](w : TwoFace.Int[W])(implicit tfs : TwoFace.Int.Shell1[Negate, W, Int]) = -w+1
//We want to calculate `-w+1`, even though we have not provided a complete implicit.
//While returning `TwoFace.Int[Int](-w+1)` is possible in this case, we would rather reserve
//the ability to have a literal return type, so `TwoFace.Int[Negate[W]+1](-w+1)` is returned.
//So even if we can have a `Some(CalcType)` returning, we force it as an upper-bound calc type.
case Some(t) => Some(CalcUBType(t))
case _ => None
}
case RefinedType(parents, scope) =>
parents.iterator map unapply collectFirst { case Some(x) => x }
case NullaryMethodType(tpe) => unapply(tpe)
case TypeRef(_, sym, _) if sym.isAliasType =>
val tpDealias = tp.dealias
if (tpDealias == tp)
abort("Unable to dealias type: " + showRaw(tp))
else
unapply(tpDealias)
case TypeRef(pre, sym, Nil) => unapply(sym.info asSeenFrom (pre, sym.owner))
case SingleType(pre, sym) => unapply(sym.info asSeenFrom (pre, sym.owner))
////////////////////////////////////////////////////////////////////////
case _ =>
// println("Exhausted search at: " + showRaw(tp))
None
}
VerboseTraversal.decIdent
tpCalc
}
}
////////////////////////////////////////////////////////////////////
def abort(msg: String, annotatedSym : Option[TypeSymbol] = defaultAnnotatedSym): Nothing = {
if (annotatedSym.isDefined) setAnnotation(msg, annotatedSym.get)
c.abort(c.enclosingPosition, msg)
}
def buildWarningMsgLoc : String = s"${c.enclosingPosition.source.path}:${c.enclosingPosition.line}:${c.enclosingPosition.column}"
def buildWarningMsg(msg: String): String = s"Warning: $buildWarningMsgLoc $msg"
def buildWarningMsg(msg: Tree): Tree = q""" "Warning: " + $buildWarningMsgLoc + " " + $msg """
def constantTreeOf[T](t : T) : Tree = Literal(Constant(t))
def constantTypeOf[T](t: T) : Type = c.internal.constantType(Constant(t))
def genOpTreeLit[T](opTpe : Type, t: T) : Tree = {
val outTpe = constantTypeOf(t)
val outTree = constantTreeOf(t)
val outWideTpe = outTpe.widen
val outTypeName = TypeName("Out" + wideTypeName(outTpe))
val outWideLiteral = outTree
q"""
new $opTpe {
type OutWide = $outWideTpe
type Out = $outTpe
type $outTypeName = $outTpe
final val value: $outTpe = $outWideLiteral
final val isLiteral = true
final val valueWide: $outWideTpe = $outWideLiteral
}
"""
}
def genOpTreeNat(opTpe : Type, t: Int) : Tree = {
val outWideTpe = typeOf[Int]
val outWideLiteral = constantTreeOf(t)
val outTypeName = TypeName("OutNat")
val outTpe = mkNatTpe(t)
val outTree = q"new ${mkNatTpt(t)}"
q"""
new $opTpe {
type OutWide = $outWideTpe
type Out = $outTpe
type $outTypeName = $outTpe
final val value: $outTpe = $outTree
final val isLiteral = true
final val valueWide: $outWideTpe = $outWideLiteral
}
"""
}
def genOpTreeSymbol(opTpe : Type, t: String) : Tree = {
val outTpe = SingletonSymbolType(t)
val outTree = mkSingletonSymbol(t)
val outWideTpe = typeOf[scala.Symbol]
val outWideLiteral = mkSingletonSymbolWide(t)
val outTypeName = TypeName("OutSymbol")
q"""
new $opTpe {
type OutWide = $outWideTpe
type Out = $outTpe
type $outTypeName = $outTpe
final val value: $outTpe = $outTree
final val isLiteral = true
final val valueWide: $outWideTpe = $outWideLiteral
}
"""
}
def genOpTreeNLit(opTpe : Type, calc : CalcNLit) : Tree = {
val valueTree = calc.tree
val outTpe = calc.tpe
q"""
new $opTpe {
type OutWide = $outTpe
type Out = $outTpe
final val value: $outTpe = $valueTree
final val isLiteral = false
final val valueWide: $outTpe = $valueTree
}
"""
}
def extractionFailed(tpe: Type) = {
val msg = s"Cannot extract value from $tpe\n" + "showRaw==> " + showRaw(tpe)
abort(msg)
}
def extractionFailed(tree: Tree) = {
val msg = s"Cannot extract value from $tree\n" + "showRaw==> " + showRaw(tree)
abort(msg)
}
def extractValueFromOpTree(opTree : c.Tree) : CalcVal = {
def outFindCond(elem : c.Tree) : Boolean = elem match {
case q"final val value : $valueTpe = $valueTree" => true
case _ => false
}
def getOut(opClsBlk : List[c.Tree]) : CalcVal = opClsBlk.find(outFindCond) match {
case Some(q"final val value : $valueTpe = $valueTree") =>
valueTree match {
case Literal(Constant(t)) => CalcLit(t)
case _ => valueTpe match {
case NonLiteralCalc(t) => CalcNLit(t, q"$valueTree")
case _ => extractionFailed(opTree)
}
}
case _ => extractionFailed(opTree)
}
opTree match {
case q"""{
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends ..$parents { $self => ..$opClsBlk }
$expr(...$exprss)
}""" => getOut(opClsBlk)
case _ => extractionFailed(opTree)
}
}
def extractValueFromNumTree(numValueTree : c.Tree) : CalcVal = {
val typedTree = c.typecheck(numValueTree)
TypeCalc(typedTree.tpe) match {
case t : CalcLit => t
case t : CalcUBType => CalcNLit.applyTpe(t, numValueTree, typedTree.tpe)
case t : CalcType => CalcNLit(t, numValueTree)
case _ => extractionFailed(typedTree.tpe)
}
}
def extractValueFromTwoFaceTree(tfTree : c.Tree) : CalcVal = {
val typedTree = c.typecheck(tfTree)
TypeCalc(typedTree.tpe) match {
case t : CalcLit => t
case t : CalcType => CalcNLit(t, q"$tfTree.getValue")
case t =>
println(t)
extractionFailed(typedTree.tpe)
}
}
def wideTypeName(tpe : Type) : String = tpe.widen.typeSymbol.name.toString
///////////////////////////////////////////////////////////////////////////////////////////
// Get Argument
///////////////////////////////////////////////////////////////////////////////////////////
object MaterializeGetArg {
def apply(gaSym : TypeSymbol, auxSym : TypeSymbol, aiTpe : Type, lhs : Boolean) : c.Tree = {
val argIdx : Int = TypeCalc(aiTpe) match {
case CalcLit.Int(t) => t
case _ => abort(s"Invalid argument index. Found $aiTpe")
}
val valueTree : Tree = GetArgTree(argIdx, lhs)
val outTpe : Type = c.typecheck(valueTree).tpe
val genTree = q"""
new $gaSym[$aiTpe]{
type Out = $outTpe
val value : Out = $valueTree
}
"""
// println(genTree)
genTree
}
}
object GetArgTree {
def isMethodMacroCall : Boolean = c.enclosingImplicits.last.sym.isMacro
def getAllArgs(tree : Tree, lhs : Boolean) : List[Tree] = tree match {
case Apply(Select(q,n), args) => if (isMethodMacroCall || lhs) args else List(tree)
case t : Select => List(t)
case t : Literal => List(t)
case _ => getAllArgsRecur(tree)
}
def getAllArgsRecur(tree : Tree) : List[Tree] = tree match {
case Apply(fun, args) => getAllArgsRecur(fun) ++ args
case _ => List()
}
def getAllLHSArgs(tree : Tree) : List[Tree] = tree match {
case Apply(TypeApply(Select(t, _), _), _) => getAllArgs(t, true)
case TypeApply(Select(t, _), _) => getAllArgs(t, true)
case Select(t, _) => getAllArgs(t, true)
case _ => abort("Left-hand-side tree not found")
}
def apply(argIdx : Int, lhs : Boolean) : c.Tree = {
val tree = c.enclosingImplicits.last.tree
// println(">>>>>>> enclosingImpl: " + c.enclosingImplicits.last)
// println("tree: " + c.enclosingImplicits.last.tree)
// println("rawTree: " + showRaw(c.enclosingImplicits.last.tree))
val allArgs = if (lhs) getAllLHSArgs(tree) else getAllArgs(tree, lhs)
// println("args: " + allArgs)
// println("<<<<<<< rawArgs" + showRaw(allArgs))
if (argIdx < allArgs.length) allArgs(argIdx)
else abort(s"Argument index($argIdx) is not smaller than the total number of arguments(${allArgs.length})")
}
}
def extractFromArg(argIdx : Int, lhs : Boolean) : CalcVal = {
extractValueFromNumTree(GetArgTree(argIdx, lhs))
}
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// Three operands (Generic)
///////////////////////////////////////////////////////////////////////////////////////////
def materializeOpGen[F](implicit ev0: c.WeakTypeTag[F]): MaterializeOpAuxGen =
new MaterializeOpAuxGen(weakTypeOf[F])
def opCalc(funcType : TypeSymbol, aCalc : => Calc, bCalc : => Calc, cCalc : => Calc) : Calc = {
lazy val a = aCalc
lazy val b = bCalc
lazy val cArg = cCalc
def unsupported() = {
(a, b) match {
case (aArg : CalcVal, bArg : CalcVal) => abort(s"Unsupported $funcType[$a, $b, $cArg]")
case _ => CalcUnknown(funcType.toType)
}
}
//The output val is literal if all arguments are literal. Otherwise, it is non-literal.
lazy implicit val cvKind : CalcVal.Kind = (a, b, cArg) match {
case (_ : CalcLit, _ : CalcLit, _ : CalcLit) => CalcVal.Lit
case _ => CalcVal.NLit
}
def AcceptNonLiteral = Id //AcceptNonLiteral has a special handling in MaterializeOpAuxGen
def GetArg = a match {
case CalcLit.Int(t) if (t >= 0) => extractFromArg(t, false)
case _ => unsupported()
}
def GetLHSArg = a match {
case CalcLit.Int(t) if (t >= 0) => extractFromArg(t, true)
case _ => unsupported()
}
def Id = a match {
case (av : CalcVal) => av
case _ => unsupported()
}
def ToNat = ToInt //Same handling, but also has a special case to handle this in MaterializeOpAuxGen
def ToChar = a match {
case CalcVal.Char(t, tt) => CalcVal(t, q"$tt")
case CalcVal.Int(t, tt) => CalcVal(t.toChar, q"$tt.toChar")
case CalcVal.Long(t, tt) => CalcVal(t.toChar, q"$tt.toChar")
case CalcVal.Float(t, tt) => CalcVal(t.toChar, q"$tt.toChar")
case CalcVal.Double(t, tt) => CalcVal(t.toChar, q"$tt.toChar")
case _ => unsupported()
}
def ToInt = a match {
case CalcVal.Char(t, tt) => CalcVal(t.toInt, q"$tt.toInt")
case CalcVal.Int(t, tt) => CalcVal(t, q"$tt")
case CalcVal.Long(t, tt) => CalcVal(t.toInt, q"$tt.toInt")
case CalcVal.Float(t, tt) => CalcVal(t.toInt, q"$tt.toInt")
case CalcVal.Double(t, tt) => CalcVal(t.toInt, q"$tt.toInt")
case CalcVal.String(t, tt) => CalcVal(t.toInt, q"$tt.toInt")
case _ => unsupported()
}
def ToLong = a match {
case CalcVal.Char(t, tt) => CalcVal(t.toLong, q"$tt.toLong")
case CalcVal.Int(t, tt) => CalcVal(t.toLong, q"$tt.toLong")
case CalcVal.Long(t, tt) => CalcVal(t, q"$tt")
case CalcVal.Float(t, tt) => CalcVal(t.toLong, q"$tt.toLong")
case CalcVal.Double(t, tt) => CalcVal(t.toLong, q"$tt.toLong")
case CalcVal.String(t, tt) => CalcVal(t.toLong, q"$tt.toLong")
case _ => unsupported()
}
def ToFloat = a match {
case CalcVal.Char(t, tt) => CalcVal(t.toFloat, q"$tt.toFloat")
case CalcVal.Int(t, tt) => CalcVal(t.toFloat, q"$tt.toFloat")
case CalcVal.Long(t, tt) => CalcVal(t.toFloat, q"$tt.toFloat")
case CalcVal.Float(t, tt) => CalcVal(t, q"$tt")
case CalcVal.Double(t, tt) => CalcVal(t.toFloat, q"$tt.toFloat")
case CalcVal.String(t, tt) => CalcVal(t.toFloat, q"$tt.toFloat")
case _ => unsupported()
}
def ToDouble = a match {
case CalcVal.Char(t, tt) => CalcVal(t.toDouble, q"$tt.toDouble")
case CalcVal.Int(t, tt) => CalcVal(t.toDouble, q"$tt.toDouble")
case CalcVal.Long(t, tt) => CalcVal(t.toDouble, q"$tt.toDouble")
case CalcVal.Float(t, tt) => CalcVal(t.toDouble, q"$tt.toDouble")
case CalcVal.Double(t, tt) => CalcVal(t, q"$tt")
case CalcVal.String(t, tt) => CalcVal(t.toDouble, q"$tt.toDouble")
case _ => unsupported()
}
def ToString = a match {
case CalcVal.Char(t, tt) => CalcVal(t.toString, q"$tt.toString")
case CalcVal.Int(t, tt) => CalcVal(t.toString, q"$tt.toString")
case CalcVal.Long(t, tt) => CalcVal(t.toString, q"$tt.toString")
case CalcVal.Float(t, tt) => CalcVal(t.toString, q"$tt.toString")
case CalcVal.Double(t, tt) => CalcVal(t.toString, q"$tt.toString")
case CalcVal.String(t, tt) => CalcVal(t, q"$tt")
case CalcVal.Boolean(t, tt) => CalcVal(t.toString, q"$tt.toString")
case _ => unsupported()
}
def ToSymbol = ToString //Same handling, but has also has a special case in MaterializeOpAuxGen
def Negate = a match {
case CalcVal.Char(t, tt) => CalcVal(-t, q"-$tt")
case CalcVal.Int(t, tt) => CalcVal(-t, q"-$tt")
case CalcVal.Long(t, tt) => CalcVal(-t, q"-$tt")
case CalcVal.Float(t, tt) => CalcVal(-t, q"-$tt")
case CalcVal.Double(t, tt) => CalcVal(-t, q"-$tt")
case _ => unsupported()
}
def Abs = a match {
case CalcVal.Int(t, tt) => CalcVal(math.abs(t), q"_root_.scala.math.abs($tt)")
case CalcVal.Long(t, tt) => CalcVal(math.abs(t), q"_root_.scala.math.abs($tt)")
case CalcVal.Float(t, tt) => CalcVal(math.abs(t), q"_root_.scala.math.abs($tt)")
case CalcVal.Double(t, tt) => CalcVal(math.abs(t), q"_root_.scala.math.abs($tt)")
case _ => unsupported()
}
def NumberOfLeadingZeros = a match {
case CalcVal.Int(t, tt) => CalcVal(nlz(t), q"_root_.singleton.ops.impl.nlz($tt)")
case CalcVal.Long(t, tt) => CalcVal(nlz(t), q"_root_.singleton.ops.impl.nlz($tt)")
case _ => unsupported()
}
def Floor = a match {
case CalcVal.Double(t, tt) => CalcVal(math.floor(t), q"_root_.scala.math.floor($tt)")
case _ => unsupported()
}
def Ceil = a match {
case CalcVal.Double(t, tt) => CalcVal(math.ceil(t), q"_root_.scala.math.ceil($tt)")
case _ => unsupported()
}
def Round = a match {
case CalcVal.Float(t, tt) => CalcVal(math.round(t), q"_root_.scala.math.round($tt)")
case CalcVal.Double(t, tt) => CalcVal(math.round(t), q"_root_.scala.math.round($tt)")
case _ => unsupported()
}
def Sin = a match {
case CalcVal.Double(t, tt) => CalcVal(math.sin(t), q"_root_.scala.math.sin($tt)")
case _ => unsupported()
}
def Cos = a match {
case CalcVal.Double(t, tt) => CalcVal(math.cos(t), q"_root_.scala.math.cos($tt)")
case _ => unsupported()
}
def Tan = a match {
case CalcVal.Double(t, tt) => CalcVal(math.tan(t), q"_root_.scala.math.tan($tt)")
case _ => unsupported()
}
def Sqrt = a match {
case CalcVal.Double(t, tt) => CalcVal(math.sqrt(t), q"_root_.scala.math.sqrt($tt)")
case _ => unsupported()
}
def Log = a match {
case CalcVal.Double(t, tt) => CalcVal(math.log(t), q"_root_.scala.math.log($tt)")
case _ => unsupported()
}
def Log10 = a match {
case CalcVal.Double(t, tt) => CalcVal(math.log10(t), q"_root_.scala.math.log10($tt)")
case _ => unsupported()
}
def Reverse = a match {
case CalcVal.String(t, tt) => CalcVal(t.reverse, q"$tt.reverse")
case _ => unsupported()
}
def Not = a match {
case CalcVal.Boolean(t, tt) => CalcVal(!t, q"!$tt")
case _ => unsupported()
}
def Require = a match {
case CalcLit.Boolean(true) => CalcLit(true)
case CalcLit.Boolean(false) => b match {
case CalcLit.String(msg) =>
if (cArg.tpe.typeSymbol == symbolOf[Warn]) {
println(buildWarningMsg(msg))
CalcLit(false)
} else if (cArg.tpe.typeSymbol == symbolOf[NoSym]) {
abort(msg)
} else {
//redirection of implicit not found annotation is required to the given symbol
abort(msg, Some(cArg.tpe.typeSymbol.asType))
}
//directly using the java lib `require` resulted in compiler crash, so we use wrapped require instead
case CalcNLit.String(msg,_) => cArg match {
case CalcUnknown(t) if t.typeSymbol == symbolOf[Warn] =>
CalcNLit.Boolean(q"""{println(${buildWarningMsg(msg)}); false}""")
case _ =>
CalcNLit.Boolean(q"{_root_.singleton.ops.impl._require(false, $msg); false}")
}
case _ => unsupported()
}
case CalcNLit.Boolean(cond,_) => b match {
//directly using the java lib `require` resulted in compiler crash, so we use wrapped require instead
case CalcVal.String(msg, msgt) => cArg match {
case CalcUnknown(t) if t == symbolOf[Warn] =>
CalcNLit.Boolean(
q"""{
if ($cond) true
else {
println(${buildWarningMsg(msgt)})
false
}
}""")
case _ =>
CalcNLit.Boolean(q"{_root_.singleton.ops.impl._require($cond, $msgt); true}")
}
case _ => unsupported()
}
case _ => unsupported()
}
def ITE = (a, b, cArg) match {
//Also has special case handling inside unapply
case (CalcVal.Boolean(it,itt), CalcVal.Char(tt,ttt), CalcVal.Char(et,ett)) =>
CalcVal(if(it) tt else et, q"if ($itt) $ttt else $ett")
case (CalcVal.Boolean(it,itt), CalcVal.Int(tt,ttt), CalcVal.Int(et,ett)) =>
CalcVal(if(it) tt else et, q"if ($itt) $ttt else $ett")
case (CalcVal.Boolean(it,itt), CalcVal.Long(tt,ttt), CalcVal.Long(et,ett)) =>
CalcVal(if(it) tt else et, q"if ($itt) $ttt else $ett")
case (CalcVal.Boolean(it,itt), CalcVal.Float(tt,ttt), CalcVal.Float(et,ett)) =>
CalcVal(if(it) tt else et, q"if ($itt) $ttt else $ett")
case (CalcVal.Boolean(it,itt), CalcVal.Double(tt,ttt), CalcVal.Double(et,ett)) =>
CalcVal(if(it) tt else et, q"if ($itt) $ttt else $ett")
case (CalcVal.Boolean(it,itt), CalcVal.String(tt,ttt), CalcVal.String(et,ett)) =>
CalcVal(if(it) tt else et, q"if ($itt) $ttt else $ett")
case (CalcVal.Boolean(it,itt), CalcVal.Boolean(tt,ttt), CalcVal.Boolean(et,ett)) =>
CalcVal(if(it) tt else et, q"if ($itt) $ttt else $ett")
case _ => unsupported()
}
def Next = b match {
case (bv : CalcVal) => bv
case _ => unsupported()
}
def Plus = (a, b) match {
case (CalcVal.Char(at, att), CalcVal.Char(bt, btt)) => CalcVal(at + bt, q"$att + $btt")
case (CalcVal.Int(at, att), CalcVal.Int(bt, btt)) => CalcVal(at + bt, q"$att + $btt")
case (CalcVal.Long(at, att), CalcVal.Long(bt, btt)) => CalcVal(at + bt, q"$att + $btt")
case (CalcVal.Float(at, att), CalcVal.Float(bt, btt)) => CalcVal(at + bt, q"$att + $btt")
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) => CalcVal(at + bt, q"$att + $btt")
case (CalcVal.String(at, att), CalcVal.String(bt, btt)) => CalcVal(at + bt, q"$att + $btt")
case _ => unsupported()
}
def Minus = (a, b) match {
case (CalcVal.Char(at, att), CalcVal.Char(bt, btt)) => CalcVal(at - bt, q"$att - $btt")
case (CalcVal.Int(at, att), CalcVal.Int(bt, btt)) => CalcVal(at - bt, q"$att - $btt")
case (CalcVal.Long(at, att), CalcVal.Long(bt, btt)) => CalcVal(at - bt, q"$att - $btt")
case (CalcVal.Float(at, att), CalcVal.Float(bt, btt)) => CalcVal(at - bt, q"$att - $btt")
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) => CalcVal(at - bt, q"$att - $btt")
case _ => unsupported()
}
def Mul = (a, b) match {
case (CalcVal.Char(at, att), CalcVal.Char(bt, btt)) => CalcVal(at * bt, q"$att * $btt")
case (CalcVal.Int(at, att), CalcVal.Int(bt, btt)) => CalcVal(at * bt, q"$att * $btt")
case (CalcVal.Long(at, att), CalcVal.Long(bt, btt)) => CalcVal(at * bt, q"$att * $btt")
case (CalcVal.Float(at, att), CalcVal.Float(bt, btt)) => CalcVal(at * bt, q"$att * $btt")
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) => CalcVal(at * bt, q"$att * $btt")
case _ => unsupported()
}
def Div = (a, b) match {
case (CalcVal.Char(at, att), CalcVal.Char(bt, btt)) => CalcVal(at / bt, q"$att / $btt")
case (CalcVal.Int(at, att), CalcVal.Int(bt, btt)) => CalcVal(at / bt, q"$att / $btt")
case (CalcVal.Long(at, att), CalcVal.Long(bt, btt)) => CalcVal(at / bt, q"$att / $btt")
case (CalcVal.Float(at, att), CalcVal.Float(bt, btt)) => CalcVal(at / bt, q"$att / $btt")
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) => CalcVal(at / bt, q"$att / $btt")
case _ => unsupported()
}
def Mod = (a, b) match {
case (CalcVal.Char(at, att), CalcVal.Char(bt, btt)) => CalcVal(at % bt, q"$att % $btt")
case (CalcVal.Int(at, att), CalcVal.Int(bt, btt)) => CalcVal(at % bt, q"$att % $btt")
case (CalcVal.Long(at, att), CalcVal.Long(bt, btt)) => CalcVal(at % bt, q"$att % $btt")
case (CalcVal.Float(at, att), CalcVal.Float(bt, btt)) => CalcVal(at % bt, q"$att % $btt")
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) => CalcVal(at % bt, q"$att % $btt")
case _ => unsupported()
}
def Sml = (a, b) match {
case (CalcVal.Char(at, att), CalcVal.Char(bt, btt)) => CalcVal(at < bt, q"$att < $btt")
case (CalcVal.Int(at, att), CalcVal.Int(bt, btt)) => CalcVal(at < bt, q"$att < $btt")
case (CalcVal.Long(at, att), CalcVal.Long(bt, btt)) => CalcVal(at < bt, q"$att < $btt")
case (CalcVal.Float(at, att), CalcVal.Float(bt, btt)) => CalcVal(at < bt, q"$att < $btt")
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) => CalcVal(at < bt, q"$att < $btt")
case _ => unsupported()
}
def Big = (a, b) match {
case (CalcVal.Char(at, att), CalcVal.Char(bt, btt)) => CalcVal(at > bt, q"$att > $btt")
case (CalcVal.Int(at, att), CalcVal.Int(bt, btt)) => CalcVal(at > bt, q"$att > $btt")
case (CalcVal.Long(at, att), CalcVal.Long(bt, btt)) => CalcVal(at > bt, q"$att > $btt")
case (CalcVal.Float(at, att), CalcVal.Float(bt, btt)) => CalcVal(at > bt, q"$att > $btt")
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) => CalcVal(at > bt, q"$att > $btt")
case _ => unsupported()
}
def SmlEq = (a, b) match {
case (CalcVal.Char(at, att), CalcVal.Char(bt, btt)) => CalcVal(at <= bt, q"$att <= $btt")
case (CalcVal.Int(at, att), CalcVal.Int(bt, btt)) => CalcVal(at <= bt, q"$att <= $btt")
case (CalcVal.Long(at, att), CalcVal.Long(bt, btt)) => CalcVal(at <= bt, q"$att <= $btt")
case (CalcVal.Float(at, att), CalcVal.Float(bt, btt)) => CalcVal(at <= bt, q"$att <= $btt")
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) => CalcVal(at <= bt, q"$att <= $btt")
case _ => unsupported()
}
def BigEq = (a, b) match {
case (CalcVal.Char(at, att), CalcVal.Char(bt, btt)) => CalcVal(at >= bt, q"$att >= $btt")
case (CalcVal.Int(at, att), CalcVal.Int(bt, btt)) => CalcVal(at >= bt, q"$att >= $btt")
case (CalcVal.Long(at, att), CalcVal.Long(bt, btt)) => CalcVal(at >= bt, q"$att >= $btt")
case (CalcVal.Float(at, att), CalcVal.Float(bt, btt)) => CalcVal(at >= bt, q"$att >= $btt")
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) => CalcVal(at >= bt, q"$att >= $btt")
case _ => unsupported()
}
def Eq = (a, b) match {
case (CalcVal.Char(at, att), CalcVal.Char(bt, btt)) => CalcVal(at == bt, q"$att == $btt")
case (CalcVal.Int(at, att), CalcVal.Int(bt, btt)) => CalcVal(at == bt, q"$att == $btt")
case (CalcVal.Long(at, att), CalcVal.Long(bt, btt)) => CalcVal(at == bt, q"$att == $btt")
case (CalcVal.Float(at, att), CalcVal.Float(bt, btt)) => CalcVal(at == bt, q"$att == $btt")
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) => CalcVal(at == bt, q"$att == $btt")
case (CalcVal.String(at, att), CalcVal.String(bt, btt)) => CalcVal(at == bt, q"$att == $btt")
case (CalcVal.Boolean(at, att), CalcVal.Boolean(bt, btt)) => CalcVal(at == bt, q"$att == $btt")
case _ => unsupported()
}
def Neq = (a, b) match {
case (CalcVal.Char(at, att), CalcVal.Char(bt, btt)) => CalcVal(at != bt, q"$att != $btt")
case (CalcVal.Int(at, att), CalcVal.Int(bt, btt)) => CalcVal(at != bt, q"$att != $btt")
case (CalcVal.Long(at, att), CalcVal.Long(bt, btt)) => CalcVal(at != bt, q"$att != $btt")
case (CalcVal.Float(at, att), CalcVal.Float(bt, btt)) => CalcVal(at != bt, q"$att != $btt")
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) => CalcVal(at != bt, q"$att != $btt")
case (CalcVal.String(at, att), CalcVal.String(bt, btt)) => CalcVal(at != bt, q"$att != $btt")
case (CalcVal.Boolean(at, att), CalcVal.Boolean(bt, btt)) => CalcVal(at != bt, q"$att != $btt")
case _ => unsupported()
}
def And = a match {
case CalcLit.Boolean(ab) => //`And` expressions where the LHS is a literal can be inlined
if (ab) b match {
case CalcVal.Boolean(_,_) => b //inlining the value of RHS when the LHS is true
case _ => unsupported()
} else CalcLit.Boolean(false) //inlining as false when the LHS is false
case _ => (a, b) match {
case (CalcVal.Boolean(at, att), CalcVal.Boolean(bt, btt)) => CalcVal(at && bt, q"$att && $btt")
case _ => unsupported()
}
}
def Or = a match {
case CalcLit.Boolean(ab) => //`Or` expressions where the LHS is a literal can be inlined
if (!ab) b match {
case CalcVal.Boolean(_,_) => b //inlining the value of RHS when the LHS is false
case _ => unsupported()
} else CalcLit.Boolean(true) //inlining as true when the LHS is true
case _ => (a, b) match {
case (CalcVal.Boolean(at, att), CalcVal.Boolean(bt, btt)) => CalcVal(at || bt, q"$att || $btt")
case _ => unsupported()
}
}
def Pow = (a, b) match {
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) =>
CalcVal(math.pow(at.toDouble, bt.toDouble), q"_root_.scala.math.pow($att.toDouble, $btt.toDouble)")
case _ => unsupported()
}
def Min = (a, b) match {
case (CalcVal.Int(at, att), CalcVal.Int(bt, btt)) =>
CalcVal(math.min(at, bt), q"_root_.scala.math.min($att, $btt)")
case (CalcVal.Long(at, att), CalcVal.Long(bt, btt)) =>
CalcVal(math.min(at, bt), q"_root_.scala.math.min($att, $btt)")
case (CalcVal.Float(at, att), CalcVal.Float(bt, btt)) =>
CalcVal(math.min(at, bt), q"_root_.scala.math.min($att, $btt)")
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) =>
CalcVal(math.min(at, bt), q"_root_.scala.math.min($att, $btt)")
case _ => unsupported()
}
def Max = (a, b) match {
case (CalcVal.Int(at, att), CalcVal.Int(bt, btt)) =>
CalcVal(math.max(at, bt), q"_root_.scala.math.max($att, $btt)")
case (CalcVal.Long(at, att), CalcVal.Long(bt, btt)) =>
CalcVal(math.max(at, bt), q"_root_.scala.math.max($att, $btt)")
case (CalcVal.Float(at, att), CalcVal.Float(bt, btt)) =>
CalcVal(math.max(at, bt), q"_root_.scala.math.max($att, $btt)")
case (CalcVal.Double(at, att), CalcVal.Double(bt, btt)) =>
CalcVal(math.max(at, bt), q"_root_.scala.math.max($att, $btt)")
case _ => unsupported()
}
def Substring = (a, b) match {
case (CalcLit.String(at), CalcLit.Int(bt)) => CalcLit(at.substring(bt))
case (CalcVal.String(at, att), CalcVal.Int(bt, btt)) => CalcNLit.String(q"$att.substring($btt)")
case _ => unsupported()
}
def CharAt = (a, b) match {
case (CalcLit.String(at), CalcLit.Int(bt)) => CalcLit(at.charAt(bt))
case (CalcVal.String(at, att), CalcVal.Int(bt, btt)) => CalcNLit.Char(q"$att.charAt($btt)")
case _ => unsupported()
}
def Length = a match {
case CalcVal.String(at, att) => CalcVal(at.length, q"$att.length")
case _ => unsupported()
}
funcType match {
case funcTypes.AcceptNonLiteral => AcceptNonLiteral
case funcTypes.GetArg => GetArg
case funcTypes.GetLHSArg => GetLHSArg
case funcTypes.Id => Id
case funcTypes.ToNat => ToNat
case funcTypes.ToChar => ToChar
case funcTypes.ToInt => ToInt
case funcTypes.ToLong => ToLong
case funcTypes.ToFloat => ToFloat
case funcTypes.ToDouble => ToDouble
case funcTypes.ToString => ToString
case funcTypes.ToSymbol => ToSymbol
case funcTypes.Negate => Negate
case funcTypes.Abs => Abs
case funcTypes.NumberOfLeadingZeros => NumberOfLeadingZeros
case funcTypes.Floor => Floor
case funcTypes.Ceil => Ceil
case funcTypes.Round => Round
case funcTypes.Sin => Sin
case funcTypes.Cos => Cos
case funcTypes.Tan => Tan
case funcTypes.Sqrt => Sqrt
case funcTypes.Log => Log
case funcTypes.Log10 => Log10
case funcTypes.Reverse => Reverse
case funcTypes.! => Not
case funcTypes.Require => Require
case funcTypes.ITE => ITE
case funcTypes.==> => Next
case funcTypes.+ => Plus
case funcTypes.- => Minus
case funcTypes.* => Mul
case funcTypes./ => Div
case funcTypes.% => Mod
case funcTypes.< => Sml
case funcTypes.> => Big
case funcTypes.<= => SmlEq
case funcTypes.>= => BigEq
case funcTypes.== => Eq
case funcTypes.!= => Neq
case funcTypes.&& => And
case funcTypes.|| => Or
case funcTypes.Pow => Pow
case funcTypes.Min => Min
case funcTypes.Max => Max
case funcTypes.Substring => Substring
case funcTypes.CharAt => CharAt
case funcTypes.Length => Length
case _ => abort(s"Unsupported $funcType[$a, $b, $cArg]")
}
}
final class MaterializeOpAuxGen(opTpe: Type) {
def usingFuncName : Tree = {
val funcType = opTpe.typeArgs.head.typeSymbol.asType
val opResult = TypeCalc(opTpe)
val genTree = (funcType, opResult) match {
case (funcTypes.ToNat, CalcLit.Int(t)) =>
if (t < 0) abort(s"Nat cannot be a negative literal. Found: $t")
else genOpTreeNat(opTpe, t)
case (funcTypes.ToSymbol, CalcLit.String(t)) => genOpTreeSymbol(opTpe, t)
case (_, CalcLit(t)) => genOpTreeLit(opTpe, t)
case (funcTypes.AcceptNonLiteral, t : CalcNLit) => genOpTreeNLit(opTpe, t)
case (_, t: CalcNLit) =>
abort("Calculation has returned a non-literal type/value.\nTo accept non-literal values, use `AcceptNonLiteral[T]`.")
case _ => extractionFailed(opTpe)
}
// println(genTree)
genTree
}
}
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// TwoFace Shell
///////////////////////////////////////////////////////////////////////////////////////////
def TwoFaceShellMaterializer[Shell](implicit shell : c.WeakTypeTag[Shell])
: TwoFaceShellMaterializer[Shell] = new TwoFaceShellMaterializer[Shell](weakTypeOf[Shell])
final class TwoFaceShellMaterializer[Shell](shellTpe : Type) {
def shell(shellAliasTpe : TypeSymbol) : c.Tree = {
val owner = c.internal.enclosingOwner
if (owner.asTerm.name.toString == "equals" && owner.owner.isClass && owner.owner.asClass.isCaseClass) {
abort("A case class equals workaround is required. See https://github.com/scala/bug/issues/10536")
}
val funcApplyTpe = shellTpe.typeArgs(0)
val funcArgsTpe = shellTpe.typeArgs(1)
val (tfValueTree, tfName) = TypeCalc(funcArgsTpe) match {
case (t: CalcVal) => (t.tree, t.name)
case _ => extractionFailed(shellTpe)
}
val tfTerm = TermName(tfName)
val tfType = TypeName(tfName)
val outTpe = TypeCalc(funcApplyTpe).tpe
val paramVec = for (i <- 4 to shellTpe.typeArgs.length by 2; typeTree = AppliedTypeTree(Ident(TypeName("")), List(tq"${shellTpe.typeArgs(i-1)}")))
yield ValDef(Modifiers(Flag.PARAM | Flag.BYNAMEPARAM),TermName(s"arg${(i-4)/2+1}"),typeTree,EmptyTree)
val paramTree = List(paramVec.toList)
val genTree =
q"""
new $shellTpe {
type Out = $outTpe
def apply(...$paramTree) : _root_.singleton.twoface.TwoFace.$tfType[$outTpe] = {
_root_.singleton.twoface.TwoFace.$tfTerm.create[$outTpe]($tfValueTree)
}
}
"""
// println(showCode(genTree))
genTree
}
}
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// TwoFace
///////////////////////////////////////////////////////////////////////////////////////////
def TwoFaceMaterializer : TwoFaceMaterializer = new TwoFaceMaterializer
final class TwoFaceMaterializer {
def genTwoFace(outTpe : Type, outTree : Tree, tfName : String) : c.Tree = {
val tfTerm = TermName(tfName)
q"""
_root_.singleton.twoface.TwoFace.$tfTerm.create[$outTpe]($outTree)
"""
}
def genTwoFace(calc : CalcVal) : c.Tree = {
genTwoFace(calc.tpe, calc.tree, calc.name)
}
def fromNumValue(numValueTree : c.Tree, tfSym : TypeSymbol) : c.Tree = {
// println(tfSym.name)
val genTree = genTwoFace(extractValueFromNumTree(numValueTree))
// println(genTree)
genTree
}
def toNumValue(tfTree : c.Tree, tfSym : TypeSymbol, tTpe : Type) : c.Tree = {
val calc = extractValueFromTwoFaceTree(tfTree)
val outTpe = calc.tpe
val outTree = calc.tree
val genTree =
q"""
$outTree.asInstanceOf[$tTpe]
"""
// println(genTree)
genTree
}
}
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// Checked0Param TwoFace
///////////////////////////////////////////////////////////////////////////////////////////
def Checked0ParamMaterializer[Chk, Cond, Msg, T](implicit chk : c.WeakTypeTag[Chk], cond : c.WeakTypeTag[Cond], msg : c.WeakTypeTag[Msg], t : c.WeakTypeTag[T]) :
Checked0ParamMaterializer[Chk, Cond, Msg, T] = new Checked0ParamMaterializer[Chk, Cond, Msg, T](symbolOf[Chk], weakTypeOf[Cond], weakTypeOf[Msg], weakTypeOf[T])
final class Checked0ParamMaterializer[Chk, Cond, Msg, T](chkSym : TypeSymbol, condTpe : Type, msgTpe : Type, tTpe : Type) {
def newChecked(calc : CalcVal, chkArgTpe : Type) : c.Tree = {
val outTpe = calc.tpe
val outTree = calc.tree
val outTpeWide = outTpe.widen
val fixedCondTpe = appliedType(condTpe.typeConstructor, outTpe).dealias
val fixedMsgTpe = appliedType(msgTpe.typeConstructor, outTpe).dealias
val condCalc = TypeCalc(fixedCondTpe) match {
case t : CalcVal => t
case _ => extractionFailed(fixedCondTpe)
}
val msgCalc = condCalc match {
case (CalcLit.Boolean(true)) => CalcLit.String("") //Not calculating message if condition is constant true
case _ => TypeCalc(fixedMsgTpe) match {
case t : CalcVal => t
case _ => extractionFailed(fixedMsgTpe)
}
}
val reqCalc = opCalc(funcTypes.Require, condCalc, msgCalc, CalcUnknown(typeOf[NoSym]))
q"""
(new $chkSym[$condTpe, $msgTpe, $chkArgTpe]($outTree.asInstanceOf[$outTpe]))
"""
}
def newChecked(calc : CalcVal) : c.Tree = newChecked(calc, calc.tpe)
def fromOpImpl(opTree : c.Tree) : c.Tree = {
val numValueCalc = extractValueFromOpTree(opTree)
val genTree = newChecked(numValueCalc, tTpe)
// println(genTree)
genTree
}
def fromNumValue(numValueTree : c.Tree) : c.Tree = {
val numValueCalc = extractValueFromNumTree(numValueTree)
val genTree = newChecked(numValueCalc)
// println(genTree)
genTree
}
def fromTF(tfTree : c.Tree) : c.Tree = {
val tfValueCalc = extractValueFromTwoFaceTree(tfTree)
val genTree = newChecked(tfValueCalc)
// println(genTree)
genTree
}
def widen(chkTree : c.Tree) : c.Tree = {
val tfValueCalc = extractValueFromTwoFaceTree(chkTree)
val genTree = newChecked(tfValueCalc, tTpe)
// println(genTree)
genTree
}
}
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// Checked1Param TwoFace
///////////////////////////////////////////////////////////////////////////////////////////
def Checked1ParamMaterializer[Chk, Cond, Msg, T, ParamFace, Param](implicit chk : c.WeakTypeTag[Chk], cond : c.WeakTypeTag[Cond], msg : c.WeakTypeTag[Msg], t : c.WeakTypeTag[T], paramFace : c.WeakTypeTag[ParamFace], p : c.WeakTypeTag[Param]) :
Checked1ParamMaterializer[Chk, Cond, Msg, T, ParamFace, Param] = new Checked1ParamMaterializer[Chk, Cond, Msg, T, ParamFace, Param](symbolOf[Chk], weakTypeOf[Cond], weakTypeOf[Msg], weakTypeOf[T], weakTypeOf[ParamFace], weakTypeOf[Param])
final class Checked1ParamMaterializer[Chk, Cond, Msg, T, ParamFace, Param](chkSym : TypeSymbol, condTpe : Type, msgTpe : Type, tTpe : Type, paramFaceTpe : Type, paramTpe : Type) {
def newChecked(tCalc : CalcVal, chkArgTpe : Type) : c.Tree = {
val outTpe = tCalc.tpe
val outTree = tCalc.tree
val paramCalc = TypeCalc(paramTpe) match {
case t : CalcVal => t
case _ => extractionFailed(paramTpe)
}
val fixedCondTpe = appliedType(condTpe.typeConstructor, tCalc.tpe, paramCalc.tpe).dealias
val fixedMsgTpe = appliedType(msgTpe.typeConstructor, tCalc.tpe, paramCalc.tpe).dealias
val condCalc = TypeCalc(fixedCondTpe) match {
case t : CalcVal => t
case _ => extractionFailed(fixedCondTpe)
}
val msgCalc = condCalc match {
case (CalcLit.Boolean(true)) => CalcLit.String("") //Not calculating message if condition is constant true
case _ => TypeCalc(fixedMsgTpe) match {
case t : CalcVal => t
case _ => extractionFailed(fixedMsgTpe)
}
}
val reqCalc = opCalc(funcTypes.Require, condCalc, msgCalc, CalcUnknown(typeOf[NoSym]))
q"""
(new $chkSym[$condTpe, $msgTpe, $chkArgTpe, $paramFaceTpe, $paramTpe]($outTree.asInstanceOf[$outTpe]))
"""
}
def newChecked(tCalc : CalcVal) : c.Tree =
newChecked(tCalc, tCalc.tpe)
def fromOpImpl(tOpTree : c.Tree) : c.Tree = {
val tCalc = extractValueFromOpTree(tOpTree)
val genTree = newChecked(tCalc, tTpe)
// println(genTree)
genTree
}
def fromNumValue(tNumTree : c.Tree) : c.Tree = {
val tCalc = extractValueFromNumTree(tNumTree)
val genTree = newChecked(tCalc)
// println(genTree)
genTree
}
def fromTF(tTFTree : c.Tree) : c.Tree = {
val tCalc = extractValueFromTwoFaceTree(tTFTree)
val genTree = newChecked(tCalc)
// println(genTree)
genTree
}
def widen(tTFTree : c.Tree) : c.Tree = {
val tCalc = extractValueFromTwoFaceTree(tTFTree)
val genTree = newChecked(tCalc, tTpe)
// println(genTree)
genTree
}
}
///////////////////////////////////////////////////////////////////////////////////////////
//copied from Shapeless
import scala.annotation.tailrec
def mkNatTpt(i: Int): Tree = {
val succSym = typeOf[shapeless.Succ[_]].typeConstructor.typeSymbol
val _0Sym = typeOf[shapeless._0].typeSymbol
@tailrec
def loop(i: Int, acc: Tree): Tree = {
if (i == 0) acc
else loop(i - 1, AppliedTypeTree(Ident(succSym), List(acc)))
}
loop(i, Ident(_0Sym))
}
//copied from Shapeless
def mkNatTpe(i: Int): Type = {
val succTpe = typeOf[shapeless.Succ[_]].typeConstructor
val _0Tpe = typeOf[shapeless._0]
@tailrec
def loop(i: Int, acc: Type): Type = {
if (i == 0) acc
else loop(i - 1, appliedType(succTpe, acc))
}
loop(i, _0Tpe)
}
//copied from Shapeless
def mkNatValue(i: Int): Tree =
q""" new ${mkNatTpt(i)} """
//copied from Shapeless
val SymTpe = typeOf[scala.Symbol]
object SingletonSymbolType {
val atatTpe = typeOf[@@[_,_]].typeConstructor
val TaggedSym = typeOf[tag.Tagged[_]].typeConstructor.typeSymbol
def unrefine(t: Type): Type =
t.dealias match {
case RefinedType(List(t), scope) if scope.isEmpty => unrefine(t)
case t => t
}
def apply(s: String): Type = appliedType(atatTpe, List(SymTpe, c.internal.constantType(Constant(s))))
def unapply(t: Type): Option[String] =
unrefine(t).dealias match {
case RefinedType(List(SymTpe, TypeRef(_, TaggedSym, List(ConstantType(Constant(s: String))))), _) => Some(s)
case _ => None
}
}
//copied from Shapeless
def mkSingletonSymbol(s: String): Tree = {
val sTpe = SingletonSymbolType(s)
q"""_root_.scala.Symbol($s).asInstanceOf[$sTpe]"""
}
def mkSingletonSymbolWide(s: String): Tree = {
q"""_root_.scala.Symbol($s)"""
}
///////////////////////////////////////////////////////////////////////////////////////////
}