Skip to content

Commit 6d75b45

Browse files
oderskyneko-kai
authored andcommitted
Refactor remaining statement and declaration loops
1 parent 0836316 commit 6d75b45

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
@@ -3643,10 +3630,10 @@ object Parsers {
36433630
*/
36443631
def extMethods(numLeadParams: Int): List[DefDef] = checkNoEscapingPlaceholders {
36453632
val meths = new ListBuffer[DefDef]
3646-
val exitOnError = false
3647-
while !isStatSeqEnd && !exitOnError do
3633+
while
36483634
meths += extMethod(numLeadParams)
3649-
acceptStatSepUnlessAtEnd(meths)
3635+
statSepOrEnd(meths, what = "extension method")
3636+
do ()
36503637
if meths.isEmpty then syntaxError("`def` expected")
36513638
meths.toList
36523639
}
@@ -3782,7 +3769,8 @@ object Parsers {
37823769
*/
37833770
def topStatSeq(outermost: Boolean = false): List[Tree] = {
37843771
val stats = new ListBuffer[Tree]
3785-
while (!isStatSeqEnd) {
3772+
while
3773+
var empty = false
37863774
if (in.token == PACKAGE) {
37873775
val start = in.skipToken()
37883776
if (in.token == OBJECT) {
@@ -3799,13 +3787,10 @@ object Parsers {
37993787
stats += extension()
38003788
else if isDefIntro(modifierTokens) then
38013789
stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens))
3802-
else if !isStatSep then
3803-
if (in.token == CASE)
3804-
syntaxErrorOrIncomplete(OnlyCaseClassOrCaseObjectAllowed())
3805-
else
3806-
syntaxErrorOrIncomplete(ExpectedToplevelDef())
3807-
acceptStatSepUnlessAtEnd(stats)
3808-
}
3790+
else
3791+
empty = true
3792+
statSepOrEnd(stats, empty, "toplevel definition")
3793+
do ()
38093794
stats.toList
38103795
}
38113796

@@ -3837,13 +3822,12 @@ object Parsers {
38373822
in.token = SELFARROW // suppresses INDENT insertion after `=>`
38383823
in.nextToken()
38393824
}
3840-
else {
3825+
else
38413826
stats += first
3842-
acceptStatSepUnlessAtEnd(stats)
3843-
}
3827+
statSepOrEnd(stats)
38443828
}
3845-
var exitOnError = false
3846-
while (!isStatSeqEnd && !exitOnError) {
3829+
while
3830+
var empty = false
38473831
if (in.token == IMPORT)
38483832
stats ++= importClause(IMPORT, mkImport())
38493833
else if (in.token == EXPORT)
@@ -3854,12 +3838,10 @@ object Parsers {
38543838
stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens))
38553839
else if (isExprIntro)
38563840
stats += expr1()
3857-
else if (!isStatSep) {
3858-
exitOnError = mustStartStat
3859-
syntaxErrorOrIncomplete("illegal start of definition")
3860-
}
3861-
acceptStatSepUnlessAtEnd(stats)
3862-
}
3841+
else
3842+
empty = true
3843+
statSepOrEnd(stats, empty)
3844+
do ()
38633845
(self, if (stats.isEmpty) List(EmptyTree) else stats.toList)
38643846
}
38653847

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

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

@@ -3935,7 +3915,7 @@ object Parsers {
39353915
stats +++= localDef(in.offset)
39363916
else
39373917
empty = true
3938-
!exitStats(stats, empty, CASE)
3918+
statSepOrEnd(stats, empty, altEnd = CASE)
39393919
do ()
39403920
stats.toList
39413921
}
@@ -3953,7 +3933,7 @@ object Parsers {
39533933
in.nextToken()
39543934
ts += objectDef(start, Modifiers(Package))
39553935
if (in.token != EOF) {
3956-
acceptStatSepUnlessAtEnd(ts)
3936+
statSepOrEnd(ts, what = "toplevel definition")
39573937
ts ++= topStatSeq()
39583938
}
39593939
}
@@ -3970,7 +3950,7 @@ object Parsers {
39703950
acceptStatSep()
39713951
ts += makePackaging(start, pkg, topstats())
39723952
if continue then
3973-
acceptStatSepUnlessAtEnd(ts)
3953+
statSepOrEnd(ts, what = "toplevel definition")
39743954
ts ++= topStatSeq()
39753955
}
39763956
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)