blob: 80f2091e2459be452bbd9bdf83c133392d93c70c [file] [log] [blame]
// 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.
part of tree;
/**
* 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(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();
int get length;
bool get isEmpty => length == 0;
Iterator<int> get iterator;
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');
String toString() => "DartString#${length}:${slowToString()}";
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> get 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.
*/
abstract class SourceBasedDartString extends DartString {
String toStringCache = null;
final SourceString 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.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> get 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.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;
}
SourceString get source => new StringWrapper(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(SourceString source) : this.source = source.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;
}
}