@@ -167,6 +167,7 @@ object Parsers {
167
167
class Parser (source : SourceFile )(using Context ) extends ParserCommon (source) {
168
168
169
169
val in : Scanner = new Scanner (source)
170
+ // in.debugTokenStream = true // uncomment to see the token stream of the standard scanner, but not syntax highlighting
170
171
171
172
/** This is the general parse entry point.
172
173
* Overridden by ScriptParser
@@ -233,15 +234,6 @@ object Parsers {
233
234
234
235
/* ------------- ERROR HANDLING ------------------------------------------- */
235
236
236
- /** The offset of the last time when a statement on a new line was definitely
237
- * encountered in the current scope or an outer scope.
238
- */
239
- private var lastStatOffset = - 1
240
-
241
- def setLastStatOffset (): Unit =
242
- if (mustStartStat && in.isAfterLineEnd)
243
- lastStatOffset = in.offset
244
-
245
237
/** Is offset1 less or equally indented than offset2?
246
238
* This is the case if the characters between the preceding end-of-line and offset1
247
239
* are a prefix of the characters between the preceding end-of-line and offset2.
@@ -262,6 +254,7 @@ object Parsers {
262
254
|| skipStopTokens.contains(in.token) && (in.currentRegion eq lastRegion)
263
255
while ! atStop do
264
256
in.nextToken()
257
+ lastErrorOffset = in.offset
265
258
266
259
def warning (msg : Message , sourcePos : SourcePosition ): Unit =
267
260
report.warning(msg, sourcePos)
@@ -281,11 +274,9 @@ object Parsers {
281
274
*/
282
275
def syntaxErrorOrIncomplete (msg : Message , offset : Int = in.offset): Unit =
283
276
if (in.token == EOF ) incompleteInputError(msg)
284
- else {
277
+ else
285
278
syntaxError(msg, offset)
286
279
skip()
287
- lastErrorOffset = in.offset
288
- }
289
280
290
281
/** Consume one token of the specified type, or
291
282
* signal an error if it is not there.
@@ -320,22 +311,45 @@ object Parsers {
320
311
def acceptStatSep (): Unit =
321
312
if in.isNewLine then in.nextToken() else accept(SEMI )
322
313
323
- def acceptStatSepUnlessAtEnd [T <: Tree ](stats : ListBuffer [T ], altEnd : Token = EOF ): Unit =
324
- def skipEmptyStats (): Unit =
325
- while (in.token == SEMI || in.token == NEWLINE || in.token == NEWLINES ) do in.nextToken()
326
-
327
- in.observeOutdented()
328
- in.token match
329
- case SEMI | NEWLINE | NEWLINES =>
330
- skipEmptyStats()
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
+ * @param stats the statements parsed to far
317
+ * @param noPrevStat true if there was no immediately preceding statement parsed
318
+ * @param what a string indicating what kind of statement is parsed
319
+ * @param altEnd a token that is also considered as a terminator of the statement
320
+ * sequence (the default `EOF` already assumes to terminate a statement
321
+ * sequence).
322
+ * @return true if the statement sequence continues, false if it terminates.
323
+ */
324
+ def statSepOrEnd [T <: Tree ](stats : ListBuffer [T ], noPrevStat : Boolean = false , what : String = " statement" , altEnd : Token = EOF ): Boolean =
325
+ def recur (sepSeen : Boolean , endSeen : Boolean ): Boolean =
326
+ if isStatSep then
327
+ in.nextToken()
328
+ recur(true , endSeen)
329
+ else if in.token == END then
330
+ if endSeen then syntaxError(" duplicate end marker" )
331
331
checkEndMarker(stats)
332
- skipEmptyStats()
333
- case `altEnd` =>
334
- case _ =>
335
- if ! isStatSeqEnd then
336
- syntaxError(i " end of statement expected but ${showToken(in.token)} found " )
332
+ recur(sepSeen, true )
333
+ else if isStatSeqEnd || in.token == altEnd then
334
+ false
335
+ else if sepSeen || endSeen then
336
+ true
337
+ else
338
+ val found = in.token
339
+ val statFollows = mustStartStatTokens.contains(found)
340
+ syntaxError(
341
+ if noPrevStat then IllegalStartOfStatement (what, isModifier, statFollows)
342
+ else i " end of $what expected but ${showToken(found)} found " )
343
+ if mustStartStatTokens.contains(found) then
344
+ false // it's a statement that might be legal in an outer context
345
+ else
337
346
in.nextToken() // needed to ensure progress; otherwise we might cycle forever
338
- accept(SEMI )
347
+ skip()
348
+ true
349
+
350
+ in.observeOutdented()
351
+ recur(false , false )
352
+ end statSepOrEnd
339
353
340
354
def rewriteNotice (version : String = " 3.0" , additionalOption : String = " " ) = {
341
355
val optionStr = if (additionalOption.isEmpty) " " else " " ++ additionalOption
@@ -533,11 +547,8 @@ object Parsers {
533
547
if (in.rewriteToIndent) bracesToIndented(body, rewriteWithColon)
534
548
else inBraces(body)
535
549
536
- def inDefScopeBraces [T ](body : => T , rewriteWithColon : Boolean = false ): T = {
537
- val saved = lastStatOffset
538
- try inBracesOrIndented(body, rewriteWithColon)
539
- finally lastStatOffset = saved
540
- }
550
+ def inDefScopeBraces [T ](body : => T , rewriteWithColon : Boolean = false ): T =
551
+ inBracesOrIndented(body, rewriteWithColon)
541
552
542
553
/** part { `separator` part }
543
554
*/
@@ -1254,7 +1265,7 @@ object Parsers {
1254
1265
def possibleTemplateStart (isNew : Boolean = false ): Unit =
1255
1266
in.observeColonEOL()
1256
1267
if in.token == COLONEOL then
1257
- if in.lookahead.isIdent(nme.end) then in.token = NEWLINE
1268
+ if in.lookahead.token == END then in.token = NEWLINE
1258
1269
else
1259
1270
in.nextToken()
1260
1271
if in.token != INDENT && in.token != LBRACE then
@@ -1284,25 +1295,12 @@ object Parsers {
1284
1295
case _ : (ForYield | ForDo ) => in.token == FOR
1285
1296
case _ => false
1286
1297
1287
- if isIdent(nme.end) then
1288
- val start = in.offset
1289
- val isEndMarker =
1290
- val endLine = source.offsetToLine(start)
1291
- val lookahead = in.LookaheadScanner ()
1292
- lookahead.nextToken()
1293
- source.offsetToLine(lookahead.offset) == endLine
1294
- && endMarkerTokens.contains(in.token)
1295
- && {
1296
- lookahead.nextToken()
1297
- lookahead.token == EOF
1298
- || source.offsetToLine(lookahead.offset) > endLine
1299
- }
1300
- if isEndMarker then
1301
- in.nextToken()
1302
- if stats.isEmpty || ! matches(stats.last) then
1303
- syntaxError(" misaligned end marker" , Span (start, in.lastCharOffset))
1304
- in.token = IDENTIFIER // Leaving it as the original token can confuse newline insertion
1305
- in.nextToken()
1298
+ if in.token == END then
1299
+ val start = in.skipToken()
1300
+ if stats.isEmpty || ! matches(stats.last) then
1301
+ syntaxError(" misaligned end marker" , Span (start, in.lastCharOffset))
1302
+ in.token = IDENTIFIER // Leaving it as the original token can confuse newline insertion
1303
+ in.nextToken()
1306
1304
end checkEndMarker
1307
1305
1308
1306
/* ------------- TYPES ------------------------------------------------------ */
@@ -1538,10 +1536,7 @@ object Parsers {
1538
1536
else t
1539
1537
1540
1538
/** The block in a quote or splice */
1541
- def stagedBlock () =
1542
- val saved = lastStatOffset
1543
- try inBraces(block(simplify = true ))
1544
- finally lastStatOffset = saved
1539
+ def stagedBlock () = inBraces(block(simplify = true ))
1545
1540
1546
1541
/** SimpleEpxr ::= spliceId | ‘$’ ‘{’ Block ‘}’)
1547
1542
* SimpleType ::= spliceId | ‘$’ ‘{’ Block ‘}’)
@@ -3641,11 +3636,10 @@ object Parsers {
3641
3636
*/
3642
3637
def extMethods (numLeadParams : Int ): List [DefDef ] = checkNoEscapingPlaceholders {
3643
3638
val meths = new ListBuffer [DefDef ]
3644
- val exitOnError = false
3645
- while ! isStatSeqEnd && ! exitOnError do
3646
- setLastStatOffset()
3639
+ while
3647
3640
meths += extMethod(numLeadParams)
3648
- acceptStatSepUnlessAtEnd(meths)
3641
+ statSepOrEnd(meths, what = " extension method" )
3642
+ do ()
3649
3643
if meths.isEmpty then syntaxError(" `def` expected" )
3650
3644
meths.toList
3651
3645
}
@@ -3781,8 +3775,8 @@ object Parsers {
3781
3775
*/
3782
3776
def topStatSeq (outermost : Boolean = false ): List [Tree ] = {
3783
3777
val stats = new ListBuffer [Tree ]
3784
- while ( ! isStatSeqEnd) {
3785
- setLastStatOffset()
3778
+ while
3779
+ var empty = false
3786
3780
if (in.token == PACKAGE ) {
3787
3781
val start = in.skipToken()
3788
3782
if (in.token == OBJECT ) {
@@ -3799,13 +3793,10 @@ object Parsers {
3799
3793
stats += extension()
3800
3794
else if isDefIntro(modifierTokens) then
3801
3795
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
- }
3796
+ else
3797
+ empty = true
3798
+ statSepOrEnd(stats, empty, " toplevel definition" )
3799
+ do ()
3809
3800
stats.toList
3810
3801
}
3811
3802
@@ -3837,14 +3828,12 @@ object Parsers {
3837
3828
in.token = SELFARROW // suppresses INDENT insertion after `=>`
3838
3829
in.nextToken()
3839
3830
}
3840
- else {
3831
+ else
3841
3832
stats += first
3842
- acceptStatSepUnlessAtEnd(stats)
3843
- }
3833
+ statSepOrEnd(stats)
3844
3834
}
3845
- var exitOnError = false
3846
- while (! isStatSeqEnd && ! exitOnError) {
3847
- setLastStatOffset()
3835
+ while
3836
+ var empty = false
3848
3837
if (in.token == IMPORT )
3849
3838
stats ++= importClause(IMPORT , mkImport())
3850
3839
else if (in.token == EXPORT )
@@ -3855,12 +3844,10 @@ object Parsers {
3855
3844
stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens))
3856
3845
else if (isExprIntro)
3857
3846
stats += expr1()
3858
- else if (! isStatSep) {
3859
- exitOnError = mustStartStat
3860
- syntaxErrorOrIncomplete(" illegal start of definition" )
3861
- }
3862
- acceptStatSepUnlessAtEnd(stats)
3863
- }
3847
+ else
3848
+ empty = true
3849
+ statSepOrEnd(stats, empty)
3850
+ do ()
3864
3851
(self, if (stats.isEmpty) List (EmptyTree ) else stats.toList)
3865
3852
}
3866
3853
@@ -3889,16 +3876,14 @@ object Parsers {
3889
3876
if problem.isEmpty then tree :: Nil
3890
3877
else { syntaxError(problem, tree.span); Nil }
3891
3878
3892
- while (! isStatSeqEnd) {
3893
- if (isDclIntro)
3879
+ while
3880
+ val dclFound = isDclIntro
3881
+ if dclFound then
3894
3882
stats ++= checkLegal(defOrDcl(in.offset, Modifiers ()))
3895
- else if (! isStatSep)
3896
- syntaxErrorOrIncomplete(
3897
- " illegal start of declaration" +
3898
- (if (inFunReturnType) " (possible cause: missing `=` in front of current method body)"
3899
- else " " ))
3900
- acceptStatSepUnlessAtEnd(stats)
3901
- }
3883
+ var what = " declaration"
3884
+ if inFunReturnType then what += " (possible cause: missing `=` in front of current method body)"
3885
+ statSepOrEnd(stats, ! dclFound, what)
3886
+ do ()
3902
3887
stats.toList
3903
3888
}
3904
3889
@@ -3922,9 +3907,8 @@ object Parsers {
3922
3907
*/
3923
3908
def blockStatSeq (): List [Tree ] = checkNoEscapingPlaceholders {
3924
3909
val stats = new ListBuffer [Tree ]
3925
- var exitOnError = false
3926
- while (! isStatSeqEnd && in.token != CASE && ! exitOnError) {
3927
- setLastStatOffset()
3910
+ while
3911
+ var empty = false
3928
3912
if (in.token == IMPORT )
3929
3913
stats ++= importClause(IMPORT , mkImport())
3930
3914
else if (isExprIntro)
@@ -3935,12 +3919,10 @@ object Parsers {
3935
3919
stats += extension()
3936
3920
else if isDefIntro(localModifierTokens, excludedSoftModifiers = Set (nme.`opaque`)) then
3937
3921
stats +++= localDef(in.offset)
3938
- else if (! isStatSep && (in.token != CASE )) {
3939
- exitOnError = mustStartStat
3940
- syntaxErrorOrIncomplete(IllegalStartOfStatement (isModifier))
3941
- }
3942
- acceptStatSepUnlessAtEnd(stats, CASE )
3943
- }
3922
+ else
3923
+ empty = true
3924
+ statSepOrEnd(stats, empty, altEnd = CASE )
3925
+ do ()
3944
3926
stats.toList
3945
3927
}
3946
3928
@@ -3957,7 +3939,7 @@ object Parsers {
3957
3939
in.nextToken()
3958
3940
ts += objectDef(start, Modifiers (Package ))
3959
3941
if (in.token != EOF ) {
3960
- acceptStatSepUnlessAtEnd (ts)
3942
+ statSepOrEnd (ts, what = " toplevel definition " )
3961
3943
ts ++= topStatSeq()
3962
3944
}
3963
3945
}
@@ -3974,7 +3956,7 @@ object Parsers {
3974
3956
acceptStatSep()
3975
3957
ts += makePackaging(start, pkg, topstats())
3976
3958
if continue then
3977
- acceptStatSepUnlessAtEnd (ts)
3959
+ statSepOrEnd (ts, what = " toplevel definition " )
3978
3960
ts ++= topStatSeq()
3979
3961
}
3980
3962
else
0 commit comments