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

macrame.enum.scala Maven / Gradle / Ivy

Go to download

Macrame provides macro-based replacements for parts of the Scala standard library.

The newest version!
package macrame

import macrame.internal.renderName

import scala.reflect.macros.whitebox.Context
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation

class enum extends StaticAnnotation {
   def macroTransform(annottees : Any*) : Any = macro enum.impl
}

object enum {

   def impl(c : Context)(annottees : c.Expr[Any]*) : c.Expr[Any] = {
      import c.universe._

      case class Case(name : TermName, str : Tree)

      def zipWithIndex[A](as : List[A]) : List[(Int, A)] = {
         var i = -1
         as.map { a =>
            i = i + 1
            i -> a
         }
      }

      val (input : Tree, companion : Option[Tree]) = annottees match {
         case clazz :: obj :: Nil => (clazz.tree, Some(obj.tree))
         case clazz :: Nil        => (clazz.tree, None)
         case _                   => c.abort(NoPosition, "Enum must be a class.")
      }
      val outputs = input match {
         case ClassDef(mods @ _, enumName, tparams @ _, impl) =>
            impl.body.foreach {
               case Ident(_) =>
               case Apply(Ident(_), _ :: Nil) =>
               case DefDef(_, name, _, _, _, _) if name.decodedName.toString == "" =>
               case t =>
                  // println(showRaw(t))
                  c.abort(t.pos, "Invalid case in Enum declaration.")
            }
            val init = impl.body.find {
               case DefDef(_, name, _, _, _, _) if name.decodedName.toString == "" => true
            }.get

            val cases : List[Case] = impl.body.collect {
               case Apply(Ident(name), str :: Nil) => Case(name.toTermName, str)
               case Ident(name) =>
                  Case(name.toTermName, Literal(Constant(renderName(name))))
            }

            val Enum = enumName.toTypeName

            val caseObjects = cases.map(cse =>
               q"""case object ${cse.name} extends $Enum""")

            val asString = {
               val caseDefs : List[CaseDef] = cases.map(cse =>
                  cq"""`${cse.name}` => ${cse.str}""")

               q"""protected def asStringImpl(e : $Enum) = e match {
                  case ..$caseDefs
               }"""
            }

            val fromString = {
               val caseDefs : List[CaseDef] = cases.map { cse =>
                  cq"""`${cse.str}` => Some(${cse.name})"""
               }

               q"""protected def fromStringImpl(s : String) : Option[$Enum] = s match {
                  case ..$caseDefs
                  case _ => None
               }"""
            }

            val indexedCases = zipWithIndex(cases)

            val asInt = {
               val caseDefs : List[CaseDef] = indexedCases map {
                  case (i, cse) => cq"`${cse.name}` => $i"
               }

               q"""protected def asIntImpl(e : $Enum) = e match {
                  case ..$caseDefs
               }"""
            }

            val fromInt = {
               val caseDefs : List[CaseDef] = indexedCases map {
                  case (i, cse) => cq"$i => Some(${cse.name})"
               }

               q"""protected def fromIntImpl(i : Int) : Option[$Enum] = i match {
                  case ..$caseDefs
                  case _ => None
               }"""
            }

            val first = q"protected def firstImpl : $Enum = ${cases.head.name}"
            val last = q"protected def lastImpl : $Enum = ${cases.last.name}"

            val values = q"""
               protected lazy val valuesImpl : Set[$Enum] = Set(..${cases.map(_.name)})
            """

            val className = q"""
               protected lazy val className : String = ${Enum.decodedName.toString}
            """

            val apiImpl = List(
               asString,
               fromString,
               asInt,
               fromInt,
               first,
               last,
               values,
               className)

            val enumApi = tq"macrame.EnumApi[$Enum]"

            val companionObj = companion match {
               case Some(ModuleDef(mods, objName, objImpl)) =>
                  val newParents = enumApi :: (objImpl.parents.filter {
                     case Select(Ident(scala), anyRef) if scala.toString == "scala" && anyRef.toString == "AnyRef" => false
                     case _ => true
                  })
                  ModuleDef(
                     mods,
                     objName,
                     Template(
                        newParents,
                        objImpl.self,
                        caseObjects ++ apiImpl ++ objImpl.body))
               case None =>
                  val newParents = enumApi :: (impl.parents.filter {
                     case Select(Ident(scala), anyRef) if scala.toString == "scala" && anyRef.toString == "AnyRef" => false
                     case _ => true
                  })
                  ModuleDef(
                     Modifiers(),
                     enumName.toTermName,
                     Template(newParents, impl.self, init :: caseObjects ++ apiImpl))
            }
            List(
               q"sealed abstract class ${enumName.toTypeName} extends Product with Serializable",
               companionObj)
         case _ => c.abort(NoPosition, "Enum must be a class.")
      }
      // outputs.foreach(println)
      c.Expr[Any](Block(outputs, Literal(Constant(()))))
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy