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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/src/util/dart.dart ('k') | test/runner/runner_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/util/string_literal_iterator.dart
diff --git a/lib/src/util/string_literal_iterator.dart b/lib/src/util/string_literal_iterator.dart
new file mode 100644
index 0000000000000000000000000000000000000000..67fffbb58a76cab577f568504fea3ca96e972fa2
--- /dev/null
+++ b/lib/src/util/string_literal_iterator.dart
@@ -0,0 +1,245 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:collection';
+
+import 'package:analyzer/src/generated/ast.dart';
+
+// ASCII character codes.
+
+const _zero = 0x30;
+const _nine = 0x39;
+const _backslash = 0x5C;
+const _openCurly = 0x7B;
+const _closeCurly = 0x7D;
+const _capitalA = 0x41;
+const _capitalZ = 0x5A;
+const _a = 0x61;
+const _n = 0x6E;
+const _r = 0x72;
+const _f = 0x66;
+const _b = 0x62;
+const _t = 0x74;
+const _u = 0x75;
+const _v = 0x76;
+const _x = 0x78;
+const _z = 0x7A;
+const _newline = 0xA;
+const _carriageReturn = 0xD;
+const _formFeed = 0xC;
+const _backspace = 0x8;
+const _tab = 0x9;
+const _verticalTab = 0xB;
+
+/// An iterator over the runes in the value of a [StringLiteral].
+///
+/// In addition to exposing the values of the runes themselves, this also
+/// exposes the offset of the current rune in the Dart source file.
+class StringLiteralIterator extends Iterator<int> {
+ int get current => _current;
+ int _current;
+
+ /// The offset of the beginning of [current] in the Dart source file that
+ /// contains the string literal.
+ ///
+ /// Before iteration begins, this points to the character before the first
+ /// rune.
+ int get offset => _offset;
+ int _offset;
+
+ /// The offset of the next rune.
+ ///
+ /// This isn't necessarily just `offset + 1`, since a single rune may be
+ /// represented by multiple characters in the source file, or a string literal
+ /// may be composed of several adjacent string literals.
+ int _nextOffset;
+
+ /// All [SimpleStringLiteral]s that compose the input literal.
+ ///
+ /// If the input literal is itself a [SimpleStringLiteral], this just contains
+ /// that literal; otherwise, the literal is an [AdjacentStrings], and this
+ /// contains its component literals.
+ final _strings = new Queue<SimpleStringLiteral>();
+
+ /// Whether this is a raw string that begins with `r`.
+ ///
+ /// This is necessary for knowing how to parse escape sequences.
+ bool _isRaw;
+
+ /// The iterator over the runes in the Dart source file.
+ ///
+ /// When switching to a new string in [_strings], this is updated to point to
+ /// that string's component runes.
+ Iterator<int> _runes;
+
+ /// Whether this has finished iterating.
+ bool _done = false;
+
+ /// Creates a new [StringLiteralIterator] iterating over the contents of
+ /// [literal].
+ ///
+ /// Throws an [ArgumentError] if [literal] contains interpolated strings.
+ StringLiteralIterator(StringLiteral literal) {
+ if (literal is StringInterpolation) {
+ throw new ArgumentError("Can't iterate over an interpolated string.");
+ } else if (literal is SimpleStringLiteral) {
+ _strings.add(literal);
+ } else {
+ assert(literal is AdjacentStrings);
+
+ for (var string in literal.strings) {
+ if (string is StringInterpolation) {
+ throw new ArgumentError("Can't iterate over an interpolated string.");
+ }
+ assert(string is SimpleStringLiteral);
+ _strings.add(string);
+ }
+ }
+
+ _offset = _strings.first.contentsOffset - 1;
+ }
+
+ bool moveNext() {
+ if (_done) return false;
+
+ // If we're at beginning of a [SimpleStringLiteral], move forward until
+ // there's actually text to consume.
+ while (_runes == null || _runes.current == null) {
+ if (_strings.isEmpty) {
+ // Move the offset past the end of the text.
+ _offset = _nextOffset;
+ _current = null;
+ return false;
+ }
+
+ var string = _strings.removeFirst();
+ var start = string.contentsOffset - string.offset;
+
+ // Compensate for the opening and closing quotes.
+ var end = start + string.literal.lexeme.length -
+ 2 * (string.isMultiline ? 3 : 1) -
+ (string.isRaw ? 1 : 0);
+ var text = string.literal.lexeme.substring(start, end);
+
+ _nextOffset = string.contentsOffset;
+ _isRaw = string.isRaw;
+ _runes = text.runes.iterator;
+ _runes.moveNext();
+ }
+
+ _offset = _nextOffset;
+ _current = _nextRune();
+ if (_current != null) return true;
+
+ // If we encounter a parse failure, stop moving forward immediately.
+ _strings.clear();
+ return false;
+ }
+
+ /// Consume and return the next rune.
+ int _nextRune() {
+ if (_isRaw || _runes.current != _backslash) {
+ var rune = _runes.current;
+ _moveRunesNext();
+ return rune;
+ }
+
+ if (!_moveRunesNext()) return null;
+ return _parseEscapeSequence();
+ }
+
+ /// Parse an escape sequence in the underlying Dart text.
+ ///
+ /// This assumes that a backslash has already been consumed. It leaves the
+ /// [_runes] cursor on the first character after the escape sequence.
+ int _parseEscapeSequence() {
+ switch (_runes.current) {
+ case _n:
+ _moveRunesNext();
+ return _newline;
+ case _r:
+ _moveRunesNext();
+ return _carriageReturn;
+ case _f:
+ _moveRunesNext();
+ return _formFeed;
+ case _b:
+ _moveRunesNext();
+ return _backspace;
+ case _t:
+ _moveRunesNext();
+ return _tab;
+ case _v:
+ _moveRunesNext();
+ return _verticalTab;
+ case _x:
+ if (!_moveRunesNext()) return null;
+ return _parseHex(2);
+ case _u:
+ if (!_moveRunesNext()) return null;
+ if (_runes.current != _openCurly) return _parseHex(4);
+ if (!_moveRunesNext()) return null;
+
+ var number = _parseHexSequence();
+ if (_runes.current != _closeCurly) return null;
+ if (!_moveRunesNext()) return null;
+ return number;
+ default:
+ var rune = _runes.current;
+ _moveRunesNext();
+ return rune;
+ }
+ }
+
+ /// Parse a variable-length sequence of hexadecimal digits and returns their
+ /// value as an [int].
+ ///
+ /// This parses digits as they appear in a unicode escape sequence: one to six
+ /// hex digits.
+ int _parseHexSequence() {
+ var number = _parseHexDigit(_runes.current);
+ if (number == null) return null;
+ if (!_moveRunesNext()) return null;
+
+ for (var i = 0; i < 5; i++) {
+ var digit = _parseHexDigit(_runes.current);
+ if (digit == null) break;
+ number = number * 16 + digit;
+ if (!_moveRunesNext()) return null;
+ }
+
+ return number;
+ }
+
+ /// Parses [digits] hexadecimal digits and returns their value as an [int].
+ int _parseHex(int digits) {
+ var number = 0;
+ for (var i = 0; i < digits; i++) {
+ if (_runes.current == null) return null;
+ var digit = _parseHexDigit(_runes.current);
+ if (digit == null) return null;
+ number = number * 16 + digit;
+ _moveRunesNext();
+ }
+ return number;
+ }
+
+ /// Parses a single hexadecimal digit.
+ int _parseHexDigit(int rune) {
+ if (rune < _zero) return null;
+ if (rune <= _nine) return rune - _zero;
+ if (rune < _capitalA) return null;
+ if (rune <= _capitalZ) return 10 + rune - _capitalA;
+ if (rune < _a) return null;
+ if (rune <= _z) return 10 + rune - _a;
+ return null;
+ }
+
+ /// Move [_runes] to the next rune and update [_nextOffset].
+ bool _moveRunesNext() {
+ var result = _runes.moveNext();
+ _nextOffset++;
+ return result;
+ }
+}
« 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