Skip to content

Commit

Permalink
Initial annotation example that creates companion object to access pa…
Browse files Browse the repository at this point in the history
…rameter names by String
  • Loading branch information
ohde committed Oct 17, 2014
1 parent c714cec commit 4385335
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 9 deletions.
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
scalaVersion := "2.11.2"

scalaVersion := "2.11.0-M5"

addCompilerPlugin("org.scalamacros" % "paradise" % "2.0.1" cross CrossVersion.full)
4 changes: 2 additions & 2 deletions macro/build.sbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
scalaVersion := "2.11.2"

scalaVersion := "2.11.0-M5"

addCompilerPlugin("org.scalamacros" % "paradise" % "2.0.1" cross CrossVersion.full)
54 changes: 51 additions & 3 deletions macro/src/main/scala/Macros.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,54 @@
import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.reflect.macros.Context
import scala.reflect.macros.whitebox.Context

object Macros {
// write macros here
class fieldNames extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro fieldNamesMacro.impl
}

object fieldNamesMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
import termNames.CONSTRUCTOR
import typeNames.EMPTY

val inputs = annottees.map(_.tree).toList
val (expanded, objName, accessorFields) = inputs match {
case (param: ClassDef) :: (tail) => {
val constructAccessor = param.collect {
case (values: ValDef)
if values.mods.hasFlag(Flag.PARAMACCESSOR) &&
!values.mods.hasFlag(Flag.PRIVATE) &&
!values.mods.hasFlag(Flag.CASEACCESSOR) => {
values
}
}
val stringDef = constructAccessor.map { field
val name = field.name.toTermName
val decoded = name.decodedName.toString
q"val $name: String = $decoded"
}
(inputs, Some(param.name.decodedName.toString), Some(stringDef))
}
case (_) => (inputs, None, None)
}

val companionObject = for {
accessorFieldsStringValues <- accessorFields
if accessorFieldsStringValues.length > 0
} yield {
val constructorBody = Block(List(Apply(Select(Super(This(EMPTY), EMPTY), CONSTRUCTOR), List())), Literal(Constant(())))
val constructor = DefDef(NoMods, CONSTRUCTOR, List(), List(List()), TypeTree(), constructorBody)
val objInheritance = List(Ident(TypeName("AnyRef")))
ModuleDef(NoMods, TermName(objName.getOrElse("Zombie")),
Template(objInheritance, noSelfType, constructor :: accessorFieldsStringValues))
}

val completeBlock = companionObject match {
case Some(x) => Block(expanded :+ x,Literal(Constant(())))
case _ => Block(expanded, Literal(Constant(())))
}
c.Expr[Any](completeBlock)
}
}

9 changes: 7 additions & 2 deletions src/main/scala/Main.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
object Main extends App {
import Macros._

println("it works!")
@fieldNames
case class Person(name: String, age: Int)

@fieldNames
class Address(street: List[String])

println(s"${Person.name} ${Person.age} ${Address.street}")
}

0 comments on commit 4385335

Please sign in to comment.