diff options
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("&&") |