Skip to content

Instantly share code, notes, and snippets.

@DmytroMitin
Last active November 19, 2022 04:38
ThisBuild / version := "0.1.0-SNAPSHOT"
//ThisBuild / scalaVersion := "2.13.10"
ThisBuild / scalaVersion := "2.12.17"
lazy val macroAnnotationSettings = Seq(
scalacOptions ++= (CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, v)) if v >= 13 => Seq("-Ymacro-annotations")
case _ => Nil
}),
libraryDependencies ++= (CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, v)) if v <= 12 =>
Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full))
case _ => Nil
})
)
lazy val core = project
.settings(
macroAnnotationSettings,
scalacOptions ++= Seq(
"-Ymacro-debug-lite",
),
)
.dependsOn(macros)
lazy val macros = project
.settings(
macroAnnotationSettings,
libraryDependencies ++= Seq(
scalaOrganization.value % "scala-reflect" % scalaVersion.value,
),
)
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
object Macros {
@compileTimeOnly("enable macro paradise to expand macro annotations")
class GenerateCompanion extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro GenerateCompanion.impl
}
object GenerateCompanion {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
def vals(paramss: Seq[Seq[ValDef]]): Seq[ValDef] =
paramss.flatten.map(p => {
val name = p.name.toString
q"val ${TermName(underscoreToCamel(name))}: String = $name"
})
annottees match {
case (c@q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") :: Nil =>
q"""
$c
object ${tpname.toTermName} {
..${vals(paramss)}
}
"""
case (c@q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") ::
q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" :: Nil =>
q"""
$c
$mods object $tname extends { ..$earlydefns } with ..$parents { $self =>
..$body
..${vals(paramss)}
}
"""
}
}
def underscoreToCamel(name: String): String = "_([a-z\\d])".r.replaceAllIn(name, _.group(1).toUpperCase)
}
}
import Macros.GenerateCompanion
object Main extends App {
@GenerateCompanion
class Pizza(val crust_type: String, val foo_foo: Int)
object Pizza {
def bar: String = "bar"
}
println(Pizza.crustType) //crust_type
println(Pizza.fooFoo) //foo_foo
println(Pizza.bar) //bar
}
@DmytroMitin
Copy link
Author

sbt

@DmytroMitin
Copy link
Author

log

/usr/local/graalvm-ee-java8-19.3.0/bin/java -server -Xmx1536M -Dsbt.supershell=false -Didea.managed=true -Dfile.encoding=UTF-8 -Didea.installation.dir=/media/data/idea-IU-211.7628.21 -jar /home/dmitin/.local/share/JetBrains/IntelliJIdea2022.2/Scala/launcher/sbt-launch.jar early(addPluginSbtFile=\"\"\"/tmp/idea2.sbt\"\"\") "; set ideaPort in Global := 39859 ; idea-shell"
[info] welcome to sbt 1.8.0 (Oracle Corporation Java 1.8.0_231)
[info] loading global plugins from /home/dmitin/.sbt/1.0/plugins
[info] loading settings for project scalademo18-build from idea2.sbt ...
[info] loading project definition from /media/data/Projects1/scalademo18/project
[info] loading settings for project scalademo18 from build.sbt ...
[info] set current project to scalademo18 (in build file:/media/data/Projects1/scalademo18/)
[info] Defining Global / ideaPort
[info] The new value will be used by Compile / compile, Test / compile and 3 others.
[info] 	Run `last` for details.
[info] Reapplying settings...
[info] set current project to scalademo18 (in build file:/media/data/Projects1/scalademo18/)
[IJ]clean
[success] Total time: 0 s, completed 19.11.2022 6:36:58
[IJ]compile
[info] compiling 1 Scala source to /media/data/Projects1/scalademo18/macros/target/scala-2.12/classes ...
[info] done compiling
[info] compiling 1 Scala source to /media/data/Projects1/scalademo18/core/target/scala-2.12/classes ...
performing macro expansion new GenerateCompanion().macroTransform(class Pizza extends scala.AnyRef {
  <paramaccessor> val crust_type: String = _;
  <paramaccessor> val foo_foo: Int = _;
  def <init>(crust_type: String, foo_foo: Int) = {
    super.<init>();
    ()
  }
}, object Pizza extends scala.AnyRef {
  def <init>() = {
    super.<init>();
    ()
  };
  def bar: String = "bar"
}) at source-/media/data/Projects1/scalademo18/core/src/main/scala/Main.scala,line-4,offset=62
{
  class Pizza extends scala.AnyRef {
    <paramaccessor> val crust_type: String = _;
    <paramaccessor> val foo_foo: Int = _;
    def <init>(crust_type: String, foo_foo: Int) = {
      super.<init>();
      ()
    }
  };
  object Pizza extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    def bar: String = "bar";
    val crustType: String = "crust_type";
    val fooFoo: String = "foo_foo"
  };
  ()
}
Block(List(ClassDef(Modifiers(), TypeName("Pizza"), List(), Template(List(Select(Ident(scala), TypeName("AnyRef"))), noSelfType, List(ValDef(Modifiers(PARAMACCESSOR), TermName("crust_type"), Ident(TypeName("String")), EmptyTree), ValDef(Modifiers(PARAMACCESSOR), TermName("foo_foo"), Ident(TypeName("Int")), EmptyTree), DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List(ValDef(Modifiers(PARAM | PARAMACCESSOR), TermName("crust_type"), Ident(TypeName("String")), EmptyTree), ValDef(Modifiers(PARAM | PARAMACCESSOR), TermName("foo_foo"), Ident(TypeName("Int")), EmptyTree))), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(()))))))), ModuleDef(Modifiers(), TermName("Pizza"), Template(List(Select(Ident(scala), TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))), DefDef(Modifiers(), TermName("bar"), List(), List(), Ident(TypeName("String")), Literal(Constant("bar"))), ValDef(Modifiers(), TermName("crustType"), Ident(TypeName("String")), Literal(Constant("crust_type"))), ValDef(Modifiers(), TermName("fooFoo"), Ident(TypeName("String")), Literal(Constant("foo_foo"))))))), Literal(Constant(())))
[info] done compiling
[success] Total time: 7 s, completed 19.11.2022 6:37:07

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment