Skip to content

Commit 67be15a

Browse files
committed
Change fewer-braces parsing to allow inline lambdas
- Allow lambda arguments on one line # Conflicts: # compiler/src/dotty/tools/dotc/parsing/Parsers.scala # docs/_docs/internals/syntax.md # tests/neg/closure-args.scala # tests/pos/closure-args.scala
1 parent 8a09181 commit 67be15a

File tree

5 files changed

+51
-44
lines changed

5 files changed

+51
-44
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -936,24 +936,17 @@ object Parsers {
936936

937937
/** Is the token sequence following the current `:` token classified as a lambda?
938938
* This is the case if the input starts with an identifier, a wildcard, or
939-
* something enclosed in (...) or [...], and this is followed by a `=>` or `?=>`
940-
* and an INDENT.
939+
* something enclosed in (...) or [...], and this is followed by a `=>` or `?=>`.
941940
*/
942941
def followingIsLambdaAfterColon(): Boolean =
943-
val lookahead = in.LookaheadScanner(allowIndent = true)
944-
def isArrowIndent() =
945-
lookahead.isArrow
946-
&& {
947-
lookahead.nextToken()
948-
lookahead.token == INDENT
949-
}
942+
val lookahead = in.LookaheadScanner()
950943
lookahead.nextToken()
951944
if lookahead.isIdent || lookahead.token == USCORE then
952945
lookahead.nextToken()
953-
isArrowIndent()
946+
lookahead.isArrow
954947
else if lookahead.token == LPAREN || lookahead.token == LBRACKET then
955948
lookahead.skipParens()
956-
isArrowIndent()
949+
lookahead.isArrow
957950
else false
958951

959952
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
@@ -2255,7 +2248,13 @@ object Parsers {
22552248
accept(ARROW)
22562249
val body =
22572250
if location == Location.InBlock then block()
2258-
else if location == Location.InColonArg && in.token == INDENT then blockExpr()
2251+
else if location == Location.InColonArg then
2252+
if in.token == INDENT then
2253+
blockExpr()
2254+
else
2255+
in.insertMaxIndent()
2256+
try casesOrBlock(simplify = true)
2257+
finally accept(OUTDENT)
22592258
else expr()
22602259
Function(params, body)
22612260
}
@@ -2312,6 +2311,7 @@ object Parsers {
23122311
* | FunParams (‘=>’ | ‘?=>’) ColonArgBody
23132312
* | HkTypeParamClause ‘=>’ ColonArgBody
23142313
* ColonArgBody ::= indent (CaseClauses | Block) outdent
2314+
* | <silent-indent> (CaseClauses | Block) outdent -- silent-indent is inserted by Scanner if no real indent is found
23152315
* Quoted ::= ‘'’ ‘{’ Block ‘}’
23162316
* | ‘'’ ‘[’ Type ‘]’
23172317
*/
@@ -2502,12 +2502,13 @@ object Parsers {
25022502
*/
25032503
def blockExpr(): Tree = atSpan(in.offset) {
25042504
val simplify = in.token == INDENT
2505-
inDefScopeBraces {
2506-
if (in.token == CASE) Match(EmptyTree, caseClauses(() => caseClause()))
2507-
else block(simplify)
2508-
}
2505+
inDefScopeBraces { casesOrBlock(simplify) }
25092506
}
25102507

2508+
def casesOrBlock(simplify: Boolean): Tree =
2509+
if (in.token == CASE) Match(EmptyTree, caseClauses(() => caseClause()))
2510+
else block(simplify)
2511+
25112512
/** Block ::= BlockStatSeq
25122513
* @note Return tree does not have a defined span.
25132514
*/

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -618,9 +618,13 @@ object Scanners {
618618
if next.token != COLON then
619619
handleNewIndentWidth(r.enclosing, ir =>
620620
errorButContinue(
621-
i"""The start of this line does not match any of the previous indentation widths.
622-
|Indentation width of current line : $nextWidth
623-
|This falls between previous widths: ${ir.width} and $lastWidth"""))
621+
if r.indentWidth == IndentWidth.Max then
622+
i"""Enclosing expression is nested in a one line lambda expression following `:`.
623+
|It may not spill over to a new line."""
624+
else
625+
i"""The start of this line does not match any of the previous indentation widths.
626+
|Indentation width of current line : $nextWidth
627+
|This falls between previous widths: ${ir.width} and $lastWidth"""))
624628
case r =>
625629
if skipping then
626630
if r.enclosing.isClosedByUndentAt(nextWidth) then
@@ -661,7 +665,9 @@ object Scanners {
661665
currentRegion = Indented(nextWidth, COLONEOL, currentRegion)
662666
offset = next.offset
663667
token = INDENT
664-
end observeIndented
668+
669+
def insertMaxIndent() =
670+
currentRegion = Indented(IndentWidth.Max, ARROW, currentRegion)
665671

666672
/** Insert an <outdent> token if next token closes an indentation region.
667673
* Exception: continue if indentation region belongs to a `match` and next token is `case`.
@@ -1651,25 +1657,29 @@ object Scanners {
16511657
enum IndentWidth {
16521658
case Run(ch: Char, n: Int)
16531659
case Conc(l: IndentWidth, r: Run)
1660+
case Max // delimits one-line lambdas following a `:`
16541661

1655-
def <= (that: IndentWidth): Boolean = this match {
1662+
def <= (that: IndentWidth): Boolean = this match
16561663
case Run(ch1, n1) =>
1657-
that match {
1664+
that match
16581665
case Run(ch2, n2) => n1 <= n2 && (ch1 == ch2 || n1 == 0)
16591666
case Conc(l, r) => this <= l
1660-
}
1667+
case Max => true
16611668
case Conc(l1, r1) =>
16621669
that match {
16631670
case Conc(l2, r2) => l1 == l2 && r1 <= r2
1671+
case Max => true
16641672
case _ => false
16651673
}
1666-
}
1674+
case Max =>
1675+
that == Max
16671676

16681677
def < (that: IndentWidth): Boolean = this <= that && !(that <= this)
16691678

16701679
def toPrefix: String = this match {
16711680
case Run(ch, n) => ch.toString * n
16721681
case Conc(l, r) => l.toPrefix ++ r.toPrefix
1682+
case Max => "(max >>)"
16731683
}
16741684

16751685
override def toString: String = {
@@ -1681,6 +1691,7 @@ object Scanners {
16811691
this match {
16821692
case Run(ch, n) => s"$n ${kind(ch)}${if (n == 1) "" else "s"}"
16831693
case Conc(l, r) => s"$l, $r"
1694+
case Max => "(max >>)"
16841695
}
16851696
}
16861697
}

docs/_docs/internals/syntax.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ ColonArgument ::= indent CaseClauses | Block outdent
261261
| FunParams (‘=>’ | ‘?=>’) ColonArgBody
262262
| HkTypeParamClause ‘=>’ ColonArgBody
263263
ColonArgBody ::= indent (CaseClauses | Block) outdent
264+
| <silent-indent> (CaseClauses | Block) outdent -- silent-indent is inserted by Lexer if no real indent is found
264265
ExprsInParens ::= ExprInParens {‘,’ ExprInParens}
265266
ExprInParens ::= PostfixExpr ‘:’ Type -- normal Expr allows only RefinedType here
266267
| Expr

tests/neg/closure-args.scala

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,7 @@ val z = List().map: + => // ok
88
???
99

1010
val xs = List(1)
11-
val b: Int = xs // error
12-
.map: x => x * x // error
13-
.filter: y => y > 0 // error
14-
(0)
15-
val d = xs // error
11+
val d = xs
1612
.map: x => x.toString + xs.dropWhile:
17-
y => y > 0
13+
y => y > 0 // error
1814

19-
val c = List(xs.map: y => y + y) // error // error
20-
val d2: String = xs // error
21-
.map: x => x.toString + xs.dropWhile: y => y > 0 // error // error
22-
.filter: z => !z.isEmpty // error
23-
(0)
24-
25-
val fs: List[List[Int] => Int] = xs.map: x => case y :: ys => y case Nil => -1 // error // error

tests/pos/closure-args.scala

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,24 @@ object Test1:
1515
.filter: (y: Int) =>
1616
y > 0
1717
(0)
18+
val b: Int = xs
19+
.map: x => x * x
20+
.filter: y => y > 0
21+
(0)
22+
val c = List(xs.map: y => y + y)
23+
val d: String = xs
24+
.map: x => x.toString + xs.dropWhile: y => y > 0
25+
.filter: z => !z.isEmpty
26+
(0)
1827
val e = xs.map:
1928
case 1 => 2
2029
case 2 => 3
2130
case x => x
22-
.filter:
23-
x => x > 0
31+
.filter: x => x > 0
32+
val fs: List[List[Int] => Int] = xs.map: x => case y :: ys => y case Nil => -1
2433

2534
extension (xs: List[Int]) def foo(f: [X] => X => X) = ()
2635

27-
val p = xs.foo:
28-
[X] => (x: X) => x
36+
val p = xs.foo: [X] => (x: X) => x
2937

3038
val q = (x: String => String) => x
31-
32-
val r = x < 0 && :
33-
y > 0

0 commit comments

Comments
 (0)