Skip to content

Commit c8a9329

Browse files
committed
add syntactic extractor for assignment-like trees
There are three kinds of assign-like trees: 1. Assign(lhs, rhs) // $lhs = $rhs 3. AssignOrNamedArg(lhs, rhs) // $lhs = $rhs 2. Apply(Select(f, nme.update), args :+ rhs) // $f(..$args) = $rhs New syntactic combinator unifies all of them and lets users not to think about these implementations details.
1 parent a8543ef commit c8a9329

File tree

10 files changed

+77
-15
lines changed

10 files changed

+77
-15
lines changed

src/compiler/scala/tools/nsc/ast/parser/Parsers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1403,7 +1403,7 @@ self =>
14031403
if (in.token == EQUALS) {
14041404
t match {
14051405
case Ident(_) | Select(_, _) | Apply(_, _) =>
1406-
t = atPos(t.pos.startOrPoint, in.skipToken()) { makeAssign(t, expr()) }
1406+
t = atPos(t.pos.startOrPoint, in.skipToken()) { gen.mkAssign(t, expr()) }
14071407
case _ =>
14081408
}
14091409
} else if (in.token == COLON) {

src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -191,14 +191,6 @@ abstract class TreeBuilder {
191191
}
192192
}
193193

194-
/** Create a tree representing an assignment <lhs = rhs> */
195-
def makeAssign(lhs: Tree, rhs: Tree): Tree = lhs match {
196-
case Apply(fn, args) =>
197-
Apply(atPos(fn.pos) { Select(fn, nme.update) }, args ::: List(rhs))
198-
case _ =>
199-
Assign(lhs, rhs)
200-
}
201-
202194
/** Tree for `od op`, start is start0 if od.pos is borked. */
203195
def makePostfixSelect(start0: Int, end: Int, od: Tree, op: Name): Tree = {
204196
val start = if (od.pos.isDefined) od.pos.startOrPoint else start0

src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ trait Reifiers { self: Quasiquotes =>
1010
import global.build.{SyntacticClassDef, SyntacticTraitDef, SyntacticModuleDef,
1111
SyntacticDefDef, SyntacticValDef, SyntacticVarDef,
1212
SyntacticBlock, SyntacticApplied, SyntacticTypeApplied,
13-
SyntacticFunction, SyntacticNew}
13+
SyntacticFunction, SyntacticNew, SyntacticAssign}
1414
import global.treeInfo._
1515
import global.definitions._
1616
import Cardinality._
@@ -71,9 +71,9 @@ trait Reifiers { self: Quasiquotes =>
7171
reifyBuildCall(nme.SyntacticValDef, mods, name, tpt, rhs)
7272
case SyntacticVarDef(mods, name, tpt, rhs) =>
7373
reifyBuildCall(nme.SyntacticVarDef, mods, name, tpt, rhs)
74-
case SyntacticApplied(fun, argss) if argss.length > 1 =>
75-
reifyBuildCall(nme.SyntacticApplied, fun, argss)
76-
case SyntacticApplied(fun, argss @ (_ :+ (_ :+ Placeholder(_, _, DotDotDot)))) =>
74+
case SyntacticAssign(lhs, rhs) =>
75+
reifyBuildCall(nme.SyntacticAssign, lhs, rhs)
76+
case SyntacticApplied(fun, argss) if argss.nonEmpty =>
7777
reifyBuildCall(nme.SyntacticApplied, fun, argss)
7878
case SyntacticTypeApplied(fun, targs) if targs.nonEmpty =>
7979
reifyBuildCall(nme.SyntacticTypeApplied, fun, targs)

src/reflect/scala/reflect/api/BuildUtils.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,5 +193,12 @@ private[reflect] trait BuildUtils { self: Universe =>
193193
def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef
194194
def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)]
195195
}
196+
197+
val SyntacticAssign: SyntacticAssignExtractor
198+
199+
trait SyntacticAssignExtractor {
200+
def apply(lhs: Tree, rhs: Tree): Tree
201+
def unapply(tree: Tree): Option[(Tree, Tree)]
202+
}
196203
}
197204
}

