Skip to content

Commit 2e5df79

Browse files
committed
Refactor remaining statement and declaration loops
1 parent 0214700 commit 2e5df79

16 files changed

+82
-89
lines changed

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

Lines changed: 37 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,11 @@ object Parsers {
311311
def acceptStatSep(): Unit =
312312
if in.isNewLine then in.nextToken() else accept(SEMI)
313313

314-
def exitStats[T <: Tree](stats: ListBuffer[T], noPrevStat: Boolean, altEnd: Token = EOF, what: String = "statement"): Boolean =
314+
/** Parse statement separators and end markers. Ensure that there is at least
315+
* one statement separator unless the next token terminates a statement sequence.
316+
* @return true if the statement sequence continues, false if it terminates.
317+
*/
318+
def statSepOrEnd[T <: Tree](stats: ListBuffer[T], noPrevStat: Boolean = false, what: String = "statement", altEnd: Token = EOF): Boolean =
315319
def recur(sepSeen: Boolean, endSeen: Boolean): Boolean =
316320
if isStatSep then
317321
in.nextToken()
@@ -321,42 +325,25 @@ object Parsers {
321325
checkEndMarker(stats)
322326
recur(sepSeen, true)
323327
else if isStatSeqEnd || in.token == altEnd then
324-
true
325-
else if sepSeen || endSeen then
326328
false
329+
else if sepSeen || endSeen then
330+
true
327331
else
328332
val found = in.token
329333
val statFollows = mustStartStatTokens.contains(found)
330334
syntaxError(
331335
if noPrevStat then IllegalStartOfStatement(what, isModifier, statFollows)
332336
else i"end of $what expected but ${showToken(found)} found")
333337
if mustStartStatTokens.contains(found) then
334-
true // it's a statement that might be legal in an outer context
338+
false // it's a statement that might be legal in an outer context
335339
else
336340
in.nextToken() // needed to ensure progress; otherwise we might cycle forever
337341
skip()
338-
false
342+
true
339343

340344
in.observeOutdented()
341345
recur(false, false)
342-
end exitStats
343-
344-
def acceptStatSepUnlessAtEnd[T <: Tree](stats: ListBuffer[T], altEnd: Token = EOF): Unit =
345-
def skipEmptyStats(): Unit =
346-
while (in.token == SEMI || in.token == NEWLINE || in.token == NEWLINES) do in.nextToken()
347-
348-
in.observeOutdented()
349-
in.token match
350-
case SEMI | NEWLINE | NEWLINES =>
351-
skipEmptyStats()
352-
checkEndMarker(stats)
353-
skipEmptyStats()
354-
case `altEnd` =>
355-
case _ =>
356-
if !isStatSeqEnd then
357-
syntaxError(i"end of statement expected but ${showToken(in.token)} found")
358-
in.nextToken() // needed to ensure progress; otherwise we might cycle forever
359-
accept(SEMI)
346+
end statSepOrEnd
360347

361348
def rewriteNotice(version: String = "3.0", additionalOption: String = "") = {
362349
val optionStr = if (additionalOption.isEmpty) "" else " " ++ additionalOption
@@ -3642,10 +3629,10 @@ object Parsers {
36423629
*/
36433630
def extMethods(numLeadParams: Int): List[DefDef] = checkNoEscapingPlaceholders {
36443631
val meths = new ListBuffer[DefDef]
3645-
val exitOnError = false
3646-
while !isStatSeqEnd && !exitOnError do
3632+
while
36473633
meths += extMethod(numLeadParams)
3648-
acceptStatSepUnlessAtEnd(meths)
3634+
statSepOrEnd(meths, what = "extension method")
3635+
do ()
36493636
if meths.isEmpty then syntaxError("`def` expected")
36503637
meths.toList
36513638
}
@@ -3781,7 +3768,8 @@ object Parsers {
37813768
*/
37823769
def topStatSeq(outermost: Boolean = false): List[Tree] = {
37833770
val stats = new ListBuffer[Tree]
3784-
while (!isStatSeqEnd) {
3771+
while
3772+
var empty = false
37853773
if (in.token == PACKAGE) {
37863774
val start = in.skipToken()
37873775
if (in.token == OBJECT) {
@@ -3798,13 +3786,10 @@ object Parsers {
37983786
stats += extension()
37993787
else if isDefIntro(modifierTokens) then
38003788
stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens))
3801-
else if !isStatSep then
3802-
if (in.token == CASE)
3803-
syntaxErrorOrIncomplete(OnlyCaseClassOrCaseObjectAllowed())
3804-
else
3805-
syntaxErrorOrIncomplete(ExpectedToplevelDef())
3806-
acceptStatSepUnlessAtEnd(stats)
3807-
}
3789+
else
3790+
empty = true
3791+
statSepOrEnd(stats, empty, "toplevel definition")
3792+
do ()
38083793
stats.toList
38093794
}
38103795

@@ -3836,13 +3821,12 @@ object Parsers {
38363821
in.token = SELFARROW // suppresses INDENT insertion after `=>`
38373822
in.nextToken()
38383823
}
3839-
else {
3824+
else
38403825
stats += first
3841-
acceptStatSepUnlessAtEnd(stats)
3842-
}
3826+
statSepOrEnd(stats)
38433827
}
3844-
var exitOnError = false
3845-
while (!isStatSeqEnd && !exitOnError) {
3828+
while
3829+
var empty = false
38463830
if (in.token == IMPORT)
38473831
stats ++= importClause(IMPORT, mkImport())
38483832
else if (in.token == EXPORT)
@@ -3853,12 +3837,10 @@ object Parsers {
38533837
stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens))
38543838
else if (isExprIntro)
38553839
stats += expr1()
3856-
else if (!isStatSep) {
3857-
exitOnError = mustStartStat
3858-
syntaxErrorOrIncomplete("illegal start of definition")
3859-
}
3860-
acceptStatSepUnlessAtEnd(stats)
3861-
}
3840+
else
3841+
empty = true
3842+
statSepOrEnd(stats, empty)
3843+
do ()
38623844
(self, if (stats.isEmpty) List(EmptyTree) else stats.toList)
38633845
}
38643846

@@ -3887,16 +3869,14 @@ object Parsers {
38873869
if problem.isEmpty then tree :: Nil
38883870
else { syntaxError(problem, tree.span); Nil }
38893871

3890-
while (!isStatSeqEnd) {
3891-
if (isDclIntro)
3872+
while
3873+
val dclFound = isDclIntro
3874+
if dclFound then
38923875
stats ++= checkLegal(defOrDcl(in.offset, Modifiers()))
3893-
else if (!isStatSep)
3894-
syntaxErrorOrIncomplete(
3895-
"illegal start of declaration" +
3896-
(if (inFunReturnType) " (possible cause: missing `=` in front of current method body)"
3897-
else ""))
3898-
acceptStatSepUnlessAtEnd(stats)
3899-
}
3876+
var what = "declaration"
3877+
if inFunReturnType then what += " (possible cause: missing `=` in front of current method body)"
3878+
statSepOrEnd(stats, !dclFound, what)
3879+
do ()
39003880
stats.toList
39013881
}
39023882

@@ -3934,7 +3914,7 @@ object Parsers {
39343914
stats +++= localDef(in.offset)
39353915
else
39363916
empty = true
3937-
!exitStats(stats, empty, CASE)
3917+
statSepOrEnd(stats, empty, altEnd = CASE)
39383918
do ()
39393919
stats.toList
39403920
}
@@ -3952,7 +3932,7 @@ object Parsers {
39523932
in.nextToken()
39533933
ts += objectDef(start, Modifiers(Package))
39543934
if (in.token != EOF) {
3955-
acceptStatSepUnlessAtEnd(ts)
3935+
statSepOrEnd(ts, what = "toplevel definition")
39563936
ts ++= topStatSeq()
39573937
}
39583938
}
@@ -3969,7 +3949,7 @@ object Parsers {
39693949
acceptStatSep()
39703950
ts += makePackaging(start, pkg, topstats())
39713951
if continue then
3972-
acceptStatSepUnlessAtEnd(ts)
3952+
statSepOrEnd(ts, what = "toplevel definition")
39733953
ts ++= topStatSeq()
39743954
}
39753955
else

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] {
8787
ValueClassesMayNotWrapAnotherValueClassID,
8888
ValueClassParameterMayNotBeAVarID,
8989
ValueClassNeedsExactlyOneValParamID,
90-
OnlyCaseClassOrCaseObjectAllowedID,
91-
ExpectedTopLevelDefID,
90+
UNUSED1,
91+
UNUSED2,
9292
AnonymousFunctionMissingParamTypeID,
9393
SuperCallsNotAllowedInlineableID,
9494
NotAPathID,

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,18 +1656,6 @@ import transform.SymUtils._
16561656
def explain = ""
16571657
}
16581658

1659-
class OnlyCaseClassOrCaseObjectAllowed()(using Context)
1660-
extends SyntaxMsg(OnlyCaseClassOrCaseObjectAllowedID) {
1661-
def msg = em"""Only ${hl("case class")} or ${hl("case object")} allowed"""
1662-
def explain = ""
1663-
}
1664-
1665-
class ExpectedToplevelDef()(using Context)
1666-
extends SyntaxMsg(ExpectedTopLevelDefID) {
1667-
def msg = "Expected a toplevel definition"
1668-
def explain = ""
1669-
}
1670-
16711659
class SuperCallsNotAllowedInlineable(symbol: Symbol)(using Context)
16721660
extends SyntaxMsg(SuperCallsNotAllowedInlineableID) {
16731661
def msg = em"Super call not allowed in inlineable $symbol"

tests/neg/i10546.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
object test:
22
def times(num : Int)(block : => Unit) : Unit = ()
3-
times(10): println("ah") // error: end of statement expected but '(' found // error
3+
times(10): println("ah") // error: end of statement expected but '(' found
44

55
def foo: Set(Int) = Set(1)

tests/neg/i4373.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ class A4 extends _ with Base // error
1515

1616
object Test {
1717
type T1 = _ // error
18-
type T2 = _[Int] // error // error
19-
type T3 = _ { type S } // error // error
18+
type T2 = _[Int] // error
19+
type T3 = _ { type S } // error
2020
type T4 = [X] =>> _ // error
2121

2222
// Open questions:

tests/neg/i8731.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ object test:
1111
end if
1212
else // error: illegal start of definition
1313
()
14-
end if // error: misaligned end marker
14+
end if
1515

1616
class Test {
1717
val test = 3
18-
end Test // error: misaligned end marker
19-
} // error: eof expected, but unindent found
18+
end Test
19+
}

tests/neg/match-infix.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
def f = 1 + 1 match {
22
case 2 => 3
3-
} + 1 // error // error
3+
} + 1 // error

tests/neg/parser-stability-17.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
trait x0[] { x0: x0 => } // error // error
2-
class x0[x1] extends x0[x0 x0] x2 x0 // error // error // error
2+
class x0[x1] extends x0[x0 x0] x2 x0 // error // error

tests/neg/parser-stability-19.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
object x0 {
22
case class x0[](): // error
33
def x0( ) ] // error
4-
def x0 ( x0:x0 ):x0.type = x1 x0 // error // error // error
4+
def x0 ( x0:x0 ):x0.type = x1 x0 // error // error
55
// error

tests/neg/parser-stability-23.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
object i0 {
2-
import Ordering.{ implicitly as } (true: Boolean) match { case _: i1 as true } // error // error // error
2+
import Ordering.{ implicitly as } (true: Boolean) match { case _: i1 as true } // error // error
33
}

tests/neg/spaces-vs-tabs.check

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,8 @@
2323
| Previous indent : 2 tabs
2424
| Latest indent : 1 space
2525
-- Error: tests/neg/spaces-vs-tabs.scala:14:2 --------------------------------------------------------------------------
26-
14 | else 2 // error // error
26+
14 | else 2 // error
2727
| ^
2828
| The start of this line does not match any of the previous indentation widths.
2929
| Indentation width of current line : 1 tab, 2 spaces
3030
| This falls between previous widths: 1 tab and 1 tab, 4 spaces
31-
-- [E040] Syntax Error: tests/neg/spaces-vs-tabs.scala:14:7 ------------------------------------------------------------
32-
14 | else 2 // error // error
33-
| ^
34-
| ';' expected, but integer literal found

tests/neg/spaces-vs-tabs.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ object Test:
1111

1212
if true then
1313
1
14-
else 2 // error // error
14+
else 2 // error
1515

tests/neg/t6476.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ class C {
66
s"\ "
77
s"\\"
88
s"\" // error
9-
} // error (should not be one)
9+
}

tests/neg/validate-parsing-2.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
case class ByName(x: => Int) // error

tests/neg/validate-parsing.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,3 @@ class C () {
1010
}
1111
class D override() // error: ';' expected but 'override' found.
1212

13-
case class ByName(x: => Int) // error: `val' parameters may not be call-by-name

tests/pos/zipper.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
enum Tree[+A]:
2+
case Branch(left: Tree[A], right: Tree[A])
3+
case Leaf(value: A)
4+
5+
enum Context[+A]:
6+
case Empty
7+
case InLeft(right: Tree[A], parent: Context[A])
8+
case InRight(left: Tree[A], parent: Context[A])
9+
10+
import Tree.*, Context.*
11+
12+
class Zipper[+A](val focus: Tree[A], val context: Context[A]):
13+
def unfocus: Tree[A] = context match
14+
case Empty => focus
15+
case _ => moveUp.unfocus
16+
def moveUp: Zipper[A] = context match
17+
case Empty => this
18+
case InLeft(right, parent) => Zipper(Branch(focus, right), parent)
19+
case InRight(left, parent) => Zipper(Branch(left, focus), parent)
20+
def moveLeft: Zipper[A] = focus match
21+
case Leaf(_) => this
22+
case Branch(left, right) => Zipper(left, InLeft(right, context))
23+
def moveRight: Zipper[A] = focus match
24+
case Leaf(_) => this
25+
case Branch(left, right) => Zipper(right, InRight(left, context))
26+
def replaceFocus[B >: A](newFocus: Tree[B]): Zipper[B] =
27+
Zipper(newFocus, context)
28+
29+
extension[A](tree: Tree[A]) def focus: Zipper[A] = Zipper(tree, Empty)

0 commit comments

Comments
 (0)