Skip to content

Commit 5feebea

Browse files
committed
Allow new syntax
1 parent c204ab0 commit 5feebea

21 files changed

+132
-109
lines changed

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

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,7 @@ object Parsers {
913913
lookahead.nextToken()
914914
skipParams()
915915
skipParams()
916-
lookahead.isIdent(nme.as)
916+
lookahead.token == COLON || lookahead.isIdent(nme.as)
917917

918918
def followingIsExtension() =
919919
val next = in.lookahead.token
@@ -1281,7 +1281,7 @@ object Parsers {
12811281

12821282
def possibleTemplateStart(isNew: Boolean = false): Unit =
12831283
in.observeColonEOL()
1284-
if in.token == COLONEOL then
1284+
if in.token == COLONEOL || in.token == WITHEOL then
12851285
if in.lookahead.isIdent(nme.end) then in.token = NEWLINE
12861286
else
12871287
in.nextToken()
@@ -3516,6 +3516,17 @@ object Parsers {
35163516
syntaxError(i"extension clause can only define methods", stat.span)
35173517
}
35183518

3519+
def givenConstrApps(): List[Tree] =
3520+
val t = constrApp()
3521+
if in.token == WITH then
3522+
in.observeWithEOL() // converts token to WITHEOL if at end of line
3523+
if in.token == WITH && in.lookahead.token != LBRACE then
3524+
in.nextToken()
3525+
t :: givenConstrApps()
3526+
else t :: Nil
3527+
else
3528+
t :: Nil
3529+
35193530
/** GivenDef ::= [GivenSig] Type ‘=’ Expr
35203531
* | [GivenSig] ConstrApps [TemplateBody]
35213532
* GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘as’
@@ -3535,23 +3546,38 @@ object Parsers {
35353546
else Nil
35363547
newLinesOpt()
35373548
val noParams = tparams.isEmpty && vparamss.isEmpty
3549+
val newSyntax = in.token == COLON
35383550
if !(name.isEmpty && noParams) then
3539-
accept(nme.as)
3540-
val parents = constrApps(commaOK = true)
3541-
if in.token == EQUALS && parents.length == 1 && parents.head.isType then
3551+
if isIdent(nme.as) then in.nextToken()
3552+
else accept(COLON)
3553+
val parents = givenConstrApps()
3554+
val parentsIsType = parents.length == 1 && parents.head.isType
3555+
if in.token == EQUALS && parentsIsType then
35423556
accept(EQUALS)
35433557
mods1 |= Final
35443558
if noParams && !mods.is(Inline) then
35453559
mods1 |= Lazy
35463560
ValDef(name, parents.head, subExpr())
35473561
else
35483562
DefDef(name, tparams, vparamss, parents.head, subExpr())
3563+
else if newSyntax && in.token != WITH && in.token != WITHEOL && parentsIsType then
3564+
if name.isEmpty then
3565+
syntaxError(em"anonymous given cannot be abstract")
3566+
DefDef(name, tparams, vparamss, parents.head, EmptyTree)
35493567
else
3550-
possibleTemplateStart()
35513568
val tparams1 = tparams.map(tparam => tparam.withMods(tparam.mods | PrivateLocal))
35523569
val vparamss1 = vparamss.map(_.map(vparam =>
35533570
vparam.withMods(vparam.mods &~ Param | ParamAccessor | Protected)))
3554-
val templ = templateBodyOpt(makeConstructor(tparams1, vparamss1), parents, Nil)
3571+
val constr = makeConstructor(tparams1, vparamss1)
3572+
val templ =
3573+
if newSyntax || in.token == WITHEOL || in.token == WITH then
3574+
if in.token != WITHEOL then accept(WITH)
3575+
possibleTemplateStart()
3576+
val (self, stats) = templateBody()
3577+
Template(constr, parents, Nil, self, stats)
3578+
else
3579+
possibleTemplateStart()
3580+
templateBodyOpt(makeConstructor(tparams1, vparamss1), parents, Nil)
35553581
if tparams.isEmpty && vparamss.isEmpty then ModuleDef(name, templ)
35563582
else TypeDef(name.toTypeName, templ)
35573583
end gdef

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ object Scanners {
150150
private[Scanners] var allowLeadingInfixOperators = true
151151

152152
var debugTokenStream = false
153+
val showLookAheadOnDebug = false
153154

154155
val rewrite = ctx.settings.rewrite.value.isDefined
155156
val oldSyntax = ctx.settings.oldSyntax.value
@@ -315,7 +316,8 @@ object Scanners {
315316
}
316317

317318
final def printState() =
318-
if debugTokenStream then print("[" + show + "]")
319+
if debugTokenStream && (showLookAheadOnDebug || !isInstanceOf[LookaheadScanner]) then
320+
print(s"[$show${if isInstanceOf[LookaheadScanner] then "(LA)" else ""}]")
319321

320322
/** Insert `token` at assumed `offset` in front of current one. */
321323
def insert(token: Token, offset: Int) = {
@@ -505,12 +507,15 @@ object Scanners {
505507
|Previous indent : $lastWidth
506508
|Latest indent : $nextWidth"""
507509

508-
def observeColonEOL(): Unit =
509-
if token == COLON then
510+
private def switchAtEOL(testToken: Token, eolToken: Token): Unit =
511+
if token == testToken then
510512
lookAhead()
511513
val atEOL = isAfterLineEnd || token == EOF
512514
reset()
513-
if atEOL then token = COLONEOL
515+
if atEOL then token = eolToken
516+
517+
def observeColonEOL(): Unit = switchAtEOL(COLON, COLONEOL)
518+
def observeWithEOL(): Unit = switchAtEOL(WITH, WITHEOL)
514519

515520
def observeIndented(): Unit =
516521
if indentSyntax && isNewLine then

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

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,14 @@ abstract class TokensCommon {
121121
def isKeyword(token: Token): Boolean = keywords contains token
122122

123123
/** parentheses */
124-
final val LPAREN = 90; enter(LPAREN, "'('")
125-
final val RPAREN = 91; enter(RPAREN, "')'")
126-
final val LBRACKET = 92; enter(LBRACKET, "'['")
127-
final val RBRACKET = 93; enter(RBRACKET, "']'")
128-
final val LBRACE = 94; enter(LBRACE, "'{'")
129-
final val RBRACE = 95; enter(RBRACE, "'}'")
130-
final val INDENT = 96; enter(INDENT, "indent")
131-
final val OUTDENT = 97; enter(OUTDENT, "unindent")
124+
final val LPAREN = 91; enter(LPAREN, "'('")
125+
final val RPAREN = 92; enter(RPAREN, "')'")
126+
final val LBRACKET = 93; enter(LBRACKET, "'['")
127+
final val RBRACKET = 94; enter(RBRACKET, "']'")
128+
final val LBRACE = 95; enter(LBRACE, "'{'")
129+
final val RBRACE = 96; enter(RBRACE, "'}'")
130+
final val INDENT = 97; enter(INDENT, "indent")
131+
final val OUTDENT = 98; enter(OUTDENT, "unindent")
132132

133133
final val firstParen = LPAREN
134134
final val lastParen = OUTDENT
@@ -204,10 +204,11 @@ object Tokens extends TokensCommon {
204204
final val QUOTE = 87; enter(QUOTE, "'")
205205

206206
final val COLONEOL = 88; enter(COLONEOL, ":", ": at eol")
207-
final val SELFARROW = 89; enter(SELFARROW, "=>") // reclassified ARROW following self-type
207+
final val WITHEOL = 89; enter(WITHEOL, "with", "with at eol")
208+
final val SELFARROW = 90; enter(SELFARROW, "=>") // reclassified ARROW following self-type
208209

209210
/** XML mode */
210-
final val XMLSTART = 98; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
211+
final val XMLSTART = 99; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
211212

212213
final val alphaKeywords: TokenSet = tokenRange(IF, MACRO)
213214
final val symbolicKeywords: TokenSet = tokenRange(USCORE, CTXARROW)
@@ -276,7 +277,7 @@ object Tokens extends TokensCommon {
276277
final val closingRegionTokens = BitSet(RBRACE, RPAREN, RBRACKET, CASE) | statCtdTokens
277278

278279
final val canStartIndentTokens: BitSet =
279-
statCtdTokens | BitSet(COLONEOL, EQUALS, ARROW, LARROW, WHILE, TRY, FOR, IF)
280+
statCtdTokens | BitSet(COLONEOL, WITHEOL, EQUALS, ARROW, LARROW, WHILE, TRY, FOR, IF)
280281
// `if` is excluded because it often comes after `else` which makes for awkward indentation rules TODO: try to do without the exception
281282

282283
/** Faced with the choice between a type and a formal parameter, the following

tests/neg-custom-args/infix.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class C:
66
def +(x: Int): Int = ???
77

88
object C:
9-
given AnyRef:
9+
given AnyRef with
1010
extension (x: C)
1111
infix def iop (y: Int) = ???
1212
def mop (y: Int) = ???

tests/neg/extmethod-overload.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
object Test {
2-
given a as AnyRef:
2+
given a: AnyRef with
33
extension (x: Int) {
44
def |+| (y: Int) = x + y
55
}
6-
given b as AnyRef:
6+
given b: AnyRef with
77
extension (x: Int) {
88
def |+| (y: String) = x + y.length
99
}

tests/neg/i9928.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ trait Magic[F]:
22
extension (x: Int) def read: F
33

44
object Magic:
5-
given Magic[String]:
5+
given Magic[String] with
66
extension(x: Int) def read: String =
77
println("In string")
88
s"$x"
@@ -12,7 +12,7 @@ object Foo:
1212
import Magic.given
1313
def apply(s: String): Foo = s
1414

15-
given Magic[Foo]:
15+
given Magic[Foo] with
1616
extension (x: Int) def read: Foo =
1717
println("In foo")
1818
Foo(s"$x")

tests/pos/end-given.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
given Conversion[Int, String]:
1+
given Conversion[Int, String] with
22
def apply(x: Int) = ""
33
end given

tests/pos/multi-given.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ trait C
55
def fancy(using a: A, b: B, c: C) = "Fancy!"
66
def foo(implicit a: A, b: B, c: C) = "foo"
77

8-
given A, B {}
8+
given A with B with {}
99

10-
given ops as A with B {}
10+
given ops: A with B with {}

tests/pos/reference/delegates.scala

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ object Instances extends Common:
3030
extension (x: Int) def compareTo(y: Int) =
3131
if (x < y) -1 else if (x > y) +1 else 0
3232

33-
given listOrd[T](using Ord[T]) as Ord[List[T]]:
33+
given listOrd[T](using Ord[T]): Ord[List[T]] with
3434
extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys) match
3535
case (Nil, Nil) => 0
3636
case (Nil, _) => -1
@@ -49,13 +49,13 @@ object Instances extends Common:
4949
def second = xs.tail.head
5050
def third = xs.tail.tail.head
5151

52-
given listMonad as Monad[List]:
52+
given listMonad: Monad[List] with
5353
extension [A, B](xs: List[A]) def flatMap (f: A => List[B]): List[B] =
5454
xs.flatMap(f)
5555
def pure[A](x: A): List[A] =
5656
List(x)
5757

58-
given readerMonad[Ctx] as Monad[[X] =>> Ctx => X]:
58+
given readerMonad[Ctx]: Monad[[X] =>> Ctx => X] with
5959
extension [A, B](r: Ctx => A) def flatMap (f: A => Ctx => B): Ctx => B =
6060
ctx => f(r(ctx))(ctx)
6161
def pure[A](x: A): Ctx => A =
@@ -89,12 +89,11 @@ object Instances extends Common:
8989
type Symbol
9090
trait SymDeco:
9191
extension (sym: Symbol) def name: String
92-
def symDeco: SymDeco
93-
given SymDeco = symDeco
92+
given symDeco: SymDeco
9493

9594
object TastyImpl extends TastyAPI:
9695
type Symbol = String
97-
val symDeco = new SymDeco:
96+
given symDeco: SymDeco with
9897
extension (sym: Symbol) def name = sym
9998

10099
class D[T]
@@ -111,19 +110,19 @@ object Instances extends Common:
111110
println(summon[Context].value)
112111
}
113112
locally {
114-
given d[T] as D[T]
113+
given d[T]: D[T] with {}
115114
println(summon[D[Int]])
116115
}
117116
locally {
118-
given (using Context) as D[Int]
117+
given (using Context): D[Int] with {}
119118
println(summon[D[Int]])
120119
}
121120
end C
122121

123122
class Token(str: String)
124123

125124
object Token:
126-
given StringToToken as Conversion[String, Token]:
125+
given StringToToken: Conversion[String, Token] with
127126
def apply(str: String): Token = new Token(str)
128127

129128
val x: Token = "if"
@@ -141,11 +140,11 @@ object PostConditions:
141140
end PostConditions
142141

143142
object AnonymousInstances extends Common:
144-
given Ord[Int]:
143+
given Ord[Int] with
145144
extension (x: Int) def compareTo(y: Int) =
146145
if (x < y) -1 else if (x > y) +1 else 0
147146

148-
given [T: Ord] as Ord[List[T]]:
147+
given [T: Ord]: Ord[List[T]] with
149148
extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys).match
150149
case (Nil, Nil) => 0
151150
case (Nil, _) => -1
@@ -162,10 +161,11 @@ object AnonymousInstances extends Common:
162161
extension [T](xs: List[T])
163162
def second = xs.tail.head
164163

165-
given [From, To](using c: Convertible[From, To]) as Convertible[List[From], List[To]]:
164+
given [From, To](using c: Convertible[From, To])
165+
: Convertible[List[From], List[To]] with
166166
extension (x: List[From]) def convert: List[To] = x.map(c.convert)
167167

168-
given Monoid[String]:
168+
given Monoid[String] with
169169
extension (x: String) def combine(y: String): String = x.concat(y)
170170
def unit: String = ""
171171

@@ -231,9 +231,9 @@ object Completions:
231231
//
232232
// CompletionArg.from(statusCode)
233233

234-
given fromString as Conversion[String, CompletionArg] = Error(_)
235-
given fromFuture as Conversion[Future[HttpResponse], CompletionArg] = Response(_)
236-
given fromStatusCode as Conversion[Future[StatusCode], CompletionArg] = Status(_)
234+
given fromString : Conversion[String, CompletionArg] = Error(_)
235+
given fromFuture : Conversion[Future[HttpResponse], CompletionArg] = Response(_)
236+
given fromStatusCode: Conversion[Future[StatusCode], CompletionArg] = Status(_)
237237
import CompletionArg._
238238

239239
def complete[T](arg: CompletionArg) = arg match

tests/pos/reference/extension-methods.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ object ExtMethods:
7070
end SafeDiv
7171

7272
def test1 =
73-
given ops1 as IntOps // brings safeMod into scope
73+
given ops1: IntOps with {} // brings safeMod into scope
7474
1.safeMod(2)
7575

7676
class Lst[T](xs: T*):
@@ -81,7 +81,7 @@ object ExtMethods:
8181
trait Ord[T]:
8282
extension (x: T) def less (y: T): Boolean
8383
object Ord:
84-
given Ord[Int]:
84+
given Ord[Int] with
8585
extension (x: Int) def less (y: Int): Boolean = x < y
8686
end Ord
8787

@@ -90,7 +90,7 @@ object ExtMethods:
9090
extension [T](xs: Lst[Lst[T]])
9191
def flatten: Lst[T] = xs.foldLeft(Lst())(_ ++ _)
9292

93-
given ord[T: Ord] as Ord[Lst[T]]:
93+
given ord[T: Ord]: Ord[Lst[T]] with
9494
extension (xs: Lst[T])
9595
def less (ys: Lst[T]): Boolean = ???
9696
end Lst

tests/run/extmethod-overload.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ object Test extends App {
6161
extension [T](xs: List[T]) def +++ (ys: List[T]): List[T] = xs ++ ys ++ ys
6262
extension [T](xs: List[T]) def +++ (ys: Iterator[T]): List[T] = xs ++ ys ++ ys
6363
}
64-
given Bar as Foo
64+
given Bar: Foo with {}
6565

6666
assert((1 |+| 2) == 3)
6767
assert((1 |+| "2") == 2)
@@ -97,7 +97,7 @@ object Test extends App {
9797
extension (x: Int) def yy(y: Int) = x + y
9898
}
9999

100-
given AnyRef:
100+
given AnyRef with
101101
extension (x: Int) {
102102
def yy (y: Int) = x - y
103103
}

tests/run/given-eta.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def g(x: Int)(using d: D) (y: d.T): d.T = d.trans(y)
1212
val x = f
1313
assert(x(2)(3) == 6)
1414

15-
given D:
15+
given D with
1616
type T = Int
1717
def trans(other: T) = 2 * other
1818
val y = g

0 commit comments

Comments
 (0)