src/reflect/scala/reflect/internal/BuildUtils.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ trait BuildUtils { self: SymbolTable =>
139139

140140
object SyntacticApplied extends SyntacticAppliedExtractor {
141141
def apply(tree: Tree, argss: List[List[Tree]]): Tree =
142-
argss.foldLeft(tree) { Apply(_, _) }
142+
argss.foldLeft(tree) { (f, args) => Apply(f, args.map(treeInfo.assignmentToMaybeNamedArg)) }
143143

144144
def unapply(tree: Tree): Some[(Tree, List[List[Tree]])] = {
145145
val treeInfo.Applied(fun, targs, argss) = tree
@@ -400,6 +400,16 @@ trait BuildUtils { self: SymbolTable =>
400400

401401
object SyntacticValDef extends SyntacticValDefBase { val isMutable = false }
402402
object SyntacticVarDef extends SyntacticValDefBase { val isMutable = true }
403+
404+
object SyntacticAssign extends SyntacticAssignExtractor {
405+
def apply(lhs: Tree, rhs: Tree): Tree = gen.mkAssign(lhs, rhs)
406+
def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
407+
case Assign(lhs, rhs) => Some((lhs, rhs))
408+
case AssignOrNamedArg(lhs, rhs) => Some((lhs, rhs))
409+
case Apply(Select(fn, nme.update), args :+ rhs) => Some((atPos(fn.pos)(Apply(fn, args)), rhs))
410+
case _ => None
411+
}
412+
}
403413
}
404414

405415
val build: BuildApi = new BuildImpl

src/reflect/scala/reflect/internal/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,7 @@ trait StdNames {
602602
val SelectFromTypeTree: NameType = "SelectFromTypeTree"
603603
val StringContext: NameType = "StringContext"
604604
val SyntacticApplied: NameType = "SyntacticApplied"
605+
val SyntacticAssign: NameType = "SyntacticAssign"
605606
val SyntacticBlock: NameType = "SyntacticBlock"
606607
val SyntacticClassDef: NameType = "SyntacticClassDef"
607608
val SyntacticDefDef: NameType = "SyntacticDefDef"

src/reflect/scala/reflect/internal/TreeGen.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,4 +443,12 @@ abstract class TreeGen extends macros.TreeBuilder {
443443
case head :: Nil => head
444444
case _ => gen.mkBlock(stats)
445445
}
446+
447+
/** Create a tree representing an assignment <lhs = rhs> */
448+
def mkAssign(lhs: Tree, rhs: Tree): Tree = lhs match {
449+
case Apply(fn, args) =>
450+
Apply(atPos(fn.pos)(Select(fn, nme.update)), args :+ rhs)
451+
case _ =>
452+
Assign(lhs, rhs)
453+
}
446454
}

test/files/neg/macro-quasiquotes.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Macros_1.scala:14: error: macro implementation has wrong shape:
22
required: (x: Impls.this.c.Expr[Int]): Impls.this.c.Expr[Any]
3-
found : (x: Impls.this.c.universe.Block): Impls.this.c.universe.Apply
3+
found : (x: Impls.this.c.universe.Block): Impls.this.c.universe.Tree
44
type mismatch for parameter x: Impls.this.c.Expr[Int] does not conform to Impls.this.c.universe.Block
55
def m3(x: Int) = macro Impls.impl3
66
^

test/files/scalacheck/quasiquotes/TermConstructionProps.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,28 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") {
167167
val x = q"val x: Int = 1"
168168
assertThrows[IllegalArgumentException] { q"($x) => x" }
169169
}
170+
171+
property("assign variable") = test {
172+
val v = q"v"
173+
val value = q"foo"
174+
assertEqAst(q"$v = $value", "v = foo")
175+
}
176+
177+
property("assign update 1") = test {
178+
val v = q"v"
179+
val args = q"1" :: q"2" :: Nil
180+
val value = q"foo"
181+
assertEqAst(q"$v(..$args) = $value", "v(1, 2) = foo")
182+
}
183+
184+
property("assign update 2") = test {
185+
val a = q"v(0)"
186+
val value = q"foo"
187+
assertEqAst(q"$a = $value", "v(0) = foo")
188+
}
189+
190+
property("assign or named arg") = test {
191+
val assignx = q"x = 1"
192+
assertEqAst(q"f($assignx)", "f(x = 1)")
193+
}
170194
}

test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,24 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction
9191
matches("new { val early = 1} with Parent[Int] { body }")
9292
matches("new Foo { selfie => }")
9393
}
94+
95+
property("exhaustive assign pattern") = test {
96+
def matches(tree: Tree) { val q"$rhs = $lhs" = tree }
97+
matches(parse("left = right"))
98+
matches(parse("arr(1) = 2"))
99+
matches(AssignOrNamedArg(EmptyTree, EmptyTree))
100+
}
101+
102+
property("deconstruct update 1") = test {
103+
val q"$obj(..$args) = $value" = q"foo(bar) = baz"
104+
assert(obj q"foo")
105+
assert(args List(q"bar"))
106+
assert(value q"baz")
107+
}
108+
109+
property("deconstruct update 2") = test {
110+
val q"$left = $value" = q"foo(bar) = baz"
111+
assert(left q"foo(bar)")
112+
assert(value q"baz")
113+
}
94114
}

0 commit comments

Comments
 (0)