All Downloads are FREE. Search and download functionalities are using the official Maven repository.

spire.macros.Auto.scala Maven / Gradle / Ivy

The newest version!
package spire
package macros

import language.experimental.macros

import spire.algebra._
import spire.macros.compat.{Context, termName}

object Auto {
  object scala {
    def semiring[A]: Semiring[A] = macro ScalaAutoMacros.semiringImpl[A]
    def rig[A](z: A, o: A): Rig[A] = macro ScalaAutoMacros.rigImpl[A]
    def rng[A](z: A): Rng[A] = macro ScalaAutoMacros.rngImpl[A]
    def ring[A](z: A, o: A): Ring[A] = macro ScalaAutoMacros.ringImpl[A]
    def euclideanRing[A](z: A, o: A)(implicit ev: Eq[A]): EuclideanRing[A] = macro ScalaAutoMacros.euclideanRingImpl[A]
    def field[A](z: A, o: A)(implicit ev: Eq[A]): Field[A] = macro ScalaAutoMacros.fieldImpl[A]
    def eq[A]: Eq[A] = macro ScalaAutoMacros.eqImpl[A]
    // TODO: partialOrder ?
    def order[A]: Order[A] = macro ScalaAutoMacros.orderImpl[A]

    object collection {
      def semigroup[A]: Semigroup[A] = macro ScalaAutoMacros.collectionSemigroupImpl[A]
      def monoid[A](z: A): Monoid[A] = macro ScalaAutoMacros.collectionMonoidImpl[A]
    }
  }

  object java {
    def semiring[A]: Semiring[A] = macro JavaAutoMacros.semiringImpl[A]
    def rig[A](z: A, o: A): Rig[A] = macro JavaAutoMacros.rigImpl[A]
    def rng[A](z: A): Rng[A] = macro JavaAutoMacros.rngImpl[A]
    def ring[A](z: A, o: A): Ring[A] = macro JavaAutoMacros.ringImpl[A]
    def euclideanRing[A](z: A, o: A)(implicit ev: Eq[A]): EuclideanRing[A] = macro JavaAutoMacros.euclideanRingImpl[A]
    def field[A](z: A, o: A)(implicit ev: Eq[A]): Field[A] = macro JavaAutoMacros.fieldImpl[A]
    def eq[A]: Eq[A] = macro JavaAutoMacros.eqImpl[A]
    // TODO: partialOrder ?
    def order[A]: Order[A] = macro JavaAutoMacros.orderImpl[A]

    object collection {
      def monoid[A](empty: A): Monoid[A] = macro JavaAutoMacros.collectionMonoidImpl[A]
    }
  }
}

abstract class AutoOps {
  val c: Context
  import c.universe._

  def unop[A](name: String, x: String = "x"): c.Expr[A] =
    c.Expr[A](Select(Ident(termName(c)(x)), termName(c)(name)))

  def binop[A](name: String, x: String = "x", y: String = "y"): c.Expr[A] =
    c.Expr[A](Apply(
      Select(Ident(termName(c)(x)), termName(c)(name)),
      List(Ident(termName(c)(y)))))

  def binopSearch[A: c.WeakTypeTag](names: List[String], x: String = "x", y: String = "y"): Option[c.Expr[A]] =
    names find { name => hasMethod1[A, A, A](name) } map (binop[A](_, x, y))

  def unopSearch[A: c.WeakTypeTag](names: List[String], x: String = "x"): Option[c.Expr[A]] =
    names find { name => hasMethod0[A, A](name) } map (unop[A](_, x))

  def hasMethod0[A: c.WeakTypeTag, B: c.WeakTypeTag](name: String): Boolean = {
    val tpeA = c.weakTypeTag[A].tpe
    val tpeB = c.weakTypeTag[B].tpe
    tpeA.members exists { m =>
      m.isMethod && m.isPublic && m.name.encodedName.toString == name && (m.typeSignature match {
        case MethodType(Nil, ret) => ret =:= tpeB
        case _ => false
      })
    }
  }

  def hasMethod1[A: c.WeakTypeTag, B: c.WeakTypeTag, C: c.WeakTypeTag](name: String): Boolean = {
    val tpeA = c.weakTypeTag[A].tpe
    val tpeB = c.weakTypeTag[B].tpe
    val tpeC = c.weakTypeTag[C].tpe
    tpeA.members exists { m =>
      m.isMethod && m.isPublic && m.name.encodedName.toString == name && (m.typeSignature match {
        case MethodType(List(param), ret) =>
          param.typeSignature =:= tpeB && ret =:= tpeC
        case _ =>
          false
      })
    }
  }

  def failedSearch(name: String, op: String): c.Expr[Nothing] =
    c.abort(c.enclosingPosition,
      "Couldn't find matching method for op %s (%s)." format (name, op))
}

abstract class AutoAlgebra extends AutoOps { ops =>

  def plus[A: c.WeakTypeTag]: c.Expr[A]
  def minus[A: c.WeakTypeTag]: c.Expr[A]
  def times[A: c.WeakTypeTag]: c.Expr[A]
  def negate[A: c.WeakTypeTag]: c.Expr[A]
  def div[A: c.WeakTypeTag]: c.Expr[A]
  def quot[A: c.WeakTypeTag]: c.Expr[A]
  def mod[A: c.WeakTypeTag](stub: => c.Expr[A] = failedSearch("mod", "%")): c.Expr[A]
  def equals: c.Expr[Boolean]
  def compare: c.Expr[Int]

  def Semiring[A: c.WeakTypeTag](): c.Expr[Semiring[A]] = {
    c.universe.reify {
      new Semiring[A] {
        def plus(x: A, y: A): A = ops.plus[A].splice
        def times(x: A, y: A): A = ops.times[A].splice
      }
    }
  }

  def Rig[A: c.WeakTypeTag](z: c.Expr[A], o: c.Expr[A]): c.Expr[Rig[A]] = {
    c.universe.reify {
      new Rig[A] {
        def zero: A = z.splice
        def one: A = o.splice
        def plus(x: A, y: A): A = ops.plus[A].splice
        def times(x: A, y: A): A = ops.times[A].splice
      }
    }
  }

  def Rng[A: c.WeakTypeTag](z: c.Expr[A]): c.Expr[Rng[A]] = {
    c.universe.reify {
      new Rng[A] {
        def zero: A = z.splice
        def plus(x: A, y: A): A = ops.plus[A].splice
        def times(x: A, y: A): A = ops.times[A].splice
        override def minus(x: A, y: A): A = ops.minus[A].splice
        def negate(x: A): A = ops.negate[A].splice
      }
    }
  }

  def Ring[A: c.WeakTypeTag](z: c.Expr[A], o: c.Expr[A]): c.Expr[Ring[A]] = {
    c.universe.reify {
      new Ring[A] {
        def zero: A = z.splice
        def one: A = o.splice
        def plus(x: A, y: A): A = ops.plus[A].splice
        def times(x: A, y: A): A = ops.times[A].splice
        override def minus(x: A, y: A): A = ops.minus[A].splice
        def negate(x: A): A = ops.negate[A].splice
      }
    }
  }

  def EuclideanRing[A: c.WeakTypeTag](z: c.Expr[A], o: c.Expr[A])
      (ev: c.Expr[Eq[A]]): c.Expr[EuclideanRing[A]] = {
    c.universe.reify {
      new EuclideanRing[A] {
        def zero: A = z.splice
        def one: A = o.splice
        def plus(x: A, y: A): A = ops.plus[A].splice
        def times(x: A, y: A): A = ops.times[A].splice
        override def minus(x: A, y: A): A = ops.minus[A].splice
        def negate(x: A): A = ops.negate[A].splice
        def quot(x: A, y: A): A = ops.quot[A].splice
        def mod(x: A, y: A): A = ops.mod[A]().splice
        def gcd(x: A, y: A): A = euclid(x, y)(ev.splice)
      }
    }
  }

  def Field[A: c.WeakTypeTag]
      (z: c.Expr[A], o: c.Expr[A])(ev: c.Expr[Eq[A]]): c.Expr[Field[A]] = {
    c.universe.reify {
      new Field[A] {
        def zero: A = z.splice
        def one: A = o.splice
        def plus(x: A, y: A): A = ops.plus[A].splice
        def times(x: A, y: A): A = ops.times[A].splice
        override def minus(x: A, y: A): A = ops.minus[A].splice
        def negate(x: A): A = ops.negate[A].splice
        def quot(x: A, y: A): A = ops.div[A].splice
        def mod(x: A, y: A): A = ops.mod[A](z).splice
        def gcd(x: A, y: A): A = euclid(x, y)(ev.splice)
        def div(x: A, y: A): A = ops.div[A].splice
      }
    }
  }

