OLD | NEW |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library fasta.scanner.array_based_scanner; | 5 library fasta.scanner.array_based_scanner; |
6 | 6 |
7 import 'error_token.dart' show ErrorToken, UnmatchedToken; | 7 import 'error_token.dart' show ErrorToken, UnmatchedToken; |
8 | 8 |
9 import '../../scanner/token.dart' | 9 import '../../scanner/token.dart' |
10 show | 10 show |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
132 if (!identical(type.kind, LT_TOKEN) && | 132 if (!identical(type.kind, LT_TOKEN) && |
133 !identical(type.kind, OPEN_PAREN_TOKEN)) { | 133 !identical(type.kind, OPEN_PAREN_TOKEN)) { |
134 discardOpenLt(); | 134 discardOpenLt(); |
135 } | 135 } |
136 groupingStack = groupingStack.prepend(token); | 136 groupingStack = groupingStack.prepend(token); |
137 } | 137 } |
138 | 138 |
139 /** | 139 /** |
140 * Appends a token that begins an end group, represented by [type]. | 140 * Appends a token that begins an end group, represented by [type]. |
141 * It handles the group end tokens '}', ')' and ']'. The tokens '>' and | 141 * It handles the group end tokens '}', ')' and ']'. The tokens '>' and |
142 * '>>' are handled separately bo [appendGt] and [appendGtGt]. | 142 * '>>' are handled separately by [appendGt] and [appendGtGt]. |
143 */ | 143 */ |
144 int appendEndGroup(TokenType type, int openKind) { | 144 int appendEndGroup(TokenType type, int openKind) { |
145 assert(!identical(openKind, LT_TOKEN)); // openKind is < for > and >> | 145 assert(!identical(openKind, LT_TOKEN)); // openKind is < for > and >> |
146 discardBeginGroupUntil(openKind); | 146 if (!discardBeginGroupUntil(openKind)) { |
| 147 // No begin group found. Just continue. |
| 148 appendPrecedenceToken(type); |
| 149 return advance(); |
| 150 } |
147 appendPrecedenceToken(type); | 151 appendPrecedenceToken(type); |
148 Token close = tail; | 152 Token close = tail; |
149 if (groupingStack.isEmpty) { | |
150 return advance(); | |
151 } | |
152 BeginToken begin = groupingStack.head; | 153 BeginToken begin = groupingStack.head; |
153 if (!identical(begin.kind, openKind)) { | 154 if (!identical(begin.kind, openKind)) { |
154 assert(begin.kind == STRING_INTERPOLATION_TOKEN && | 155 assert(begin.kind == STRING_INTERPOLATION_TOKEN && |
155 openKind == OPEN_CURLY_BRACKET_TOKEN); | 156 openKind == OPEN_CURLY_BRACKET_TOKEN); |
156 // We're ending an interpolated expression. | 157 // We're ending an interpolated expression. |
157 begin.endGroup = close; | 158 begin.endGroup = close; |
158 groupingStack = groupingStack.tail; | 159 groupingStack = groupingStack.tail; |
159 // Using "start-of-text" to signal that we're back in string | 160 // Using "start-of-text" to signal that we're back in string |
160 // scanning mode. | 161 // scanning mode. |
161 return $STX; | 162 return $STX; |
162 } | 163 } |
163 begin.endGroup = close; | 164 begin.endGroup = close; |
164 groupingStack = groupingStack.tail; | 165 groupingStack = groupingStack.tail; |
165 return advance(); | 166 return advance(); |
166 } | 167 } |
167 | 168 |
168 /** | 169 /** |
169 * Discards begin group tokens until a match with [openKind] is found. | 170 * If a begin group token matches [openKind], |
170 * This recovers nicely from from a situation like "{[}". | 171 * then discard begin group tokens up to that match and return `true`, |
| 172 * otherwise return `false`. |
| 173 * This recovers nicely from from situations like "{[}" and "{foo());}", |
| 174 * but not "foo(() {bar());}); |
171 */ | 175 */ |
172 void discardBeginGroupUntil(int openKind) { | 176 bool discardBeginGroupUntil(int openKind) { |
173 while (!groupingStack.isEmpty) { | 177 Link<BeginToken> originalStack = groupingStack; |
| 178 |
| 179 bool first = true; |
| 180 do { |
174 // Don't report unmatched errors for <; it is also the less-than operator. | 181 // Don't report unmatched errors for <; it is also the less-than operator. |
175 discardOpenLt(); | 182 discardOpenLt(); |
176 if (groupingStack.isEmpty) return; | 183 if (groupingStack.isEmpty) break; // recover |
177 BeginToken begin = groupingStack.head; | 184 BeginToken begin = groupingStack.head; |
178 if (openKind == begin.kind) return; | 185 if (openKind == begin.kind || |
179 if (openKind == OPEN_CURLY_BRACKET_TOKEN && | 186 (openKind == OPEN_CURLY_BRACKET_TOKEN && |
180 begin.kind == STRING_INTERPOLATION_TOKEN) return; | 187 begin.kind == STRING_INTERPOLATION_TOKEN)) { |
181 unmatchedBeginGroup(begin); | 188 if (first) { |
| 189 // If the expected opener has been found on the first pass |
| 190 // then no recovery necessary. |
| 191 return true; |
| 192 } |
| 193 break; // recover |
| 194 } |
| 195 first = false; |
182 groupingStack = groupingStack.tail; | 196 groupingStack = groupingStack.tail; |
| 197 } while (!groupingStack.isEmpty); |
| 198 |
| 199 // If the stack does not have any opener of the given type, |
| 200 // then return without discarding anything. |
| 201 // This recovers nicely from from situations like "{foo());}". |
| 202 if (groupingStack.isEmpty) { |
| 203 groupingStack = originalStack; |
| 204 return false; |
183 } | 205 } |
| 206 |
| 207 // Insert synthetic closers and report errors for any unbalanced openers. |
| 208 // This recovers nicely from from situations like "{[}". |
| 209 while (!identical(originalStack, groupingStack)) { |
| 210 // Don't report unmatched errors for <; it is also the less-than operator. |
| 211 if (!identical(groupingStack.head.kind, LT_TOKEN)) |
| 212 unmatchedBeginGroup(originalStack.head); |
| 213 originalStack = originalStack.tail; |
| 214 } |
| 215 return true; |
184 } | 216 } |
185 | 217 |
186 /** | 218 /** |
187 * Appends a token for '>'. | 219 * Appends a token for '>'. |
188 * This method does not issue unmatched errors, because > is also the | 220 * This method does not issue unmatched errors, because > is also the |
189 * greater-than operator. It does not necessarily have to close a group. | 221 * greater-than operator. It does not necessarily have to close a group. |
190 */ | 222 */ |
191 void appendGt(TokenType type) { | 223 void appendGt(TokenType type) { |
192 appendPrecedenceToken(type); | 224 appendPrecedenceToken(type); |
193 if (groupingStack.isEmpty) return; | 225 if (groupingStack.isEmpty) return; |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 // | | 367 // | |
336 // next | 368 // next |
337 // v | 369 // v |
338 // EOF | 370 // EOF |
339 TokenType type = closeBraceInfoFor(begin); | 371 TokenType type = closeBraceInfoFor(begin); |
340 appendToken(new SyntheticToken(type, tokenStart)); | 372 appendToken(new SyntheticToken(type, tokenStart)); |
341 begin.endGroup = tail; | 373 begin.endGroup = tail; |
342 appendErrorToken(new UnmatchedToken(begin)); | 374 appendErrorToken(new UnmatchedToken(begin)); |
343 } | 375 } |
344 } | 376 } |
OLD | NEW |