Skip to content

Commit 02617be

Browse files
committed
Change match syntax
`match` is treated like an alphabetic operator, can be appear infix as well as after `.`. Change the `inline` syntax from ``` inline if ... inline s match ... ``` to ``` if inline ... s match inline ... ``` this makes it work better with the new rules for `match`.
1 parent 14e8847 commit 02617be

File tree

8 files changed

+76
-54
lines changed

8 files changed

+76
-54
lines changed

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

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -951,7 +951,7 @@ object Parsers {
951951
recur(top)
952952
}
953953

954-
/** operand { infixop operand | ‘given’ (operand | ParArgumentExprs) } [postfixop],
954+
/** operand { infixop operand | MatchClause } [postfixop],
955955
*
956956
* respecting rules of associativity and precedence.
957957
* @param isOperator the current token counts as an operator.
@@ -981,18 +981,14 @@ object Parsers {
981981
}
982982
else recur(operand())
983983
}
984-
else reduceStack(base, top, minPrec, leftAssoc = true, in.name, isType)
984+
else
985+
val t = reduceStack(base, top, minPrec, leftAssoc = true, in.name, isType)
986+
if !isType && in.token == MATCH then recur(matchClause(t, t.span.start, Match))
987+
else t
985988

986989
recur(first)
987990
}
988991

989-
def applyGiven(t: Tree, operand: () => Tree): Tree =
990-
atSpan(startOffset(t), in.offset) {
991-
in.nextToken()
992-
val args = if (in.token == LPAREN) parArgumentExprs()._1 else operand() :: Nil
993-
Apply(t, args)
994-
}.setGivenApply()
995-
996992
/* -------- IDENTIFIERS AND LITERALS ------------------------------------------- */
997993

998994
/** Accept identifier and return its name as a term name. */
@@ -1028,9 +1024,12 @@ object Parsers {
10281024
def wildcardIdent(): Ident =
10291025
atSpan(accept(USCORE)) { Ident(nme.WILDCARD) }
10301026

1031-
/** Accept identifier acting as a selector on given tree `t`. */
1027+
/** Accept identifier or match clause acting as a selector on given tree `t` */
10321028
def selector(t: Tree): Tree =
1033-
atSpan(startOffset(t), in.offset) { Select(t, ident()) }
1029+
atSpan(startOffset(t), in.offset) {
1030+
if in.token == MATCH then matchClause(t, t.span.start, Match)
1031+
else Select(t, ident())
1032+
}
10341033

10351034
/** Selectors ::= id { `.' id }
10361035
*
@@ -1877,7 +1876,7 @@ object Parsers {
18771876
}
18781877
}
18791878
case _ =>
1880-
if isIdent(nme.inline)
1879+
if isIdent(nme.inline) // TODO: drop this clause
18811880
&& !in.inModifierPosition()
18821881
&& in.lookaheadIn(in.canStartExprTokens)
18831882
then
@@ -1886,31 +1885,27 @@ object Parsers {
18861885
case IF =>
18871886
ifExpr(start, InlineIf)
18881887
case _ =>
1889-
val t = postfixExpr()
1890-
if (in.token == MATCH) matchExpr(t, start, InlineMatch)
1888+
val t = prefixExpr()
1889+
if (in.token == MATCH) matchClause(t, start, InlineMatch)
18911890
else
1892-
syntaxErrorOrIncomplete(i"`match` or `if` expected but ${in.token} found")
1891+
syntaxErrorOrIncomplete(i"`match` or `if` expected but ${in} found")
18931892
t
18941893
else expr1Rest(postfixExpr(), location)
18951894
}
18961895

1897-
def expr1Rest(t: Tree, location: Location.Value): Tree = in.token match {
1896+
def expr1Rest(t: Tree, location: Location.Value): Tree = in.token match
18981897
case EQUALS =>
1899-
t match {
1900-
case Ident(_) | Select(_, _) | Apply(_, _) =>
1901-
atSpan(startOffset(t), in.skipToken()) { Assign(t, subExpr()) }
1902-
case _ =>
1903-
t
1904-
}
1898+
t match
1899+
case Ident(_) | Select(_, _) | Apply(_, _) =>
1900+
atSpan(startOffset(t), in.skipToken()) { Assign(t, subExpr()) }
1901+
case _ =>
1902+
t
19051903
case COLON =>
19061904
in.nextToken()
1907-
val t1 = ascription(t, location)
1908-
if (in.token == MATCH) expr1Rest(t1, location) else t1
1909-
case MATCH =>
1910-
matchExpr(t, startOffset(t), Match)
1905+
ascription(t, location)
19111906
case _ =>
19121907
t
1913-
}
1908+
end expr1Rest
19141909

19151910
def ascription(t: Tree, location: Location.Value): Tree = atSpan(startOffset(t)) {
19161911
in.token match {
@@ -1938,11 +1933,14 @@ object Parsers {
19381933
}
19391934
}
19401935

1941-
/** `if' `(' Expr `)' {nl} Expr [[semi] else Expr]
1942-
* `if' Expr `then' Expr [[semi] else Expr]
1936+
/** `if' [‘inline’] `(' Expr `)' {nl} Expr [[semi] else Expr]
1937+
* `if' [‘inline’] Expr `then' Expr [[semi] else Expr]
19431938
*/
1944-
def ifExpr(start: Offset, mkIf: (Tree, Tree, Tree) => If): If =
1939+
def ifExpr(start: Offset, mkIf0: (Tree, Tree, Tree) => If): If =
19451940
atSpan(start, in.skipToken()) {
1941+
val mkIf =
1942+
if isIdent(nme.inline) then { in.nextToken(); InlineIf }
1943+
else mkIf0
19461944
val cond = condExpr(THEN)
19471945
newLinesOpt()
19481946
val thenp = subExpr()
@@ -1951,11 +1949,14 @@ object Parsers {
19511949
mkIf(cond, thenp, elsep)
19521950
}
19531951

1954-
/** `match' `{' CaseClauses `}'
1952+
/** MatchClause ::= `match' [‘inline’] `{' CaseClauses `}'
19551953
*/
1956-
def matchExpr(t: Tree, start: Offset, mkMatch: (Tree, List[CaseDef]) => Match) =
1954+
def matchClause(t: Tree, start: Offset, mkMatch0: (Tree, List[CaseDef]) => Match) =
19571955
in.endMarkerScope(MATCH) {
19581956
atSpan(start, in.skipToken()) {
1957+
val mkMatch =
1958+
if isIdent(nme.inline) then { in.nextToken(); InlineMatch }
1959+
else mkMatch0
19591960
mkMatch(t, inBracesOrIndented(caseClauses(caseClause)))
19601961
}
19611962
}
@@ -2032,7 +2033,7 @@ object Parsers {
20322033
/** PostfixExpr ::= InfixExpr [id [nl]]
20332034
* InfixExpr ::= PrefixExpr
20342035
* | InfixExpr id [nl] InfixExpr
2035-
* | InfixExpr ‘given’ (InfixExpr | ParArgumentExprs)
2036+
* | InfixExpr MatchClause
20362037
*/
20372038
def postfixExpr(): Tree = postfixExprRest(prefixExpr())
20382039

@@ -2063,6 +2064,7 @@ object Parsers {
20632064
* | Path
20642065
* | `(' [ExprsInParens] `)'
20652066
* | SimpleExpr `.' id
2067+
* | SimpleExpr `.' MatchClause
20662068
* | SimpleExpr (TypeArgs | NamedTypeArgs)
20672069
* | SimpleExpr1 ArgumentExprs
20682070
* Quoted ::= ‘'’ ‘{’ Block ‘}’

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,8 @@ object Scanners {
525525
else if (lastWidth != nextWidth)
526526
errorButContinue(spaceTabMismatchMsg(lastWidth, nextWidth))
527527
currentRegion match {
528-
case Indented(curWidth, others, prefix, outer) if curWidth < nextWidth && !others.contains(nextWidth) =>
528+
case Indented(curWidth, others, prefix, outer)
529+
if curWidth < nextWidth && !others.contains(nextWidth) && nextWidth != lastWidth =>
529530
if (token == OUTDENT)
530531
errorButContinue(
531532
i"""The start of this line does not match any of the previous indentation widths.

docs/docs/internals/syntax.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,8 @@ BlockResult ::= [‘implicit’] FunParams ‘=>’ Block
184184
FunParams ::= Bindings
185185
| id
186186
| ‘_’
187-
Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl}
188-
Expr [[semi] ‘else’ Expr] If(Parens(cond), thenp, elsep?)
189-
| ‘if’ Expr ‘then’ Expr [[semi] ‘else’ Expr] If(cond, thenp, elsep?)
187+
Expr1 ::= ‘if’ [‘inline’]‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] If(Parens(cond), thenp, elsep?)
188+
| ‘if’ [‘inline’] Expr ‘then’ Expr [[semi] ‘else’ Expr] If(cond, thenp, elsep?)
190189
| ‘while’ ‘(’ Expr ‘)’ {nl} Expr WhileDo(Parens(cond), body)
191190
| ‘while’ Expr ‘do’ Expr WhileDo(cond, body)
192191
| ‘try’ Expr Catches [‘finally’ Expr] Try(expr, catches, expr?)
@@ -198,15 +197,15 @@ Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl}
198197
| [SimpleExpr ‘.’] id ‘=’ Expr Assign(expr, expr)
199198
| SimpleExpr1 ArgumentExprs ‘=’ Expr Assign(expr, expr)
200199
| Expr2
201-
| [‘inline’] Expr2 ‘match’ ‘{’ CaseClauses ‘}’ Match(expr, cases) -- point on match
202200
Expr2 ::= PostfixExpr [Ascription]
203201
Ascription ::= ‘:’ InfixType Typed(expr, tp)
204202
| ‘:’ Annotation {Annotation} Typed(expr, Annotated(EmptyTree, annot)*)
205203
Catches ::= ‘catch’ (Expr | CaseClause)
206204
PostfixExpr ::= InfixExpr [id] PostfixOp(expr, op)
207205
InfixExpr ::= PrefixExpr
208206
| InfixExpr id [nl] InfixExpr InfixOp(expr, op, expr)
209-
| InfixExpr ‘given’ (InfixExpr | ParArgumentExprs)
207+
| InfixExpr MatchClause
208+
MatchClause ::= ‘match’ [‘inline’] ‘{’ CaseClauses ‘}’ Match(expr, cases)
210209
PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr PrefixOp(expr, op)
211210
SimpleExpr ::= Path
212211
| Literal
@@ -219,6 +218,7 @@ SimpleExpr ::= Path
219218
| ‘new’ TemplateBody
220219
| ‘(’ ExprsInParens ‘)’ Parens(exprs)
221220
| SimpleExpr ‘.’ id Select(expr, id)
221+
| SimpleExpr ‘.’ MatchClause
222222
| SimpleExpr TypeArgs TypeApply(expr, args)
223223
| SimpleExpr ArgumentExprs Apply(expr, args)
224224
| SimpleExpr ‘_’ PostfixOp(expr, _)

docs/docs/reference/contextual/typeclasses.md

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,22 @@ with canonical implementations defined by given instances. Here are some example
1010
### Semigroups and monoids:
1111

1212
```scala
13-
trait SemiGroup[T] {
13+
trait SemiGroup[T] with
1414
def (x: T) combine (y: T): T
15-
}
16-
trait Monoid[T] extends SemiGroup[T] {
15+
16+
trait Monoid[T] extends SemiGroup[T] with
1717
def unit: T
18-
}
19-
object Monoid {
18+
19+
object Monoid with
2020
def apply[T](given Monoid[T]) = summon[Monoid[T]]
21-
}
2221

23-
given Monoid[String] {
22+
given Monoid[String] with
2423
def (x: String) combine (y: String): String = x.concat(y)
2524
def unit: String = ""
26-
}
2725

28-
given Monoid[Int] {
26+
given Monoid[Int] with
2927
def (x: Int) combine (y: Int): Int = x + y
3028
def unit: Int = 0
31-
}
3229

3330
def sum[T: Monoid](xs: List[T]): T =
3431
xs.foldLeft(Monoid[T].unit)(_.combine(_))
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
object Test {
2-
List(1: @unchecked, 2, 3): @unchecked match {
2+
(List(1: @unchecked, 2, 3): @unchecked) match {
33
case a :: as =>
44
}
55
}

tests/pos/matches.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package matches
2+
object Test with
3+
2 min 3 match
4+
case 2 => "OK"
5+
case 3 => "?"
6+
match
7+
case "OK" =>
8+
case "?" => assertFail()
9+
10+
val x = 4
11+
if 2 < 3
12+
|| x.match
13+
case 4 => true
14+
case _ => false
15+
|| (2 + 2).match
16+
case 4 => true
17+
case _ => false
18+
then
19+
println("ok")
20+
end Test

tests/pos/multi-given.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ trait C
44

55
def fancy(given a: A, b: B, c: C) = "Fancy!"
66
def foo(implicit a: A, b: B, c: C) = "foo"
7+
8+
given A, B {}

tests/run/typeclass-derivation2.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ object Pickler {
293293
}
294294

295295
inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Mirror, n: Int): Unit =
296-
inline erasedValue[Elems] match {
296+
erasedValue[Elems] match inline {
297297
case _: (elem *: elems1) =>
298298
tryPickle[elem](buf, elems(n).asInstanceOf[elem])
299299
pickleElems[elems1](buf, elems, n + 1)
@@ -304,9 +304,9 @@ object Pickler {
304304
pickleElems[Elems](buf, r.reflect(x), 0)
305305

306306
inline def pickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit =
307-
inline erasedValue[Alts] match {
307+
erasedValue[Alts] match inline {
308308
case _: (Shape.Case[alt, elems] *: alts1) =>
309-
inline typeOf[alt] match {
309+
typeOf[alt] match inline {
310310
case _: Subtype[T] =>
311311
x match {
312312
case x: `alt` =>
@@ -326,7 +326,7 @@ object Pickler {
326326
}
327327

328328
inline def unpickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Array[AnyRef], n: Int): Unit =
329-
inline erasedValue[Elems] match {
329+
erasedValue[Elems] match inline {
330330
case _: (elem *: elems1) =>
331331
elems(n) = tryUnpickle[elem](buf).asInstanceOf[AnyRef]
332332
unpickleElems[elems1](buf, elems, n + 1)

0 commit comments

Comments
 (0)