diff options
author | Den Shabalin <den.shabalin@gmail.com> | 2013-10-31 12:21:28 +0100 |
---|---|---|
committer | Den Shabalin <den.shabalin@gmail.com> | 2013-11-12 14:04:42 +0100 |
commit | c62a0e78c33dda1db6e73fa5cd3c6ddbd2b238f4 (patch) | |
tree | e5b1d545931db70c83844474a802804b164f75e5 | |
parent | a4a3ab0d722412b9ecf267b178bb866087867cf9 (diff) | |
download | scala-c62a0e78c33dda1db6e73fa5cd3c6ddbd2b238f4.tar.gz scala-c62a0e78c33dda1db6e73fa5cd3c6ddbd2b238f4.tar.bz2 scala-c62a0e78c33dda1db6e73fa5cd3c6ddbd2b238f4.zip |
add support for for loops and for enumerators to quasiquotes
1. q"for (..$enums) $body", q"for (..$enums) yield $body"
2. fq"..." quote to construct/deconstruct enumerators
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 |