  def Eq[A: c.WeakTypeTag](): c.Expr[Eq[A]] = {
    c.universe.reify {
      new Eq[A] {
        def eqv(x: A, y: A): Boolean = ops.equals.splice
      }
    }
  }

  def Order[A: c.WeakTypeTag](): c.Expr[Order[A]] = {
    c.universe.reify {
      new Order[A] {
        override def eqv(x: A, y: A): Boolean = ops.equals.splice
        def compare(x: A, y: A): Int = ops.compare.splice
      }
    }
  }
}

case class ScalaAlgebra[C <: Context](c: C) extends AutoAlgebra {
  // we munge these names with dollar signs in them to avoid getting
  // warnings about possible interpolations. these are not intended to
  // be interpolations.
  def plusplus[A]: c.Expr[A] = binop[A]("$" + "plus" + "$" + "plus")
  def plus[A: c.WeakTypeTag]: c.Expr[A] = binop[A]("$" + "plus")
  def minus[A: c.WeakTypeTag]: c.Expr[A] = binop[A]("$" + "minus")
  def times[A: c.WeakTypeTag]: c.Expr[A] = binop[A]("$" + "times")
  def negate[A: c.WeakTypeTag]: c.Expr[A] = unop[A]("unary_" + "$" + "minus")
  def quot[A: c.WeakTypeTag]: c.Expr[A] = binopSearch[A]("quot" :: ("$" + "div") :: Nil) getOrElse failedSearch("quot", "/~")
  def div[A: c.WeakTypeTag]: c.Expr[A] = binop[A]("$" + "div")
  def mod[A: c.WeakTypeTag](stub: => c.Expr[A]): c.Expr[A] = binop[A]("$" + "percent")
  def equals: c.Expr[Boolean] = binop[Boolean]("$" + "eq" + "$" + "eq")
  def compare: c.Expr[Int] = binop[Int]("compare")
}

case class JavaAlgebra[C <: Context](c: C) extends AutoAlgebra {
  def plus[A: c.WeakTypeTag]: c.Expr[A] =
    binopSearch[A]("add" :: "plus" :: Nil) getOrElse failedSearch("plus", "+")
  def minus[A: c.WeakTypeTag]: c.Expr[A] =
    binopSearch[A]("subtract" :: "minus" :: Nil) getOrElse failedSearch("minus", "-")
  def times[A: c.WeakTypeTag]: c.Expr[A] =
    binopSearch[A]("multiply" :: "times" :: Nil) getOrElse failedSearch("times", "*")
  def div[A: c.WeakTypeTag]: c.Expr[A] =
    binopSearch[A]("divide" :: "div" :: Nil) getOrElse failedSearch("div", "/")
  def negate[A: c.WeakTypeTag]: c.Expr[A] =
    unopSearch[A]("negate" :: "negative" :: Nil) getOrElse {
      // We can implement negate interms of minus. This is actually required
      // for JScience's Rational :(
      import c.universe._
      c.Expr[A](Apply(
        Select(Ident(termName(c)("zero")), termName(c)("minus")),
        List(Ident(termName(c)("x")))))
    }
  def quot[A: c.WeakTypeTag]: c.Expr[A] =
    binopSearch[A]("quot" :: "divide" :: "div" :: Nil) getOrElse failedSearch("quot", "/~")
  def mod[A: c.WeakTypeTag](stub: => c.Expr[A]): c.Expr[A] =
    binopSearch("mod" :: "remainder" :: Nil) getOrElse stub

  def equals: c.Expr[Boolean] = binop[Boolean]("equals")
  def compare: c.Expr[Int] = binop[Int]("compareTo")
}

object ScalaAutoMacros {
  def semiringImpl[A: c.WeakTypeTag](c: Context): c.Expr[Semiring[A]] =
    ScalaAlgebra[c.type](c).Semiring[A]()

  def rigImpl[A: c.WeakTypeTag](c: Context)(z: c.Expr[A], o: c.Expr[A]): c.Expr[Rig[A]] =
    ScalaAlgebra[c.type](c).Rig[A](z, o)

