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

edu.stanford.dawn.virtualized.Structs.scala Maven / Gradle / Ivy

The newest version!
package virtualized

import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.reflect.macros.blackbox //TODO: should be whitebox?

/** Annotation class for @struct macro annotation. */
final class struct extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro StagedStructsMacro.impl
}

/**
  * Towards modularity:
  * Defining and composing macros in different DSLs
  *
  * Want possible type classes for struct to be generated here, based on the type classes defined/available in the DSL
  * This should eventually be generated from Forge (?), for now just outlining how it might work.
  */
abstract class TypeclassMacro {
  def generateLookup(c: blackbox.Context)(name: c.TypeName): Option[c.Tree]
  def generateImplementation(c: blackbox.Context)(className: c.Tree): List[c.Tree]
}


object StagedStructsMacro {
  val typeclasses: List[TypeclassMacro] = List(BitsTypeclassMacro, ArithsTypeclassMacro)

  def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
    import c.universe._

    if (annottees.length > 1)
      c.abort(c.enclosingPosition, "Only classes can be transformed using the @struct annotation")

    val tree = annottees.head
    tree match {
      // look for class definition
      case ClassDef(mods, className: TypeName, tparams, impl@Template(parents, selfType, bodyList)) =>
        val (fields, methods) = bodyList.partition { case _:ValDef => true case _ => false }
        if (fields.isEmpty)
          c.abort(c.enclosingPosition, "Classes need at least one field in order to be transformed into structs")
        if (methods.size > 1)
          c.abort(c.enclosingPosition, "Classes with a body (e.g. methods) cannot be transformed into structs")
        if (tparams.size > 0)
          c.abort(c.enclosingPosition, "Classes with type parameters cannot be transformed into structs")

        assert(methods.head match { case _: DefDef => true }) // the constructor

        val constructorArgs = methods.collect{
          case DefDef(ms,name,tparams,argss,_,_) if name == termNames.CONSTRUCTOR => argss
        }.head

        /**
          * Staged class definition
          */
        val fieldList = fields map {
          case ValDef(mods, termName, typeIdent, rhs) if mods.hasFlag(Flag.MUTABLE) =>
            //q"var $termName: $typeIdent"
            c.abort(c.enclosingPosition, "virtualization of variable fields is currently unsupported")
          case ValDef(mods, termName, typeIdent, rhs) =>
            q"""def $termName(implicit ctx: virtualized.SourceContext, state: argon.core.State): $typeIdent = field[$typeIdent](${Literal(Constant(termName.toString))})(implicitly[Type[$typeIdent]],ctx,state)"""
        }

        /**
          * Typeclass evidences and lookup traits
          */
        val evidences = typeclasses.flatMap{_.generateImplementation(c)(tree) }

        /**
          * Staged type
          */
        val childList = fields.map{
          case ValDef(_, termName, typeIdent, rhs) =>
            q""" ${Literal(Constant(termName.toString))} -> typ[$typeIdent]"""
        }

        val lookups = typeclasses.flatMap(_.generateLookup(c)(className) )
        val parent = {
          if (lookups.length > 1) CompoundTypeTree(Template(lookups, noSelfType, Nil))
          else lookups.head
        }

        val stg =
          q"""
            object ${TermName(className.toString + "Type")} extends argon.nodes.StructType[$className] with $parent {
              override def wrapped(x: Exp[$className]) = ${className.toTermName}(x)
              override def typeArguments = Nil
              override def stagedClass = classOf[$className]
              override def fields = Seq(..$childList)
            }
           """
        /**
          * Staged Evidence
          */
        val ev =
          q"""
            implicit def ${TermName(className.toString + "TypeEvidence")}: argon.nodes.StructType[$className] = ${TermName(className.toString + "Type")}
          """

        /**
          * Constructor
          */
        val argss = constructorArgs.map{ args => args.map{
          case ValDef(_, termName, typeIdent, rhs) =>
            val mods = if (rhs == EmptyTree) Modifiers(Flag.PARAM) else Modifiers(Flag.PARAM | Flag.DEFAULTPARAM)
            val rhsHack = if (rhs == EmptyTree) EmptyTree else q"implicitly[Type[$typeIdent]].fakeT"
            ValDef(mods, termName, typeIdent, rhsHack)
        }}
        val body = constructorArgs.flatten.map{
          case ValDef(_, termName, typeIdent, EmptyTree) =>
            q"${Literal(Constant(termName.toString))} -> $termName.s"
          case ValDef(_, termName, typeIdent, rhs) =>
            q"${Literal(Constant(termName.toString))} -> $termName.getOrElseCreate{$rhs}.s"
        }

        // TODO: We assume for now that struct annotation is always used within a trait - any way to be more general?

        val mdef = q"def apply(...$argss)(implicit ctx: virtualized.SourceContext, state: argon.core.State): $className = struct[$className]( ..$body )(implicitly[argon.nodes.StructType[$className]], ctx, state)"

        val companionObject = q"""
          object ${className.toTermName} {
            $stg ; $mdef; $ev ; ..$evidences
          }
        """

        val copyMethod = {
          val optionalArgss = constructorArgs.map{args => args.map{
            case ValDef(_, termName, typeIdent, rhs) =>
              ValDef(Modifiers(Flag.PARAM | Flag.DEFAULTPARAM), termName, typeIdent, q"implicitly[Type[$typeIdent]].fakeT") //q"this.$termName")
          }}
          val actualArgss = constructorArgs.map{args => args.map{
            case ValDef(_, termName, _, _) => q"$termName.getOrElseCreate{ this.$termName(ctx, state) }"
          }}

          q"""
             def copy(...$optionalArgss)(implicit ctx: virtualized.SourceContext, state: argon.core.State): $className = {
               ${className.toTermName}.apply(...$actualArgss)(ctx, state)
             }
           """
        }

        val cls =
          q"""
            case class $className(s: Exp[$className]) extends Struct[$className] {
              ..$fieldList ; $copyMethod
            }
           """

        // Implicit object must come before class definition
        val cc = q"$cls ; $companionObject "

        // Debugging
        // c.info(tree.pos, showCode(cc), force = true)
        // c.info(tree.pos, showRaw(mdef), force = true)
        cc

      case _ =>
        c.error(c.enclosingPosition, "Only classes can be transformed using the @struct annotation")
        annottees.head
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy