| Index: frog/leg/tree/dartstring.dart
|
| diff --git a/frog/leg/tree/dartstring.dart b/frog/leg/tree/dartstring.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e46a03af67be8f698e67962c378fdbb8ebda5e8b
|
| --- /dev/null
|
| +++ b/frog/leg/tree/dartstring.dart
|
| @@ -0,0 +1,209 @@
|
| +// 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;
|
| + }
|
| +}
|
| +
|
|
|