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

Unified Diff: lib/yaml/model.dart

Issue 10153004: Add a basic YAML processor. Much of the language is still unimplemented. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 8 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
Index: lib/yaml/model.dart
diff --git a/lib/yaml/model.dart b/lib/yaml/model.dart
new file mode 100644
index 0000000000000000000000000000000000000000..3b92baaeb6e64d6bbb78d1173f7276c10a7d3871
--- /dev/null
+++ b/lib/yaml/model.dart
@@ -0,0 +1,240 @@
+// 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.
+
+// This file contains the node classes for the internal representations of YAML
+// documents. These nodes are used for both the serialization tree and the
+// representation graph.
+
+/** A tag that indicates the type of a YAML node. */
+class _Tag implements Hashable {
+ // TODO(nweiz): it would better match the semantics of the spec if there were
+ // a singleton instance of this class for each tag.
+
+ static final SCALAR_KIND = 0;
+ static final SEQUENCE_KIND = 1;
+ static final MAPPING_KIND = 2;
+
+ /** The name of the tag, either a URI or a local tag beginning with "!". */
+ final String name;
+
+ /** The kind of the tag: SCALAR_KIND, SEQUENCE_KIND, or MAPPING_KIND. */
+ final int kind;
+
+ _Tag(String this.name, int this.kind);
+
+ _Tag.scalar(String name) : this(name, SCALAR_KIND);
+ _Tag.sequence(String name) : this(name, SEQUENCE_KIND);
+ _Tag.mapping(String name) : this(name, MAPPING_KIND);
Bob Nystrom 2012/04/20 20:28:55 These are nice.
+
+ /** Returns the standard YAML tag URI for [type]. */
+ static String yaml(String type) => "tag:yaml.org,2002:$type";
+
+ /** Two tags are equal if their URIs are equal. */
+ operator ==(var other) {
Bob Nystrom 2012/04/20 20:28:55 No "var" here.
nweiz 2012/04/23 23:06:33 Done.
+ if (other is! _Tag) return false;
+ _Tag otherTag = other;
Bob Nystrom 2012/04/20 20:28:55 Unused var.
nweiz 2012/04/23 23:06:33 Done.
+ return name == other.name;
+ }
+
+ String toString() {
+ if (name.startsWith('tag:yaml.org,2002:')) {
Bob Nystrom 2012/04/20 20:28:55 Pull this string out into a constant?
nweiz 2012/04/23 23:06:33 Done.
+ return '!!${name.substring('tag:yaml.org,2002:'.length)}';
+ } else {
+ return '!<$name>';
+ }
+ }
+
+ int hashCode() => name.hashCode();
+}
+
+/** The abstract class for YAML nodes. */
+class _Node implements Hashable {
+ /** Every YAML node has a tag that describes its type. */
+ _Tag tag;
+
+ /** Any YAML node can have an anchor associated with it. */
+ String anchor;
+
+ _Node(_Tag this.tag, [String this.anchor]);
Bob Nystrom 2012/04/20 20:28:55 Don't need types for "this." params. Here and else
nweiz 2012/04/23 23:06:33 Done.
+
+ bool operator ==(var other) {
Bob Nystrom 2012/04/20 20:28:55 Remove "var" here and elsewhere.
nweiz 2012/04/23 23:06:33 Done.
+ if (other is! _Node) return false;
+ _Node otherNode = other;
Bob Nystrom 2012/04/20 20:28:55 You can ditch this and be consistent with _Tag.
nweiz 2012/04/23 23:06:33 Done.
+ return tag == otherNode.tag;
+ }
+
+ int hashCode() => _hashCode([tag, anchor]);
+
+ abstract visit(_Visitor v);
+}
+
+/** A sequence node represents an ordered list of nodes. */
+class _SequenceNode extends _Node {
+ /** The nodes in the sequence. */
+ List<_Node> content;
+
+ _SequenceNode(String tagName, List<_Node> this.content)
+ : super(new _Tag.sequence(tagName));
+
+ /** Two sequences are equal if their tags and contents are equal. */
+ bool operator ==(var other) {
+ // Should be super != other; bug 2554
+ if (!(super == other) || other is! _SequenceNode) return false;
+ _SequenceNode otherSeq = other;
+ if (content.length != otherSeq.content.length) return false;
+ for (var i = 0; i < content.length; i++) {
+ if (content[i] != otherSeq.content[i]) return false;
+ }
+ return true;
+ }
+
+ String toString() => '$tag [${Strings.join(content.map((e) => '$e'), ', ')}]';
Bob Nystrom 2012/04/20 20:28:55 This map here is weird. Seems silly that Strings.j
nweiz 2012/04/23 23:06:33 Bug 2690 filed.
+
+ int hashCode() => super.hashCode() ^ _hashCode(content);
+
+ visit(_Visitor v) => v.visitSequence(this);
+}
+
+/** An alias node is a reference to an anchor. */
+class _AliasNode extends _Node {
+ _AliasNode(String anchor) : super(new _Tag.scalar(_Tag.yaml("str")), anchor);
Bob Nystrom 2012/04/20 20:28:55 Why "str" here?
nweiz 2012/04/23 23:06:33 I needed something. The spec isn't clear on what t
+
+ visit(_Visitor v) => v.visitAlias(this);
+}
+
+/** A scalar node represents all YAML nodes that have a single value. */
+class _ScalarNode extends _Node {
+ /** The string value of the scalar node, if it was created by the parser. */
+ final String _content;
+
+ /** The Dart value of the scalar node, if it was created by the composer. */
+ final value;
+
+ /**
+ * Creates a new Scalar node.
+ *
+ * Exactly one of [content] and [value] should be specified. Content should be
+ * specified for a newly-parsed scalar that hasn't yet been composed. Value
+ * should be specified for a composed scalar, although `null` is a valid
+ * value.
+ */
+ _ScalarNode(String tagName, [String content, this.value])
+ : _content = content,
+ super(new _Tag.scalar(tagName));
+
+ /** Two scalars are equal if their string representations are equal. */
+ bool operator ==(var other) {
+ // Should be super != other; bug 2554
+ if (!(super == other) || other is! _ScalarNode) return false;
+ _ScalarNode otherScalar = other;
+ return content == other.content;
+ }
+
+ /**
+ * Returns the string representation of the scalar. After composition, this is
+ * equal to the canonical serialization of the value of the scalar.
+ */
+ String get content() => _content != null ? _content : canonicalContent;
+
+ /**
+ * Returns the canonical serialization of the value of the scalar. If the
+ * value isn't given, the result of this will be "null".
+ */
+ String get canonicalContent() {
+ if (value == null || value is bool || value is int) return '$value';
+
+ if (value is num) {
+ // 20 is the maximum value for this argument, which we use since YAML
+ // doesn't specify a maximum.
+ return value.toStringAsExponential(20).
+ replaceFirst(const RegExp("0+e"), "e");
+ }
+
+ if (value is String) {
+ var escapedValue = value.charCodes().map((c) {
Bob Nystrom 2012/04/20 20:28:55 For the common case where the string doesn't conta
nweiz 2012/04/23 23:06:33 Done.
+ switch (c) {
+ case _Parser.TAB: return "\\t";
+ case _Parser.LF: return "\\n";
+ case _Parser.CR: return "\\r";
+ case _Parser.DOUBLE_QUOTE: return '\\"';
+ case _Parser.NULL: return "\\0";
+ case _Parser.BELL: return "\\a";
+ case _Parser.BACKSPACE: return "\\b";
+ case _Parser.VERTICAL_TAB: return "\\v";
+ case _Parser.FORM_FEED: return "\\f";
+ case _Parser.ESCAPE: return "\\e";
+ case _Parser.BACKSLASH: return "\\\\";
+ case _Parser.NEL: return "\\N";
+ case _Parser.NBSP: return "\\_";
+ case _Parser.LINE_SEPARATOR: return "\\L";
+ case _Parser.PARAGRAPH_SEPARATOR: return "\\P";
+ default:
+ if (c < 0x20 || (c >= 0x7f && c < 0x100)) {
+ return "\\x${_pad(c.toRadixString(16).toUpperCase(), 2)}";
+ } else if (c >= 0x100 && c < 0x10000) {
+ return "\\u${_pad(c.toRadixString(16).toUpperCase(), 4)}";
+ } else if (c >= 0x10000) {
+ return "\\u${_pad(c.toRadixString(16).toUpperCase(), 8)}";
+ } else {
+ return new String.fromCharCodes([c]);
+ }
+ }
+ });
+ return '"${Strings.join(escapedValue, '')}"';
+ }
+
+ throw new Error("unknown scalar value: $value");
+ }
+
+ String toString() {
Bob Nystrom 2012/04/20 20:28:55 =>
nweiz 2012/04/23 23:06:33 Done.
+ return '$tag "$content"';
+ }
+
+ /**
+ * Left-pads [str] with zeros so that it's at least [length] characters
+ * long.
+ */
+ String _pad(String str, int length) {
Bob Nystrom 2012/04/20 20:28:55 "_zeroPad"?
nweiz 2012/04/23 23:06:33 Done.
+ assert(length >= str.length);
+ var prefix = new List();
Bob Nystrom 2012/04/20 20:28:55 new List() -> []
nweiz 2012/04/23 23:06:33 Done.
+ prefix.insertRange(0, length - str.length, '0');
Bob Nystrom 2012/04/20 20:28:55 Clever!
nweiz 2012/04/23 23:06:33 I'd rather write '0' * (length - str.length), but
+ return '${Strings.join(prefix, '')}$str';
+ }
+
+ int hashCode() => super.hashCode() ^ content.hashCode();
+
+ visit(_Visitor v) => v.visitScalar(this);
+}
+
+/** A mapping node represents an unordered map of nodes to nodes. */
+class _MappingNode extends _Node {
+ /** The node map. */
+ Map<_Node, _Node> content;
+
+ _MappingNode(String tagName, Map<_Node, _Node> this.content)
+ : super(new _Tag.mapping(tagName));
+
+ /** Two mappings are equal if their tags and contents are equal. */
+ bool operator ==(var other) {
+ // Should be super != other; bug 2554
+ if (!(super == other) || other is! _MappingNode) return false;
+ _MappingNode otherMap = other;
+ if (content.length != otherMap.content.length) return false;
+ for (var key in content.getKeys()) {
+ if (!otherMap.content.containsKey(key)) return false;
+ if (content[key] != otherMap.content[key]) return false;
+ }
+ return true;
+ }
+
+ String toString() {
+ var strContent = Strings.join(content.getKeys().
+ map((k) => '${k}: ${content[k]}'), ', ');
+ return '$tag {$strContent}';
+ }
+
+ int hashCode() => super.hashCode() ^ _hashCode(content);
+
+ visit(_Visitor v) => v.visitMapping(this);
+}

Powered by Google App Engine
This is Rietveld 408576698