  def rngImpl[A: c.WeakTypeTag](c: Context)(z: c.Expr[A]): c.Expr[Rng[A]] =
    ScalaAlgebra[c.type](c).Rng[A](z)

  def ringImpl[A: c.WeakTypeTag](c: Context)(z: c.Expr[A], o: c.Expr[A]): c.Expr[Ring[A]] =
    ScalaAlgebra[c.type](c).Ring[A](z, o)

  def euclideanRingImpl[A: c.WeakTypeTag](c: Context)
      (z: c.Expr[A], o: c.Expr[A])(ev: c.Expr[Eq[A]]): c.Expr[EuclideanRing[A]] =
    ScalaAlgebra[c.type](c).EuclideanRing[A](z, o)(ev)

  def fieldImpl[A: c.WeakTypeTag](c: Context)
      (z: c.Expr[A], o: c.Expr[A])(ev: c.Expr[Eq[A]]): c.Expr[Field[A]] =
    ScalaAlgebra[c.type](c).Field[A](z, o)(ev)

  def eqImpl[A: c.WeakTypeTag](c: Context): c.Expr[Eq[A]] =
    ScalaAlgebra[c.type](c).Eq[A]()

  def orderImpl[A: c.WeakTypeTag](c: Context): c.Expr[Order[A]] =
    ScalaAlgebra[c.type](c).Order[A]()

  def collectionSemigroupImpl[A: c.WeakTypeTag](c: Context): c.Expr[Semigroup[A]] = {
    val ops = ScalaAlgebra[c.type](c)
    c.universe.reify {
      new Semigroup[A] {
        def op(x: A, y: A): A = ops.plusplus[A].splice
      }
    }
  }

  def collectionMonoidImpl[A: c.WeakTypeTag](c: Context)(z: c.Expr[A]): c.Expr[Monoid[A]] = {
    val ops = ScalaAlgebra[c.type](c)
    c.universe.reify {
      new Monoid[A] {
        def id: A = z.splice
        def op(x: A, y: A): A = ops.plusplus[A].splice
      }
    }
  }
}

object JavaAutoMacros {
  def semiringImpl[A: c.WeakTypeTag](c: Context): c.Expr[Semiring[A]] =
    JavaAlgebra[c.type](c).Semiring[A]()

  def rigImpl[A: c.WeakTypeTag](c: Context)(z: c.Expr[A], o: c.Expr[A]): c.Expr[Rig[A]] =
    JavaAlgebra[c.type](c).Rig[A](z, o)

  def rngImpl[A: c.WeakTypeTag](c: Context)(z: c.Expr[A]): c.Expr[Rng[A]] =
    JavaAlgebra[c.type](c).Rng[A](z)

  def ringImpl[A: c.WeakTypeTag](c: Context)(z: c.Expr[A], o: c.Expr[A]): c.Expr[Ring[A]] =
    JavaAlgebra[c.type](c).Ring[A](z, o)

  def euclideanRingImpl[A: c.WeakTypeTag](c: Context)
      (z: c.Expr[A], o: c.Expr[A])(ev: c.Expr[Eq[A]]): c.Expr[EuclideanRing[A]] =
    JavaAlgebra[c.type](c).EuclideanRing[A](z, o)(ev)

  def fieldImpl[A: c.WeakTypeTag](c: Context)
      (z: c.Expr[A], o: c.Expr[A])(ev: c.Expr[Eq[A]]): c.Expr[Field[A]] =
    JavaAlgebra[c.type](c).Field[A](z, o)(ev)

  def eqImpl[A: c.WeakTypeTag](c: Context): c.Expr[Eq[A]] =
    JavaAlgebra[c.type](c).Eq[A]()

  def orderImpl[A: c.WeakTypeTag](c: Context): c.Expr[Order[A]] =
    JavaAlgebra[c.type](c).Order[A]()

  def collectionMonoidImpl[A: c.WeakTypeTag](c: Context)(empty: c.Expr[A]): c.Expr[Monoid[A]] = {
    val ops = JavaAlgebra[c.type](c)
    val addx = ops.binop[Unit]("addAll", "z", "x")
    val addy = ops.binop[Unit]("addAll", "z", "y")
    c.universe.reify {
      new Monoid[A] {
        def id: A = empty.splice
        def op(x: A, y: A): A = {
          val z = id
          addx.splice
          addy.splice
          z
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy