Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 engine.incremental_scanner; | 5 library engine.incremental_scanner; |
| 6 | 6 |
| 7 import "dart:math" as math; | 7 import "dart:math" as math; |
| 8 | 8 |
| 9 import 'error.dart'; | 9 import 'error.dart'; |
| 10 import 'scanner.dart'; | 10 import 'scanner.dart'; |
| 11 import 'source.dart'; | 11 import 'source.dart'; |
| 12 import 'utilities_collection.dart' show TokenMap; | 12 import 'utilities_collection.dart' show TokenMap; |
| 13 | 13 |
| 14 /** | 14 /** |
| 15 * Instances of the class `IncrementalScanner` implement a scanner that scans a subset of a | 15 * An `IncrementalScanner` is a scanner that scans a subset of a string and |
| 16 * string and inserts the resulting tokens into the middle of an existing token stream. | 16 * inserts the resulting tokens into the middle of an existing token stream. |
| 17 */ | 17 */ |
| 18 class IncrementalScanner extends Scanner { | 18 class IncrementalScanner { |
| 19 /** | |
| 20 * The source being scanned. | |
| 21 */ | |
| 22 final Source source; | |
| 23 | |
| 19 /** | 24 /** |
| 20 * The reader used to access the characters in the source. | 25 * The reader used to access the characters in the source. |
| 21 */ | 26 */ |
| 22 CharacterReader _reader; | 27 final CharacterReader reader; |
| 28 | |
| 29 /** | |
| 30 * The error listener that will be informed of any errors that are found | |
| 31 * during the scan. | |
| 32 * | |
| 33 * TODO(brianwilkerson) Replace this with a list of errors so that we can | |
| 34 * update the errors. | |
| 35 */ | |
| 36 final AnalysisErrorListener errorListener; | |
| 23 | 37 |
| 24 /** | 38 /** |
| 25 * A map from tokens that were copied to the copies of the tokens. | 39 * A map from tokens that were copied to the copies of the tokens. |
| 26 */ | 40 */ |
| 27 TokenMap _tokenMap = new TokenMap(); | 41 TokenMap _tokenMap = new TokenMap(); |
| 28 | 42 |
| 29 /** | 43 /** |
| 30 * The token in the new token stream immediately to the left of the range of t okens that were | 44 * The token in the new token stream immediately to the left of the range of t okens that were |
| 31 * inserted, or the token immediately to the left of the modified region if th ere were no new | 45 * inserted, or the token immediately to the left of the modified region if th ere were no new |
| 32 * tokens. | 46 * tokens. |
| 33 */ | 47 */ |
| 34 Token _leftToken; | 48 Token _leftToken; |
| 35 | 49 |
| 36 /** | 50 /** |
| 37 * The token in the new token stream immediately to the right of the range of tokens that were | 51 * The token in the new token stream immediately to the right of the range of tokens that were |
| 38 * inserted, or the token immediately to the right of the modified region if t here were no new | 52 * inserted, or the token immediately to the right of the modified region if t here were no new |
| 39 * tokens. | 53 * tokens. |
| 40 */ | 54 */ |
| 41 Token _rightToken; | 55 Token _rightToken; |
| 42 | 56 |
| 43 /** | 57 /** |
| 44 * A flag indicating whether there were any tokens changed as a result of the modification. | 58 * A flag indicating whether there were any tokens changed as a result of the modification. |
| 45 */ | 59 */ |
| 46 bool _hasNonWhitespaceChange = false; | 60 bool _hasNonWhitespaceChange = false; |
| 47 | 61 |
| 48 /** | 62 /** |
| 49 * Initialize a newly created scanner. | 63 * Initialize a newly created scanner to scan characters within the given |
| 50 * | 64 * [source]. The content of the source can be read using the given [reader]. |
| 51 * @param source the source being scanned | 65 * Any errors that are found will be reported to the given [errorListener]. |
| 52 * @param reader the character reader used to read the characters in the sourc e | |
| 53 * @param errorListener the error listener that will be informed of any errors that are found | |
| 54 */ | 66 */ |
| 55 IncrementalScanner(Source source, CharacterReader reader, | 67 IncrementalScanner(this.source, this.reader, this.errorListener); |
| 56 AnalysisErrorListener errorListener) | |
| 57 : super(source, reader, errorListener) { | |
| 58 this._reader = reader; | |
| 59 } | |
| 60 | 68 |
| 61 /** | 69 /** |
| 62 * Return `true` if there were any tokens either added or removed (or both) as a result of | 70 * Return `true` if there were any tokens either added or removed (or both) as a result of |
| 63 * the modification. | 71 * the modification. |
| 64 * | 72 * |
| 65 * @return `true` if there were any tokens changed as a result of the modifica tion | 73 * @return `true` if there were any tokens changed as a result of the modifica tion |
| 66 */ | 74 */ |
| 67 bool get hasNonWhitespaceChange => _hasNonWhitespaceChange; | 75 bool get hasNonWhitespaceChange => _hasNonWhitespaceChange; |
| 68 | 76 |
| 69 /** | 77 /** |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 85 Token get rightToken => _rightToken; | 93 Token get rightToken => _rightToken; |
| 86 | 94 |
| 87 /** | 95 /** |
| 88 * Return a map from tokens that were copied to the copies of the tokens. | 96 * Return a map from tokens that were copied to the copies of the tokens. |
| 89 * | 97 * |
| 90 * @return a map from tokens that were copied to the copies of the tokens | 98 * @return a map from tokens that were copied to the copies of the tokens |
| 91 */ | 99 */ |
| 92 TokenMap get tokenMap => _tokenMap; | 100 TokenMap get tokenMap => _tokenMap; |
| 93 | 101 |
| 94 /** | 102 /** |
| 95 * Given the stream of tokens scanned from the original source, the modified s ource (the result of | 103 * Given the [stream] of tokens scanned from the original source, the modified |
| 96 * replacing one contiguous range of characters with another string of charact ers), and a | 104 * source (the result of replacing one contiguous range of characters with |
| 97 * specification of the modification that was made, return a stream of tokens scanned from the | 105 * another string of characters), and a specification of the modification that |
| 98 * modified source. The original stream of tokens will not be modified. | 106 * was made, update the token stream to reflect the modified source. Return |
| 107 * the first token in the updated token stream. | |
| 99 * | 108 * |
| 100 * @param originalStream the stream of tokens scanned from the original source | 109 * The [stream] is expected to be the first non-EOF token in the token stream. |
| 101 * @param index the index of the first character in both the original and modi fied source that was | 110 * |
| 102 * affected by the modification | 111 * The modification is specified by the [index] of the first character in both |
| 103 * @param removedLength the number of characters removed from the original sou rce | 112 * the original and modified source that was affected by the modification, the |
| 104 * @param insertedLength the number of characters added to the modified source | 113 * number of characters removed from the original source (the [removedLength]) |
| 114 * and the number of characters added to the modified source (the | |
| 115 * [insertedLength]). | |
| 105 */ | 116 */ |
| 106 Token rescan(Token originalStream, int index, int removedLength, | 117 Token rescan(Token stream, int index, int removedLength, int insertedLength) { |
| 107 int insertedLength) { | 118 Token leftEof = stream.previous; |
| 108 // | |
| 109 // Copy all of the tokens in the originalStream whose end is less than the | |
| 110 // replacement start. (If the replacement start is equal to the end of an | |
| 111 // existing token, then it means that the existing token might have been | |
| 112 // modified, so we need to rescan it.) | |
| 113 // | |
| 114 while (originalStream.type != TokenType.EOF && originalStream.end < index) { | |
| 115 originalStream = _copyAndAdvance(originalStream, 0); | |
| 116 } | |
| 117 Token oldFirst = originalStream; | |
| 118 Token oldLeftToken = originalStream.previous; | |
| 119 _leftToken = tail; | |
| 120 // | |
| 121 // Skip tokens in the original stream until we find a token whose offset is | |
| 122 // greater than the end of the removed region. (If the end of the removed | |
| 123 // region is equal to the beginning of an existing token, then it means that | |
| 124 // the existing token might have been modified, so we need to rescan it.) | |
| 125 // | |
| 126 int removedEnd = index + (removedLength == 0 ? 0 : removedLength - 1); | |
| 127 while (originalStream.type != TokenType.EOF && | |
| 128 originalStream.offset <= removedEnd) { | |
| 129 originalStream = originalStream.next; | |
| 130 } | |
| 131 Token oldLast; | |
| 132 Token oldRightToken; | |
| 133 if (originalStream.type != TokenType.EOF && | |
| 134 removedEnd + 1 == originalStream.offset) { | |
| 135 oldLast = originalStream; | |
| 136 originalStream = originalStream.next; | |
| 137 oldRightToken = originalStream; | |
| 138 } else { | |
| 139 oldLast = originalStream.previous; | |
| 140 oldRightToken = originalStream; | |
| 141 } | |
| 142 // | 119 // |
| 143 // Compute the delta between the character index of characters after the | 120 // Compute the delta between the character index of characters after the |
| 144 // modified region in the original source and the index of the corresponding | 121 // modified region in the original source and the index of the corresponding |
| 145 // character in the modified source. | 122 // character in the modified source. |
| 146 // | 123 // |
| 147 int delta = insertedLength - removedLength; | 124 int delta = insertedLength - removedLength; |
| 148 // | 125 // |
| 126 // Skip past the tokens whose end is less than the replacement start. (If | |
| 127 // the replacement start is equal to the end of an existing token, then it | |
| 128 // means that the existing token might have been modified, so we need to | |
| 129 // rescan it.) | |
| 130 // | |
| 131 while (stream.type != TokenType.EOF && stream.end < index) { | |
| 132 _tokenMap.put(stream, stream); | |
| 133 stream = stream.next; | |
| 134 } | |
| 135 Token oldFirst = stream; | |
| 136 Token oldLeftToken = stream.previous; | |
| 137 _leftToken = oldLeftToken; | |
| 138 // | |
| 139 // Skip past tokens until we find a token whose offset is greater than the | |
| 140 // end of the removed region. (If the end of the removed region is equal to | |
| 141 // the beginning of an existing token, then it means that the existing token | |
| 142 // might have been modified, so we need to rescan it.) | |
| 143 // | |
| 144 int removedEnd = index + (removedLength == 0 ? 0 : removedLength - 1); | |
| 145 while (stream.type != TokenType.EOF && stream.offset <= removedEnd) { | |
| 146 stream = stream.next; | |
| 147 } | |
| 148 // | |
| 149 // Figure out which region of characters actually needs to be re-scanned. | |
| 150 // | |
| 151 Token oldLast; | |
| 152 Token oldRightToken; | |
| 153 if (stream.type != TokenType.EOF && removedEnd + 1 == stream.offset) { | |
| 154 oldLast = stream; | |
| 155 stream = stream.next; | |
| 156 oldRightToken = stream; | |
| 157 } else { | |
| 158 oldLast = stream.previous; | |
| 159 oldRightToken = stream; | |
| 160 } | |
| 161 // | |
| 149 // Compute the range of characters that are known to need to be rescanned. | 162 // Compute the range of characters that are known to need to be rescanned. |
| 150 // If the index is within an existing token, then we need to start at the | 163 // If the index is within an existing token, then we need to start at the |
| 151 // beginning of the token. | 164 // beginning of the token. |
| 152 // | 165 // |
| 153 int scanStart = math.min(oldFirst.offset, index); | 166 int scanStart = math.max(oldFirst.previous.end, 0); |
| 154 int oldEnd = oldLast.end + delta - 1; | 167 int scanEnd = oldLast.end + delta; |
| 155 int newEnd = index + insertedLength - 1; | |
| 156 int scanEnd = math.max(newEnd, oldEnd); | |
| 157 // | 168 // |
| 158 // Starting at the start of the scan region, scan tokens from the | 169 // Starting at the start of the scan region, scan tokens from the modified |
| 159 // modifiedSource until the end of the just scanned token is greater than or | 170 // source until the end of the just scanned token is greater than or equal |
| 160 // equal to end of the scan region in the modified source. Include trailing | 171 // to end of the scan region in the modified source. Include trailing |
| 161 // characters of any token that was split as a result of inserted text, | 172 // characters of any token that was split as a result of inserted text, as |
| 162 // as in "ab" --> "a.b". | 173 // in "ab" --> "a.b". |
| 163 // | 174 // |
| 164 _reader.offset = scanStart - 1; | 175 Token replacementStart = _scanRange(scanStart, scanEnd); |
| 165 int next = _reader.advance(); | 176 oldLeftToken.setNext(replacementStart); |
| 166 while (next != -1 && _reader.offset <= scanEnd) { | 177 Token replacementEnd = _findEof(replacementStart).previous; |
| 167 next = bigSwitch(next); | 178 replacementEnd.setNext(stream); |
| 168 } | |
| 169 // | 179 // |
| 170 // Copy the remaining tokens in the original stream, but apply the delta to | 180 // Apply the delta to the tokens after the last new token. |
| 171 // the token's offset. | |
| 172 // | 181 // |
| 173 if (originalStream.type == TokenType.EOF) { | 182 _updateOffsets(stream, delta); |
| 174 _copyAndAdvance(originalStream, delta); | 183 _rightToken = stream; |
| 175 _rightToken = tail; | |
| 176 _rightToken.setNextWithoutSettingPrevious(_rightToken); | |
| 177 } else { | |
| 178 originalStream = _copyAndAdvance(originalStream, delta); | |
| 179 _rightToken = tail; | |
| 180 while (originalStream.type != TokenType.EOF) { | |
| 181 originalStream = _copyAndAdvance(originalStream, delta); | |
| 182 } | |
| 183 Token eof = _copyAndAdvance(originalStream, delta); | |
| 184 eof.setNextWithoutSettingPrevious(eof); | |
| 185 } | |
| 186 // | 184 // |
| 187 // If the index is immediately after an existing token and the inserted | 185 // If the index is immediately after an existing token and the inserted |
| 188 // characters did not change that original token, then adjust the leftToken | 186 // characters did not change that original token, then adjust the leftToken |
| 189 // to be the next token. For example, in "a; c;" --> "a;b c;", the leftToken | 187 // to be the next token. For example, in "a; c;" --> "a;b c;", the leftToken |
| 190 // was ";", but this code advances it to "b" since "b" is the first new | 188 // was ";", but this code advances it to "b" since "b" is the first new |
| 191 // token. | 189 // token. |
| 192 // | 190 // |
| 193 Token newFirst = _leftToken.next; | 191 Token newFirst = _leftToken.next; |
| 194 while (!identical(newFirst, _rightToken) && | 192 while (!identical(newFirst, _rightToken) && |
| 195 !identical(oldFirst, oldRightToken) && | 193 !identical(oldFirst, oldRightToken) && |
| 196 newFirst.type != TokenType.EOF && | 194 newFirst.type != TokenType.EOF && |
| 197 _equalTokens(oldFirst, newFirst)) { | 195 _equalTokens(oldFirst, newFirst)) { |
| 198 _tokenMap.put(oldFirst, newFirst); | 196 _tokenMap.put(oldFirst, newFirst); |
| 199 oldLeftToken = oldFirst; | 197 oldLeftToken = oldFirst; |
|
Paul Berry
2014/11/20 22:41:59
Instead of these four statements, I think we need
Brian Wilkerson
2014/11/20 22:55:02
I think we want even bigger changes. My plan is to
| |
| 200 oldFirst = oldFirst.next; | 198 oldFirst = oldFirst.next; |
| 201 _leftToken = newFirst; | 199 _leftToken = newFirst; |
| 202 newFirst = newFirst.next; | 200 newFirst = newFirst.next; |
| 203 } | 201 } |
| 204 Token newLast = _rightToken.previous; | 202 Token newLast = _rightToken.previous; |
| 205 while (!identical(newLast, _leftToken) && | 203 while (!identical(newLast, _leftToken) && |
| 206 !identical(oldLast, oldLeftToken) && | 204 !identical(oldLast, oldLeftToken) && |
| 207 newLast.type != TokenType.EOF && | 205 newLast.type != TokenType.EOF && |
| 208 _equalTokens(oldLast, newLast)) { | 206 _equalTokens(oldLast, newLast)) { |
| 209 _tokenMap.put(oldLast, newLast); | 207 _tokenMap.put(oldLast, newLast); |
| 210 oldRightToken = oldLast; | 208 oldRightToken = oldLast; |
|
Paul Berry
2014/11/20 22:41:59
Similar comment here, mutatis mutandis.
| |
| 211 oldLast = oldLast.previous; | 209 oldLast = oldLast.previous; |
| 212 _rightToken = newLast; | 210 _rightToken = newLast; |
| 213 newLast = newLast.previous; | 211 newLast = newLast.previous; |
| 214 } | 212 } |
| 215 _hasNonWhitespaceChange = !identical(_leftToken.next, _rightToken) || | 213 _hasNonWhitespaceChange = !identical(_leftToken.next, _rightToken) || |
| 216 !identical(oldLeftToken.next, oldRightToken); | 214 !identical(oldLeftToken.next, oldRightToken); |
| 217 // | 215 // |
| 218 // TODO(brianwilkerson) Begin tokens are not getting associated with the | 216 // TODO(brianwilkerson) Begin tokens are not getting associated with the |
| 219 // corresponding end tokens (because the end tokens have not been copied | 217 // corresponding end tokens (because the end tokens have not been copied |
| 220 // when we're copying the begin tokens). This could have implications for | 218 // when we're copying the begin tokens). This could have implications for |
| 221 // parsing. | 219 // parsing. |
| 222 // TODO(brianwilkerson) Update the lineInfo. | 220 // TODO(brianwilkerson) Update the lineInfo. |
| 223 // | 221 // |
| 224 return firstToken; | 222 return leftEof.next; |
| 225 } | |
| 226 | |
| 227 Token _copyAndAdvance(Token originalToken, int delta) { | |
| 228 Token copiedToken = originalToken.copy(); | |
| 229 _tokenMap.put(originalToken, copiedToken); | |
| 230 copiedToken.offset += delta; | |
| 231 appendToken(copiedToken); | |
| 232 Token originalComment = originalToken.precedingComments; | |
| 233 Token copiedComment = originalToken.precedingComments; | |
| 234 while (originalComment != null) { | |
| 235 _tokenMap.put(originalComment, copiedComment); | |
| 236 originalComment = originalComment.next; | |
| 237 copiedComment = copiedComment.next; | |
| 238 } | |
| 239 return originalToken.next; | |
| 240 } | 223 } |
| 241 | 224 |
| 242 /** | 225 /** |
| 243 * Return `true` if the two tokens are equal to each other. For the purposes o f the | 226 * Return `true` if the two tokens are equal to each other. For the purposes o f the |
| 244 * incremental scanner, two tokens are equal if they have the same type and le xeme. | 227 * incremental scanner, two tokens are equal if they have the same type and le xeme. |
| 245 * | 228 * |
| 246 * @param oldToken the token from the old stream that is being compared | 229 * @param oldToken the token from the old stream that is being compared |
| 247 * @param newToken the token from the new stream that is being compared | 230 * @param newToken the token from the new stream that is being compared |
| 248 * @return `true` if the two tokens are equal to each other | 231 * @return `true` if the two tokens are equal to each other |
| 249 */ | 232 */ |
| 250 bool _equalTokens(Token oldToken, Token newToken) => | 233 bool _equalTokens(Token oldToken, Token newToken) => |
| 251 oldToken.type == newToken.type && | 234 oldToken.type == newToken.type && |
| 252 oldToken.length == newToken.length && | 235 oldToken.length == newToken.length && |
| 253 oldToken.lexeme == newToken.lexeme; | 236 oldToken.lexeme == newToken.lexeme; |
| 237 | |
| 238 /** | |
| 239 * Given a [token], return the EOF token that follows the token. | |
| 240 */ | |
| 241 Token _findEof(Token token) { | |
| 242 while (token.type != TokenType.EOF) { | |
| 243 token = token.next; | |
| 244 } | |
| 245 return token; | |
| 246 } | |
| 247 | |
| 248 /** | |
| 249 * Scan the token between the [start] (inclusive) and [end] (exclusive) | |
| 250 * offsets. | |
| 251 */ | |
| 252 Token _scanRange(int start, int end) { | |
| 253 Scanner scanner = new Scanner( | |
| 254 source, | |
| 255 new CharacterRangeReader(reader, start, end), | |
| 256 errorListener); | |
| 257 return scanner.tokenize(); | |
| 258 } | |
| 259 | |
| 260 /** | |
| 261 * Update the offsets of every token from the given [token] to the end of the | |
| 262 * stream by adding the given [delta]. | |
| 263 */ | |
| 264 void _updateOffsets(Token token, int delta) { | |
| 265 while (token.type != TokenType.EOF) { | |
| 266 _tokenMap.put(token, token); | |
| 267 token.offset += delta; | |
| 268 Token comment = token.precedingComments; | |
| 269 while (comment != null) { | |
| 270 comment.offset += delta; | |
| 271 comment = comment.next; | |
| 272 } | |
| 273 token = token.next; | |
| 274 } | |
| 275 _tokenMap.put(token, token); | |
| 276 token.offset += delta; | |
|
Paul Berry
2014/11/20 22:41:59
Does the EOF token ever have precedingComments? (
Brian Wilkerson
2014/11/20 22:55:02
Yes, when there are comments at the end of the fil
| |
| 277 } | |
| 254 } | 278 } |
| OLD | NEW |