scala.reflect.quasiquotes.Placeholders.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-compiler Show documentation
Show all versions of scala-compiler Show documentation
Compiler for the SubScript extension of the Scala Programming Language
The newest version!
package scala.reflect
package quasiquotes
import java.util.UUID.randomUUID
import scala.collection.{immutable, mutable}
/** Emulates hole support (see Holes.scala) in the quasiquote parser (see Parsers.scala).
* A principled solution to splicing into Scala syntax would be a parser that natively supports holes.
* Unfortunately, that's outside of our reach in Scala 2.11, so we have to emulate.
* This trait stores knowledge of how to represent the holes as something understandable by the parser
* and how to recover holes from the results of parsing the produced representation.
*/
trait Placeholders { self: Quasiquotes =>
import global._
import Rank._
import universeTypes._
// Step 1: Transform Scala source with holes into vanilla Scala source
lazy val posMap = mutable.LinkedHashMap[Position, (Int, Int)]()
lazy val code = {
val sb = new StringBuilder()
val sessionSuffix = randomUUID().toString.replace("-", "").substring(0, 8) + "$"
def appendPart(value: String, pos: Position) = {
val start = sb.length
sb.append(value)
val end = sb.length
posMap += pos -> ((start, end))
}
def appendHole(tree: Tree, rank: Rank) = {
val placeholderName = c.freshName(TermName(nme.QUASIQUOTE_PREFIX + sessionSuffix))
sb.append(placeholderName)
val holeTree =
if (method != nme.unapply) tree
else Bind(placeholderName, tree)
holeMap(placeholderName) = Hole(rank, holeTree)
}
val iargs = method match {
case nme.apply => args
case nme.unapply => internal.subpatterns(args.head).get
case _ => global.abort("unreachable")
}
foreach2(iargs, parts.init) { case (tree, (p, pos)) =>
val (part, rank) = parseDots(p)
appendPart(part, pos)
appendHole(tree, rank)
}
val (p, pos) = parts.last
appendPart(p, pos)
sb.toString
}
object holeMap {
private val underlying = mutable.LinkedHashMap.empty[String, Hole]
private val accessed = mutable.Set.empty[String]
def unused: Set[Name] = (underlying.keys.toSet -- accessed).map(TermName(_))
def contains(key: Name): Boolean = underlying.contains(key.toString)
def apply(key: Name): Hole = {
val skey = key.toString
val value = underlying(skey)
accessed += skey
value
}
def update(key: Name, hole: Hole) =
underlying += key.toString -> hole
def get(key: Name): Option[Hole] = {
val skey = key.toString
underlying.get(skey).map { v =>
accessed += skey
v
}
}
def keysIterator: Iterator[TermName] = underlying.keysIterator.map(TermName(_))
}
// Step 2: Transform vanilla Scala AST into an AST with holes
trait HolePlaceholder {
def matching: PartialFunction[Any, Name]
def unapply(scrutinee: Any): Option[Hole] = {
val name = matching.lift(scrutinee)
name.flatMap { holeMap.get(_) }
}
}
object Placeholder extends HolePlaceholder {
def matching = {
case name: Name => name
case Ident(name) => name
case Bind(name, Ident(nme.WILDCARD)) => name
case TypeDef(_, name, List(), TypeBoundsTree(EmptyTree, EmptyTree)) => name
}
}
object ModsPlaceholder extends HolePlaceholder {
def apply(name: Name) =
Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(name.toString))))
def matching = {
case Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(s: String)))) => TermName(s)
}
}
object AnnotPlaceholder extends HolePlaceholder {
def matching = {
case Apply(Select(New(Ident(name)), nme.CONSTRUCTOR), Nil) => name
}
}
object ParamPlaceholder extends HolePlaceholder {
def apply(flags: FlagSet, name: Name) =
ValDef(Modifiers(flags), nme.QUASIQUOTE_PARAM, Ident(name), EmptyTree)
def matching = {
case ValDef(_, nme.QUASIQUOTE_PARAM, Ident(name), EmptyTree) => name
}
}
object TuplePlaceholder {
def apply(args: List[Tree]) =
Apply(Ident(nme.QUASIQUOTE_TUPLE), args)
def unapply(tree: Tree): Option[List[Tree]] = tree match {
case Apply(Ident(nme.QUASIQUOTE_TUPLE), args) => Some(args)
case _ => None
}
}
object TupleTypePlaceholder {
def apply(args: List[Tree]) =
AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), args)
def unapply(tree: Tree): Option[List[Tree]] = tree match {
case AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), args) => Some(args)
case _ => None
}
}
object FunctionTypePlaceholder {
def apply(args: List[Tree], res: Tree) =
AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), args :+ res)
def unapply(tree: Tree): Option[(List[Tree], Tree)] = tree match {
case AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), args :+ res) => Some((args, res))
case _ => None
}
}
object SymbolPlaceholder {
def unapply(scrutinee: Any): Option[Hole] = scrutinee match {
case Placeholder(hole: ApplyHole) if hole.tpe <:< symbolType => Some(hole)
case _ => None
}
}
object CasePlaceholder {
def apply(name: Name) =
CaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), Ident(name) :: Nil), EmptyTree, EmptyTree)
def unapply(tree: Tree): Option[Hole] = tree match {
case CaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), List(Placeholder(hole))), EmptyTree, EmptyTree) => Some(hole)
case _ => None
}
}
object RefineStatPlaceholder {
def apply(name: Name) =
ValDef(NoMods, nme.QUASIQUOTE_REFINE_STAT, Ident(name), EmptyTree)
def unapply(tree: Tree): Option[Hole] = tree match {
case ValDef(_, nme.QUASIQUOTE_REFINE_STAT, Ident(Placeholder(hole)), _) => Some(hole)
case _ => None
}
}
object EarlyDefPlaceholder {
def apply(name: Name) =
ValDef(Modifiers(Flag.PRESUPER), nme.QUASIQUOTE_EARLY_DEF, Ident(name), EmptyTree)
def unapply(tree: Tree): Option[Hole] = tree match {
case ValDef(_, nme.QUASIQUOTE_EARLY_DEF, Ident(Placeholder(hole)), _) => Some(hole)
case _ => None
}
}
object PackageStatPlaceholder {
def apply(name: Name) =
ValDef(NoMods, nme.QUASIQUOTE_PACKAGE_STAT, Ident(name), EmptyTree)
def unapply(tree: Tree): Option[Hole] = tree match {
case ValDef(NoMods, nme.QUASIQUOTE_PACKAGE_STAT, Ident(Placeholder(hole)), EmptyTree) => Some(hole)
case _ => None
}
}
object ForEnumPlaceholder {
def apply(name: Name) =
build.SyntacticValFrom(Bind(name, Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM))
def unapply(tree: Tree): Option[Hole] = tree match {
case build.SyntacticValFrom(Bind(Placeholder(hole), Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) =>
Some(hole)
case _ => None
}
}
}