Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(401)

Side by Side Diff: lib/src/util/string_literal_iterator.dart

Issue 1034813004: Add code to contextualize a span relative to its original source file. (Closed) Base URL: git@github.com:dart-lang/unittest@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/util/dart.dart ('k') | test/runner/runner_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « lib/src/util/dart.dart ('k') | test/runner/runner_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698