Skip to content
Browse files

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
  • Loading branch information...
1 parent b5ef79f commit a1c00ae4b2df051c33384269b6b983746e537aee @paulp paulp committed Dec 17, 2013
View
22 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:"
View
12 src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
@@ -55,20 +55,26 @@ 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)
case _ => List(right)
}
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)
View
4 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
View
5 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
+}
View
9 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)
+}

0 comments on commit a1c00ae

Please sign in to comment.
Something went wrong with that request. Please try again.