Attach source range information to parsed YAML nodes. Also release yaml 1.0.0. R=efortuna@google.com, rnystrom@google.com, sigmund@google.com Review URL: https://codereview.chromium.org//302313007 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/yaml@36937 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkgs/yaml/CHANGELOG.md b/pkgs/yaml/CHANGELOG.md index 3520d4c..4b243d8 100644 --- a/pkgs/yaml/CHANGELOG.md +++ b/pkgs/yaml/CHANGELOG.md
@@ -1,3 +1,18 @@ +## 1.0.0 + +* **Backwards incompatibility**: The data structures returned by `loadYaml` and + `loadYamlStream` are now immutable. + +* **Backwards incompatibility**: The interface of the `YamlMap` class has + changed substantially in numerous ways. External users may no longer construct + their own instances. + +* Maps and lists returned by `loadYaml` and `loadYamlStream` now contain + information about their source locations. + +* A new `loadYamlNode` function returns the source location of top-level scalars + as well. + ## 0.10.0 * Improve error messages when a file fails to parse.
diff --git a/pkgs/yaml/lib/src/composer.dart b/pkgs/yaml/lib/src/composer.dart index 3178096..4c19da1 100644 --- a/pkgs/yaml/lib/src/composer.dart +++ b/pkgs/yaml/lib/src/composer.dart
@@ -4,6 +4,8 @@ library yaml.composer; +import 'package:source_maps/source_maps.dart'; + import 'model.dart'; import 'visitor.dart'; import 'yaml_exception.dart'; @@ -45,13 +47,13 @@ /// Currently this only supports the YAML core type schema. Node visitScalar(ScalarNode scalar) { if (scalar.tag.name == "!") { - return setAnchor(scalar, parseString(scalar.content)); + return setAnchor(scalar, parseString(scalar)); } else if (scalar.tag.name == "?") { for (var fn in [parseNull, parseBool, parseInt, parseFloat]) { - var result = fn(scalar.content); + var result = fn(scalar); if (result != null) return result; } - return setAnchor(scalar, parseString(scalar.content)); + return setAnchor(scalar, parseString(scalar)); } var result = _parseByTag(scalar); @@ -62,11 +64,11 @@ ScalarNode _parseByTag(ScalarNode scalar) { switch (scalar.tag.name) { - case "null": return parseNull(scalar.content); - case "bool": return parseBool(scalar.content); - case "int": return parseInt(scalar.content); - case "float": return parseFloat(scalar.content); - case "str": return parseString(scalar.content); + case "null": return parseNull(scalar); + case "bool": return parseBool(scalar); + case "int": return parseInt(scalar); + case "float": return parseFloat(scalar); + case "str": return parseString(scalar); } throw new YamlException('Undefined tag: ${scalar.tag}.'); } @@ -78,7 +80,8 @@ throw new YamlException("Invalid tag for sequence: ${seq.tag}."); } - var result = setAnchor(seq, new SequenceNode(Tag.yaml('seq'), null)); + var result = setAnchor(seq, + new SequenceNode(Tag.yaml('seq'), null, seq.span)); result.content = super.visitSequence(seq); return result; } @@ -90,7 +93,8 @@ throw new YamlException("Invalid tag for mapping: ${map.tag}."); } - var result = setAnchor(map, new MappingNode(Tag.yaml('map'), null)); + var result = setAnchor(map, + new MappingNode(Tag.yaml('map'), null, map.span)); result.content = super.visitMapping(map); return result; } @@ -105,36 +109,40 @@ } /// Parses a null scalar. - ScalarNode parseNull(String content) { - if (!new RegExp(r"^(null|Null|NULL|~|)$").hasMatch(content)) return null; - return new ScalarNode(Tag.yaml("null"), value: null); + ScalarNode parseNull(ScalarNode scalar) { + if (new RegExp(r"^(null|Null|NULL|~|)$").hasMatch(scalar.content)) { + return new ScalarNode(Tag.yaml("null"), scalar.span, value: null); + } else { + return null; + } } /// Parses a boolean scalar. - ScalarNode parseBool(String content) { + ScalarNode parseBool(ScalarNode scalar) { var match = new RegExp(r"^(?:(true|True|TRUE)|(false|False|FALSE))$"). - firstMatch(content); + firstMatch(scalar.content); if (match == null) return null; - return new ScalarNode(Tag.yaml("bool"), value: match.group(1) != null); + return new ScalarNode(Tag.yaml("bool"), scalar.span, + value: match.group(1) != null); } /// Parses an integer scalar. - ScalarNode parseInt(String content) { - var match = new RegExp(r"^[-+]?[0-9]+$").firstMatch(content); + ScalarNode parseInt(ScalarNode scalar) { + var match = new RegExp(r"^[-+]?[0-9]+$").firstMatch(scalar.content); if (match != null) { - return new ScalarNode(Tag.yaml("int"), + return new ScalarNode(Tag.yaml("int"), scalar.span, value: int.parse(match.group(0))); } - match = new RegExp(r"^0o([0-7]+)$").firstMatch(content); + match = new RegExp(r"^0o([0-7]+)$").firstMatch(scalar.content); if (match != null) { int n = int.parse(match.group(1), radix: 8); - return new ScalarNode(Tag.yaml("int"), value: n); + return new ScalarNode(Tag.yaml("int"), scalar.span, value: n); } - match = new RegExp(r"^0x[0-9a-fA-F]+$").firstMatch(content); + match = new RegExp(r"^0x[0-9a-fA-F]+$").firstMatch(scalar.content); if (match != null) { - return new ScalarNode(Tag.yaml("int"), + return new ScalarNode(Tag.yaml("int"), scalar.span, value: int.parse(match.group(0))); } @@ -142,33 +150,33 @@ } /// Parses a floating-point scalar. - ScalarNode parseFloat(String content) { + ScalarNode parseFloat(ScalarNode scalar) { var match = new RegExp( - r"^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$"). - firstMatch(content); + r"^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$"). + firstMatch(scalar.content); if (match != null) { // YAML allows floats of the form "0.", but Dart does not. Fix up those // floats by removing the trailing dot. var matchStr = match.group(0).replaceAll(new RegExp(r"\.$"), ""); - return new ScalarNode(Tag.yaml("float"), + return new ScalarNode(Tag.yaml("float"), scalar.span, value: double.parse(matchStr)); } - match = new RegExp(r"^([+-]?)\.(inf|Inf|INF)$").firstMatch(content); + match = new RegExp(r"^([+-]?)\.(inf|Inf|INF)$").firstMatch(scalar.content); if (match != null) { var value = match.group(1) == "-" ? -double.INFINITY : double.INFINITY; - return new ScalarNode(Tag.yaml("float"), value: value); + return new ScalarNode(Tag.yaml("float"), scalar.span, value: value); } - match = new RegExp(r"^\.(nan|NaN|NAN)$").firstMatch(content); + match = new RegExp(r"^\.(nan|NaN|NAN)$").firstMatch(scalar.content); if (match != null) { - return new ScalarNode(Tag.yaml("float"), value: double.NAN); + return new ScalarNode(Tag.yaml("float"), scalar.span, value: double.NAN); } return null; } /// Parses a string scalar. - ScalarNode parseString(String content) => - new ScalarNode(Tag.yaml("str"), value: content); + ScalarNode parseString(ScalarNode scalar) => + new ScalarNode(Tag.yaml("str"), scalar.span, value: scalar.content); }
diff --git a/pkgs/yaml/lib/src/constructor.dart b/pkgs/yaml/lib/src/constructor.dart index 116809d..a8224a4 100644 --- a/pkgs/yaml/lib/src/constructor.dart +++ b/pkgs/yaml/lib/src/constructor.dart
@@ -4,9 +4,10 @@ library yaml.constructor; +import 'equality.dart'; import 'model.dart'; import 'visitor.dart'; -import 'yaml_map.dart'; +import 'yaml_node.dart'; /// Takes a parsed and composed YAML document (what the spec calls the /// "representation graph") and creates native Dart objects that represent that @@ -16,43 +17,54 @@ final Node _root; /// Map from anchor names to the most recent Dart node with that anchor. - final _anchors = <String, dynamic>{}; + final _anchors = <String, YamlNode>{}; Constructor(this._root); /// Runs the Constructor to produce a Dart object. - construct() => _root.visit(this); + YamlNode construct() => _root.visit(this); /// Returns the value of a scalar. - visitScalar(ScalarNode scalar) => scalar.value; + YamlScalar visitScalar(ScalarNode scalar) => + new YamlScalar(scalar.value, scalar.span); /// Converts a sequence into a List of Dart objects. - visitSequence(SequenceNode seq) { + YamlList visitSequence(SequenceNode seq) { var anchor = getAnchor(seq); if (anchor != null) return anchor; - var dartSeq = setAnchor(seq, []); - dartSeq.addAll(super.visitSequence(seq)); + var nodes = []; + var dartSeq = setAnchor(seq, new YamlList(nodes, seq.span)); + nodes.addAll(super.visitSequence(seq)); return dartSeq; } - /// Converts a mapping into a Map of Dart objects. - visitMapping(MappingNode map) { + /// Converts a mapping into a [Map] of Dart objects. + YamlMap visitMapping(MappingNode map) { var anchor = getAnchor(map); if (anchor != null) return anchor; - var dartMap = setAnchor(map, new YamlMap()); - super.visitMapping(map).forEach((k, v) { dartMap[k] = v; }); + var nodes = deepEqualsMap(); + var dartMap = setAnchor(map, new YamlMap(nodes, map.span)); + super.visitMapping(map).forEach((k, v) => nodes[k] = v); return dartMap; } - /// Returns the Dart object that already represents [anchored], if such a - /// thing exists. - getAnchor(Node anchored) { + /// Returns a new Dart object wrapping the object that already represents + /// [anchored], if such a thing exists. + YamlNode getAnchor(Node anchored) { if (anchored.anchor == null) return null; - if (_anchors.containsKey(anchored.anchor)) return _anchors[anchored.anchor]; + var value = _anchors[anchored.anchor]; + if (vaule == null) return null; + + // Re-wrap [value]'s contents so that it's associated with the span of the + // anchor rather than its original definition. + if (value is YamlMap) return new YamlMap(value.nodes, anchored.span); + if (value is YamlList) return new YamlList(value.nodes, anchored.span); + assert(value is YamlScalar); + return new YamlScalar(value.value, anchored.span); } /// Records that [value] is the Dart object representing [anchored]. - setAnchor(Node anchored, value) { + YamlNode setAnchor(Node anchored, YamlNode value) { if (anchored.anchor == null) return value; _anchors[anchored.anchor] = value; return value;
diff --git a/pkgs/yaml/lib/src/deep_equals.dart b/pkgs/yaml/lib/src/deep_equals.dart deleted file mode 100644 index 68fb236..0000000 --- a/pkgs/yaml/lib/src/deep_equals.dart +++ /dev/null
@@ -1,81 +0,0 @@ -// 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. - -library yaml.deep_equals; - -/// Returns whether two objects are structurally equivalent. -/// -/// This considers `NaN` values to be equivalent. It also handles -/// self-referential structures. -bool deepEquals(obj1, obj2) => new _DeepEquals().equals(obj1, obj2); - -/// A class that provides access to the list of parent objects used for loop -/// detection. -class _DeepEquals { - final _parents1 = []; - final _parents2 = []; - - /// Returns whether [obj1] and [obj2] are structurally equivalent. - bool equals(obj1, obj2) { - // _parents1 and _parents2 are guaranteed to be the same size. - for (var i = 0; i < _parents1.length; i++) { - var loop1 = identical(obj1, _parents1[i]); - var loop2 = identical(obj2, _parents2[i]); - // If both structures loop in the same place, they're equal at that point - // in the structure. If one loops and the other doesn't, they're not - // equal. - if (loop1 && loop2) return true; - if (loop1 || loop2) return false; - } - - _parents1.add(obj1); - _parents2.add(obj2); - try { - if (obj1 is List && obj2 is List) { - return _listEquals(obj1, obj2); - } else if (obj1 is Map && obj2 is Map) { - return _mapEquals(obj1, obj2); - } else if (obj1 is num && obj2 is num) { - return _numEquals(obj1, obj2); - } else { - return obj1 == obj2; - } - } finally { - _parents1.removeLast(); - _parents2.removeLast(); - } - } - - /// Returns whether [list1] and [list2] are structurally equal. - bool _listEquals(List list1, List list2) { - if (list1.length != list2.length) return false; - - for (var i = 0; i < list1.length; i++) { - if (!equals(list1[i], list2[i])) return false; - } - - return true; - } - - /// Returns whether [map1] and [map2] are structurally equal. - bool _mapEquals(Map map1, Map map2) { - if (map1.length != map2.length) return false; - - for (var key in map1.keys) { - if (!map2.containsKey(key)) return false; - if (!equals(map1[key], map2[key])) return false; - } - - return true; - } - - /// Returns whether two numbers are equivalent. - /// - /// This differs from `n1 == n2` in that it considers `NaN` to be equal to - /// itself. - bool _numEquals(num n1, num n2) { - if (n1.isNaN && n2.isNaN) return true; - return n1 == n2; - } -}
diff --git a/pkgs/yaml/lib/src/equality.dart b/pkgs/yaml/lib/src/equality.dart new file mode 100644 index 0000000..5408135 --- /dev/null +++ b/pkgs/yaml/lib/src/equality.dart
@@ -0,0 +1,126 @@ +// Copyright (c) 2014, 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. + +library yaml.equality; + +import 'dart:collection'; + +import 'package:collection/collection.dart'; + +import 'yaml_node.dart'; + +/// Returns a [Map] that compares its keys based on [deepEquals]. +Map deepEqualsMap() => new HashMap(equals: deepEquals, hashCode: deepHashCode); + +/// Returns whether two objects are structurally equivalent. +/// +/// This considers `NaN` values to be equivalent, handles self-referential +/// structures, and considers [YamlScalar]s to be equal to their values. +bool deepEquals(obj1, obj2) => new _DeepEquals().equals(obj1, obj2); + +/// A class that provides access to the list of parent objects used for loop +/// detection. +class _DeepEquals { + final _parents1 = []; + final _parents2 = []; + + /// Returns whether [obj1] and [obj2] are structurally equivalent. + bool equals(obj1, obj2) { + if (obj1 is YamlScalar) obj1 = obj1.value; + if (obj2 is YamlScalar) obj2 = obj2.value; + + // _parents1 and _parents2 are guaranteed to be the same size. + for (var i = 0; i < _parents1.length; i++) { + var loop1 = identical(obj1, _parents1[i]); + var loop2 = identical(obj2, _parents2[i]); + // If both structures loop in the same place, they're equal at that point + // in the structure. If one loops and the other doesn't, they're not + // equal. + if (loop1 && loop2) return true; + if (loop1 || loop2) return false; + } + + _parents1.add(obj1); + _parents2.add(obj2); + try { + if (obj1 is List && obj2 is List) { + return _listEquals(obj1, obj2); + } else if (obj1 is Map && obj2 is Map) { + return _mapEquals(obj1, obj2); + } else if (obj1 is num && obj2 is num) { + return _numEquals(obj1, obj2); + } else { + return obj1 == obj2; + } + } finally { + _parents1.removeLast(); + _parents2.removeLast(); + } + } + + /// Returns whether [list1] and [list2] are structurally equal. + bool _listEquals(List list1, List list2) { + if (list1.length != list2.length) return false; + + for (var i = 0; i < list1.length; i++) { + if (!equals(list1[i], list2[i])) return false; + } + + return true; + } + + /// Returns whether [map1] and [map2] are structurally equal. + bool _mapEquals(Map map1, Map map2) { + if (map1.length != map2.length) return false; + + for (var key in map1.keys) { + if (!map2.containsKey(key)) return false; + if (!equals(map1[key], map2[key])) return false; + } + + return true; + } + + /// Returns whether two numbers are equivalent. + /// + /// This differs from `n1 == n2` in that it considers `NaN` to be equal to + /// itself. + bool _numEquals(num n1, num n2) { + if (n1.isNaN && n2.isNaN) return true; + return n1 == n2; + } +} + +/// Returns a hash code for [obj] such that structurally equivalent objects +/// will have the same hash code. +/// +/// This supports deep equality for maps and lists, including those with +/// self-referential structures, and returns the same hash code for +/// [YamlScalar]s and their values. +int deepHashCode(obj) { + var parents = []; + + _deepHashCode(value) { + if (parents.any((parent) => identical(parent, value))) return -1; + + parents.add(value); + try { + if (value is Map) { + var equality = const UnorderedIterableEquality(); + return equality.hash(value.keys.map(_deepHashCode)) ^ + equality.hash(value.values.map(_deepHashCode)); + } else if (value is Iterable) { + return const IterableEquality().hash(value.map(deepHashCode)); + } else if (value is YamlScalar) { + return value.value.hashCode; + } else { + return value.hashCode; + } + } finally { + parents.removeLast(); + } + } + + return _deepHashCode(obj); +}
diff --git a/pkgs/yaml/lib/src/model.dart b/pkgs/yaml/lib/src/model.dart index 564cac6..f0e839c 100644 --- a/pkgs/yaml/lib/src/model.dart +++ b/pkgs/yaml/lib/src/model.dart
@@ -7,8 +7,10 @@ /// representation graph. library yaml.model; +import 'package:source_maps/source_maps.dart'; + +import 'equality.dart'; import 'parser.dart'; -import 'utils.dart'; import 'visitor.dart'; import 'yaml_exception.dart'; @@ -80,14 +82,17 @@ /// Any YAML node can have an anchor associated with it. String anchor; - Node(this.tag, [this.anchor]); + /// The source span for this node. + Span span; + + Node(this.tag, this.span, [this.anchor]); bool operator ==(other) { if (other is! Node) return false; return tag == other.tag; } - int get hashCode => hashCodeFor([tag, anchor]); + int get hashCode => tag.hashCode ^ anchor.hashCode; visit(Visitor v); } @@ -97,8 +102,8 @@ /// The nodes in the sequence. List<Node> content; - SequenceNode(String tagName, this.content) - : super(new Tag.sequence(tagName)); + SequenceNode(String tagName, this.content, Span span) + : super(new Tag.sequence(tagName), span); /// Two sequences are equal if their tags and contents are equal. bool operator ==(other) { @@ -113,14 +118,15 @@ String toString() => '$tag [${content.map((e) => '$e').join(', ')}]'; - int get hashCode => super.hashCode ^ hashCodeFor(content); + int get hashCode => super.hashCode ^ deepHashCode(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); + AliasNode(String anchor, Span span) + : super(new Tag.scalar(Tag.yaml("str")), span, anchor); visit(Visitor v) => v.visitAlias(this); } @@ -139,9 +145,9 @@ /// 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}) + ScalarNode(String tagName, Span span, {String content, this.value}) : _content = content, - super(new Tag.scalar(tagName)); + super(new Tag.scalar(tagName), span); /// Two scalars are equal if their string representations are equal. bool operator ==(other) { @@ -225,8 +231,8 @@ /// The node map. Map<Node, Node> content; - MappingNode(String tagName, this.content) - : super(new Tag.mapping(tagName)); + MappingNode(String tagName, this.content, Span span) + : super(new Tag.mapping(tagName), span); /// Two mappings are equal if their tags and contents are equal. bool operator ==(other) { @@ -247,7 +253,7 @@ return '$tag {$strContent}'; } - int get hashCode => super.hashCode ^ hashCodeFor(content); + int get hashCode => super.hashCode ^ deepHashCode(content); visit(Visitor v) => v.visitMapping(this); }
diff --git a/pkgs/yaml/lib/src/parser.dart b/pkgs/yaml/lib/src/parser.dart index d203c32..98658e6 100644 --- a/pkgs/yaml/lib/src/parser.dart +++ b/pkgs/yaml/lib/src/parser.dart
@@ -6,11 +6,13 @@ import 'dart:collection'; +import 'package:source_maps/source_maps.dart'; import 'package:string_scanner/string_scanner.dart'; +import 'equality.dart'; import 'model.dart'; +import 'utils.dart'; import 'yaml_exception.dart'; -import 'yaml_map.dart'; /// Translates a string of characters into a YAML serialization tree. /// @@ -317,7 +319,7 @@ } /// Adds a tag and an anchor to [node], if they're defined. - Node addProps(Node node, _Pair<Tag, String> props) { + Node addProps(Node node, Pair<Tag, String> props) { if (props == null || node == null) return node; if (truth(props.first)) node.tag = props.first; if (truth(props.last)) node.anchor = props.last; @@ -325,10 +327,10 @@ } /// Creates a MappingNode from [pairs]. - MappingNode map(List<_Pair<Node, Node>> pairs) { + MappingNode map(List<Pair<Node, Node>> pairs, Span span) { var content = new Map<Node, Node>(); pairs.forEach((pair) => content[pair.first] = pair.last); - return new MappingNode("?", content); + return new MappingNode("?", content, span); } /// Runs [fn] in a context named [name]. Used for error reporting. @@ -759,7 +761,7 @@ bool l_directive() => false; // TODO(nweiz): implement // 96 - _Pair<Tag, String> c_ns_properties(int indent, int ctx) { + Pair<Tag, String> c_ns_properties(int indent, int ctx) { var tag, anchor; tag = c_ns_tagProperty(); if (truth(tag)) { @@ -767,7 +769,7 @@ if (!truth(s_separate(indent, ctx))) return null; return c_ns_anchorProperty(); }); - return new _Pair<Tag, String>(tag, anchor); + return new Pair<Tag, String>(tag, anchor); } anchor = c_ns_anchorProperty(); @@ -776,7 +778,7 @@ if (!truth(s_separate(indent, ctx))) return null; return c_ns_tagProperty(); }); - return new _Pair<Tag, String>(tag, anchor); + return new Pair<Tag, String>(tag, anchor); } return null; @@ -797,13 +799,14 @@ // 104 Node c_ns_aliasNode() { + var start = _scanner.state; if (!truth(c_indicator(C_ALIAS))) return null; var name = expect(ns_anchorName(), 'anchor name'); - return new AliasNode(name); + return new AliasNode(name, _scanner.spanFrom(start)); } // 105 - ScalarNode e_scalar() => new ScalarNode("?", content: ""); + ScalarNode e_scalar() => new ScalarNode("?", _scanner.emptySpan, content: ""); // 106 ScalarNode e_node() => e_scalar(); @@ -820,10 +823,11 @@ // 109 Node c_doubleQuoted(int indent, int ctx) => context('string', () { return transaction(() { + var start = _scanner.state; if (!truth(c_indicator(C_DOUBLE_QUOTE))) return null; var contents = nb_doubleText(indent, ctx); if (!truth(c_indicator(C_DOUBLE_QUOTE))) return null; - return new ScalarNode("!", content: contents); + return new ScalarNode("!", _scanner.spanFrom(start), content: contents); }); }); @@ -908,10 +912,11 @@ // 120 Node c_singleQuoted(int indent, int ctx) => context('string', () { return transaction(() { + var start = _scanner.state; if (!truth(c_indicator(C_SINGLE_QUOTE))) return null; var contents = nb_singleText(indent, ctx); if (!truth(c_indicator(C_SINGLE_QUOTE))) return null; - return new ScalarNode("!", content: contents); + return new ScalarNode("!", _scanner.spanFrom(start), content: contents); }); }); @@ -1075,11 +1080,13 @@ // 137 SequenceNode c_flowSequence(int indent, int ctx) => transaction(() { + var start = _scanner.state; if (!truth(c_indicator(C_SEQUENCE_START))) return null; zeroOrOne(() => s_separate(indent, ctx)); var content = zeroOrOne(() => ns_s_flowSeqEntries(indent, inFlow(ctx))); if (!truth(c_indicator(C_SEQUENCE_END))) return null; - return new SequenceNode("?", new List<Node>.from(content)); + return new SequenceNode("?", new List<Node>.from(content), + _scanner.spanFrom(start)); }); // 138 @@ -1108,17 +1115,18 @@ // 140 Node c_flowMapping(int indent, int ctx) { + var start = _scanner.state; if (!truth(c_indicator(C_MAPPING_START))) return null; zeroOrOne(() => s_separate(indent, ctx)); var content = zeroOrOne(() => ns_s_flowMapEntries(indent, inFlow(ctx))); if (!truth(c_indicator(C_MAPPING_END))) return null; - return new MappingNode("?", content); + return new MappingNode("?", content, _scanner.spanFrom(start)); } // 141 - YamlMap ns_s_flowMapEntries(int indent, int ctx) { + Map ns_s_flowMapEntries(int indent, int ctx) { var first = ns_flowMapEntry(indent, ctx); - if (!truth(first)) return new YamlMap(); + if (!truth(first)) return deepEqualsMap(); zeroOrOne(() => s_separate(indent, ctx)); var rest; @@ -1127,7 +1135,7 @@ rest = ns_s_flowMapEntries(indent, ctx); } - if (rest == null) rest = new YamlMap(); + if (rest == null) rest = deepEqualsMap(); // TODO(nweiz): Duplicate keys should be an error. This includes keys with // different representations but the same value (e.g. 10 vs 0xa). To make @@ -1139,7 +1147,7 @@ } // 142 - _Pair<Node, Node> ns_flowMapEntry(int indent, int ctx) => or([ + Pair<Node, Node> ns_flowMapEntry(int indent, int ctx) => or([ () => transaction(() { if (!truth(c_indicator(C_MAPPING_KEY))) return false; if (!truth(s_separate(indent, ctx))) return false; @@ -1149,20 +1157,20 @@ ]); // 143 - _Pair<Node, Node> ns_flowMapExplicitEntry(int indent, int ctx) => or([ + Pair<Node, Node> ns_flowMapExplicitEntry(int indent, int ctx) => or([ () => ns_flowMapImplicitEntry(indent, ctx), - () => new _Pair<Node, Node>(e_node(), e_node()) + () => new Pair<Node, Node>(e_node(), e_node()) ]); // 144 - _Pair<Node, Node> ns_flowMapImplicitEntry(int indent, int ctx) => or([ + Pair<Node, Node> ns_flowMapImplicitEntry(int indent, int ctx) => or([ () => ns_flowMapYamlKeyEntry(indent, ctx), () => c_ns_flowMapEmptyKeyEntry(indent, ctx), () => c_ns_flowMapJsonKeyEntry(indent, ctx) ]); // 145 - _Pair<Node, Node> ns_flowMapYamlKeyEntry(int indent, int ctx) { + Pair<Node, Node> ns_flowMapYamlKeyEntry(int indent, int ctx) { var key = ns_flowYamlNode(indent, ctx); if (!truth(key)) return null; var value = or([ @@ -1172,14 +1180,14 @@ }), e_node ]); - return new _Pair<Node, Node>(key, value); + return new Pair<Node, Node>(key, value); } // 146 - _Pair<Node, Node> c_ns_flowMapEmptyKeyEntry(int indent, int ctx) { + Pair<Node, Node> c_ns_flowMapEmptyKeyEntry(int indent, int ctx) { var value = c_ns_flowMapSeparateValue(indent, ctx); if (!truth(value)) return null; - return new _Pair<Node, Node>(e_node(), value); + return new Pair<Node, Node>(e_node(), value); } // 147 @@ -1197,7 +1205,7 @@ }); // 148 - _Pair<Node, Node> c_ns_flowMapJsonKeyEntry(int indent, int ctx) { + Pair<Node, Node> c_ns_flowMapJsonKeyEntry(int indent, int ctx) { var key = c_flowJsonNode(indent, ctx); if (!truth(key)) return null; var value = or([ @@ -1207,7 +1215,7 @@ }), e_node ]); - return new _Pair<Node, Node>(key, value); + return new Pair<Node, Node>(key, value); } // 149 @@ -1224,6 +1232,7 @@ // 150 Node ns_flowPair(int indent, int ctx) { + var start = _scanner.state; var pair = or([ () => transaction(() { if (!truth(c_indicator(C_MAPPING_KEY))) return null; @@ -1234,34 +1243,34 @@ ]); if (!truth(pair)) return null; - return map([pair]); + return map([pair], _scanner.spanFrom(start)); } // 151 - _Pair<Node, Node> ns_flowPairEntry(int indent, int ctx) => or([ + Pair<Node, Node> ns_flowPairEntry(int indent, int ctx) => or([ () => ns_flowPairYamlKeyEntry(indent, ctx), () => c_ns_flowMapEmptyKeyEntry(indent, ctx), () => c_ns_flowPairJsonKeyEntry(indent, ctx) ]); // 152 - _Pair<Node, Node> ns_flowPairYamlKeyEntry(int indent, int ctx) => + Pair<Node, Node> ns_flowPairYamlKeyEntry(int indent, int ctx) => transaction(() { var key = ns_s_implicitYamlKey(FLOW_KEY); if (!truth(key)) return null; var value = c_ns_flowMapSeparateValue(indent, ctx); if (!truth(value)) return null; - return new _Pair<Node, Node>(key, value); + return new Pair<Node, Node>(key, value); }); // 153 - _Pair<Node, Node> c_ns_flowPairJsonKeyEntry(int indent, int ctx) => + Pair<Node, Node> c_ns_flowPairJsonKeyEntry(int indent, int ctx) => transaction(() { var key = c_s_implicitJsonKey(FLOW_KEY); if (!truth(key)) return null; var value = c_ns_flowMapAdjacentValue(indent, ctx); if (!truth(value)) return null; - return new _Pair<Node, Node>(key, value); + return new Pair<Node, Node>(key, value); }); // 154 @@ -1288,9 +1297,10 @@ // 156 Node ns_flowYamlContent(int indent, int ctx) { + var start = _scanner.state; var str = ns_plain(indent, ctx); if (!truth(str)) return null; - return new ScalarNode("?", content: str); + return new ScalarNode("?", _scanner.spanFrom(start), content: str); } // 157 @@ -1437,6 +1447,7 @@ // 170 Node c_l_literal(int indent) => transaction(() { + var start = _scanner.state; if (!truth(c_indicator(C_LITERAL))) return null; var header = c_b_blockHeader(); if (!truth(header)) return null; @@ -1445,7 +1456,7 @@ var content = l_literalContent(indent + additionalIndent, header.chomping); if (!truth(content)) return null; - return new ScalarNode("!", content: content); + return new ScalarNode("!", _scanner.spanFrom(start), content: content); }); // 171 @@ -1474,6 +1485,7 @@ // 174 Node c_l_folded(int indent) => transaction(() { + var start = _scanner.state; if (!truth(c_indicator(C_FOLDED))) return null; var header = c_b_blockHeader(); if (!truth(header)) return null; @@ -1482,7 +1494,7 @@ var content = l_foldedContent(indent + additionalIndent, header.chomping); if (!truth(content)) return null; - return new ScalarNode("!", content: content); + return new ScalarNode("!", _scanner.spanFrom(start), content: content); }); // 175 @@ -1562,13 +1574,14 @@ var additionalIndent = countIndentation() - indent; if (additionalIndent <= 0) return null; + var start = _scanner.state; var content = oneOrMore(() => transaction(() { if (!truth(s_indent(indent + additionalIndent))) return null; return c_l_blockSeqEntry(indent + additionalIndent); })); if (!truth(content)) return null; - return new SequenceNode("?", content); + return new SequenceNode("?", content, _scanner.spanFrom(start)); }); // 184 @@ -1595,6 +1608,7 @@ // 186 Node ns_l_compactSequence(int indent) => context('sequence', () { + var start = _scanner.state; var first = c_l_blockSeqEntry(indent); if (!truth(first)) return null; @@ -1604,7 +1618,7 @@ })); content.insert(0, first); - return new SequenceNode("?", content); + return new SequenceNode("?", content, _scanner.spanFrom(start)); }); // 187 @@ -1612,23 +1626,24 @@ var additionalIndent = countIndentation() - indent; if (additionalIndent <= 0) return null; + var start = _scanner.state; var pairs = oneOrMore(() => transaction(() { if (!truth(s_indent(indent + additionalIndent))) return null; return ns_l_blockMapEntry(indent + additionalIndent); })); if (!truth(pairs)) return null; - return map(pairs); + return map(pairs, _scanner.spanFrom(start)); }); // 188 - _Pair<Node, Node> ns_l_blockMapEntry(int indent) => or([ + Pair<Node, Node> ns_l_blockMapEntry(int indent) => or([ () => c_l_blockMapExplicitEntry(indent), () => ns_l_blockMapImplicitEntry(indent) ]); // 189 - _Pair<Node, Node> c_l_blockMapExplicitEntry(int indent) { + Pair<Node, Node> c_l_blockMapExplicitEntry(int indent) { var key = c_l_blockMapExplicitKey(indent); if (!truth(key)) return null; @@ -1637,7 +1652,7 @@ e_node ]); - return new _Pair<Node, Node>(key, value); + return new Pair<Node, Node>(key, value); } // 190 @@ -1654,10 +1669,10 @@ }); // 192 - _Pair<Node, Node> ns_l_blockMapImplicitEntry(int indent) => transaction(() { + Pair<Node, Node> ns_l_blockMapImplicitEntry(int indent) => transaction(() { var key = or([ns_s_blockMapImplicitKey, e_node]); var value = c_l_blockMapImplicitValue(indent); - return truth(value) ? new _Pair<Node, Node>(key, value) : null; + return truth(value) ? new Pair<Node, Node>(key, value) : null; }); // 193 @@ -1678,6 +1693,7 @@ // 195 Node ns_l_compactMapping(int indent) => context('mapping', () { + var start = _scanner.state; var first = ns_l_blockMapEntry(indent); if (!truth(first)) return null; @@ -1687,7 +1703,7 @@ })); pairs.insert(0, first); - return map(pairs); + return map(pairs, _scanner.spanFrom(start)); }); // 196 @@ -1807,7 +1823,8 @@ or([l_directiveDocument, l_explicitDocument, l_bareDocument]); // 211 - List<Node> l_yamlStream() { + Pair<List<Node>, Span> l_yamlStream() { + var start = _scanner.state; var docs = []; zeroOrMore(l_documentPrefix); var first = zeroOrOne(l_anyDocument); @@ -1828,20 +1845,10 @@ }); if (!_scanner.isDone) parseFailed(); - return docs; + return new Pair(docs, _scanner.spanFrom(start)); } } -/// A pair of values. -class _Pair<E, F> { - E first; - F last; - - _Pair(this.first, this.last); - - String toString() => '($first, $last)'; -} - /// The information in the header for a block scalar. class _BlockHeader { final int additionalIndent; @@ -1872,7 +1879,7 @@ /// expensive. class _RangeMap<E> { /// The ranges and their associated elements. - final List<_Pair<_Range, E>> _contents = <_Pair<_Range, E>>[]; + final List<Pair<_Range, E>> _contents = <Pair<_Range, E>>[]; _RangeMap(); @@ -1890,5 +1897,5 @@ /// Associates [value] with [range]. operator[]=(_Range range, E value) => - _contents.add(new _Pair<_Range, E>(range, value)); + _contents.add(new Pair<_Range, E>(range, value)); }
diff --git a/pkgs/yaml/lib/src/utils.dart b/pkgs/yaml/lib/src/utils.dart index 463af70..84c1113 100644 --- a/pkgs/yaml/lib/src/utils.dart +++ b/pkgs/yaml/lib/src/utils.dart
@@ -4,33 +4,12 @@ library yaml.utils; -import 'package:collection/collection.dart'; +/// A pair of values. +class Pair<E, F> { + final E first; + final F last; -/// Returns a hash code for [obj] such that structurally equivalent objects -/// will have the same hash code. -/// -/// This supports deep equality for maps and lists, including those with -/// self-referential structures. -int hashCodeFor(obj) { - var parents = []; + Pair(this.first, this.last); - _hashCodeFor(value) { - if (parents.any((parent) => identical(parent, value))) return -1; - - parents.add(value); - try { - if (value is Map) { - var equality = const UnorderedIterableEquality(); - return equality.hash(value.keys.map(_hashCodeFor)) ^ - equality.hash(value.values.map(_hashCodeFor)); - } else if (value is Iterable) { - return const IterableEquality().hash(value.map(hashCodeFor)); - } - return value.hashCode; - } finally { - parents.removeLast(); - } - } - - return _hashCodeFor(obj); + String toString() => '($first, $last)'; }
diff --git a/pkgs/yaml/lib/src/visitor.dart b/pkgs/yaml/lib/src/visitor.dart index 7095268..3f4c455 100644 --- a/pkgs/yaml/lib/src/visitor.dart +++ b/pkgs/yaml/lib/src/visitor.dart
@@ -4,8 +4,8 @@ library yaml.visitor; +import 'equality.dart'; import 'model.dart'; -import 'yaml_map.dart'; /// The visitor pattern for YAML documents. class Visitor { @@ -21,7 +21,7 @@ /// Visits each key and value in [map] and returns a map of the results. visitMapping(MappingNode map) { - var out = new YamlMap(); + var out = deepEqualsMap(); for (var key in map.content.keys) { out[key.visit(this)] = map.content[key].visit(this); }
diff --git a/pkgs/yaml/lib/src/yaml_map.dart b/pkgs/yaml/lib/src/yaml_map.dart deleted file mode 100644 index ff3da36..0000000 --- a/pkgs/yaml/lib/src/yaml_map.dart +++ /dev/null
@@ -1,40 +0,0 @@ -// 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. - -library yaml.map; - -import 'dart:collection'; - -import 'package:collection/collection.dart'; - -import 'deep_equals.dart'; -import 'utils.dart'; - -/// This class behaves almost identically to the normal Dart [Map] -/// implementation, with the following differences: -/// -/// * It allows NaN, list, and map keys. -/// * It defines `==` structurally. That is, `yamlMap1 == yamlMap2` if they -/// have the same contents. -/// * It has a compatible [hashCode] method. -/// -/// This class is deprecated. In future releases, this package will use -/// a [HashMap] with a custom equality operation rather than a custom class. -@Deprecated('1.0.0') -class YamlMap extends DelegatingMap { - YamlMap() - : super(new HashMap(equals: deepEquals, hashCode: hashCodeFor)); - - YamlMap.from(Map map) - : super(new HashMap(equals: deepEquals, hashCode: hashCodeFor)) { - addAll(map); - } - - int get hashCode => hashCodeFor(this); - - bool operator ==(other) { - if (other is! YamlMap) return false; - return deepEquals(this, other); - } -}
diff --git a/pkgs/yaml/lib/src/yaml_node.dart b/pkgs/yaml/lib/src/yaml_node.dart new file mode 100644 index 0000000..d0e0578 --- /dev/null +++ b/pkgs/yaml/lib/src/yaml_node.dart
@@ -0,0 +1,92 @@ +// 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. + +library yaml.map; + +import 'dart:collection' as collection; + +import 'package:collection/collection.dart'; +import 'package:source_maps/source_maps.dart'; + +/// An interface for parsed nodes from a YAML source tree. +/// +/// [YamlMap]s and [YamlList]s implement this interface in addition to the +/// normal [Map] and [List] interfaces, so any maps and lists will be +/// [YamlNode]s regardless of how they're accessed. +/// +/// Scalars values like strings and numbers, on the other hand, don't have this +/// interface by default. Instead, they can be accessed as [YamlScalar]s via +/// [YamlMap.nodes] or [YamlList.nodes]. +abstract class YamlNode { + /// The source span for this node. + /// + /// [Span.getLocationMessage] can be used to produce a human-friendly message + /// about this node. + Span get span; + + /// The inner value of this node. + /// + /// For [YamlScalar]s, this will return the wrapped value. For [YamlMap] and + /// [YamlList], it will return [this], since they already implement [Map] and + /// [List], respectively. + get value; +} + +/// A read-only [Map] parsed from YAML. +class YamlMap extends YamlNode with collection.MapMixin, UnmodifiableMapMixin { + final Span span; + + final Map<dynamic, YamlNode> nodes; + + Map get value => this; + + Iterable get keys => nodes.keys.map((node) => node.value); + + /// Users of the library should not construct [YamlMap]s manually. + YamlMap(Map<dynamic, YamlNode> nodes, this.span) + : nodes = new UnmodifiableMapView<dynamic, YamlNode>(nodes); + + operator [](key) { + var node = nodes[key]; + return node == null ? null : node.value; + } +} + +// TODO(nweiz): Use UnmodifiableListMixin when issue 18970 is fixed. +/// A read-only [List] parsed from YAML. +class YamlList extends YamlNode with collection.ListMixin { + final Span span; + + final List<YamlNode> nodes; + + List get value => this; + + int get length => nodes.length; + + set length(int index) { + throw new UnsupportedError("Cannot modify an unmodifiable List"); + } + + /// Users of the library should not construct [YamlList]s manually. + YamlList(List<YamlNode> nodes, this.span) + : nodes = new UnmodifiableListView<YamlNode>(nodes); + + operator [](int index) => nodes[index].value; + + operator []=(int index, value) { + throw new UnsupportedError("Cannot modify an unmodifiable List"); + } +} + +/// A wrapped scalar value parsed from YAML. +class YamlScalar extends YamlNode { + final Span span; + + final value; + + /// Users of the library should not construct [YamlScalar]s manually. + YamlScalar(this.value, this.span); + + String toString() => value.toString(); +}
diff --git a/pkgs/yaml/lib/yaml.dart b/pkgs/yaml/lib/yaml.dart index 209db8c..f59719d 100644 --- a/pkgs/yaml/lib/yaml.dart +++ b/pkgs/yaml/lib/yaml.dart
@@ -8,9 +8,10 @@ import 'src/constructor.dart'; import 'src/parser.dart'; import 'src/yaml_exception.dart'; +import 'src/yaml_node.dart'; export 'src/yaml_exception.dart'; -export 'src/yaml_map.dart'; +export 'src/yaml_node.dart'; /// Loads a single document from a YAML string. /// @@ -28,12 +29,20 @@ /// /// If [sourceName] is passed, it's used as the name of the file or URL from /// which the YAML originated for error reporting. -loadYaml(String yaml, {String sourceName}) { +loadYaml(String yaml, {String sourceName}) => + loadYamlNode(yaml, sourceName: sourceName).value; + +/// Loads a single document from a YAML string as a [YamlNode]. +/// +/// This is just like [loadYaml], except that where [loadYaml] would return a +/// normal Dart value this returns a [YamlNode] instead. This allows the caller +/// to be confident that the return value will always be a [YamlNode]. +YamlNode loadYamlNode(String yaml, {String sourceName}) { var stream = loadYamlStream(yaml, sourceName: sourceName); if (stream.length != 1) { throw new YamlException("Expected 1 document, were ${stream.length}."); } - return stream[0]; + return stream.nodes[0]; } /// Loads a stream of documents from a YAML string. @@ -49,15 +58,16 @@ /// /// If [sourceName] is passed, it's used as the name of the file or URL from /// which the YAML originated for error reporting. -List loadYamlStream(String yaml, {String sourceName}) { - var stream; +YamlList loadYamlStream(String yaml, {String sourceName}) { + var pair; try { - stream = new Parser(yaml, sourceName).l_yamlStream(); + pair = new Parser(yaml, sourceName).l_yamlStream(); } on FormatException catch (error) { throw new YamlException(error.toString()); } - return stream + var nodes = pair.first .map((doc) => new Constructor(new Composer(doc).compose()).construct()) .toList(); + return new YamlList(nodes, pair.last); }
diff --git a/pkgs/yaml/pubspec.yaml b/pkgs/yaml/pubspec.yaml index b57224c..2672161 100644 --- a/pkgs/yaml/pubspec.yaml +++ b/pkgs/yaml/pubspec.yaml
@@ -1,5 +1,5 @@ name: yaml -version: 0.10.0 +version: 1.0.0 author: "Dart Team <misc@dartlang.org>" homepage: http://www.dartlang.org description: A parser for YAML.
diff --git a/pkgs/yaml/test/utils.dart b/pkgs/yaml/test/utils.dart index 0bac9f9..9c96ec1 100644 --- a/pkgs/yaml/test/utils.dart +++ b/pkgs/yaml/test/utils.dart
@@ -5,7 +5,7 @@ library yaml.test.utils; import 'package:unittest/unittest.dart'; -import 'package:yaml/src/deep_equals.dart' as de; +import 'package:yaml/src/equality.dart' as equality; import 'package:yaml/yaml.dart'; /// A matcher that validates that a closure or Future throws a [YamlException]. @@ -14,25 +14,28 @@ /// Returns a matcher that asserts that the value equals [expected]. /// /// This handles recursive loops and considers `NaN` to equal itself. -Matcher deepEquals(expected) => - predicate((actual) => de.deepEquals(actual, expected), "equals $expected"); +Matcher deepEquals(expected) => predicate((actual) => + equality.deepEquals(actual, expected), "equals $expected"); /// Constructs a new yaml.YamlMap, optionally from a normal Map. -Map yamlMap([Map from]) => - from == null ? new YamlMap() : new YamlMap.from(from); +Map deepEqualsMap([Map from]) { + var map = equality.deepEqualsMap(); + if (from != null) map.addAll(from); + return map; +} /// Asserts that a string containing a single YAML document produces a given /// value when loaded. void expectYamlLoads(expected, String source) { var actual = loadYaml(cleanUpLiteral(source)); - expect(expected, deepEquals(actual)); + expect(actual, deepEquals(expected)); } /// Asserts that a string containing a stream of YAML documents produces a given /// list of values when loaded. void expectYamlStreamLoads(List expected, String source) { var actual = loadYamlStream(cleanUpLiteral(source)); - expect(expected, deepEquals(actual)); + expect(actual, deepEquals(expected)); } /// Asserts that a string containing a single YAML document throws a
diff --git a/pkgs/yaml/test/yaml_test.dart b/pkgs/yaml/test/yaml_test.dart index fcfc12e..4fbb108 100644 --- a/pkgs/yaml/test/yaml_test.dart +++ b/pkgs/yaml/test/yaml_test.dart
@@ -14,37 +14,6 @@ var infinity = double.parse("Infinity"); var nan = double.parse("NaN"); - group('YamlMap', () { - group('accepts as a key', () { - _expectKeyWorks(keyFn()) { - var map = yamlMap(); - map[keyFn()] = 5; - expect(map.containsKey(keyFn()), isTrue); - expect(map[keyFn()], 5); - } - - test('null', () => _expectKeyWorks(() => null)); - test('true', () => _expectKeyWorks(() => true)); - test('false', () => _expectKeyWorks(() => false)); - test('a list', () => _expectKeyWorks(() => [1, 2, 3])); - test('a map', () => _expectKeyWorks(() => {'foo': 'bar'})); - test('a YAML map', () => _expectKeyWorks(() => yamlMap({'foo': 'bar'}))); - }); - - test('works as a hash key', () { - var normalMap = new Map(); - normalMap[yamlMap({'foo': 'bar'})] = 'baz'; - expect(normalMap.containsKey(yamlMap({'foo': 'bar'})), isTrue); - expect(normalMap[yamlMap({'foo': 'bar'})], 'baz'); - }); - - test('treats YamlMap keys the same as normal maps', () { - var map = yamlMap(); - map[{'a': 'b'}] = 5; - expect(map[yamlMap({'a': 'b'})], 5); - }); - }); - group('has a friendly error message for', () { var tabError = predicate((e) => e.toString().contains('tab characters are not allowed as indentation')); @@ -221,7 +190,7 @@ // }); test('[Example 2.11]', () { - var doc = yamlMap(); + var doc = deepEqualsMap(); doc[["Detroit Tigers", "Chicago cubs"]] = ["2001-07-23"]; doc[["New York Yankees", "Atlanta Braves"]] = ["2001-07-02", "2001-08-12", "2001-08-14"]; @@ -382,7 +351,7 @@ }); test('[Example 2.21]', () { - var doc = yamlMap({ + var doc = deepEqualsMap({ "booleans": [true, false], "string": "012345" }); @@ -900,7 +869,7 @@ }); test('[Example 6.12]', () { - var doc = yamlMap(); + var doc = deepEqualsMap(); doc[{'first': 'Sammy', 'last': 'Sosa'}] = { 'hr': 65, 'avg': 0.278 @@ -1092,7 +1061,7 @@ // }); test('[Example 7.3]', () { - var doc = yamlMap({"foo": null}); + var doc = deepEqualsMap({"foo": null}); doc[null] = "bar"; expectYamlLoads(doc, """ @@ -1239,7 +1208,7 @@ }); test('[Example 7.16]', () { - var doc = yamlMap({ + var doc = deepEqualsMap({ "explicit": "entry", "implicit": "entry" }); @@ -1254,7 +1223,7 @@ }); test('[Example 7.17]', () { - var doc = yamlMap({ + var doc = deepEqualsMap({ "unquoted": "separate", "http://foo.com": null, "omitted value": null @@ -1302,10 +1271,10 @@ }); test('[Example 7.21]', () { - var el1 = yamlMap(); + var el1 = deepEqualsMap(); el1[null] = "empty key entry"; - var el2 = yamlMap(); + var el2 = deepEqualsMap(); el2[{"JSON": "like"}] = "adjacent"; expectYamlLoads([[{"YAML": "separate"}], [el1], [el2]], @@ -1577,7 +1546,7 @@ }); test('[Example 8.18]', () { - var doc = yamlMap({ + var doc = deepEqualsMap({ 'plain key': 'in-line value', "quoted key": ["entry"] }); @@ -1591,7 +1560,7 @@ }); test('[Example 8.19]', () { - var el = yamlMap(); + var el = deepEqualsMap(); el[{'earth': 'blue'}] = {'moon': 'white'}; expectYamlLoads([{'sun': 'yellow'}, el], """ @@ -1763,7 +1732,7 @@ group('10.2: JSON Schema', () { // test('[Example 10.4]', () { - // var doc = yamlMap({"key with null value": null}); + // var doc = deepEqualsMap({"key with null value": null}); // doc[null] = "value for null key"; // expectYamlStreamLoads(doc, // """