dc099c4a00
Review URL: https://chromiumcodereview.appspot.com//9873021 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@5926 260f80e4-7a28-3924-810f-c04153c831b5
210 lines
6.3 KiB
Dart
210 lines
6.3 KiB
Dart
// 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.
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
class DartString implements Iterable<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(SourceString source, int length) =>
|
|
new RawSourceDartString(source, length);
|
|
factory DartString.escapedString(SourceString 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();
|
|
abstract int get length();
|
|
bool isEmpty() => length == 0;
|
|
abstract Iterator<int> iterator();
|
|
abstract 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.hasNext()) {
|
|
if (it1.next() != it2.next()) return false;
|
|
}
|
|
return true;
|
|
}
|
|
String toString() => "DartString#${length}:${slowToString()}";
|
|
abstract SourceString get source();
|
|
}
|
|
|
|
|
|
/**
|
|
* 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> iterator() => new StringCodeIterator(string);
|
|
String slowToString() => string;
|
|
SourceString get source() => new StringWrapper(string);
|
|
}
|
|
|
|
/**
|
|
* A [DartString] where the content comes from a slice of the program source.
|
|
*/
|
|
class SourceBasedDartString extends DartString {
|
|
String toStringCache = null;
|
|
final SourceString source;
|
|
final int length;
|
|
SourceBasedDartString(this.source, this.length);
|
|
abstract Iterator<int> 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> iterator() => source.iterator();
|
|
String slowToString() {
|
|
if (toStringCache !== null) return toStringCache;
|
|
toStringCache = source.slowToString();
|
|
return toStringCache;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* General case of a [SourceBasedDartString] where the source might contain
|
|
* escapes.
|
|
*/
|
|
class EscapedSourceDartString extends SourceBasedDartString {
|
|
EscapedSourceDartString(source, length) : super(source, length);
|
|
Iterator<int> iterator() {
|
|
if (toStringCache !== null) return new StringCodeIterator(toStringCache);
|
|
return new StringEscapeIterator(source);
|
|
}
|
|
String slowToString() {
|
|
if (toStringCache !== null) return toStringCache;
|
|
StringBuffer buffer = new StringBuffer();
|
|
StringEscapeIterator it = new StringEscapeIterator(source);
|
|
while (it.hasNext()) {
|
|
buffer.addCharCode(it.next());
|
|
}
|
|
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> iterator() => new ConsDartStringIterator(this);
|
|
|
|
String slowToString() {
|
|
if (toStringCache !== null) return toStringCache;
|
|
toStringCache = left.slowToString().concat(right.slowToString());
|
|
return toStringCache;
|
|
}
|
|
SourceString get source() => new StringWrapper(slowToString());
|
|
}
|
|
|
|
class ConsDartStringIterator implements Iterator<int> {
|
|
Iterator<int> current;
|
|
DartString right;
|
|
bool hasNextLookAhead;
|
|
ConsDartStringIterator(ConsDartString cons)
|
|
: current = cons.left.iterator(),
|
|
right = cons.right {
|
|
hasNextLookAhead = current.hasNext();
|
|
if (!hasNextLookAhead) {
|
|
nextPart();
|
|
}
|
|
}
|
|
bool hasNext() {
|
|
return hasNextLookAhead;
|
|
}
|
|
int next() {
|
|
assert(hasNextLookAhead);
|
|
int result = current.next();
|
|
hasNextLookAhead = current.hasNext();
|
|
if (!hasNextLookAhead) {
|
|
nextPart();
|
|
}
|
|
return result;
|
|
}
|
|
void nextPart() {
|
|
if (right !== null) {
|
|
current = right.iterator();
|
|
right = null;
|
|
hasNextLookAhead = current.hasNext();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*Iterator that returns the actual string contents of a string with escapes.
|
|
*/
|
|
class StringEscapeIterator implements Iterator<int>{
|
|
final Iterator<int> source;
|
|
StringEscapeIterator(SourceString source) : this.source = source.iterator();
|
|
bool hasNext() => source.hasNext();
|
|
int next() {
|
|
int code = source.next();
|
|
if (code !== $BACKSLASH) {
|
|
return code;
|
|
}
|
|
code = source.next();
|
|
if (code === $n) return $LF;
|
|
if (code === $r) return $CR;
|
|
if (code === $t) return $TAB;
|
|
if (code === $b) return $BS;
|
|
if (code === $f) return $FF;
|
|
if (code === $v) return $VTAB;
|
|
if (code === $x) {
|
|
int value = hexDigitValue(source.next());
|
|
value = value * 16 + hexDigitValue(source.next());
|
|
return value;
|
|
}
|
|
if (code === $u) {
|
|
int value = 0;
|
|
code = source.next();
|
|
if (code === $OPEN_CURLY_BRACKET) {
|
|
for (code = source.next();
|
|
code != $CLOSE_CURLY_BRACKET;
|
|
code = source.next()) {
|
|
value = value * 16 + hexDigitValue(code);
|
|
}
|
|
return value;
|
|
}
|
|
// Four digit hex value.
|
|
value = hexDigitValue(code);
|
|
for (int i = 0; i < 3; i++) {
|
|
code = source.next();
|
|
value = value * 16 + hexDigitValue(code);
|
|
}
|
|
return value;
|
|
}
|
|
return code;
|
|
}
|
|
}
|
|
|