| // Copyright (c) 2012, 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:front_end/src/fasta/scanner/characters.dart'; |
| |
| /** |
| * The [DartString] type represents a Dart string value as a sequence of Unicode |
| * Scalar Values. |
| * After parsing, any valid [LiteralString] will contain a [DartString] |
| * representing its content after removing quotes and resolving escapes in |
| * its source. |
| */ |
| abstract class DartString extends IterableBase<int> { |
| factory DartString.empty() => const LiteralDartString(""); |
| // This is a convenience constructor. If you need a const literal DartString, |
| // use [const LiteralDartString(string)] directly. |
| factory DartString.literal(String string) => new LiteralDartString(string); |
| factory DartString.rawString(String source, int length) => |
| new RawSourceDartString(source, length); |
| factory DartString.escapedString(String source, int length) => |
| new EscapedSourceDartString(source, length); |
| factory DartString.concat(DartString first, DartString second) { |
| if (first.isEmpty) return second; |
| if (second.isEmpty) return first; |
| return new ConsDartString(first, second); |
| } |
| const DartString(); |
| |
| /** |
| * The length of this [DartString], which is the string length after |
| * escapes have been resolved. |
| */ |
| int get length; |
| bool get isEmpty => length == 0; |
| |
| Iterator<int> get iterator; |
| |
| /** |
| * The string represented by this [DartString]. |
| */ |
| String slowToString(); |
| |
| bool operator ==(var other) { |
| if (other is! DartString) return false; |
| DartString otherString = other; |
| if (length != otherString.length) return false; |
| Iterator it1 = iterator; |
| Iterator it2 = otherString.iterator; |
| while (it1.moveNext()) { |
| if (!it2.moveNext()) return false; |
| if (it1.current != it2.current) return false; |
| } |
| return true; |
| } |
| |
| int get hashCode => throw new UnsupportedError('DartString.hashCode'); |
| |
| /** |
| * A textual representation of this [DartString] with some debugging |
| * information. |
| */ |
| String toString() => "DartString#${length}:${slowToString()}"; |
| } |
| |
| /** |
| * A [DartString] where the content is represented by an actual [String]. |
| */ |
| class LiteralDartString extends DartString { |
| final String string; |
| const LiteralDartString(this.string); |
| int get length => string.length; |
| Iterator<int> get iterator => string.codeUnits.iterator; |
| String slowToString() => string; |
| } |
| |
| /** |
| * A [DartString] whereSource the content comes from a slice of the program |
| * source. |
| */ |
| abstract class SourceBasedDartString extends DartString { |
| /** |
| * The source string containing explicit escapes from which this [DartString] |
| * is built. |
| */ |
| final String source; |
| final int length; |
| SourceBasedDartString(this.source, this.length); |
| Iterator<int> get iterator; |
| } |
| |
| /** |
| * Special case of a [SourceBasedDartString] where we know the source doesn't |
| * contain any escapes. |
| */ |
| class RawSourceDartString extends SourceBasedDartString { |
| RawSourceDartString(source, length) : super(source, length); |
| Iterator<int> get iterator => source.codeUnits.iterator; |
| String slowToString() => source; |
| } |
| |
| /** |
| * General case of a [SourceBasedDartString] where the source might contain |
| * escapes. |
| */ |
| class EscapedSourceDartString extends SourceBasedDartString { |
| String toStringCache; |
| EscapedSourceDartString(source, length) : super(source, length); |
| Iterator<int> get iterator { |
| if (toStringCache != null) return toStringCache.codeUnits.iterator; |
| return new StringEscapeIterator(source); |
| } |
| |
| String slowToString() { |
| if (toStringCache != null) return toStringCache; |
| StringBuffer buffer = new StringBuffer(); |
| StringEscapeIterator it = new StringEscapeIterator(source); |
| while (it.moveNext()) { |
| buffer.writeCharCode(it.current); |
| } |
| toStringCache = buffer.toString(); |
| return toStringCache; |
| } |
| } |
| |
| /** |
| * The concatenation of two [DartString]s. |
| */ |
| class ConsDartString extends DartString { |
| final DartString left; |
| final DartString right; |
| final int length; |
| String toStringCache; |
| ConsDartString(DartString left, DartString right) |
| : this.left = left, |
| this.right = right, |
| length = left.length + right.length; |
| |
| Iterator<int> get iterator => new ConsDartStringIterator(this); |
| |
| String slowToString() { |
| if (toStringCache != null) return toStringCache; |
| toStringCache = left.slowToString() + right.slowToString(); |
| return toStringCache; |
| } |
| |
| String get source => slowToString(); |
| } |
| |
| class ConsDartStringIterator implements Iterator<int> { |
| HasNextIterator<int> currentIterator; |
| DartString right; |
| bool hasNextLookAhead; |
| int _current = null; |
| |
| ConsDartStringIterator(ConsDartString cons) |
| : currentIterator = new HasNextIterator<int>(cons.left.iterator), |
| right = cons.right { |
| hasNextLookAhead = currentIterator.hasNext; |
| if (!hasNextLookAhead) { |
| nextPart(); |
| } |
| } |
| |
| int get current => _current; |
| |
| bool moveNext() { |
| if (!hasNextLookAhead) { |
| _current = null; |
| return false; |
| } |
| _current = currentIterator.next(); |
| hasNextLookAhead = currentIterator.hasNext; |
| if (!hasNextLookAhead) { |
| nextPart(); |
| } |
| return true; |
| } |
| |
| void nextPart() { |
| if (right != null) { |
| currentIterator = new HasNextIterator<int>(right.iterator); |
| right = null; |
| hasNextLookAhead = currentIterator.hasNext; |
| } |
| } |
| } |
| |
| /** |
| *Iterator that returns the actual string contents of a string with escapes. |
| */ |
| class StringEscapeIterator implements Iterator<int> { |
| final Iterator<int> source; |
| int _current = null; |
| |
| StringEscapeIterator(String source) : this.source = source.codeUnits.iterator; |
| |
| int get current => _current; |
| |
| bool moveNext() { |
| if (!source.moveNext()) { |
| _current = null; |
| return false; |
| } |
| int code = source.current; |
| if (code != $BACKSLASH) { |
| _current = code; |
| return true; |
| } |
| source.moveNext(); |
| code = source.current; |
| switch (code) { |
| case $n: |
| _current = $LF; |
| break; |
| case $r: |
| _current = $CR; |
| break; |
| case $t: |
| _current = $TAB; |
| break; |
| case $b: |
| _current = $BS; |
| break; |
| case $f: |
| _current = $FF; |
| break; |
| case $v: |
| _current = $VTAB; |
| break; |
| case $x: |
| source.moveNext(); |
| int value = hexDigitValue(source.current); |
| source.moveNext(); |
| value = value * 16 + hexDigitValue(source.current); |
| _current = value; |
| break; |
| case $u: |
| int value = 0; |
| source.moveNext(); |
| code = source.current; |
| if (code == $OPEN_CURLY_BRACKET) { |
| source.moveNext(); |
| while (source.current != $CLOSE_CURLY_BRACKET) { |
| value = value * 16 + hexDigitValue(source.current); |
| source.moveNext(); |
| } |
| _current = value; |
| break; |
| } |
| // Four digit hex value. |
| value = hexDigitValue(code); |
| for (int i = 0; i < 3; i++) { |
| source.moveNext(); |
| value = value * 16 + hexDigitValue(source.current); |
| } |
| _current = value; |
| break; |
| default: |
| _current = code; |
| } |
| return true; |
| } |
| } |