summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect/internal/BuildUtils.scala
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/reflect/scala/reflect/internal/BuildUtils.scala
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/reflect/scala/reflect/internal/BuildUtils.scala')
-rw-r--r--src/reflect/scala/reflect/internal/BuildUtils.scala120
1 files changed, 120 insertions, 0 deletions
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