|
12 | 12 |
|
13 | 13 | @_spi(RawSyntax) import SwiftSyntax
|
14 | 14 |
|
| 15 | +extension TokenConsumer { |
| 16 | + /// Returns `true` if the current token represents the start of a statement |
| 17 | + /// item. |
| 18 | + /// |
| 19 | + /// - Note: This function must be kept in sync with `parseStatement()`. |
| 20 | + /// - Seealso: ``Parser/parseStatement()`` |
| 21 | + func atStartOfStatement() -> Bool { |
| 22 | + var lookahead = self.lookahead() |
| 23 | + _ = lookahead.consume(if: .identifier, followedBy: .colon) |
| 24 | + return lookahead.at(anyIn: CanBeStatementStart.self) != nil |
| 25 | + } |
| 26 | +} |
| 27 | + |
15 | 28 | extension Parser {
|
16 | 29 | /// Parse a statement.
|
17 | 30 | ///
|
18 | 31 | /// This function is meant to be invoked as part of parsing an item. As such
|
19 | 32 | /// it does not deal with parsing expressions, declarations, or consuming
|
20 | 33 | /// any trailing semicolons.
|
21 | 34 | ///
|
22 |
| - /// - Note: This function must be kept in sync with ``Parser/Lookahead/isStartOfStatement()`` |
23 |
| - /// - Seealso: ``Parser/Lookahead/isStartOfStatement()`` |
24 |
| - /// |
25 | 35 | /// Grammar
|
26 | 36 | /// =======
|
27 | 37 | ///
|
@@ -67,45 +77,44 @@ extension Parser {
|
67 | 77 | }
|
68 | 78 |
|
69 | 79 | let optLabel = self.parseOptionalStatementLabel()
|
70 |
| - switch self.currentToken.tokenKind { |
71 |
| - case .forKeyword: |
| 80 | + switch self.at(anyIn: CanBeStatementStart.self) { |
| 81 | + case (.forKeyword, _)?: |
72 | 82 | return label(self.parseForEachStatement(), with: optLabel)
|
73 |
| - case .whileKeyword: |
| 83 | + case (.whileKeyword, _)?: |
74 | 84 | return label(self.parseWhileStatement(), with: optLabel)
|
75 |
| - case .repeatKeyword: |
| 85 | + case (.repeatKeyword, _)?: |
76 | 86 | return label(self.parseRepeatWhileStatement(), with: optLabel)
|
77 | 87 |
|
78 |
| - case .ifKeyword: |
| 88 | + case (.ifKeyword, _)?: |
79 | 89 | return label(self.parseIfStatement(), with: optLabel)
|
80 |
| - case .guardKeyword: |
| 90 | + case (.guardKeyword, _)?: |
81 | 91 | return label(self.parseGuardStatement(), with: optLabel)
|
82 |
| - case .switchKeyword: |
| 92 | + case (.switchKeyword, _)?: |
83 | 93 | return label(self.parseSwitchStatement(), with: optLabel)
|
84 | 94 |
|
85 |
| - case .breakKeyword: |
| 95 | + case (.breakKeyword, _)?: |
86 | 96 | return label(self.parseBreakStatement(), with: optLabel)
|
87 |
| - case .continueKeyword: |
| 97 | + case (.continueKeyword, _)?: |
88 | 98 | return label(self.parseContinueStatement(), with: optLabel)
|
89 |
| - case .fallthroughKeyword: |
| 99 | + case (.fallthroughKeyword, _)?: |
90 | 100 | return label(self.parseFallthroughStatement(), with: optLabel)
|
91 |
| - case .returnKeyword: |
| 101 | + case (.returnKeyword, _)?: |
92 | 102 | return label(self.parseReturnStatement(), with: optLabel)
|
93 |
| - case .throwKeyword: |
| 103 | + case (.throwKeyword, _)?: |
94 | 104 | return label(self.parseThrowStatement(), with: optLabel)
|
95 |
| - case .deferKeyword: |
| 105 | + case (.deferKeyword, _)?: |
96 | 106 | return label(self.parseDeferStatement(), with: optLabel)
|
97 |
| - case .doKeyword: |
| 107 | + case (.doKeyword, _)?: |
98 | 108 | return label(self.parseDoStatement(), with: optLabel)
|
99 | 109 |
|
100 |
| - case .poundAssertKeyword: |
| 110 | + case (.poundAssertKeyword, _)?: |
101 | 111 | // FIXME: This drops `optLabel`.
|
102 | 112 | return RawStmtSyntax(self.parsePoundAssertStatement())
|
103 |
| - case _ where self.atContextualKeyword("yield"): |
104 |
| - fallthrough |
105 |
| - case .yield: |
| 113 | + case (.yieldAsIdentifier, _)?, |
| 114 | + (.yield, _)?: |
106 | 115 | // FIXME: This drops `optLabel`.
|
107 | 116 | return RawStmtSyntax(self.parseYieldStatement())
|
108 |
| - default: |
| 117 | + case nil: |
109 | 118 | let missingStmt = RawStmtSyntax(RawMissingStmtSyntax(arena: self.arena))
|
110 | 119 | return label(missingStmt, with: optLabel)
|
111 | 120 | }
|
@@ -824,7 +833,7 @@ extension Parser {
|
824 | 833 | .poundIfKeyword, .poundErrorKeyword, .poundWarningKeyword,
|
825 | 834 | .poundEndifKeyword, .poundElseKeyword, .poundElseifKeyword
|
826 | 835 | ])
|
827 |
| - && !self.lookahead().isStartOfStatement() && !self.lookahead().isStartOfDeclaration() { |
| 836 | + && !self.atStartOfStatement() && !self.lookahead().isStartOfDeclaration() { |
828 | 837 | expr = self.parseExpression()
|
829 | 838 | } else {
|
830 | 839 | expr = nil
|
@@ -982,7 +991,7 @@ extension Parser {
|
982 | 991 |
|
983 | 992 | guard
|
984 | 993 | self.at(.identifier) &&
|
985 |
| - !self.lookahead().isStartOfStatement() && |
| 994 | + !self.atStartOfStatement() && |
986 | 995 | !self.lookahead().isStartOfDeclaration()
|
987 | 996 | else {
|
988 | 997 | return nil
|
@@ -1023,82 +1032,6 @@ extension Parser {
|
1023 | 1032 | // MARK: Lookahead
|
1024 | 1033 |
|
1025 | 1034 | extension Parser.Lookahead {
|
1026 |
| - /// Returns `true` if the current token represents the start of a statement |
1027 |
| - /// item. |
1028 |
| - /// |
1029 |
| - /// - Note: This function must be kept in sync with `parseStatement()`. |
1030 |
| - /// - Seealso: ``Parser/parseStatement()`` |
1031 |
| - public func isStartOfStatement() -> Bool { |
1032 |
| - switch self.currentToken.tokenKind { |
1033 |
| - case .returnKeyword, |
1034 |
| - .throwKeyword, |
1035 |
| - .deferKeyword, |
1036 |
| - .ifKeyword, |
1037 |
| - .guardKeyword, |
1038 |
| - .whileKeyword, |
1039 |
| - .doKeyword, |
1040 |
| - .repeatKeyword, |
1041 |
| - .forKeyword, |
1042 |
| - .breakKeyword, |
1043 |
| - .continueKeyword, |
1044 |
| - .fallthroughKeyword, |
1045 |
| - .switchKeyword, |
1046 |
| - .caseKeyword, |
1047 |
| - .defaultKeyword, |
1048 |
| - .yield, |
1049 |
| - .poundAssertKeyword, |
1050 |
| - .poundIfKeyword, |
1051 |
| - .poundWarningKeyword, |
1052 |
| - .poundErrorKeyword, |
1053 |
| - .poundSourceLocationKeyword: |
1054 |
| - return true |
1055 |
| - |
1056 |
| - case .poundLineKeyword: |
1057 |
| - // #line at the start of a line is a directive, when within, it is an expr. |
1058 |
| - return self.currentToken.isAtStartOfLine |
1059 |
| - |
1060 |
| - case .identifier: |
1061 |
| - // "identifier ':' for/while/do/switch" is a label on a loop/switch. |
1062 |
| - guard self.peek().tokenKind == .colon else { |
1063 |
| - // "yield" in the right context begins a yield statement. |
1064 |
| - if self.atContextualKeyword("yield") { |
1065 |
| - return true |
1066 |
| - } |
1067 |
| - return false |
1068 |
| - } |
1069 |
| - |
1070 |
| - // To disambiguate other cases of "identifier :", which might be part of a |
1071 |
| - // question colon expression or something else, we look ahead to the second |
1072 |
| - // token. |
1073 |
| - var backtrack = self.lookahead() |
1074 |
| - backtrack.expectIdentifierWithoutRecovery() |
1075 |
| - backtrack.eat(.colon) |
1076 |
| - |
1077 |
| - // We treating IDENTIFIER: { as start of statement to provide missed 'do' |
1078 |
| - // diagnostics. This case will be handled in parseStmt(). |
1079 |
| - if self.at(.leftBrace) { |
1080 |
| - return true |
1081 |
| - } |
1082 |
| - // For better recovery, we just accept a label on any statement. We reject |
1083 |
| - // putting a label on something inappropriate in parseStmt(). |
1084 |
| - return backtrack.isStartOfStatement() |
1085 |
| - |
1086 |
| - case .atSign: |
1087 |
| - // Might be a statement or case attribute. The only one of these we have |
1088 |
| - // right now is `@unknown default`, so hardcode a check for an attribute |
1089 |
| - // without any parens. |
1090 |
| - guard self.peek().tokenKind == .identifier else { |
1091 |
| - return false |
1092 |
| - } |
1093 |
| - var backtrack = self.lookahead() |
1094 |
| - backtrack.eat(.atSign) |
1095 |
| - backtrack.expectIdentifierWithoutRecovery() |
1096 |
| - return backtrack.isStartOfStatement() |
1097 |
| - default: |
1098 |
| - return false |
1099 |
| - } |
1100 |
| - } |
1101 |
| - |
1102 | 1035 | func isBooleanExpr() -> Bool {
|
1103 | 1036 | var lookahead = self.lookahead()
|
1104 | 1037 | return !lookahead.canParseTypedPattern() || !lookahead.at(.equal)
|
|
0 commit comments