OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 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. |
| 4 |
| 5 import 'dart:collection'; |
| 6 |
| 7 import 'package:analyzer/src/generated/ast.dart'; |
| 8 |
| 9 // ASCII character codes. |
| 10 |
| 11 const _zero = 0x30; |
| 12 const _nine = 0x39; |
| 13 const _backslash = 0x5C; |
| 14 const _openCurly = 0x7B; |
| 15 const _closeCurly = 0x7D; |
| 16 const _capitalA = 0x41; |
| 17 const _capitalZ = 0x5A; |
| 18 const _a = 0x61; |
| 19 const _n = 0x6E; |
| 20 const _r = 0x72; |
| 21 const _f = 0x66; |
| 22 const _b = 0x62; |
| 23 const _t = 0x74; |
| 24 const _u = 0x75; |
| 25 const _v = 0x76; |
| 26 const _x = 0x78; |
| 27 const _z = 0x7A; |
| 28 const _newline = 0xA; |
| 29 const _carriageReturn = 0xD; |
| 30 const _formFeed = 0xC; |
| 31 const _backspace = 0x8; |
| 32 const _tab = 0x9; |
| 33 const _verticalTab = 0xB; |
| 34 |
| 35 /// An iterator over the runes in the value of a [StringLiteral]. |
| 36 /// |
| 37 /// In addition to exposing the values of the runes themselves, this also |
| 38 /// exposes the offset of the current rune in the Dart source file. |
| 39 class StringLiteralIterator extends Iterator<int> { |
| 40 int get current => _current; |
| 41 int _current; |
| 42 |
| 43 /// The offset of the beginning of [current] in the Dart source file that |
| 44 /// contains the string literal. |
| 45 /// |
| 46 /// Before iteration begins, this points to the character before the first |
| 47 /// rune. |
| 48 int get offset => _offset; |
| 49 int _offset; |
| 50 |
| 51 /// The offset of the next rune. |
| 52 /// |
| 53 /// This isn't necessarily just `offset + 1`, since a single rune may be |
| 54 /// represented by multiple characters in the source file, or a string literal |
| 55 /// may be composed of several adjacent string literals. |
| 56 int _nextOffset; |
| 57 |
| 58 /// All [SimpleStringLiteral]s that compose the input literal. |
| 59 /// |
| 60 /// If the input literal is itself a [SimpleStringLiteral], this just contains |
| 61 /// that literal; otherwise, the literal is an [AdjacentStrings], and this |
| 62 /// contains its component literals. |
| 63 final _strings = new Queue<SimpleStringLiteral>(); |
| 64 |
| 65 /// Whether this is a raw string that begins with `r`. |
| 66 /// |
| 67 /// This is necessary for knowing how to parse escape sequences. |
| 68 bool _isRaw; |
| 69 |
| 70 /// The iterator over the runes in the Dart source file. |
| 71 /// |
| 72 /// When switching to a new string in [_strings], this is updated to point to |
| 73 /// that string's component runes. |
| 74 Iterator<int> _runes; |
| 75 |
| 76 /// Whether this has finished iterating. |
| 77 bool _done = false; |
| 78 |
| 79 /// Creates a new [StringLiteralIterator] iterating over the contents of |
| 80 /// [literal]. |
| 81 /// |
| 82 /// Throws an [ArgumentError] if [literal] contains interpolated strings. |
| 83 StringLiteralIterator(StringLiteral literal) { |
| 84 if (literal is StringInterpolation) { |
| 85 throw new ArgumentError("Can't iterate over an interpolated string."); |
| 86 } else if (literal is SimpleStringLiteral) { |
| 87 _strings.add(literal); |
| 88 } else { |
| 89 assert(literal is AdjacentStrings); |
| 90 |
| 91 for (var string in literal.strings) { |
| 92 if (string is StringInterpolation) { |
| 93 throw new ArgumentError("Can't iterate over an interpolated string."); |
| 94 } |
| 95 assert(string is SimpleStringLiteral); |
| 96 _strings.add(string); |
| 97 } |
| 98 } |
| 99 |
| 100 _offset = _strings.first.contentsOffset - 1; |
| 101 } |
| 102 |
| 103 bool moveNext() { |
| 104 if (_done) return false; |
| 105 |
| 106 // If we're at beginning of a [SimpleStringLiteral], move forward until |
| 107 // there's actually text to consume. |
| 108 while (_runes == null || _runes.current == null) { |
| 109 if (_strings.isEmpty) { |
| 110 // Move the offset past the end of the text. |
| 111 _offset = _nextOffset; |
| 112 _current = null; |
| 113 return false; |
| 114 } |
| 115 |
| 116 var string = _strings.removeFirst(); |
| 117 var start = string.contentsOffset - string.offset; |
| 118 |
| 119 // Compensate for the opening and closing quotes. |
| 120 var end = start + string.literal.lexeme.length - |
| 121 2 * (string.isMultiline ? 3 : 1) - |
| 122 (string.isRaw ? 1 : 0); |
| 123 var text = string.literal.lexeme.substring(start, end); |
| 124 |
| 125 _nextOffset = string.contentsOffset; |
| 126 _isRaw = string.isRaw; |
| 127 _runes = text.runes.iterator; |
| 128 _runes.moveNext(); |
| 129 } |
| 130 |
| 131 _offset = _nextOffset; |
| 132 _current = _nextRune(); |
| 133 if (_current != null) return true; |
| 134 |
| 135 // If we encounter a parse failure, stop moving forward immediately. |
| 136 _strings.clear(); |
| 137 return false; |
| 138 } |
| 139 |
| 140 /// Consume and return the next rune. |
| 141 int _nextRune() { |
| 142 if (_isRaw || _runes.current != _backslash) { |
| 143 var rune = _runes.current; |
| 144 _moveRunesNext(); |
| 145 return rune; |
| 146 } |
| 147 |
| 148 if (!_moveRunesNext()) return null; |
| 149 return _parseEscapeSequence(); |
| 150 } |
| 151 |
| 152 /// Parse an escape sequence in the underlying Dart text. |
| 153 /// |
| 154 /// This assumes that a backslash has already been consumed. It leaves the |
| 155 /// [_runes] cursor on the first character after the escape sequence. |
| 156 int _parseEscapeSequence() { |
| 157 switch (_runes.current) { |
| 158 case _n: |
| 159 _moveRunesNext(); |
| 160 return _newline; |
| 161 case _r: |
| 162 _moveRunesNext(); |
| 163 return _carriageReturn; |
| 164 case _f: |
| 165 _moveRunesNext(); |
| 166 return _formFeed; |
| 167 case _b: |
| 168 _moveRunesNext(); |
| 169 return _backspace; |
| 170 case _t: |
| 171 _moveRunesNext(); |
| 172 return _tab; |
| 173 case _v: |
| 174 _moveRunesNext(); |
| 175 return _verticalTab; |
| 176 case _x: |
| 177 if (!_moveRunesNext()) return null; |
| 178 return _parseHex(2); |
| 179 case _u: |
| 180 if (!_moveRunesNext()) return null; |
| 181 if (_runes.current != _openCurly) return _parseHex(4); |
| 182 if (!_moveRunesNext()) return null; |
| 183 |
| 184 var number = _parseHexSequence(); |
| 185 if (_runes.current != _closeCurly) return null; |
| 186 if (!_moveRunesNext()) return null; |
| 187 return number; |
| 188 default: |
| 189 var rune = _runes.current; |
| 190 _moveRunesNext(); |
| 191 return rune; |
| 192 } |
| 193 } |
| 194 |
| 195 /// Parse a variable-length sequence of hexadecimal digits and returns their |
| 196 /// value as an [int]. |
| 197 /// |
| 198 /// This parses digits as they appear in a unicode escape sequence: one to six |
| 199 /// hex digits. |
| 200 int _parseHexSequence() { |
| 201 var number = _parseHexDigit(_runes.current); |
| 202 if (number == null) return null; |
| 203 if (!_moveRunesNext()) return null; |
| 204 |
| 205 for (var i = 0; i < 5; i++) { |
| 206 var digit = _parseHexDigit(_runes.current); |
| 207 if (digit == null) break; |
| 208 number = number * 16 + digit; |
| 209 if (!_moveRunesNext()) return null; |
| 210 } |
| 211 |
| 212 return number; |
| 213 } |
| 214 |
| 215 /// Parses [digits] hexadecimal digits and returns their value as an [int]. |
| 216 int _parseHex(int digits) { |
| 217 var number = 0; |
| 218 for (var i = 0; i < digits; i++) { |
| 219 if (_runes.current == null) return null; |
| 220 var digit = _parseHexDigit(_runes.current); |
| 221 if (digit == null) return null; |
| 222 number = number * 16 + digit; |
| 223 _moveRunesNext(); |
| 224 } |
| 225 return number; |
| 226 } |
| 227 |
| 228 /// Parses a single hexadecimal digit. |
| 229 int _parseHexDigit(int rune) { |
| 230 if (rune < _zero) return null; |
| 231 if (rune <= _nine) return rune - _zero; |
| 232 if (rune < _capitalA) return null; |
| 233 if (rune <= _capitalZ) return 10 + rune - _capitalA; |
| 234 if (rune < _a) return null; |
| 235 if (rune <= _z) return 10 + rune - _a; |
| 236 return null; |
| 237 } |
| 238 |
| 239 /// Move [_runes] to the next rune and update [_nextOffset]. |
| 240 bool _moveRunesNext() { |
| 241 var result = _runes.moveNext(); |
| 242 _nextOffset++; |
| 243 return result; |
| 244 } |
| 245 } |
OLD | NEW |