scala.meta.internal.tokens.token.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of common_2.11 Show documentation
Show all versions of common_2.11 Show documentation
Bag of private and public helpers used in scala.meta's APIs and implementations
package scala.meta
package internal
package tokens
import scala.annotation.StaticAnnotation
import scala.collection.mutable.ListBuffer
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
// @freeform and @fixed are specialized versions of @org.scalameta.adt.leaf for scala.meta tokens.
class freeform(name: String) extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro TokenNamerMacros.freeform
}
class fixed(name: String) extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro TokenNamerMacros.fixed
}
class TokenNamerMacros(val c: Context) extends TokenNamerMacroHelpers {
import c.universe._
val Dialect = tq"_root_.scala.meta.Dialect"
val Input = tq"_root_.scala.meta.inputs.Input"
val Int = tq"_root_.scala.Int"
val PositionClass = tq"_root_.scala.meta.inputs.Position"
val PositionModule = q"_root_.scala.meta.inputs.Position"
def freeform(annottees: Tree*): Tree = impl(annottees, isFixed = false)
def fixed(annottees: Tree*): Tree = impl(annottees, isFixed = true)
private def impl(annottees: Seq[Tree], isFixed: Boolean): Tree = annottees.transformAnnottees(new ImplTransformer {
override def transformClass(cdef: ClassDef, mdef: ModuleDef): List[ImplDef] = {
val q"new $_(...$argss).macroTransform(..$_)" = c.macroApplication
val providedTokenName = argss match {
case List(List(Literal(Constant(tokenName: String)))) => tokenName
case _ => c.abort(c.enclosingPosition, "@token annotation takes a literal string argument")
}
val q"$mods class $name[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" =
cdef
val q"$mmods object $mname extends { ..$mearlydefns } with ..$mparents { $mself => ..$mstats }" =
mdef
val unapplyParams = paramss.head
val stats1 = ListBuffer[Tree]() ++ stats
val anns1 = ListBuffer[Tree]() ++ mods.annotations
def mods1 = mods.mapAnnotations(_ => anns1.toList)
val mstats1 = ListBuffer[Tree]() ++ mstats
val manns1 = ListBuffer[Tree]() ++ mmods.annotations
def mmods1 = mmods.mapAnnotations(_ => manns1.toList)
def hasMethod(name: String): Boolean = stats1
.exists { case DefDef(_, TermName(`name`), _, _, _, _) => true; case _ => false }
// step 1: generate boilerplate required by the @adt infrastructure
// NOTE: toString is inherited from Token, unapply is customized.
anns1 += q"new $AdtPackage.leaf(toString = false, apply = false, unapply = false)"
anns1 +=
q"new $TokenMetadataModule.tokenClass(name = $providedTokenName, freeform = ${!isFixed})"
manns1 += q"new $TokenMetadataModule.tokenCompanion"
// step 2: generate boilerplate required by the classifier infrastructure
mstats1 ++= getClassifierBoilerplate(cdef, unapplyParams.isEmpty)
// step 3: perform manual mixin composition in order to avoid the creation of Token$class.class.
// We kinda have to do that, because we want to have a `Token.Class` class.
stats1 +=
q"""
def pos: $PositionClass = $PositionModule.Range(this.input, this.start, this.end)
"""
stats1 +=
q"""
final override def toString: $StringClass = _root_.scala.meta.internal.prettyprinters.TokenToString(this)
"""
// step 4: generate implementation of `def name: String`
val tokenName = Chars.escape(providedTokenName)
stats1 += q"def name: _root_.scala.Predef.String = $tokenName"
// step 5: generate implementation of `def end: String` for fixed tokens
if (isFixed) {
val len = providedTokenName.length
if (!hasMethod("len")) stats1 += q"override final def len: _root_.scala.Int = $len"
if (!hasMethod("isEmpty")) stats1 +=
q"override final def isEmpty: _root_.scala.Boolean = ${len == 0}"
if (!hasMethod("end")) // for fixed, as simple as adding length of name
stats1 += q"final def end: _root_.scala.Int = this.start + $len"
if (!hasMethod("text")) stats1 +=
q"override final def text: _root_.scala.Predef.String = $providedTokenName"
}
// step 6: generate implementation of `Companion.unapply`
if (unapplyParams.nonEmpty) {
val successTargs = unapplyParams.map(_.tpt)
val successTpe = tq"(..$successTargs)"
val successArgs = q"(..${unapplyParams.map(p => q"x.${p.name}")})"
mstats1 +=
q"""
def unapply(x: $name): Option[$successTpe] = {
if (x == null) _root_.scala.None
else _root_.scala.Some($successArgs)
}
"""
}
// step 7: generate boilerplate parameters
var boilerplateParams = List(q"val input: $Input", q"val dialect: $Dialect")
if (!hasMethod("start")) boilerplateParams :+= q"val start: $Int"
if (!isFixed && !hasMethod("end")) boilerplateParams :+= q"val end: $Int"
val paramss1 = (boilerplateParams ++ unapplyParams) +: paramss.tail
// step 8: generate implementation of `Companion.apply`
val applyParamss = paramss1.map(_.map(_.duplicate))
val applyArgss = paramss1.map(_.map(p => q"${p.name}"))
mstats1 += q"private[meta] def apply(...$applyParamss): $name = new $name(...$applyArgss)"
val cdef1 =
q"$mods1 class $name[..$tparams] $ctorMods(...$paramss1) extends { ..$earlydefns } with ..$parents { $self => ..$stats1 }"
val mdef1 =
q"$mmods1 object $mname extends { ..$mearlydefns } with ..$mparents { $mself => ..$mstats1 }"
List(cdef1, mdef1)
}
})
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy