summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala23
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala8
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala1
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala13
-rw-r--r--src/reflect/scala/reflect/api/Quasiquotes.scala1
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala8
-rw-r--r--test/files/scalacheck/quasiquotes/ForProps.scala38
-rw-r--r--test/files/scalacheck/quasiquotes/TypecheckedProps.scala12
8 files changed, 96 insertions, 8 deletions
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
index f9ecbe35c9..d8ede24f1a 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
@@ -152,6 +152,13 @@ trait Parsers { self: Quasiquotes =>
in.nextToken()
stats
}
+
+ override def enumerator(isFirst: Boolean, allowNestedIf: Boolean = true) =
+ if (isHole && lookingAhead { in.token == EOF || in.token == RPAREN || isStatSep }) {
+ val res = build.SyntacticValFrom(Bind(in.name, Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) :: Nil
+ in.nextToken()
+ res
+ } else super.enumerator(isFirst, allowNestedIf)
}
}
@@ -174,11 +181,21 @@ trait Parsers { self: Quasiquotes =>
}
}
+ object ForEnumeratorParser extends Parser {
+ def entryPoint = { parser =>
+ val enums = parser.enumerator(isFirst = false, allowNestedIf = false)
+ assert(enums.length == 1)
+ enums.head
+ }
+ }
+
object FreshName {
def unapply(name: Name): Option[String] =
- name.toString.split("\\$") match {
- case Array(qq, left, right) if qq + "$" == nme.QUASIQUOTE_PREFIX && Try(right.toInt).isSuccess =>
- Some(left + "$")
+ name.toString.split("\\$").toSeq match {
+ case qq +: (middle :+ last)
+ if qq + "$" == nme.QUASIQUOTE_PREFIX
+ && Try(last.toInt).isSuccess && middle.nonEmpty =>
+ Some(middle.mkString("", "$", "$"))
case _ =>
None
}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
index c31d1fcd12..54be9123c7 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
@@ -161,4 +161,12 @@ trait Placeholders { self: Quasiquotes =>
case _ => None
}
}
+
+ object ForEnumPlaceholder {
+ def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
+ case build.SyntacticValFrom(Bind(Placeholder(tree, location, card), Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) =>
+ Some((tree, location, card))
+ 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
index f4d6b39d02..7d777ef7d5 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
@@ -31,6 +31,7 @@ abstract class Quasiquotes extends Parsers
case nme.tq => TypeParser.parse(_)
case nme.cq => CaseParser.parse(_)
case nme.pq => PatternParser.parse(_)
+ case nme.fq => ForEnumeratorParser.parse(_)
case other => global.abort(s"Unknown quasiquote flavor: $other")
}
(universe0, args0, parts1, parse0, reify0, method0)
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
index 3d1ecf95b2..b28c85cfc2 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
@@ -127,6 +127,7 @@ trait Reifiers { self: Quasiquotes =>
case RefineStatPlaceholder(tree, _, _) => reifyRefineStat(tree)
case EarlyDefPlaceholder(tree, _, _) => reifyEarlyDef(tree)
case PackageStatPlaceholder(tree, _, _) => reifyPackageStat(tree)
+ case ForEnumPlaceholder(tree, _, _) => tree
case _ => EmptyTree
}
@@ -148,6 +149,16 @@ trait Reifiers { self: Quasiquotes =>
reifyBuildCall(nme.SyntacticValDef, mods, name, tpt, rhs)
case SyntacticVarDef(mods, name, tpt, rhs) =>
reifyBuildCall(nme.SyntacticVarDef, mods, name, tpt, rhs)
+ case SyntacticValFrom(pat, rhs) =>
+ reifyBuildCall(nme.SyntacticValFrom, pat, rhs)
+ case SyntacticValEq(pat, rhs) =>
+ reifyBuildCall(nme.SyntacticValEq, pat, rhs)
+ case SyntacticFilter(cond) =>
+ reifyBuildCall(nme.SyntacticFilter, cond)
+ case SyntacticFor(enums, body) =>
+ reifyBuildCall(nme.SyntacticFor, enums, body)
+ case SyntacticForYield(enums, body) =>
+ reifyBuildCall(nme.SyntacticForYield, enums, body)
case SyntacticAssign(lhs, rhs) =>
reifyBuildCall(nme.SyntacticAssign, lhs, rhs)
case SyntacticApplied(fun, List(args))
@@ -275,7 +286,7 @@ trait Reifiers { self: Quasiquotes =>
case RefineStatPlaceholder(tree, _, DotDot) => reifyRefineStat(tree)
case EarlyDefPlaceholder(tree, _, DotDot) => reifyEarlyDef(tree)
case PackageStatPlaceholder(tree, _, DotDot) => reifyPackageStat(tree)
-
+ case ForEnumPlaceholder(tree, _, DotDot) => tree
case List(Placeholder(tree, _, DotDotDot)) => tree
} {
reify(_)
diff --git a/src/reflect/scala/reflect/api/Quasiquotes.scala b/src/reflect/scala/reflect/api/Quasiquotes.scala
index 3687ccba63..fcf8edcec7 100644
--- a/src/reflect/scala/reflect/api/Quasiquotes.scala
+++ b/src/reflect/scala/reflect/api/Quasiquotes.scala
@@ -14,5 +14,6 @@ trait Quasiquotes { self: Universe =>
object tq extends api
object cq extends api
object pq extends api
+ object fq extends api
}
}
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index bc9c46a141..c26e815df1 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -327,6 +327,7 @@ trait StdNames {
val QUASIQUOTE_FILE: String = "<quasiquote>"
val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$"
val QUASIQUOTE_CASE: NameType = "$quasiquote$case$"
+ val QUASIQUOTE_FOR_ENUM: NameType = "$quasiquote$for$enum$"
val MIXIN_CONSTRUCTOR: NameType = "$init$"
val MODULE_INSTANCE_FIELD: NameType = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$"
val OUTER: NameType = "$outer"
@@ -592,6 +593,9 @@ trait StdNames {
val SyntacticBlock: NameType = "SyntacticBlock"
val SyntacticClassDef: NameType = "SyntacticClassDef"
val SyntacticDefDef: NameType = "SyntacticDefDef"
+ val SyntacticFilter: NameType = "SyntacticFilter"
+ val SyntacticFor: NameType = "SyntacticFor"
+ val SyntacticForYield: NameType = "SyntacticForYield"
val SyntacticFunction: NameType = "SyntacticFunction"
val SyntacticFunctionType: NameType = "SyntacticFunctionType"
val SyntacticPackageObjectDef: NameType = "SyntacticPackageObjectDef"
@@ -602,6 +606,8 @@ trait StdNames {
val SyntacticTupleType: NameType = "SyntacticTupleType"
val SyntacticTypeApplied: NameType = "SyntacticTypeApplied"
val SyntacticValDef: NameType = "SyntacticValDef"
+ val SyntacticValEq: NameType = "SyntacticValEq"
+ val SyntacticValFrom: NameType = "SyntacticValFrom"
val SyntacticVarDef: NameType = "SyntacticVarDef"
val This: NameType = "This"
val ThisType: NameType = "ThisType"
@@ -745,7 +751,6 @@ 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"
@@ -764,6 +769,7 @@ trait StdNames {
val tq: NameType = "tq"
val cq: NameType = "cq"
val pq: NameType = "pq"
+ val fq: NameType = "fq"
// unencoded operators
object raw {
diff --git a/test/files/scalacheck/quasiquotes/ForProps.scala b/test/files/scalacheck/quasiquotes/ForProps.scala
index 234f4d10eb..e71822aaea 100644
--- a/test/files/scalacheck/quasiquotes/ForProps.scala
+++ b/test/files/scalacheck/quasiquotes/ForProps.scala
@@ -10,15 +10,15 @@ object ForProps extends QuasiquoteProperties("for") {
def genForFilter: Gen[Tree] =
for(cond <- genIdent(genTermName))
- yield SyntacticFilter(cond)
+ yield fq"if $cond"
def genForFrom: Gen[Tree] =
for(lhs <- genSimpleBind; rhs <- genIdent(genTermName))
- yield SyntacticValFrom(lhs, rhs)
+ yield fq"$lhs <- $rhs"
def genForEq: Gen[Tree] =
for(lhs <- genSimpleBind; rhs <- genIdent(genTermName))
- yield SyntacticValEq(lhs, rhs)
+ yield fq"$lhs = $rhs"
def genForEnums(size: Int): Gen[ForEnums] =
for(first <- genForFrom; rest <- listOfN(size, oneOf(genForFrom, genForFilter, genForEq)))
@@ -35,4 +35,36 @@ object ForProps extends QuasiquoteProperties("for") {
val SyntacticForYield(recoveredEnums, recoveredBody) = SyntacticForYield(enums.value, body)
recoveredEnums ≈ enums.value && recoveredBody ≈ body
}
+
+ val abcde = List(fq"a <-b", fq"if c", fq"d = e")
+ val foobarbaz = pq"foo @ Bar(baz)"
+ val fv = q"f(v)"
+
+ property("construct/deconstruct for loop with fq") = test {
+ val for0 = q"for(..$abcde) $fv"
+ assertEqAst(for0, "for(a <- b; if c; d = e) f(v)")
+ val q"for(..$enums) $body" = for0
+ assert(enums ≈ abcde)
+ assert(body ≈ fv)
+ }
+
+ property("construct/deconstruct valfrom with fq") = test {
+ assert(fq"$foobarbaz <- $fv" ≈ fq"foo @ Bar(baz) <- f(v)")
+ val fq"$lhs <- $rhs" = fq"$foobarbaz <- $fv"
+ assert(lhs ≈ foobarbaz)
+ assert(rhs ≈ fv)
+ }
+
+ property("construct/deconstruct valeq with fq") = test {
+ assert(fq"$foobarbaz = $fv" ≈ fq"foo @ Bar(baz) = f(v)")
+ val fq"$lhs = $rhs" = fq"$foobarbaz = $fv"
+ assert(lhs ≈ foobarbaz)
+ assert(rhs ≈ fv)
+ }
+
+ property("construct/deconstruct filter with fq") = test {
+ assert(fq"if $fv" ≈ fq"if f(v)")
+ val fq"if $cond" = fq"if $fv"
+ assert(cond ≈ fv)
+ }
} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/TypecheckedProps.scala b/test/files/scalacheck/quasiquotes/TypecheckedProps.scala
index 8e93422e77..a95016b634 100644
--- a/test/files/scalacheck/quasiquotes/TypecheckedProps.scala
+++ b/test/files/scalacheck/quasiquotes/TypecheckedProps.scala
@@ -27,4 +27,16 @@ object TypecheckedProps extends QuasiquoteProperties("typechecked") {
assert(originals(argtpes) ≈ intint)
assert(original(restpe).get ≈ int)
}
+
+ property("for/for-yield") = test {
+ val enums = fq"x <- xs" :: fq"x1 = x + 1" :: fq"if x1 % 2 == 0" :: Nil
+ val body = q"x1"
+ val xs = q"val xs = List(1, 2, 3)"
+ val q"$_; for(..$enums0) yield $body0" = typecheck(q"$xs; for(..$enums) yield $body")
+ assert(enums0 ≈ enums)
+ assert(body0 ≈ body)
+ val q"$_; for(..$enums1) $body1" = typecheck(q"$xs; for(..$enums) $body")
+ assert(enums1 ≈ enums)
+ assert(body1 ≈ body)
+ }
} \ No newline at end of file