@@ -8,11 +8,14 @@ import org.jetbrains.plugins.scala.lang.parser.parsing.builder.ScalaPsiBuilder
88import org .jetbrains .plugins .scala .lang .parser .parsing .expressions .Expr1
99import org .jetbrains .plugins .scala .lang .parser .parsing .{Associativity , ParsingRule }
1010import org .jetbrains .plugins .scala .lang .parser .util .ParserUtils .{isAssignmentOperator , isSymbolicIdentifier , operatorAssociativity , priority }
11+ import org .jetbrains .plugins .scala .lang .parser .util .PrecedenceClimbingInfixParsingRule .InfixStackElement
12+
13+ import scala .annotation .tailrec
1114
1215abstract class PrecedenceClimbingInfixParsingRule extends ParsingRule {
13- protected def parseFirstOperator ()(implicit builder : ScalaPsiBuilder ): Boolean
16+ protected def parseFirstOperand ()(implicit builder : ScalaPsiBuilder ): Boolean
1417
15- protected def parseOperator ()(implicit builder : ScalaPsiBuilder ): Boolean
18+ protected def parseOperand ()(implicit builder : ScalaPsiBuilder ): Boolean
1619
1720 protected def referenceElementType : IElementType
1821
@@ -24,85 +27,74 @@ abstract class PrecedenceClimbingInfixParsingRule extends ParsingRule {
2427
2528 final override def parse (implicit builder : ScalaPsiBuilder ): Boolean = {
2629 val isInlineMatch = builder.rawLookup(- 2 ) == ScalaTokenType .InlineKeyword
27- var markerStack = List .empty[PsiBuilder .Marker ]
28- var opStack = List .empty[String ]
29- val infixMarker = builder.mark()
30- var backupMarker = builder.mark()
31- var count = 0
32- if (! parseFirstOperator()) {
33- backupMarker.drop()
34- infixMarker.drop()
30+ val matchStartMarker = builder.mark()
31+ val beforeFirstOperandMarker = builder.mark()
32+
33+ if (! parseFirstOperand()) {
34+ beforeFirstOperandMarker.drop()
35+ matchStartMarker.drop()
3536 return false
3637 }
37- var exitOf = true
38- while (builder.getTokenType == ScalaTokenTypes .tIDENTIFIER && shouldContinue && exitOf) {
39- // need to know associativity
40- val s = builder.getTokenText
41-
42- var exit = false
43- while ( ! exit) {
44- if (opStack.isEmpty ) {
45- opStack = s :: opStack
46- val newMarker = backupMarker.precede
47- markerStack = newMarker :: markerStack
48- exit = true
49- }
50- else if ( ! compar(s, opStack.head, builder)) {
51- opStack = opStack.tail
52- backupMarker.drop()
53- backupMarker = markerStack.head.precede
54- markerStack.head.done(infixElementType)
55- markerStack = markerStack.tail
56- }
57- else {
58- opStack = s :: opStack
59- val newMarker = backupMarker .precede
60- markerStack = newMarker :: markerStack
61- exit = true
62- }
38+
39+ def finishOpStack ( opStack : List [ InfixStackElement ]) : Unit =
40+ opStack.foreach(_.marker.done(infixElementType))
41+
42+ @ tailrec
43+ def continueWithOperatorAndOperand ( beforePrevOperandMarker : PsiBuilder . Marker , opStack : List [ InfixStackElement ]) : Unit = {
44+ // we are before an operator
45+ if (builder.getTokenType != ScalaTokenTypes .tIDENTIFIER || ! shouldContinue ) {
46+ beforePrevOperandMarker.drop()
47+ finishOpStack(opStack)
48+ return
49+ }
50+
51+ // get operator because we need to know the precedence
52+ val opText = builder.getTokenText
53+
54+ // pop operators with higher or equal precedence or different associativity
55+ var infixMarker = beforePrevOperandMarker
56+ val restOpStack = opStack.dropWhile {
57+ case InfixStackElement (op, marker) if ! compar(opText, op, builder) =>
58+ infixMarker.drop()
59+ marker.done(infixElementType)
60+ infixMarker = marker .precede()
61+ true
62+ case _ =>
63+ false
6364 }
64- val setMarker = builder.mark()
65+
66+ // parse the operator but make sure we can rollback if the operand fails to parse
67+ val beforeOpMarker = builder.mark()
6568 val opMarker = builder.mark()
6669 builder.advanceLexer() // Ate id
6770 opMarker.done(referenceElementType)
6871
6972 parseAfterOperatorId(opMarker)
7073
71- if (builder.twoNewlinesBeforeCurrentToken) {
72- setMarker.rollbackTo()
73- count = 0
74- backupMarker.drop()
75- exitOf = false
74+ val beforeOperandMarker = builder.mark()
75+
76+ // try to parse the operand
77+ if (builder.twoNewlinesBeforeCurrentToken || ! parseOperand()) {
78+ beforeOpMarker.rollbackTo()
79+ infixMarker.drop()
80+ finishOpStack(restOpStack)
7681 } else {
77- backupMarker.drop()
78- backupMarker = builder.mark()
79- if (! parseOperator()) {
80- setMarker.rollbackTo()
81- count = 0
82- exitOf = false
83- }
84- else {
85- setMarker.drop()
86- count = count + 1
87- }
82+ beforeOpMarker.drop()
83+ continueWithOperatorAndOperand(beforeOperandMarker, InfixStackElement (opText, infixMarker) :: restOpStack)
8884 }
8985 }
90- if (exitOf) backupMarker.drop()
91- if (count > 0 ) {
92- while (count > 0 && markerStack.nonEmpty) {
93- markerStack.head.done(infixElementType)
94- markerStack = markerStack.tail
95- count -= 1
96- }
97- infixMarker.drop()
98- } else if (! isInlineMatch && isMatchConsideredInfix
99- && builder.isScala3 && builder.getTokenType == ScalaTokenTypes .kMATCH && ! builder.isOutdentHere) {
100- Expr1 .parseMatch(infixMarker)
101- } else infixMarker.drop()
102-
103- while (markerStack.nonEmpty) {
104- markerStack.head.drop()
105- markerStack = markerStack.tail
86+
87+ continueWithOperatorAndOperand(beforeFirstOperandMarker, Nil )
88+
89+ if (
90+ ! isInlineMatch &&
91+ isMatchConsideredInfix &&
92+ builder.isScala3 && builder.getTokenType == ScalaTokenTypes .kMATCH &&
93+ ! builder.isOutdentHere
94+ ) {
95+ Expr1 .parseMatch(matchStartMarker)
96+ } else {
97+ matchStartMarker.drop()
10698 }
10799 true
108100 }
@@ -185,3 +177,7 @@ abstract class PrecedenceClimbingInfixParsingRule extends ParsingRule {
185177 }
186178 }
187179}
180+
181+ private object PrecedenceClimbingInfixParsingRule {
182+ case class InfixStackElement (op : String , marker : PsiBuilder .Marker )
183+ }
0 commit comments