summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDen Shabalin <den.shabalin@gmail.com>2013-07-08 20:48:17 +0200
committerEugene Burmako <xeno.by@gmail.com>2013-07-08 21:20:28 +0200
commit7184fe0d3740ac8558067c18bdf449a65a8a26b9 (patch)
tree34afa3886443f46121710eccde1be74c553dc386 /src
parent32949c496e2703e05ff07fae8d19bf91fe733e71 (diff)
downloadscala-7184fe0d3740ac8558067c18bdf449a65a8a26b9.tar.gz
scala-7184fe0d3740ac8558067c18bdf449a65a8a26b9.tar.bz2
scala-7184fe0d3740ac8558067c18bdf449a65a8a26b9.zip
implements quasiquotes
- Additions to the reflection API: - The Quasiquotes implicit class that defines `q`, `tq`, `pq` and `cq` interpolators which now become a part of the `scala.reflect.api. Universe`. - Implementations of the interpolators are macro-based and are hardwired through `FastTrack`. - The `Liftable` class and the `StandardLiftables` slice of the cake that provide a type class and a bunch of its instances that allow to easily splice user-defined types into quasiquotes. - Additional methods in `BuildUtils` that are used by the quasiquote macro to generate trees, notably: - `SyntacticClassDef`. An extractor/constructor that allows to construct and deconstruct classes using arguments that mirror syntactic form of ClassDefs (e.g. constructor outside of the body). - `TupleN`, `TupleTypeN`. Extractor/constructor for easy construction of ast that represents a tuple term or type with given amount of elements. - Actual implementation of quasiquotes in the `scala.tools.reflect. quasiquotes` package which is organized into a cake called `Quasiquotes` with slices introducing core abstractions necessary to splice into Scala syntax, routines for interfacing with the parser, and customized reifiers for tree construction and deconstruction.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/reflect/FastTrack.scala8
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Holes.scala187
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala134
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala123
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala51
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala290
-rw-r--r--src/reflect/scala/reflect/api/BuildUtils.scala42
-rw-r--r--src/reflect/scala/reflect/api/Liftable.scala32
-rw-r--r--src/reflect/scala/reflect/api/Quasiquotes.scala20
-rw-r--r--src/reflect/scala/reflect/api/StandardLiftables.scala36
-rw-r--r--src/reflect/scala/reflect/api/Universe.scala2
-rw-r--r--src/reflect/scala/reflect/internal/BuildUtils.scala120
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala34
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala49
15 files changed, 1127 insertions, 2 deletions
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 321baba562..993f735c72 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -190,6 +190,7 @@ trait ScalaSettings extends AbsScalaSettings
val Yreifydebug = BooleanSetting("-Yreify-debug", "Trace reification.")
val Ytyperdebug = BooleanSetting("-Ytyper-debug", "Trace all type assignments.")
val Ypatmatdebug = BooleanSetting("-Ypatmat-debug", "Trace pattern matching translation.")
+ val Yquasiquotedebug = BooleanSetting("-Yquasiquote-debug", "Trace quasiquote-related activities.")
/** Groups of Settings.
*/
diff --git a/src/compiler/scala/tools/reflect/FastTrack.scala b/src/compiler/scala/tools/reflect/FastTrack.scala
index 5a0ff4f6db..ad1d4c896b 100644
--- a/src/compiler/scala/tools/reflect/FastTrack.scala
+++ b/src/compiler/scala/tools/reflect/FastTrack.scala
@@ -5,7 +5,7 @@ import scala.reflect.reify.Taggers
import scala.tools.nsc.typechecker.{ Analyzer, Macros }
import scala.reflect.runtime.Macros.currentMirror
import scala.reflect.api.Universe
-import scala.reflect.macros.compiler.DefaultMacroCompiler
+import scala.tools.reflect.quasiquotes.{ Quasiquotes => QuasiquoteImpls }
/** Optimizes system macro expansions by hardwiring them directly to their implementations
* bypassing standard reflective load and invoke to avoid the overhead of Java/Scala reflection.
@@ -22,6 +22,8 @@ trait FastTrack {
new { val c: c0.type = c0 } with Taggers
private implicit def context2macroimplementations(c0: MacroContext): MacroImplementations { val c: c0.type } =
new { val c: c0.type = c0 } with MacroImplementations
+ private implicit def context2quasiquote(c0: MacroContext): QuasiquoteImpls { val c: c0.type } =
+ new { val c: c0.type = c0 } with QuasiquoteImpls
private def make(sym: Symbol)(pf: PartialFunction[Applied, MacroContext => Tree]) =
sym -> new FastTrackEntry(pf)
@@ -41,6 +43,8 @@ trait FastTrack {
make( materializeTypeTag) { case Applied(_, ttag :: Nil, (u :: _) :: _) => _.materializeTypeTag(u, EmptyTree, ttag.tpe, concrete = true) },
make( ApiUniverseReify) { case Applied(_, ttag :: Nil, (expr :: _) :: _) => c => c.materializeExpr(c.prefix.tree, EmptyTree, expr) },
make( StringContext_f) { case Applied(Select(Apply(_, ps), _), _, args) => c => c.macro_StringInterpolation_f(ps, args.flatten, c.expandee.pos) },
- make(ReflectRuntimeCurrentMirror) { case _ => c => currentMirror(c).tree }
+ make(ReflectRuntimeCurrentMirror) { case _ => c => currentMirror(c).tree },
+ make( QuasiquoteClass_api_apply) { case _ => _.expandQuasiquote },
+ make(QuasiquoteClass_api_unapply) { case _ => _.expandQuasiquote }
)
}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
new file mode 100644
index 0000000000..9d171d52d2
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
@@ -0,0 +1,187 @@
+package scala.tools.reflect
+package quasiquotes
+
+import scala.collection.{immutable, mutable}
+import scala.reflect.internal.Flags._
+
+class Cardinality private[Cardinality](val value: Int) extends AnyVal {
+ def pred = { assert(value - 1 >= 0); new Cardinality(value - 1) }
+ def succ = new Cardinality(value + 1)
+ override def toString = if (value == 0) "no dots" else "." * (value + 1)
+}
+
+object Cardinality {
+ val NoDot = new Cardinality(0)
+ val DotDot = new Cardinality(1)
+ val DotDotDot = new Cardinality(2)
+ object Dot { def unapply(card: Cardinality) = card != NoDot }
+ def parseDots(part: String) = {
+ if (part.endsWith("...")) (part.stripSuffix("..."), DotDotDot)
+ else if (part.endsWith("..")) (part.stripSuffix(".."), DotDot)
+ else (part, NoDot)
+ }
+}
+
+/** Defines abstractions that provide support for splicing into Scala syntax.
+ */
+trait Holes { self: Quasiquotes =>
+ import global._
+ import Cardinality._
+ import definitions._
+ import universeTypes._
+
+ /** Location characterizes a kind of a non-terminal in Scala syntax where something is going to be spliced.
+ * A location is typically associated with a type of the things that can be spliced there.
+ * Associated type might be different from an actual tpe of a splicee due to lifting.
+ * This is the first pillar of modularity in the quasiquote reifier.
+ */
+ sealed abstract class Location(val tpe: Type)
+ case object UnknownLocation extends Location(NoType)
+ case class TreeLocation(override val tpe: Type) extends Location(tpe)
+ case object NameLocation extends Location(nameType)
+ case object ModsLocation extends Location(modsType)
+ case object FlagsLocation extends Location(flagsType)
+ case object SymbolLocation extends Location(symbolType)
+ case class IterableLocation(card: Cardinality, sublocation: TreeLocation) extends Location(NoType) {
+ override val tpe = {
+ def loop(n: Cardinality, tpe: Type): Type =
+ if (n == NoDot) tpe
+ else appliedType(IterableClass.toType, List(loop(n.pred, tpe)))
+ loop(card, sublocation.tpe)
+ }
+ }
+
+ /** Hole type describes location, cardinality and a pre-reification routine associated with a hole.
+ * An interesting thing about HoleType is that it can be completely inferred from the type of the splicee.
+ * This is the second pillar of modularity in the quasiquote reifier.
+ */
+ case class HoleType(preprocessor: Tree => Tree, location: Location, cardinality: Cardinality) {
+ def makeHole(tree: Tree) = Hole(preprocessor(tree), location, cardinality)
+ }
+ object HoleType {
+ def unapply(tpe: Type): Option[HoleType] = tpe match {
+ case NativeType(holeTpe) => Some(holeTpe)
+ case LiftableType(holeTpe) => Some(holeTpe)
+ case IterableTreeType(holeTpe) => Some(holeTpe)
+ case IterableLiftableType(holeTpe) => Some(holeTpe)
+ case _ => None
+ }
+
+ trait HoleTypeExtractor {
+ def unapply(tpe: Type): Option[HoleType] = {
+ for {
+ preprocessor <- this.preprocessor(tpe)
+ location <- this.location(tpe)
+ cardinality <- Some(this.cardinality(tpe))
+ } yield HoleType(preprocessor, location, cardinality)
+ }
+ def preprocessor(tpe: Type): Option[Tree => Tree]
+ def location(tpe: Type): Option[Location]
+ def cardinality(tpe: Type): Cardinality = parseCardinality(tpe)._1
+
+ def lifter(tpe: Type): Option[Tree => Tree] = {
+ val lifterTpe = appliedType(LiftableClass.toType, List(tpe))
+ val lifter = c.inferImplicitValue(lifterTpe, silent = true)
+ if (lifter != EmptyTree) Some(tree => {
+ val lifted = Apply(lifter, List(u, tree))
+ val targetType = Select(u, tpnme.Tree)
+ atPos(tree.pos)(TypeApply(Select(lifted, nme.asInstanceOf_), List(targetType)))
+ }) else None
+ }
+
+ def iterator(tpe: Type)(elementTransform: Tree => Tree): Option[Tree => Tree] = {
+ def reifyIterable(tree: Tree, n: Cardinality): Tree = {
+ def loop(tree: Tree, n: Cardinality) =
+ if (n == NoDot) elementTransform(tree)
+ else {
+ val x: TermName = c.freshName()
+ val wrapped = reifyIterable(Ident(x), n.pred)
+ val xToWrapped = Function(List(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree)), wrapped)
+ Select(Apply(Select(tree, nme.map), List(xToWrapped)), nme.toList)
+ }
+ if (tree.tpe != null && (tree.tpe <:< listTreeType || tree.tpe <:< listListTreeType)) tree
+ else atPos(tree.pos)(loop(tree, n))
+ }
+ val (card, elementTpe) = parseCardinality(tpe)
+ if (card != NoDot) Some(reifyIterable(_, card)) else None
+ }
+ }
+
+ object NativeType extends HoleTypeExtractor {
+ def preprocessor(tpe: Type) = Some(identity)
+ def location(tpe: Type) = {
+ if (tpe <:< treeType) Some(TreeLocation(tpe))
+ else if (tpe <:< nameType) Some(NameLocation)
+ else if (tpe <:< modsType) Some(ModsLocation)
+ else if (tpe <:< flagsType) Some(FlagsLocation)
+ else if (tpe <:< symbolType) Some(SymbolLocation)
+ else None
+ }
+ }
+
+ object LiftableType extends HoleTypeExtractor {
+ def preprocessor(tpe: Type) = lifter(tpe)
+ def location(tpe: Type) = Some(TreeLocation(treeType))
+ }
+
+ object IterableTreeType extends HoleTypeExtractor {
+ def preprocessor(tpe: Type) = iterator(tpe)(identity)
+ def location(tpe: Type) = {
+ val (card, elementTpe) = parseCardinality(tpe)
+ if (card != NoDot && elementTpe <:< treeType) Some(IterableLocation(card, TreeLocation(elementTpe)))
+ else None
+ }
+ }
+
+ object IterableLiftableType extends HoleTypeExtractor {
+ def preprocessor(tpe: Type) = {
+ val (_, elementTpe) = parseCardinality(tpe)
+ for {
+ lifter <- this.lifter(elementTpe)
+ iterator <- this.iterator(tpe)(lifter)
+ } yield iterator
+ }
+ def location(tpe: Type) = Some(IterableLocation(cardinality(tpe), TreeLocation(treeType)))
+ }
+ }
+
+ /** Hole encapsulates information about splices in quasiquotes.
+ * It packs together a cardinality of a splice, a splicee (possibly preprocessed)
+ * and the description of the location in Scala syntax where the splicee can be spliced.
+ * This is the third pillar of modularity in the quasiquote reifier.
+ */
+ case class Hole(tree: Tree, location: Location, cardinality: Cardinality)
+
+ object Hole {
+ def apply(splicee: Tree, holeCard: Cardinality): Hole = {
+ if (splicee.tpe == null) return new Hole(splicee, UnknownLocation, holeCard)
+ val (spliceeCard, elementTpe) = parseCardinality(splicee.tpe)
+ def cantSplice() = {
+ val holeCardMsg = if (holeCard != NoDot) s" with $holeCard" else ""
+ val action = "splice " + splicee.tpe + holeCardMsg
+ val suggestCard = holeCard != spliceeCard || holeCard != NoDot
+ val spliceeCardMsg = if (holeCard != spliceeCard && spliceeCard != NoDot) s"using $spliceeCard" else "omitting the dots"
+ val cardSuggestion = if (suggestCard) spliceeCardMsg else ""
+ def canBeLifted(tpe: Type) = HoleType.LiftableType.unapply(tpe).nonEmpty
+ val suggestLifting = (holeCard == NoDot || spliceeCard != NoDot) && !(elementTpe <:< treeType) && !canBeLifted(elementTpe)
+ val liftedTpe = if (holeCard != NoDot) elementTpe else splicee.tpe
+ val liftSuggestion = if (suggestLifting) s"providing an implicit instance of Liftable[$liftedTpe]" else ""
+ val advice = List(cardSuggestion, liftSuggestion).filter(_ != "").mkString(" or ")
+ c.abort(splicee.pos, s"Can't $action, consider $advice")
+ }
+ val holeTpe = splicee.tpe match {
+ case _ if holeCard != spliceeCard => cantSplice()
+ case HoleType(holeTpe) => holeTpe
+ case _ => cantSplice()
+ }
+ holeTpe.makeHole(splicee)
+ }
+ }
+
+ def parseCardinality(tpe: Type): (Cardinality, Type) = {
+ if (tpe != null && isIterableType(tpe)) {
+ val (card, innerTpe) = parseCardinality(tpe.typeArguments.head)
+ (card.succ, innerTpe)
+ } else (NoDot, tpe)
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
new file mode 100644
index 0000000000..9a6ba56c18
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
@@ -0,0 +1,134 @@
+package scala.tools.reflect
+package quasiquotes
+
+import scala.tools.nsc.ast.parser.{Parsers => ScalaParser}
+import scala.tools.nsc.ast.parser.Tokens._
+import scala.compat.Platform.EOL
+import scala.reflect.internal.util.{BatchSourceFile, SourceFile}
+import scala.collection.mutable.ListBuffer
+
+/** Builds upon the vanilla Scala parser and teams up together with Placeholders.scala to emulate holes.
+ * 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.
+ */
+trait Parsers { self: Quasiquotes =>
+ import global._
+
+ abstract class Parser extends {
+ val global: self.global.type = self.global
+ } with ScalaParser {
+ /** Wraps given code to obtain a desired parser mode.
+ * This way we can just re-use standard parser entry point.
+ */
+ def wrapCode(code: String): String =
+ s"object wrapper { self => $EOL $code $EOL }"
+
+ def unwrapTree(wrappedTree: Tree): Tree = {
+ val PackageDef(_, List(ModuleDef(_, _, Template(_, _, _ :: parsed)))) = wrappedTree
+ parsed match {
+ case tree :: Nil => tree
+ case stats :+ tree => Block(stats, tree)
+ }
+ }
+
+ def parse(code: String): Tree = {
+ try {
+ val wrapped = wrapCode(code)
+ debug(s"wrapped code\n=${wrapped}\n")
+ val file = new BatchSourceFile(nme.QUASIQUOTE_FILE, wrapped)
+ val tree = new QuasiquoteParser(file).parse()
+ unwrapTree(tree)
+ } catch {
+ case mi: MalformedInput => c.abort(c.macroApplication.pos, s"syntax error: ${mi.msg}")
+ }
+ }
+
+ class QuasiquoteParser(source0: SourceFile) extends SourceFileParser(source0) {
+ override val treeBuilder = new ParserTreeBuilder {
+ // q"(..$xs)"
+ override def makeTupleTerm(trees: List[Tree], flattenUnary: Boolean): Tree =
+ Apply(Ident(nme.QUASIQUOTE_TUPLE), trees)
+
+ // tq"(..$xs)"
+ override def makeTupleType(trees: List[Tree], flattenUnary: Boolean): Tree =
+ AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), trees)
+
+ // q"{ $x }"
+ override def makeBlock(stats: List[Tree]): Tree = stats match {
+ case (head @ Ident(name)) :: Nil if holeMap.contains(name) => Block(Nil, head)
+ case _ => super.makeBlock(stats)
+ }
+ }
+ import treeBuilder.{global => _, _}
+
+ // q"def foo($x)"
+ override def allowTypelessParams = true
+
+ // q"foo match { case $x }"
+ override def caseClause(): CaseDef =
+ if (isHole && lookingAhead { in.token == CASE || in.token == RBRACE || in.token == SEMI }) {
+ val c = makeCaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), List(Ident(ident()))), EmptyTree, EmptyTree)
+ while (in.token == SEMI) in.nextToken()
+ c
+ } else
+ super.caseClause()
+
+ def isHole = isIdent && holeMap.contains(in.name)
+
+ override def isAnnotation: Boolean = super.isAnnotation || (isHole && lookingAhead { isAnnotation })
+
+ override def isModifier: Boolean = super.isModifier || (isHole && lookingAhead { isModifier })
+
+ override def isLocalModifier: Boolean = super.isLocalModifier || (isHole && lookingAhead { isLocalModifier })
+
+ override def isTemplateIntro: Boolean = super.isTemplateIntro || (isHole && lookingAhead { isTemplateIntro })
+
+ override def isDclIntro: Boolean = super.isDclIntro || (isHole && lookingAhead { isDclIntro })
+
+ // $mods def foo
+ // $mods T
+ override def readAnnots(annot: => Tree): List[Tree] = in.token match {
+ case AT =>
+ in.nextToken()
+ annot :: readAnnots(annot)
+ case _ if isHole && lookingAhead { in.token == AT || isModifier || isDefIntro || isIdent} =>
+ val ann = Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(in.name.toString))))
+ in.nextToken()
+ ann :: readAnnots(annot)
+ case _ =>
+ Nil
+ }
+ }
+ }
+
+ object TermParser extends Parser
+
+ object CaseParser extends Parser {
+ override def wrapCode(code: String) = super.wrapCode("something match { case " + code + " }")
+
+ override def unwrapTree(wrappedTree: Tree): Tree = {
+ val Match(_, head :: tail) = super.unwrapTree(wrappedTree)
+ if (tail.nonEmpty)
+ c.abort(c.macroApplication.pos, "Can't parse more than one casedef, consider generating a match tree instead")
+ head
+ }
+ }
+
+ object PatternParser extends Parser {
+ override def wrapCode(code: String) = super.wrapCode("something match { case " + code + " => }")
+
+ override def unwrapTree(wrappedTree: Tree): Tree = {
+ val Match(_, List(CaseDef(pat, _, _))) = super.unwrapTree(wrappedTree)
+ pat
+ }
+ }
+
+ object TypeParser extends Parser {
+ override def wrapCode(code: String) = super.wrapCode("type T = " + code)
+
+ override def unwrapTree(wrappedTree: Tree): Tree = {
+ val TypeDef(_, _, _, rhs) = super.unwrapTree(wrappedTree)
+ rhs
+ }
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
new file mode 100644
index 0000000000..b680c25f76
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
@@ -0,0 +1,123 @@
+package scala.tools.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 Cardinality._
+
+ // Step 1: Transform Scala source with holes into vanilla Scala source
+
+ lazy val holeMap = new HoleMap()
+ lazy val code = {
+ val sb = new StringBuilder()
+ val sessionSuffix = randomUUID().toString.replace("-", "").substring(0, 8) + "$"
+
+ foreach2(args, parts.init) { (tree, p) =>
+ val (part, cardinality) = parseDots(p)
+ val placeholderName = c.freshName(TermName(nme.QUASIQUOTE_PREFIX + sessionSuffix))
+ sb.append(part)
+ sb.append(placeholderName)
+ holeMap(placeholderName) = Hole(tree, cardinality)
+ }
+ sb.append(parts.last)
+
+ sb.toString
+ }
+
+ class HoleMap {
+ private val underlying = mutable.ListMap[String, Hole]()
+ private val accessed = mutable.Set[String]()
+ def unused: Set[Name] = (underlying.keys.toSet -- accessed).map(TermName(_))
+ def contains(key: Name) = underlying.contains(key.toString)
+ def apply(key: Name) = {
+ val s = key.toString
+ accessed += s
+ underlying(s)
+ }
+ def update(key: Name, hole: Hole) = {
+ underlying += key.toString -> hole
+ }
+ def get(key: Name) = {
+ val s = key.toString
+ accessed += s
+ underlying.get(s)
+ }
+ }
+
+ // Step 2: Transform vanilla Scala AST into an AST with holes
+
+ trait HolePlaceholder {
+ def matching: PartialFunction[Any, Name]
+ def unapply(scrutinee: Any): Option[(Tree, Location, Cardinality)] = {
+ val name = matching.lift(scrutinee)
+ name.flatMap { holeMap.get(_).map { case Hole(repr, loc, card) => (repr, loc, card) } }
+ }
+ }
+
+ 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
+ case ValDef(_, name, TypeTree(), EmptyTree) => name
+ }
+ }
+
+ object ModsPlaceholder extends HolePlaceholder {
+ def matching = {
+ case Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(s: String)))) => TermName(s)
+ }
+ }
+
+ object AnnotPlaceholder {
+ def unapply(tree: Tree): Option[(Tree, Location, Cardinality, List[Tree])] = tree match {
+ case Apply(Select(New(Placeholder(tree, loc, card)), nme.CONSTRUCTOR), args) => Some(tree, loc, card, args)
+ case _ => None
+ }
+ }
+
+ object TuplePlaceholder {
+ def unapply(tree: Tree): Option[List[Tree]] = tree match {
+ case Apply(Ident(nme.QUASIQUOTE_TUPLE), args) => Some(args)
+ case _ => None
+ }
+ }
+
+ object TupleTypePlaceholder {
+ def unapply(tree: Tree): Option[List[Tree]] = tree match {
+ case AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), args) => Some(args)
+ case _ => None
+ }
+ }
+
+ object SymbolPlaceholder {
+ def unapply(scrutinee: Any): Option[Tree] = scrutinee match {
+ case Placeholder(tree, SymbolLocation, _) => Some(tree)
+ case _ => None
+ }
+ }
+
+ object CasePlaceholder {
+ def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
+ case CaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), List(Placeholder(tree, location, card))), EmptyTree, EmptyTree) => Some((tree, location, card))
+ case _ => None
+ }
+ }
+
+ object ClassPlaceholder {
+ def unapply(tree: Tree): Option[Tree] = tree match {
+ case ClassDef(_, _, _, _) => Some(tree)
+ case _ => None
+ }
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
new file mode 100644
index 0000000000..fe954e0bfd
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
@@ -0,0 +1,51 @@
+package scala.tools.reflect
+package quasiquotes
+
+import scala.reflect.macros.runtime.Context
+
+abstract class Quasiquotes extends Parsers
+ with Holes
+ with Placeholders
+ with Reifiers {
+ val c: Context
+ val global: c.universe.type = c.universe
+ import c.universe._
+
+ def debug(msg: String): Unit =
+ if (settings.Yquasiquotedebug.value) println(msg)
+
+ lazy val (universe: Tree, args, parts, parse, reify) = c.macroApplication match {
+ case Apply(Select(Select(Apply(Select(universe0, _), List(Apply(_, parts0))), interpolator0), method0), args0) =>
+ val parts1 = parts0.map {
+ case Literal(Constant(s: String)) => s
+ case part => c.abort(part.pos, "Quasiquotes can only be used with literal strings")
+ }
+ val reify0 = method0 match {
+ case nme.apply => new ApplyReifier().reifyFillingHoles(_)
+ case nme.unapply => new UnapplyReifier().reifyFillingHoles(_)
+ case other => global.abort(s"Unknown quasiquote api method: $other")
+ }
+ val parse0 = interpolator0 match {
+ case nme.q => TermParser.parse(_)
+ case nme.tq => TypeParser.parse(_)
+ case nme.cq => CaseParser.parse(_)
+ case nme.pq => PatternParser.parse(_)
+ case other => global.abort(s"Unknown quasiquote flavor: $other")
+ }
+ (universe0, args0, parts1, parse0, reify0)
+ case _ =>
+ global.abort(s"Couldn't parse call prefix tree ${c.macroApplication}.")
+ }
+
+ lazy val u = universe // shortcut
+ lazy val universeTypes = new definitions.UniverseDependentTypes(universe)
+
+ def expandQuasiquote = {
+ debug(s"\ncode to parse=\n$code\n")
+ val tree = parse(code)
+ debug(s"parsed tree\n=${tree}\n=${showRaw(tree)}\n")
+ val reified = reify(tree)
+ debug(s"reified tree\n=${reified}\n=${showRaw(reified)}\n")
+ reified
+ }
+}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
new file mode 100644
index 0000000000..ec113036a3
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
@@ -0,0 +1,290 @@
+package scala.tools.reflect
+package quasiquotes
+
+import java.lang.UnsupportedOperationException
+import scala.reflect.reify.{Reifier => ReflectReifier}
+import scala.reflect.internal.Flags._
+
+trait Reifiers { self: Quasiquotes =>
+ import global._
+ import global.build.SyntacticClassDef
+ import global.treeInfo._
+ import global.definitions._
+ import Cardinality._
+ import universeTypes._
+
+ abstract class Reifier extends {
+ val global: self.global.type = self.global
+ } with ReflectReifier {
+ val reifee = EmptyTree
+ val universe = self.universe
+ val mirror = EmptyTree
+ val concrete = false
+ lazy val typer = throw new UnsupportedOperationException
+
+ def isReifyingExpressions: Boolean
+ def isReifyingPatterns: Boolean = !isReifyingExpressions
+ def action = if (isReifyingExpressions) "splice" else "extract"
+ def holesHaveTypes = isReifyingExpressions
+
+ def reifyFillingHoles(tree: Tree): Tree = {
+ val reified = reifyTree(tree)
+ holeMap.unused.foreach { hole =>
+ c.abort(holeMap(hole).tree.pos, s"Don't know how to $action here")
+ }
+ reified
+ }
+
+ override def reifyTree(tree: Tree): Tree = {
+ val reified =
+ reifyTreePlaceholder(tree) orElse
+ reifyTreeSyntactically(tree)
+ //println(s"reified ${showRaw(tree)} as $reified")
+ reified
+ }
+
+ def reifyTreePlaceholder(tree: Tree): Tree = tree match {
+ case Placeholder(tree, TreeLocation(_), _) if isReifyingExpressions => tree
+ case Placeholder(tree, _, NoDot) if isReifyingPatterns => tree
+ case Placeholder(tree, _, card @ Dot()) => c.abort(tree.pos, s"Can't $action with $card here")
+ case TuplePlaceholder(args) => reifyTuple(args)
+ case TupleTypePlaceholder(args) => reifyTupleType(args)
+ case CasePlaceholder(tree, location, _) => reifyCase(tree, location)
+ case ClassPlaceholder(tree) => reifyClass(tree)
+ case _ => EmptyTree
+ }
+
+ override def reifyName(name: Name): Tree = name match {
+ case Placeholder(tree, location, _) =>
+ if (holesHaveTypes && !(location.tpe <:< nameType)) c.abort(tree.pos, s"$nameType expected but ${location.tpe} found")
+ tree
+ case _ =>
+ super.reifyName(name)
+ }
+
+ def reifyCase(tree: Tree, location: Location) = {
+ if (holesHaveTypes && !(location.tpe <:< caseDefType)) c.abort(tree.pos, s"$caseDefType expected but ${location.tpe} found")
+ tree
+ }
+
+ def reifyTuple(args: List[Tree]) = args match {
+ case Nil => reify(Literal(Constant(())))
+ case List(hole @ Placeholder(_, _, NoDot)) => reify(hole)
+ case List(Placeholder(_, _, _)) => reifyBuildCall(nme.TupleN, args)
+ // in a case we only have one element tuple without
+ // any cardinality annotations this means that this is
+ // just an expression wrapped in parentheses
+ case List(other) => reify(other)
+ case _ => reifyBuildCall(nme.TupleN, args)
+ }
+
+ def reifyTupleType(args: List[Tree]) = args match {
+ case Nil => reify(Select(Ident(nme.scala_), tpnme.Unit))
+ case List(hole @ Placeholder(_, _, NoDot)) => reify(hole)
+ case List(Placeholder(_, _, _)) => reifyBuildCall(nme.TupleTypeN, args)
+ case List(other) => reify(other)
+ case _ => reifyBuildCall(nme.TupleTypeN, args)
+ }
+
+ def reifyClass(tree: Tree) = {
+ val SyntacticClassDef(mods, name, tparams, constrmods, argss, parents, selfval, body) = tree
+ reifyBuildCall(nme.SyntacticClassDef, mods, name, tparams, constrmods, argss, parents, selfval, body)
+ }
+
+ /** Splits list into a list of groups where subsequent elements are considered
+ * similar by the corresponding function.
+ *
+ * Example:
+ *
+ * > group(List(1, 1, 0, 0, 1, 0)) { _ == _ }
+ * List(List(1, 1), List(0, 0), List(1), List(0))
+ *
+ */
+ def group[T](lst: List[T])(similar: (T, T) => Boolean) = lst.foldLeft[List[List[T]]](List()) {
+ case (Nil, el) => List(List(el))
+ case (ll :+ (last @ (lastinit :+ lastel)), el) if similar(lastel, el) => ll :+ (last :+ el)
+ case (ll, el) => ll :+ List(el)
+ }
+
+ /** Reifies list filling all the valid holeMap.
+ *
+ * Reification of non-trivial list is done in two steps:
+ *
+ * 1. split the list into groups where every placeholder is always
+ * put in a group of it's own and all subsquent non-holeMap are
+ * grouped together; element is considered to be a placeholder if it's
+ * in the domain of the fill function;
+ *
+ * 2. fold the groups into a sequence of lists added together with ++ using
+ * fill reification for holeMapĀ and fallback reification for non-holeMap.
+ *
+ * Example:
+ *
+ * reifyMultiCardinalityList(lst) {
+ * // first we define patterns that extract high-cardinality holeMap (currently ..)
+ * case Placeholder(CorrespondsTo(tree, tpe)) if tpe <:< iterableTreeType => tree
+ * } {
+ * // in the end we define how single elements are reified, typically with default reify call
+ * reify(_)
+ * }
+ *
+ * Sample execution of previous concrete list reifier:
+ *
+ * > val lst = List(foo, bar, qq$f3948f9s$1)
+ * > reifyMultiCardinalityList(lst) { ... } { ... }
+ * q"List($foo, $bar) ++ ${holeMap(qq$f3948f9s$1).tree}"
+ */
+ def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree
+
+ /** Reifies arbitrary list filling ..$x and ...$y holeMap when they are put
+ * in the correct position. Fallbacks to regular reification for non-high cardinality
+ * elements.
+ */
+ override def reifyList(xs: List[Any]): Tree = reifyMultiCardinalityList(xs) {
+ case Placeholder(tree, _, DotDot) => tree
+ case CasePlaceholder(tree, _, DotDot) => tree
+ case List(Placeholder(tree, _, DotDotDot)) => tree
+ } {
+ reify(_)
+ }
+
+ def reifyAnnotList(annots: List[Tree]): Tree
+
+ def ensureNoExplicitFlags(m: Modifiers, pos: Position) =
+ if ((m.flags & ExplicitFlags) != 0L) c.abort(pos, s"Can't $action modifiers together with flags, consider merging flags into modifiers")
+
+ override def mirrorSelect(name: String): Tree =
+ Select(universe, TermName(name))
+
+ override def mirrorCall(name: TermName, args: Tree*): Tree =
+ Apply(Select(universe, name), args.toList)
+
+ override def mirrorBuildCall(name: TermName, args: Tree*): Tree =
+ Apply(Select(Select(universe, nme.build), name), args.toList)
+ }
+
+ class ApplyReifier extends Reifier {
+ def isReifyingExpressions = true
+
+ override def reifyTreeSyntactically(tree: Tree): Tree = tree match {
+ case Block(stats, p @ Placeholder(_, _, _)) => reifyBuildCall(nme.Block, stats :+ p)
+ case Apply(f, List(Placeholder(argss, _, DotDotDot))) => reifyCallWithArgss(f, argss)
+ case RefTree(qual, SymbolPlaceholder(tree)) => mirrorBuildCall(nme.RefTree, reify(qual), tree)
+ case _ => super.reifyTreeSyntactically(tree)
+ }
+
+ def reifyCallWithArgss(f: Tree, argss: Tree) = {
+ val f1 = reifyTree(f)
+ val foldLeftF1 = Apply(TypeApply(Select(argss, nme.foldLeft), List(Select(u, tpnme.Tree))), List(f1))
+ val uDotApply = Function(
+ List(gen.mkSyntheticParam(nme.x_1), gen.mkSyntheticParam(nme.x_2)),
+ Apply(Select(u, nme.Apply), List(Ident(nme.x_1), Ident(nme.x_2))))
+ Apply(foldLeftF1, List(uDotApply))
+ }
+
+ override def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree = xs match {
+ case Nil => mkList(Nil)
+ case _ =>
+ def reifyGroup(group: List[T]): Tree = group match {
+ case List(elem) if fill.isDefinedAt(elem) => fill(elem)
+ case elems => mkList(elems.map(fallback))
+ }
+ val head :: tail = group(xs) { (a, b) => !fill.isDefinedAt(a) && !fill.isDefinedAt(b) }
+ tail.foldLeft[Tree](reifyGroup(head)) { (tree, lst) => Apply(Select(tree, nme.PLUSPLUS), List(reifyGroup(lst))) }
+ }
+
+ override def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) {
+ case AnnotPlaceholder(tree, _, DotDot, args) =>
+ val x: TermName = c.freshName()
+ val xToAnnotationCtor = Function(
+ List(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree)),
+ mirrorBuildCall(nme.mkAnnotationCtor, Ident(x), reify(args)))
+ Apply(Select(tree, nme.map), List(xToAnnotationCtor))
+ } {
+ case AnnotPlaceholder(tree, _: TreeLocation, _, args) =>
+ mirrorBuildCall(nme.mkAnnotationCtor, tree, reify(args))
+ case other => reify(other)
+ }
+
+ override def reifyModifiers(m: Modifiers) = {
+ val (modsPlaceholders, annots) = m.annotations.partition {
+ case ModsPlaceholder(_, _, _) => true
+ case _ => false
+ }
+ val (mods, flags) = modsPlaceholders.map {
+ case ModsPlaceholder(tree, location, card) => (tree, location)
+ }.partition { case (tree, location) =>
+ location match {
+ case ModsLocation => true
+ case FlagsLocation => false
+ case _ => c.abort(tree.pos, s"$flagsType or $modsType expected but ${tree.tpe} found")
+ }
+ }
+ mods match {
+ case (tree, _) :: Nil =>
+ if (flags.nonEmpty) c.abort(flags(0)._1.pos, "Can't splice flags together with modifiers, consider merging flags into modifiers")
+ if (annots.nonEmpty) c.abort(tree.pos, "Can't splice modifiers together with annotations, consider merging annotations into modifiers")
+ ensureNoExplicitFlags(m, tree.pos)
+ tree
+ case _ :: (second, _) :: Nil =>
+ c.abort(second.pos, "Can't splice multiple modifiers, consider merging them into a single modifiers instance")
+ case _ =>
+ val baseFlags = reifyBuildCall(nme.flagsFromBits, m.flags)
+ val reifiedFlags = flags.foldLeft[Tree](baseFlags) { case (flag, (tree, _)) => Apply(Select(flag, nme.OR), List(tree)) }
+ mirrorFactoryCall(nme.Modifiers, reifiedFlags, reify(m.privateWithin), reifyAnnotList(annots))
+ }
+ }
+ }
+
+ class UnapplyReifier extends Reifier {
+ def isReifyingExpressions = false
+
+ override def reifyTreeSyntactically(tree: Tree): Tree = tree match {
+ case treeInfo.Applied(fun, Nil, argss) if fun != tree && !tree.isInstanceOf[AppliedTypeTree] =>
+ reifyBuildCall(nme.Applied, fun, argss)
+ case treeInfo.Applied(fun, targs, argss) if fun != tree & !tree.isInstanceOf[AppliedTypeTree] =>
+ mirrorBuildCall(nme.Applied, reifyBuildCall(nme.TypeApplied, fun, targs), reifyList(argss))
+ case _ =>
+ super.reifyTreeSyntactically(tree)
+ }
+
+ override def scalaFactoryCall(name: String, args: Tree*): Tree =
+ call("scala." + name, args: _*)
+
+ override def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree) = xs match {
+ case init :+ last if fill.isDefinedAt(last) =>
+ init.foldRight[Tree](fill(last)) { (el, rest) =>
+ val cons = Select(Select(Select(Ident(nme.scala_), nme.collection), nme.immutable), nme.CONS)
+ Apply(cons, List(fallback(el), rest))
+ }
+ case _ =>
+ mkList(xs.map(fallback))
+ }
+
+ override def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) {
+ case AnnotPlaceholder(tree, _, DotDot, Nil) => tree
+ } {
+ case AnnotPlaceholder(tree, _, NoDot, Nil) => tree
+ case AnnotPlaceholder(tree, _, NoDot, args) =>
+ val selectCONSTRUCTOR = Apply(Select(u, nme.Select), List(Apply(Select(u, nme.New), List(tree)), Select(Select(u, nme.nmeNme), nme.nmeCONSTRUCTOR)))
+ Apply(Select(u, nme.Apply), List(selectCONSTRUCTOR, reify(args)))
+ case other =>
+ reify(other)
+ }
+
+ override def reifyModifiers(m: Modifiers) = {
+ val mods = m.annotations.collect { case ModsPlaceholder(tree, _, _) => tree }
+ mods match {
+ case tree :: Nil =>
+ if (m.annotations.length != 1) c.abort(tree.pos, "Can't extract modifiers together with annotations, consider extracting just modifiers")
+ ensureNoExplicitFlags(m, tree.pos)
+ tree
+ case _ :: second :: rest =>
+ c.abort(second.pos, "Can't extract multiple modifiers together, consider extracting a single modifiers instance")
+ case Nil =>
+ mirrorFactoryCall(nme.Modifiers, reifyBuildCall(nme.FlagsAsBits, m.flags),
+ reify(m.privateWithin), reifyAnnotList(m.annotations))
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala
index 2e95a01176..c568cf74c0 100644
--- a/src/reflect/scala/reflect/api/BuildUtils.scala
+++ b/src/reflect/scala/reflect/api/BuildUtils.scala
@@ -66,6 +66,8 @@ private[reflect] trait BuildUtils { self: Universe =>
def Ident(sym: Symbol): Ident
+ def Block(stats: List[Tree]): Block
+
def TypeTree(tp: Type): TypeTree
def thisPrefix(sym: Symbol): Type
@@ -73,5 +75,45 @@ private[reflect] trait BuildUtils { self: Universe =>
def setType[T <: Tree](tree: T, tpe: Type): T
def setSymbol[T <: Tree](tree: T, sym: Symbol): T
+
+ def mkAnnotationCtor(tree: Tree, args: List[Tree]): Tree
+
+ val FlagsAsBits: FlagsAsBitsExtractor
+
+ trait FlagsAsBitsExtractor {
+ def unapply(flags: Long): Option[Long]
+ }
+
+ val TypeApplied: TypeAppliedExtractor
+
+ trait TypeAppliedExtractor {
+ def unapply(tree: Tree): Some[(Tree, List[Tree])]
+ }
+
+ val Applied: AppliedExtractor
+
+ trait AppliedExtractor {
+ def unapply(tree: Tree): Some[(Tree, List[List[Tree]])]
+ }
+
+ val SyntacticClassDef: SyntacticClassDefExtractor
+
+ trait SyntacticClassDefExtractor {
+ def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef],
+ constrMods: Modifiers, vparamss: List[List[ValDef]], parents: List[Tree],
+ selfdef: ValDef, body: List[Tree]): Tree
+ def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers,
+ List[List[ValDef]], List[Tree], ValDef, List[Tree])]
+ }
+
+ val TupleN: TupleNExtractor
+ val TupleTypeN: TupleNExtractor
+
+ trait TupleNExtractor {
+ def apply(args: List[Tree]): Tree
+ def unapply(tree: Tree): Option[List[Tree]]
+ }
+
+ def RefTree(qual: Tree, sym: Symbol): Tree
}
}
diff --git a/src/reflect/scala/reflect/api/Liftable.scala b/src/reflect/scala/reflect/api/Liftable.scala
new file mode 100644
index 0000000000..8f6fe066dd
--- /dev/null
+++ b/src/reflect/scala/reflect/api/Liftable.scala
@@ -0,0 +1,32 @@
+package scala.reflect
+package api
+
+trait Liftable[T] {
+ def apply(universe: api.Universe, value: T): universe.Tree
+}
+
+object Liftable {
+ private class LiftableConstant[T] extends Liftable[T] {
+ def apply(universe: Universe, value: T): universe.Tree =
+ universe.Literal(universe.Constant(value))
+ }
+
+ implicit lazy val liftByte: Liftable[Byte] = new LiftableConstant[Byte]
+ implicit lazy val liftShort: Liftable[Short] = new LiftableConstant[Short]
+ implicit lazy val liftChar: Liftable[Char] = new LiftableConstant[Char]
+ implicit lazy val liftInt: Liftable[Int] = new LiftableConstant[Int]
+ implicit lazy val liftLong: Liftable[Long] = new LiftableConstant[Long]
+ implicit lazy val liftFloat: Liftable[Float] = new LiftableConstant[Float]
+ implicit lazy val liftDouble: Liftable[Double] = new LiftableConstant[Double]
+ implicit lazy val liftBoolean: Liftable[Boolean] = new LiftableConstant[Boolean]
+ implicit lazy val liftString: Liftable[String] = new LiftableConstant[String]
+ implicit lazy val liftUnit: Liftable[Unit] = new LiftableConstant[Unit]
+
+ implicit lazy val liftScalaSymbol: Liftable[scala.Symbol] = new Liftable[scala.Symbol] {
+ def apply(universe: Universe, value: scala.Symbol): universe.Tree = {
+ import universe._
+ val symbol = Select(Ident(TermName("scala")), TermName("Symbol"))
+ Apply(symbol, List(Literal(Constant(value.name))))
+ }
+ }
+}
diff --git a/src/reflect/scala/reflect/api/Quasiquotes.scala b/src/reflect/scala/reflect/api/Quasiquotes.scala
new file mode 100644
index 0000000000..3895b8b95f
--- /dev/null
+++ b/src/reflect/scala/reflect/api/Quasiquotes.scala
@@ -0,0 +1,20 @@
+package scala.reflect
+package api
+
+import language.experimental.macros
+
+trait Quasiquotes { self: Universe =>
+
+ // implementation is hardwired to `dispatch` method of `scala.tools.reflect.quasiquotes.Quasiquotes`
+ // using the mechanism implemented in `scala.tools.reflect.FastTrack`
+ implicit class Quasiquote(ctx: StringContext) {
+ protected trait api {
+ def apply(args: Any*): Any = macro ???
+ def unapply(subpatterns: Any*): Option[Any] = macro ???
+ }
+ object q extends api
+ object tq extends api
+ object cq extends api
+ object pq extends api
+ }
+}
diff --git a/src/reflect/scala/reflect/api/StandardLiftables.scala b/src/reflect/scala/reflect/api/StandardLiftables.scala
new file mode 100644
index 0000000000..ecea550225
--- /dev/null
+++ b/src/reflect/scala/reflect/api/StandardLiftables.scala
@@ -0,0 +1,36 @@
+package scala.reflect
+package api
+
+trait StandardLiftables { self: Universe =>
+
+ private def requireSameUniverse[T](universe: Universe, tp: String, value: T) =
+ require(universe eq self, s"Can't lift $tp ${showRaw(value)} from universe ${showRaw(universe)} using lift$tp defined for ${showRaw(self)}.")
+
+ implicit def liftExpr[T <: Expr[_]]: Liftable[T] = new Liftable[T] {
+ def apply(universe: Universe, value: T): universe.Tree = {
+ requireSameUniverse(universe, "Expr", value)
+ value.tree.asInstanceOf[universe.Tree]
+ }
+ }
+
+ implicit def liftType[T <: Type]: Liftable[T] = new Liftable[T] {
+ def apply(universe: Universe, value: T): universe.Tree = {
+ requireSameUniverse(universe, "Type", value)
+ universe.TypeTree(value.asInstanceOf[universe.Type])
+ }
+ }
+
+ implicit def liftTypeTag[T <: WeakTypeTag[_]]: Liftable[T] = new Liftable[T] {
+ def apply(universe: Universe, value: T): universe.Tree = {
+ requireSameUniverse(universe, "TypeTag", value)
+ universe.TypeTree(value.asInstanceOf[universe.WeakTypeTag[_]].tpe)
+ }
+ }
+
+ implicit def liftConstant[T <: Constant]: Liftable[T] = new Liftable[T] {
+ def apply(universe: Universe, value: T): universe.Tree = {
+ requireSameUniverse(universe, "Constant", value)
+ universe.Literal(value.asInstanceOf[universe.Constant])
+ }
+ }
+}
diff --git a/src/reflect/scala/reflect/api/Universe.scala b/src/reflect/scala/reflect/api/Universe.scala
index cb629f9c5a..77b4827eab 100644
--- a/src/reflect/scala/reflect/api/Universe.scala
+++ b/src/reflect/scala/reflect/api/Universe.scala
@@ -72,10 +72,12 @@ abstract class Universe extends Symbols
with ImplicitTags
with StandardDefinitions
with StandardNames
+ with StandardLiftables
with BuildUtils
with Mirrors
with Printers
with Importers
+ with Quasiquotes
{
/** Use `refiy` to produce the abstract syntax tree representing a given Scala expression.
*
diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala
index ece2d28be3..6526c4ce12 100644
--- a/src/reflect/scala/reflect/internal/BuildUtils.scala
+++ b/src/reflect/scala/reflect/internal/BuildUtils.scala
@@ -2,7 +2,10 @@ package scala
package reflect
package internal
+import Flags._
+
trait BuildUtils { self: SymbolTable =>
+ import definitions.{TupleClass, MaxTupleArity, ScalaPackage, UnitClass}
class BuildImpl extends BuildApi {
@@ -52,6 +55,12 @@ trait BuildUtils { self: SymbolTable =>
def Ident(sym: Symbol): Ident = self.Ident(sym)
+ def Block(stats: List[Tree]): Block = stats match {
+ case Nil => self.Block(Nil, Literal(Constant(())))
+ case elem :: Nil => self.Block(Nil, elem)
+ case elems => self.Block(elems.init, elems.last)
+ }
+
def TypeTree(tp: Type): TypeTree = self.TypeTree(tp)
def thisPrefix(sym: Symbol): Type = sym.thisPrefix
@@ -59,6 +68,117 @@ trait BuildUtils { self: SymbolTable =>
def setType[T <: Tree](tree: T, tpe: Type): T = { tree.setType(tpe); tree }
def setSymbol[T <: Tree](tree: T, sym: Symbol): T = { tree.setSymbol(sym); tree }
+
+ def mkAnnotationCtor(tree: Tree, args: List[Tree]): Tree = tree match {
+ case ident: Ident => Apply(self.Select(New(ident), nme.CONSTRUCTOR: TermName), args)
+ case call @ Apply(Select(New(ident: Ident), nme.CONSTRUCTOR), _) =>
+ if (args.nonEmpty)
+ throw new IllegalArgumentException("Can't splice annotation that already contains args with extra args, consider merging these lists together")
+ call
+ case _ => throw new IllegalArgumentException(s"Tree ${showRaw(tree)} isn't a correct representation of annotation, consider passing Ident as a first argument")
+ }
+
+ object FlagsAsBits extends FlagsAsBitsExtractor {
+ def unapply(flags: Long): Option[Long] = Some(flags)
+ }
+
+ object TypeApplied extends TypeAppliedExtractor {
+ def unapply(tree: Tree): Some[(Tree, List[Tree])] = tree match {
+ case TypeApply(fun, targs) => Some((fun, targs))
+ case _ => Some((tree, Nil))
+ }
+ }
+
+ object Applied extends AppliedExtractor {
+ def unapply(tree: Tree): Some[(Tree, List[List[Tree]])] = {
+ val treeInfo.Applied(fun, targs, argss) = tree
+ targs match {
+ case Nil => Some((fun, argss))
+ case _ => Some((TypeApply(fun, targs), argss))
+ }
+ }
+ }
+
+ object SyntacticClassDef extends SyntacticClassDefExtractor {
+ def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef],
+ constrMods: Modifiers, vparamss: List[List[ValDef]], parents: List[Tree],
+ selfdef: ValDef, body: List[Tree]): Tree =
+ ClassDef(mods, name, tparams, gen.mkTemplate(parents, selfdef, constrMods, vparamss, body, NoPosition))
+
+ def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers,
+ List[List[ValDef]], List[Tree], ValDef, List[Tree])] = tree match {
+ case ClassDef(mods, name, tparams, Template(parents, selfdef, tbody)) =>
+ // extract generated fieldDefs and constructor
+ val (defs, (ctor: DefDef) :: body) = tbody.splitAt(tbody.indexWhere {
+ case DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => true
+ case _ => false
+ })
+ val (earlyDefs, fieldDefs) = defs.span(treeInfo.isEarlyDef)
+
+ // undo conversion from (implicit ... ) to ()(implicit ... ) when its the only parameter section
+ val vparamssRestoredImplicits = ctor.vparamss match {
+ case Nil :: rest if !rest.isEmpty && !rest.head.isEmpty && rest.head.head.mods.isImplicit => rest
+ case other => other
+ }
+
+ // undo flag modifications by mergeing flag info from constructor args and fieldDefs
+ val modsMap = fieldDefs.map { case ValDef(mods, name, _, _) => name -> mods }.toMap
+ val vparamss = mmap(vparamssRestoredImplicits) { vd =>
+ val originalMods = modsMap(vd.name) | (vd.mods.flags & DEFAULTPARAM)
+ atPos(vd.pos)(ValDef(originalMods, vd.name, vd.tpt, vd.rhs))
+ }
+
+ Some((mods, name, tparams, ctor.mods, vparamss, parents, selfdef, earlyDefs ::: body))
+ case _ =>
+ None
+ }
+ }
+
+ object TupleN extends TupleNExtractor {
+ def apply(args: List[Tree]): Tree = args match {
+ case Nil => Literal(Constant(()))
+ case _ =>
+ require(args.length <= MaxTupleArity, s"Tuples with arity bigger than $MaxTupleArity aren't supported")
+ self.Apply(TupleClass(args.length).companionModule, args: _*)
+ }
+
+ def unapply(tree: Tree): Option[List[Tree]] = tree match {
+ case Literal(Constant(())) =>
+ Some(Nil)
+ case Apply(id: Ident, args)
+ if args.length <= MaxTupleArity && id.symbol == TupleClass(args.length).companionModule =>
+ Some(args)
+ case Apply(Select(Ident(nme.scala_), TermName(tuple)), args)
+ if args.length <= MaxTupleArity && tuple == TupleClass(args.length).name =>
+ Some(args)
+ case _ =>
+ None
+ }
+ }
+
+ object TupleTypeN extends TupleNExtractor {
+ def apply(args: List[Tree]): Tree = args match {
+ case Nil => self.Select(self.Ident(nme.scala_), tpnme.Unit)
+ case _ =>
+ require(args.length <= MaxTupleArity, s"Tuples with arity bigger than $MaxTupleArity aren't supported")
+ AppliedTypeTree(Ident(TupleClass(args.length)), args)
+ }
+
+ def unapply(tree: Tree): Option[List[Tree]] = tree match {
+ case Select(Ident(nme.scala_), tpnme.Unit) =>
+ Some(Nil)
+ case AppliedTypeTree(id: Ident, args)
+ if args.length <= MaxTupleArity && id.symbol == TupleClass(args.length) =>
+ Some(args)
+ case AppliedTypeTree(Select(id @ Ident(nme.scala_), TermName(tuple)), args)
+ if args.length <= MaxTupleArity && id.symbol == ScalaPackage && tuple == TupleClass(args.length).name =>
+ Some(args)
+ case _ =>
+ None
+ }
+ }
+
+ def RefTree(qual: Tree, sym: Symbol) = self.RefTree(qual, sym.name) setSymbol sym
}
val build: BuildApi = new BuildImpl
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index bf8ef79a63..d3b9c38ee1 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -442,6 +442,7 @@ trait Definitions extends api.StandardDefinitions {
// collections classes
lazy val ConsClass = requiredClass[scala.collection.immutable.::[_]]
lazy val IteratorClass = requiredClass[scala.collection.Iterator[_]]
+ lazy val IterableClass = requiredClass[scala.collection.Iterable[_]]
lazy val ListClass = requiredClass[scala.collection.immutable.List[_]]
lazy val SeqClass = requiredClass[scala.collection.Seq[_]]
lazy val StringBuilderClass = requiredClass[scala.collection.mutable.StringBuilder]
@@ -520,6 +521,7 @@ trait Definitions extends api.StandardDefinitions {
lazy val TypeCreatorClass = getClassIfDefined("scala.reflect.api.TypeCreator") // defined in scala-reflect.jar, so we need to be careful
lazy val TreeCreatorClass = getClassIfDefined("scala.reflect.api.TreeCreator") // defined in scala-reflect.jar, so we need to be careful
+ lazy val LiftableClass = getClassIfDefined("scala.reflect.api.Liftable") // defined in scala-reflect.jar, so we need to be careful
lazy val MacroClass = getClassIfDefined("scala.reflect.macros.Macro") // defined in scala-reflect.jar, so we need to be careful
lazy val MacroContextClass = getClassIfDefined("scala.reflect.macros.Context") // defined in scala-reflect.jar, so we need to be careful
@@ -534,6 +536,11 @@ trait Definitions extends api.StandardDefinitions {
lazy val StringContextClass = requiredClass[scala.StringContext]
def StringContext_f = getMemberMethod(StringContextClass, nme.f)
+ lazy val QuasiquoteClass = if (ApiUniverseClass != NoSymbol) getMember(ApiUniverseClass, tpnme.Quasiquote) else NoSymbol
+ lazy val QuasiquoteClass_api = if (QuasiquoteClass != NoSymbol) getMember(QuasiquoteClass, tpnme.api) else NoSymbol
+ lazy val QuasiquoteClass_api_apply = if (QuasiquoteClass_api != NoSymbol) getMember(QuasiquoteClass_api, nme.apply) else NoSymbol
+ lazy val QuasiquoteClass_api_unapply = if (QuasiquoteClass_api != NoSymbol) getMember(QuasiquoteClass_api, nme.unapply) else NoSymbol
+
lazy val ScalaSignatureAnnotation = requiredClass[scala.reflect.ScalaSignature]
lazy val ScalaLongSignatureAnnotation = requiredClass[scala.reflect.ScalaLongSignature]
@@ -645,6 +652,10 @@ trait Definitions extends api.StandardDefinitions {
isNonTrivial && isMacroCompatible
}
+ def isLiftableType(tp: Type) = tp <:< classExistentialType(LiftableClass)
+
+ def isIterableType(tp: Type) = tp <:< classExistentialType(IterableClass)
+
lazy val ProductRootClass: ClassSymbol = requiredClass[scala.Product]
def Product_productArity = getMemberMethod(ProductRootClass, nme.productArity)
def Product_productElement = getMemberMethod(ProductRootClass, nme.productElement)
@@ -1185,5 +1196,28 @@ trait Definitions extends api.StandardDefinitions {
val _ = symbolsNotPresentInBytecode
isInitialized = true
} //init
+
+ class UniverseDependentTypes(universe: Tree) {
+ lazy val universeType = universe.tpe
+ lazy val universeSym = universe.symbol
+ lazy val nameType = universeMemberType(tpnme.Name)
+ lazy val termNameType = universeMemberType(tpnme.TypeName)
+ lazy val typeNameType = universeMemberType(tpnme.TermName)
+ lazy val modsType = universeMemberType(tpnme.Modifiers)
+ lazy val flagsType = universeMemberType(tpnme.FlagSet)
+ lazy val symbolType = universeMemberType(tpnme.Symbol)
+ lazy val treeType0 = universeMemberType(tpnme.Tree)
+ lazy val treeType = universeMemberType(tpnme.Tree)
+ lazy val typeDefType = universeMemberType(tpnme.TypeDef)
+ lazy val caseDefType = universeMemberType(tpnme.CaseDef)
+ lazy val iterableTreeType = appliedType(IterableClass, treeType)
+ lazy val iterableCaseDefType = appliedType(IterableClass, caseDefType)
+ lazy val iterableIterableTreeType = appliedType(IterableClass, iterableTreeType)
+ lazy val listTreeType = appliedType(ListClass, treeType)
+ lazy val listListTreeType = appliedType(ListClass, listTreeType)
+ lazy val optionTreeType = appliedType(OptionClass, treeType)
+ lazy val optionNameType = appliedType(OptionClass, nameType)
+ def universeMemberType(name: TypeName) = universe.tpe.memberType(getTypeMember(universe.symbol, name))
+ }
}
}
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index 30aaaa1f3a..64713b8d41 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -216,23 +216,39 @@ trait StdNames {
final val Any: NameType = "Any"
final val AnyVal: NameType = "AnyVal"
+ final val FlagSet: NameType = "FlagSet"
final val Mirror: NameType = "Mirror"
+ final val Modifiers: NameType = "Modifiers"
final val Nothing: NameType = "Nothing"
final val Null: NameType = "Null"
final val Object: NameType = "Object"
+ final val Option: NameType = "Option"
final val PrefixType: NameType = "PrefixType"
final val Product: NameType = "Product"
final val Serializable: NameType = "Serializable"
final val Singleton: NameType = "Singleton"
final val Throwable: NameType = "Throwable"
+ final val api: NameType = "api"
final val Annotation: NameType = "Annotation"
+ final val CaseDef: NameType = "CaseDef"
final val ClassfileAnnotation: NameType = "ClassfileAnnotation"
final val ClassManifest: NameType = "ClassManifest"
final val Enum: NameType = "Enum"
final val Group: NameType = "Group"
+ final val Name: NameType = "Name"
final val Tree: NameType = "Tree"
+ final val TermName: NameType = "TermName"
final val Type : NameType = "Type"
+ final val TypeName: NameType = "TypeName"
+ final val TypeDef: NameType = "TypeDef"
+ final val Tuple: NameType = "Tuple"
+ final val Universe: NameType = "Universe"
+ final val Quasiquote: NameType = "Quasiquote"
+
+ // quasiquote-specific names
+ final val QUASIQUOTE_MODS: NameType = "$quasiquote$mods$"
+ final val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$"
// Annotation simple names, used in Namer
final val BeanPropertyAnnot: NameType = "BeanProperty"
@@ -304,6 +320,10 @@ trait StdNames {
val REIFY_FREE_THIS_SUFFIX: NameType = "$this"
val REIFY_FREE_VALUE_SUFFIX: NameType = "$value"
val REIFY_SYMDEF_PREFIX: NameType = "symdef$"
+ val QUASIQUOTE_PREFIX: String = "qq$"
+ val QUASIQUOTE_FILE: String = "<quasiquote>"
+ val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$"
+ val QUASIQUOTE_CASE: NameType = "$quasiquote$case$"
val MIXIN_CONSTRUCTOR: NameType = "$init$"
val MODULE_INSTANCE_FIELD: NameType = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$"
val OUTER: NameType = "$outer"
@@ -539,17 +559,23 @@ trait StdNames {
val Annotation: NameType = "Annotation"
val Any: NameType = "Any"
val AnyVal: NameType = "AnyVal"
+ val Apply: NameType = "Apply"
+ val Applied: NameType = "Applied"
val ArrayAnnotArg: NameType = "ArrayAnnotArg"
+ val Block: NameType = "Block"
val ConstantType: NameType = "ConstantType"
val EmptyPackage: NameType = "EmptyPackage"
val EmptyPackageClass: NameType = "EmptyPackageClass"
+ val False : NameType = "False"
val Flag : NameType = "Flag"
+ val FlagsAsBits: NameType = "FlagsAsBits"
val Ident: NameType = "Ident"
val Import: NameType = "Import"
val Literal: NameType = "Literal"
val LiteralAnnotArg: NameType = "LiteralAnnotArg"
val Modifiers: NameType = "Modifiers"
val NestedAnnotArg: NameType = "NestedAnnotArg"
+ val New: NameType = "New"
val NoFlags: NameType = "NoFlags"
val NoSymbol: NameType = "NoSymbol"
val Nothing: NameType = "Nothing"
@@ -560,10 +586,15 @@ trait StdNames {
val Select: NameType = "Select"
val SelectFromTypeTree: NameType = "SelectFromTypeTree"
val StringContext: NameType = "StringContext"
+ val SyntacticClassDef: NameType = "SyntacticClassDef"
val This: NameType = "This"
val ThisType: NameType = "ThisType"
+ val True : NameType = "True"
val Tuple2: NameType = "Tuple2"
+ val TupleN: NameType = "TupleN"
+ val TupleTypeN: NameType = "TupleTypeN"
val TYPE_ : NameType = "TYPE"
+ val TypeApplied: NameType = "TypeApplied"
val TypeRef: NameType = "TypeRef"
val TypeTree: NameType = "TypeTree"
val UNIT : NameType = "UNIT"
@@ -593,6 +624,7 @@ trait StdNames {
val checkInitialized: NameType = "checkInitialized"
val classOf: NameType = "classOf"
val clone_ : NameType = "clone"
+ val collection: NameType = "collection"
val conforms: NameType = "conforms"
val copy: NameType = "copy"
val create: NameType = "create"
@@ -619,10 +651,13 @@ trait StdNames {
val find_ : NameType = "find"
val flagsFromBits : NameType = "flagsFromBits"
val flatMap: NameType = "flatMap"
+ val flatten: NameType = "flatten"
+ val foldLeft: NameType = "foldLeft"
val foreach: NameType = "foreach"
val get: NameType = "get"
val hashCode_ : NameType = "hashCode"
val hash_ : NameType = "hash"
+ val immutable: NameType = "immutable"
val implicitly: NameType = "implicitly"
val in: NameType = "in"
val inlinedEquals: NameType = "inlinedEquals"
@@ -644,12 +679,15 @@ trait StdNames {
val materializeWeakTypeTag: NameType = "materializeWeakTypeTag"
val materializeTypeTag: NameType = "materializeTypeTag"
val moduleClass : NameType = "moduleClass"
+ val mkAnnotationCtor: NameType = "mkAnnotationCtor"
val ne: NameType = "ne"
val newArray: NameType = "newArray"
val newFreeTerm: NameType = "newFreeTerm"
val newFreeType: NameType = "newFreeType"
val newNestedSymbol: NameType = "newNestedSymbol"
val newScopeWith: NameType = "newScopeWith"
+ val nmeCONSTRUCTOR: NameType = "CONSTRUCTOR"
+ val nmeNme: NameType = "nme"
val notifyAll_ : NameType = "notifyAll"
val notify_ : NameType = "notify"
val null_ : NameType = "null"
@@ -665,6 +703,7 @@ trait StdNames {
val runtime: NameType = "runtime"
val runtimeClass: NameType = "runtimeClass"
val runtimeMirror: NameType = "runtimeMirror"
+ val RefTree: NameType = "RefTree"
val scala_ : NameType = "scala"
val selectDynamic: NameType = "selectDynamic"
val selectOverloadedMethod: NameType = "selectOverloadedMethod"
@@ -684,6 +723,7 @@ trait StdNames {
val this_ : NameType = "this"
val thisPrefix : NameType = "thisPrefix"
val toArray: NameType = "toArray"
+ val toList: NameType = "toList"
val toObjectArray : NameType = "toObjectArray"
val TopScope: NameType = "TopScope"
val toString_ : NameType = "toString"
@@ -694,6 +734,7 @@ trait StdNames {
val typedProductIterator: NameType = "typedProductIterator"
val TypeName: NameType = "TypeName"
val typeTagToManifest: NameType = "typeTagToManifest"
+
val unapply: NameType = "unapply"
val unapplySeq: NameType = "unapplySeq"
val unbox: NameType = "unbox"
@@ -708,6 +749,12 @@ trait StdNames {
val withFilter: NameType = "withFilter"
val zero: NameType = "zero"
+ // quasiquote interpolators:
+ val q: NameType = "q"
+ val tq: NameType = "tq"
+ val cq: NameType = "cq"
+ val pq: NameType = "pq"
+
// unencoded operators
object raw {
final val BANG : NameType = "!"
@@ -744,6 +791,7 @@ trait StdNames {
val ADD = encode("+")
val AND = encode("&")
val ASR = encode(">>")
+ val CONS = encode("::")
val DIV = encode("/")
val EQ = encode("==")
val EQL = encode("=")
@@ -760,6 +808,7 @@ trait StdNames {
val NE = encode("!=")
val OR = encode("|")
val PLUS = ADD // technically redundant, but ADD looks funny with MINUS
+ val PLUSPLUS = encode("++")
val SUB = MINUS // ... as does SUB with PLUS
val XOR = encode("^")
val ZAND = encode("&&")