diff options
author | Paul Phillips <paulp@improving.org> | 2013-12-17 14:20:49 -0800 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2013-12-17 14:41:42 -0800 |
commit | a1c00ae4b2df051c33384269b6b983746e537aee (patch) | |
tree | 912f47b790250c6d28e6cc7c9a101328190e1753 | |
parent | b5ef79f2f8bc010220d2920a890352d96ad84b45 (diff) | |
download | scala-a1c00ae4b2df051c33384269b6b983746e537aee.tar.gz scala-a1c00ae4b2df051c33384269b6b983746e537aee.tar.bz2 scala-a1c00ae4b2df051c33384269b6b983746e537aee.zip |
Dotless type application for infix operators.
When you have an aesthetic expresion like
def f(xs: Iterator[Int]) = (
xs takeWhile (_ < 1000)
map (_ * -1)
filter (_ % 2 == 0)
flatMap (x => List(x, x))
reduceOption (_ + _)
maxBy (_.toString)
)
And then for whatever reason you have to perform explicit
type application in the midst of that expression, it's
aggravating in the extreme that it has (had) to be rewritten
in its entirety to accommodate that change.
So now you can perform type application in the middle of it.
For reasons not entirely clear to me postfix operators are
excluded. The discussion as well as the approval for the infix
variation of it can be found at:
https://groups.google.com/forum/#!msg/scala-language/eJl1wnkEz9M/hR984-lqC5EJ
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 22 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala | 12 | ||||
-rw-r--r-- | test/files/neg/dotless-targs.check | 4 | ||||
-rw-r--r-- | test/files/neg/dotless-targs.scala | 5 | ||||
-rw-r--r-- | test/files/pos/dotless-targs.scala | 9 |
5 files changed, 41 insertions, 11 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 91db1bb92a..2134058287 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -133,7 +133,7 @@ self => val global: Global import global._ - case class OpInfo(lhs: Tree, operator: TermName, offset: Offset) { + case class OpInfo(lhs: Tree, operator: TermName, targs: List[Tree], offset: Offset) { def precedence = Precedence(operator.toString) } @@ -795,10 +795,13 @@ self => private def opHead = opstack.head private def headPrecedence = opHead.precedence private def popOpInfo(): OpInfo = try opHead finally opstack = opstack.tail - private def pushOpInfo(top: Tree) { - val opinfo = OpInfo(top, in.name, in.offset) - opstack ::= opinfo + private def pushOpInfo(top: Tree): Unit = { + val name = in.name + val offset = in.offset ident() + val targs = if (in.token == LBRACKET) exprTypeArgs() else Nil + val opinfo = OpInfo(top, name, targs, offset) + opstack ::= opinfo } def checkHeadAssoc(leftAssoc: Boolean) = checkAssoc(opHead.offset, opHead.operator, leftAssoc) @@ -808,6 +811,9 @@ self => ) def finishPostfixOp(start: Int, base: List[OpInfo], opinfo: OpInfo): Tree = { + if (opinfo.targs.nonEmpty) + syntaxError(opinfo.offset, "type application is not allowed for postfix operators") + val od = stripParens(reduceExprStack(base, opinfo.lhs)) makePostfixSelect(start, opinfo.offset, od, opinfo.operator) } @@ -817,7 +823,7 @@ self => val operatorPos: Position = Position.range(rhs.pos.source, offset, offset, offset + operator.length) val pos = lhs.pos union rhs.pos union operatorPos withPoint offset - atPos(pos)(makeBinop(isExpr, lhs, operator, rhs, operatorPos)) + atPos(pos)(makeBinop(isExpr, lhs, operator, rhs, operatorPos, opinfo.targs)) } def reduceExprStack(base: List[OpInfo], top: Tree): Tree = reduceStack(isExpr = true, base, top) @@ -1893,9 +1899,9 @@ self => def isDelimiter = in.token == RPAREN || in.token == RBRACE def isCommaOrDelimiter = isComma || isDelimiter val (isUnderscore, isStar) = opstack match { - case OpInfo(Ident(nme.WILDCARD), nme.STAR, _) :: _ => (true, true) - case OpInfo(_, nme.STAR, _) :: _ => (false, true) - case _ => (false, false) + case OpInfo(Ident(nme.WILDCARD), nme.STAR, _, _) :: _ => (true, true) + case OpInfo(_, nme.STAR, _, _) :: _ => (false, true) + case _ => (false, false) } def isSeqPatternClose = isUnderscore && isStar && isSequenceOK && isDelimiter val preamble = "bad simple pattern:" diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index d88470bd5e..cfee988efc 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -55,7 +55,13 @@ abstract class TreeBuilder { ValDef(Modifiers(PRIVATE), name, tpt, EmptyTree) /** Create tree representing (unencoded) binary operation expression or pattern. */ - def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position): Tree = { + def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position, targs: List[Tree] = Nil): Tree = { + require(isExpr || targs.isEmpty, s"Incompatible args to makeBinop: !isExpr but targs=$targs") + + def mkSelection(t: Tree) = { + def sel = atPos(opPos union t.pos)(Select(stripParens(t), op.encode)) + if (targs.isEmpty) sel else atPos(left.pos)(TypeApply(sel, targs)) + } def mkNamed(args: List[Tree]) = if (isExpr) args map treeInfo.assignmentToMaybeNamedArg else args val arguments = right match { case Parens(args) => mkNamed(args) @@ -63,12 +69,12 @@ abstract class TreeBuilder { } if (isExpr) { if (treeInfo.isLeftAssoc(op)) { - Apply(atPos(opPos union left.pos) { Select(stripParens(left), op.encode) }, arguments) + Apply(mkSelection(left), arguments) } else { val x = freshTermName() Block( List(ValDef(Modifiers(SYNTHETIC | ARTIFACT), x, TypeTree(), stripParens(left))), - Apply(atPos(opPos union right.pos) { Select(stripParens(right), op.encode) }, List(Ident(x)))) + Apply(mkSelection(right), List(Ident(x)))) } } else { Apply(Ident(op.encode), stripParens(left) :: arguments) diff --git a/test/files/neg/dotless-targs.check b/test/files/neg/dotless-targs.check new file mode 100644 index 0000000000..4aab939f61 --- /dev/null +++ b/test/files/neg/dotless-targs.check @@ -0,0 +1,4 @@ +dotless-targs.scala:2: error: type application is not allowed for postfix operators + def f1 = "f1" isInstanceOf[String] // not ok + ^ +one error found diff --git a/test/files/neg/dotless-targs.scala b/test/files/neg/dotless-targs.scala new file mode 100644 index 0000000000..eff63cbec4 --- /dev/null +++ b/test/files/neg/dotless-targs.scala @@ -0,0 +1,5 @@ +class A { + def f1 = "f1" isInstanceOf[String] // not ok + def f2 = "f2".isInstanceOf[String] // ok + def f3 = "f3" toList // ok +} diff --git a/test/files/pos/dotless-targs.scala b/test/files/pos/dotless-targs.scala new file mode 100644 index 0000000000..8c0e244e4e --- /dev/null +++ b/test/files/pos/dotless-targs.scala @@ -0,0 +1,9 @@ +class A { + def fn1 = List apply 1 + def fn2 = List apply[Int] 2 + + def g1: Char = "g1" toList 0 + def g2: Char = "g2" apply 1 + + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) +} |