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

Unified Diff: utils/yaml/composer.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: Remove tests/lib from TEST_SUITE_DIRECTORIES. 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: utils/yaml/composer.dart
diff --git a/utils/yaml/composer.dart b/utils/yaml/composer.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c586219b5d28cd9f7e279eb7a8827aefe5a81302
--- /dev/null
+++ b/utils/yaml/composer.dart
@@ -0,0 +1,177 @@
+// 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.
+
+/**
+ * Takes a parsed YAML document (what the spec calls the "serialization tree")
+ * and resolves aliases, resolves tags, and parses scalars to produce the
+ * "representation graph".
+ */
+class _Composer extends _Visitor {
+ /** The root node of the serialization tree. */
+ _Node _root;
+
+ /**
+ * Map from anchor names to the most recent representation graph node with
+ * that anchor.
+ */
+ Map<String, _Node> _anchors;
+
+ /**
+ * The next id to use for the represenation graph's anchors. The spec doesn't
+ * use anchors in the representation graph, but we do so that the constructor
+ * can ensure that the same node in the representation graph produces the same
+ * native object.
+ */
+ int _idCounter;
+
+ _Composer(_Node this._root) : this._anchors = <_Node>{}, this._idCounter = 0;
+
+ /** Runs the Composer to produce a representation graph. */
+ _Node compose() => _root.visit(this);
+
+ /** Returns the anchor to which an alias node refers. */
+ _Node visitAlias(_AliasNode alias) {
+ if (!_anchors.containsKey(alias.anchor)) {
+ throw new Error("no anchor for alias ${alias.anchor}");
+ }
+ return _anchors[alias.anchor];
+ }
+
+ /**
+ * Parses a scalar node according to its tag, or auto-detects the type if no
+ * tag exists. Currently this only supports the YAML core type schema.
+ */
+ _Node visitScalar(_ScalarNode scalar) {
+ if (scalar.tag.name == "!") {
+ return _setAnchor(scalar, _parseString(scalar.content));
+ } else if (scalar.tag.name == "?") {
+ for (var fn in [_parseNull, _parseBool, _parseInt, _parseFloat]) {
+ var result = fn(scalar.content);
+ if (result != null) return result;
+ }
+ return _setAnchor(scalar, _parseString(scalar.content));
+ }
+
+ // TODO(nweiz): support the full YAML type repository
+ var tagParsers = {
+ 'null': _parseNull, 'bool': _parseBool, 'int': _parseInt,
+ 'float': _parseFloat, 'str': _parseString
+ };
+
+ for (var key in tagParsers.getKeys()) {
+ if (scalar.tag.name != _Tag.yaml(key)) continue;
+ var result = tagParsers[key](scalar.content);
+ if (result != null) return _setAnchor(scalar, result);
+ throw new Error('invalid literal for $key: "${scalar.content}"');
+ }
+
+ throw new Error('undefined tag: "${scalar.tag.name}"');
+ }
+
+ /** Assigns a tag to the sequence and recursively composes its contents. */
+ _Node visitSequence(_SequenceNode seq) {
+ var tagName = seq.tag.name;
+ if (tagName != "!" && tagName != "?" && tagName != _Tag.yaml("seq")) {
+ throw new Error("invalid tag for sequence: ${tagName}");
+ }
+
+ var result = _setAnchor(seq, new _SequenceNode(_Tag.yaml("seq"), null));
+ result.content = super.visitSequence(seq);
+ return result;
+ }
+
+ /** Assigns a tag to the mapping and recursively composes its contents. */
+ _Node visitMapping(_MappingNode map) {
+ var tagName = map.tag.name;
+ if (tagName != "!" && tagName != "?" && tagName != _Tag.yaml("map")) {
+ throw new Error("invalid tag for mapping: ${tagName}");
+ }
+
+ var result = _setAnchor(map, new _MappingNode(_Tag.yaml("map"), null));
+ result.content = super.visitMapping(map);
+ return result;
+ }
+
+ /**
+ * If the serialization tree node [anchored] has an anchor, records that
+ * that anchor is pointing to the representation graph node [result].
+ */
+ _Node _setAnchor(_Node anchored, _Node result) {
+ if (anchored.anchor == null) return result;
+ result.anchor = '${_idCounter++}';
+ _anchors[anchored.anchor] = result;
+ return result;
+ }
+
+ /** Parses a null scalar. */
+ _ScalarNode _parseNull(String content) {
+ if (!const RegExp("^(null|Null|NULL|~|)\$").hasMatch(content)) return null;
Bob Nystrom 2012/04/20 20:28:55 Is this case-insensitive or just allowing these fo
+ return new _ScalarNode(_Tag.yaml("null"), value: null);
+ }
+
+ /** Parses a boolean scalar. */
+ _ScalarNode _parseBool(String content) {
+ var match = const RegExp("^(?:(true|True|TRUE)|(false|False|FALSE))\$").
+ firstMatch(content);
+ if (match == null) return null;
+ return new _ScalarNode(_Tag.yaml("bool"), value: match.group(1) != null);
+ }
+
+ /** Parses an integer scalar. */
+ _ScalarNode _parseInt(String content) {
+ var match = const RegExp("^[-+]?[0-9]+\$").firstMatch(content);
+ if (match != null) {
+ return new _ScalarNode(_Tag.yaml("int"),
+ value: Math.parseInt(match.group(0)));
+ }
+
+ match = const RegExp("^0o([0-7]+)\$").firstMatch(content);
+ if (match != null) {
+ // TODO(nweiz): clean this up when Dart can parse an octal string
+ var n = 0;
+ for (var c in match.group(1).charCodes()) {
+ n *= 8;
+ n += c - 48;
+ }
+ return new _ScalarNode(_Tag.yaml("int"), value: n);
+ }
+
+ match = const RegExp("^0x[0-9a-fA-F]+\$").firstMatch(content);
+ if (match != null) {
+ return new _ScalarNode(_Tag.yaml("int"),
+ value: Math.parseInt(match.group(0)));
+ }
+
+ return null;
+ }
+
+ /** Parses a floating-point scalar. */
+ _ScalarNode _parseFloat(String content) {
+ var match = const RegExp(
+ "^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?\$").
+ firstMatch(content);
+ if (match != null) {
+ return new _ScalarNode(_Tag.yaml("float"),
+ value: Math.parseDouble(match.group(0)));
+ }
+
+ match = const RegExp("^([+-]?)\.(inf|Inf|INF)\$").firstMatch(content);
+ if (match != null) {
+ return new _ScalarNode(_Tag.yaml("float"),
+ value: Math.parseDouble("${match.group(1)}Infinity"));
+ }
+
+ match = const RegExp("^\.(nan|NaN|NAN)\$").firstMatch(content);
+ if (match != null) {
+ return new _ScalarNode(_Tag.yaml("float"),
+ value: Math.parseDouble("NaN"));
+ }
+
+ return null;
+ }
+
+ /** Parses a string scalar. */
+ _ScalarNode _parseString(String content) =>
+ new _ScalarNode(_Tag.yaml("str"), value: content);
+}
« no previous file with comments | « tools/test.dart ('k') | utils/yaml/constructor.dart » ('j') | utils/yaml/parser.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698