diff options
author | Den Shabalin <den.shabalin@gmail.com> | 2013-09-02 15:51:56 +0200 |
---|---|---|
committer | Den Shabalin <den.shabalin@gmail.com> | 2013-09-05 14:39:12 +0200 |
commit | 901cdc1855e8e80957b18add4991736b9f458d77 (patch) | |
tree | b6ac94c657f262d3a4e398e36b07d042ba4fbfa1 | |
parent | f9d5e3d4e8cca61ee75072ab13c2935061a1850e (diff) | |
download | scala-901cdc1855e8e80957b18add4991736b9f458d77.tar.gz scala-901cdc1855e8e80957b18add4991736b9f458d77.tar.bz2 scala-901cdc1855e8e80957b18add4991736b9f458d77.zip |
refine block and applied/typeapplied splicing/matching semantics
1. blocks now match single term-level expressions to account for
automatic block elimination. E.g.
val q"{ ..$stats }" = q"foo"
will match into stats = List(q"foo"). This is useful to uniformly
deal with blocks on term level.
2. blocks in quasiquotes collapse into single expressions
3. Applied and TypeApplied now have constructors too which helps
to unify matching and extraction in quasiquote reifier
4. TypeApplied now matches AppliedTypeTree too
5. Add Syntactic prefix to Applied and TypeApplied
12 files changed, 82 insertions, 60 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index ed694023d7..ed7c0d1a0a 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -229,11 +229,7 @@ abstract class TreeBuilder { } /** Create block of statements `stats` */ - def makeBlock(stats: List[Tree]): Tree = - if (stats.isEmpty) Literal(Constant(())) - else if (!stats.last.isTerm) Block(stats, Literal(Constant(()))) - else if (stats.length == 1) stats.head - else Block(stats.init, stats.last) + def makeBlock(stats: List[Tree]): Tree = gen.mkBlock(stats) def makeFilter(tree: Tree, condition: Tree, scrutineeName: String): Tree = { val cases = List( diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index d04b65545d..fe0809c869 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -79,6 +79,11 @@ trait Parsers { self: Quasiquotes => } else super.caseClause() + override def caseBlock(): Tree = super.caseBlock() match { + case Block(Nil, expr) => expr + case other => other + } + def isHole: Boolean = isIdent && holeMap.contains(in.name) override def isAnnotation: Boolean = super.isAnnotation || (isHole && lookingAhead { isAnnotation }) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index d4bc2d67c6..62c246366d 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -7,7 +7,7 @@ import scala.reflect.internal.Flags._ trait Reifiers { self: Quasiquotes => import global._ - import global.build.SyntacticClassDef + import global.build.{SyntacticClassDef, SyntacticBlock, SyntacticApplied, SyntacticTypeApplied} import global.treeInfo._ import global.definitions._ import Cardinality._ @@ -54,6 +54,14 @@ trait Reifiers { self: Quasiquotes => case SyntacticClassDef(mods, name, tparams, constrmods, vparamss, parents, selfdef, body) => reifyBuildCall(nme.SyntacticClassDef, mods, name, tparams, constrmods, vparamss, parents, selfdef, body) + case SyntacticApplied(fun, argss) if argss.length > 1 => + reifyBuildCall(nme.SyntacticApplied, fun, argss) + case SyntacticApplied(fun, argss @ (_ :+ (_ :+ Placeholder(_, _, DotDotDot)))) => + reifyBuildCall(nme.SyntacticApplied, fun, argss) + case SyntacticTypeApplied(fun, targs) if targs.nonEmpty => + reifyBuildCall(nme.SyntacticTypeApplied, fun, targs) + case Block(stats, last) => + reifyBuildCall(nme.SyntacticBlock, stats :+ last) case _ => super.reifyTreeSyntactically(tree) } @@ -169,19 +177,10 @@ trait Reifiers { self: Quasiquotes => 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)) + case RefTree(qual, SymbolPlaceholder(tree)) => + mirrorBuildCall(nme.RefTree, reify(qual), tree) + case _ => + super.reifyTreeSyntactically(tree) } override def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree = xs match { @@ -245,15 +244,6 @@ trait Reifiers { self: Quasiquotes => 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: _*) diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala index 908ffb3713..a2c2ebc3e3 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala @@ -330,8 +330,6 @@ abstract class SelectiveCPSTransform extends PluginComponent with } } - def mkBlock(stms: List[Tree], expr: Tree) = if (stms.nonEmpty) Block(stms, expr) else expr - try { if (specialCaseTrivial) { debuglog("will optimize possible tail call: " + bodyExpr) @@ -350,9 +348,9 @@ abstract class SelectiveCPSTransform extends PluginComponent with val argSym = currentOwner.newValue(vd.symbol.name.toTermName).setInfo(tpe) val argDef = localTyper.typed(ValDef(argSym, Select(ctxRef, ctxRef.tpe.member(cpsNames.getTrivialValue)))) val switchExpr = localTyper.typedPos(vd.symbol.pos) { - val body2 = mkBlock(bodyStms, bodyExpr).duplicate // dup before typing! + val body2 = gen.mkBlock(bodyStms :+ bodyExpr).duplicate // dup before typing! If(Select(ctxRef, ctxSym.tpe.member(cpsNames.isTrivial)), - applyTrivial(argSym, mkBlock(argDef::bodyStms, bodyExpr)), + applyTrivial(argSym, gen.mkBlock((argDef :: bodyStms) :+ bodyExpr)), applyCombinatorFun(ctxRef, body2)) } (List(ctxDef), switchExpr) @@ -360,7 +358,7 @@ abstract class SelectiveCPSTransform extends PluginComponent with // ctx.flatMap { <lhs> => ... } // or // ctx.map { <lhs> => ... } - (Nil, applyCombinatorFun(rhs1, mkBlock(bodyStms, bodyExpr))) + (Nil, applyCombinatorFun(rhs1, gen.mkBlock(bodyStms :+ bodyExpr))) } } catch { case ex:TypeError => diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index 03980f5f0a..ef9f9ab834 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -64,8 +64,6 @@ 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 @@ -80,6 +78,8 @@ private[reflect] trait BuildUtils { self: Universe => def mkRefineStat(stats: List[Tree]): List[Tree] + def RefTree(qual: Tree, sym: Symbol): Tree + val FlagsRepr: FlagsReprExtractor trait FlagsReprExtractor { @@ -87,15 +87,17 @@ private[reflect] trait BuildUtils { self: Universe => def unapply(flags: Long): Some[Long] } - val TypeApplied: TypeAppliedExtractor + val SyntacticTypeApplied: SyntacticTypeAppliedExtractor - trait TypeAppliedExtractor { + trait SyntacticTypeAppliedExtractor { + def apply(tree: Tree, targs: List[Tree]): Tree def unapply(tree: Tree): Some[(Tree, List[Tree])] } - val Applied: AppliedExtractor + val SyntacticApplied: SyntacticAppliedExtractor - trait AppliedExtractor { + trait SyntacticAppliedExtractor { + def apply(tree: Tree, argss: List[List[Tree]]): Tree def unapply(tree: Tree): Some[(Tree, List[List[Tree]])] } @@ -117,6 +119,11 @@ private[reflect] trait BuildUtils { self: Universe => def unapply(tree: Tree): Option[List[Tree]] } - def RefTree(qual: Tree, sym: Symbol): Tree + val SyntacticBlock: SyntacticBlockExtractor + + trait SyntacticBlockExtractor { + def apply(stats: List[Tree]): Tree + def unapply(tree: Tree): Option[List[Tree]] + } } } diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index 208a61d9d3..928395b7c9 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -53,12 +53,6 @@ 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 @@ -88,25 +82,34 @@ trait BuildUtils { self: SymbolTable => def mkRefineStat(stats: List[Tree]): List[Tree] = stats.map(mkRefineStat) + def RefTree(qual: Tree, sym: Symbol) = self.RefTree(qual, sym.name) setSymbol sym + object FlagsRepr extends FlagsReprExtractor { def apply(bits: Long): FlagSet = bits def unapply(flags: Long): Some[Long] = Some(flags) } - object TypeApplied extends TypeAppliedExtractor { + object SyntacticTypeApplied extends SyntacticTypeAppliedExtractor { + def apply(tree: Tree, targs: List[Tree]): Tree = + if (targs.isEmpty) tree + else if (tree.isTerm) TypeApply(tree, targs) + else if (tree.isType) AppliedTypeTree(tree, targs) + else throw new IllegalArgumentException(s"can't apply types to $tree") + def unapply(tree: Tree): Some[(Tree, List[Tree])] = tree match { case TypeApply(fun, targs) => Some((fun, targs)) + case AppliedTypeTree(tpe, targs) => Some((tpe, targs)) case _ => Some((tree, Nil)) } } - object Applied extends AppliedExtractor { + object SyntacticApplied extends SyntacticAppliedExtractor { + def apply(tree: Tree, argss: List[List[Tree]]): Tree = + argss.foldLeft(tree) { Apply(_, _) } + 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)) - } + Some((SyntacticTypeApplied(fun, targs), argss)) } } @@ -189,7 +192,15 @@ trait BuildUtils { self: SymbolTable => } } - def RefTree(qual: Tree, sym: Symbol) = self.RefTree(qual, sym.name) setSymbol sym + object SyntacticBlock extends SyntacticBlockExtractor { + def apply(stats: List[Tree]): Tree = gen.mkBlock(stats) + + def unapply(tree: Tree): Option[List[Tree]] = tree match { + case self.Block(stats, expr) => Some(stats :+ expr) + case _ if tree.isTerm => Some(tree :: Nil) + case _ => None + } + } } val build: BuildApi = new BuildImpl diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index eb24706f29..6cbf1f1046 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -561,7 +561,6 @@ trait StdNames { 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" @@ -588,7 +587,10 @@ trait StdNames { val Select: NameType = "Select" val SelectFromTypeTree: NameType = "SelectFromTypeTree" val StringContext: NameType = "StringContext" + val SyntacticApplied: NameType = "SyntacticApplied" + val SyntacticBlock: NameType = "SyntacticBlock" val SyntacticClassDef: NameType = "SyntacticClassDef" + val SyntacticTypeApplied: NameType = "SyntacticTypeApplied" val This: NameType = "This" val ThisType: NameType = "ThisType" val True : NameType = "True" @@ -596,7 +598,6 @@ trait StdNames { 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" diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index a7ea8c816f..9623cc31f6 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -414,4 +414,11 @@ abstract class TreeGen extends macros.TreeBuilder { ) } } + + /** Create block of statements `stats` */ + def mkBlock(stats: List[Tree]): Tree = + if (stats.isEmpty) Literal(Constant(())) + else if (!stats.last.isTerm) Block(stats, Literal(Constant(()))) + else if (stats.length == 1) stats.head + else Block(stats.init, stats.last) } diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala index 5d657b931b..dc3fc60f8c 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala @@ -118,7 +118,7 @@ trait TypeDefConstruction { self: QuasiquoteProperties => property("splice into applied type tree") = forAll { (T1: TypeName, T2: TypeName, args: List[Tree]) => q"type $T1 = $T2[..$args]" ≈ TypeDef(Modifiers(), T1, List(), - AppliedTypeTree(Ident(T2), args)) + if(args.nonEmpty) AppliedTypeTree(Ident(T2), args) else Ident(T2)) } } diff --git a/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala b/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala index aee50c9c5f..504cb2a77d 100644 --- a/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala @@ -32,6 +32,6 @@ object PatternConstructionProps extends QuasiquoteProperties("pattern constructi } property("splice into casedef") = forAll { (pat: Tree, cond: Tree, body: Tree) => - cq"$pat if $cond => $body" ≈ CaseDef(pat, cond, Block(List(), body)) + cq"$pat if $cond => $body" ≈ CaseDef(pat, cond, body) } }
\ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala index 5a765f7911..599dfd442a 100644 --- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala @@ -88,7 +88,7 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { } property("splice trees into type apply") = forAll { (fun: TreeIsTerm, types: List[Tree]) => - q"$fun[..$types]" ≈ TypeApply(fun, types) + q"$fun[..$types]" ≈ (if (types.nonEmpty) TypeApply(fun, types) else fun) } property("splice names into import selector") = forAll { @@ -122,8 +122,10 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { def blockInvariant(quote: Tree, trees: List[Tree]) = quote ≈ (trees match { - case Nil => Block(Nil, q"()") - case _ => Block(trees.init, trees.last) + case Nil => q"()" + case _ :+ last if !last.isTerm => Block(trees, q"()") + case head :: Nil => head + case init :+ last => Block(init, last) }) property("splice list of trees into block (1)") = forAll { (trees: List[Tree]) => diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala index 132baeb977..6087bbdb74 100644 --- a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala @@ -62,4 +62,9 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction val q"$x match { case ..$cases }" = q"x match { case 1 => case 2 => }" x ≈ q"x" && cases ≈ List(cq"1 =>", cq"2 =>") } + + property("deconstruct block") = test { + val q"{ ..$xs }" = q"{ x1; x2; x3 }" + assert(xs ≈ List(q"x1", q"x2", q"x3")) + } }
\ No newline at end of file |