| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library yaml.loader; | |
| 6 | |
| 7 import 'package:source_span/source_span.dart'; | |
| 8 | |
| 9 import 'equality.dart'; | |
| 10 import 'event.dart'; | |
| 11 import 'parser.dart'; | |
| 12 import 'yaml_document.dart'; | |
| 13 import 'yaml_exception.dart'; | |
| 14 import 'yaml_node.dart'; | |
| 15 | |
| 16 /// A loader that reads [Event]s emitted by a [Parser] and emits | |
| 17 /// [YamlDocument]s. | |
| 18 /// | |
| 19 /// This is based on the libyaml loader, available at | |
| 20 /// https://github.com/yaml/libyaml/blob/master/src/loader.c. The license for | |
| 21 /// that is available in ../../libyaml-license.txt. | |
| 22 class Loader { | |
| 23 /// The underlying [Parser] that generates [Event]s. | |
| 24 final Parser _parser; | |
| 25 | |
| 26 /// Aliases by the alias name. | |
| 27 final _aliases = new Map<String, YamlNode>(); | |
| 28 | |
| 29 /// The span of the entire stream emitted so far. | |
| 30 FileSpan get span => _span; | |
| 31 FileSpan _span; | |
| 32 | |
| 33 /// Creates a loader that loads [source]. | |
| 34 /// | |
| 35 /// [sourceUrl] can be a String or a [Uri]. | |
| 36 Loader(String source, {sourceUrl}) | |
| 37 : _parser = new Parser(source, sourceUrl: sourceUrl) { | |
| 38 var event = _parser.parse(); | |
| 39 _span = event.span; | |
| 40 assert(event.type == EventType.STREAM_START); | |
| 41 } | |
| 42 | |
| 43 /// Loads the next document from the stream. | |
| 44 /// | |
| 45 /// If there are no more documents, returns `null`. | |
| 46 YamlDocument load() { | |
| 47 if (_parser.isDone) return null; | |
| 48 | |
| 49 var event = _parser.parse(); | |
| 50 if (event.type == EventType.STREAM_END) { | |
| 51 _span = _span.expand(event.span); | |
| 52 return null; | |
| 53 } | |
| 54 | |
| 55 var document = _loadDocument(event); | |
| 56 _span = _span.expand(document.span); | |
| 57 _aliases.clear(); | |
| 58 return document; | |
| 59 } | |
| 60 | |
| 61 /// Composes a document object. | |
| 62 YamlDocument _loadDocument(DocumentStartEvent firstEvent) { | |
| 63 var contents = _loadNode(_parser.parse()); | |
| 64 | |
| 65 var lastEvent = _parser.parse(); | |
| 66 assert(lastEvent.type == EventType.DOCUMENT_END); | |
| 67 | |
| 68 return new YamlDocument.internal( | |
| 69 contents, | |
| 70 firstEvent.span.expand(lastEvent.span), | |
| 71 firstEvent.versionDirective, | |
| 72 firstEvent.tagDirectives, | |
| 73 startImplicit: firstEvent.isImplicit, | |
| 74 endImplicit: lastEvent.isImplicit); | |
| 75 } | |
| 76 | |
| 77 /// Composes a node. | |
| 78 YamlNode _loadNode(Event firstEvent) { | |
| 79 switch (firstEvent.type) { | |
| 80 case EventType.ALIAS: return _loadAlias(firstEvent); | |
| 81 case EventType.SCALAR: return _loadScalar(firstEvent); | |
| 82 case EventType.SEQUENCE_START: return _loadSequence(firstEvent); | |
| 83 case EventType.MAPPING_START: return _loadMapping(firstEvent); | |
| 84 default: throw "Unreachable"; | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 /// Registers an anchor. | |
| 89 void _registerAnchor(String anchor, YamlNode node) { | |
| 90 if (anchor == null) return; | |
| 91 | |
| 92 // libyaml throws an error for duplicate anchors, but example 7.1 makes it | |
| 93 // clear that they should be overridden: | |
| 94 // http://yaml.org/spec/1.2/spec.html#id2786448. | |
| 95 | |
| 96 _aliases[anchor] = node; | |
| 97 } | |
| 98 | |
| 99 /// Composes a node corresponding to an alias. | |
| 100 YamlNode _loadAlias(AliasEvent event) { | |
| 101 var alias = _aliases[event.name]; | |
| 102 if (alias != null) return alias; | |
| 103 | |
| 104 throw new YamlException("Undefined alias.", event.span); | |
| 105 } | |
| 106 | |
| 107 /// Composes a scalar node. | |
| 108 YamlNode _loadScalar(ScalarEvent scalar) { | |
| 109 var node; | |
| 110 if (scalar.tag == "!") { | |
| 111 node = _parseString(scalar); | |
| 112 } else if (scalar.tag != null) { | |
| 113 node = _parseByTag(scalar); | |
| 114 } else { | |
| 115 node = _parseNull(scalar); | |
| 116 if (node == null) node = _parseBool(scalar); | |
| 117 if (node == null) node = _parseInt(scalar); | |
| 118 if (node == null) node = _parseFloat(scalar); | |
| 119 if (node == null) node = _parseString(scalar); | |
| 120 } | |
| 121 | |
| 122 _registerAnchor(scalar.anchor, node); | |
| 123 return node; | |
| 124 } | |
| 125 | |
| 126 /// Composes a sequence node. | |
| 127 YamlNode _loadSequence(SequenceStartEvent firstEvent) { | |
| 128 if (firstEvent.tag != "!" && firstEvent.tag != null && | |
| 129 firstEvent.tag != "tag:yaml.org,2002:seq") { | |
| 130 throw new YamlException("Invalid tag for sequence.", firstEvent.span); | |
| 131 } | |
| 132 | |
| 133 var children = []; | |
| 134 var node = new YamlList.internal( | |
| 135 children, firstEvent.span, firstEvent.style); | |
| 136 _registerAnchor(firstEvent.anchor, node); | |
| 137 | |
| 138 var event = _parser.parse(); | |
| 139 while (event.type != EventType.SEQUENCE_END) { | |
| 140 children.add(_loadNode(event)); | |
| 141 event = _parser.parse(); | |
| 142 } | |
| 143 | |
| 144 setSpan(node, firstEvent.span.expand(event.span)); | |
| 145 return node; | |
| 146 } | |
| 147 | |
| 148 /// Composes a mapping node. | |
| 149 YamlNode _loadMapping(MappingStartEvent firstEvent) { | |
| 150 if (firstEvent.tag != "!" && firstEvent.tag != null && | |
| 151 firstEvent.tag != "tag:yaml.org,2002:map") { | |
| 152 throw new YamlException("Invalid tag for mapping.", firstEvent.span); | |
| 153 } | |
| 154 | |
| 155 var children = deepEqualsMap(); | |
| 156 var node = new YamlMap.internal( | |
| 157 children, firstEvent.span, firstEvent.style); | |
| 158 _registerAnchor(firstEvent.anchor, node); | |
| 159 | |
| 160 var event = _parser.parse(); | |
| 161 while (event.type != EventType.MAPPING_END) { | |
| 162 var key = _loadNode(event); | |
| 163 var value = _loadNode(_parser.parse()); | |
| 164 children[key] = value; | |
| 165 event = _parser.parse(); | |
| 166 } | |
| 167 | |
| 168 setSpan(node, firstEvent.span.expand(event.span)); | |
| 169 return node; | |
| 170 } | |
| 171 | |
| 172 /// Parses a scalar according to its tag name. | |
| 173 YamlScalar _parseByTag(ScalarEvent scalar) { | |
| 174 switch (scalar.tag) { | |
| 175 case "tag:yaml.org,2002:null": return _parseNull(scalar); | |
| 176 case "tag:yaml.org,2002:bool": return _parseBool(scalar); | |
| 177 case "tag:yaml.org,2002:int": return _parseInt(scalar); | |
| 178 case "tag:yaml.org,2002:float": return _parseFloat(scalar); | |
| 179 case "tag:yaml.org,2002:str": return _parseString(scalar); | |
| 180 } | |
| 181 throw new YamlException('Undefined tag: ${scalar.tag}.', scalar.span); | |
| 182 } | |
| 183 | |
| 184 /// Parses a null scalar. | |
| 185 YamlScalar _parseNull(ScalarEvent scalar) { | |
| 186 // TODO(nweiz): stop using regexps. | |
| 187 // TODO(nweiz): add ScalarStyle and implicit metadata to the scalars. | |
| 188 if (new RegExp(r"^(null|Null|NULL|~|)$").hasMatch(scalar.value)) { | |
| 189 return new YamlScalar.internal(null, scalar.span, scalar.style); | |
| 190 } else { | |
| 191 return null; | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 /// Parses a boolean scalar. | |
| 196 YamlScalar _parseBool(ScalarEvent scalar) { | |
| 197 var match = new RegExp(r"^(?:(true|True|TRUE)|(false|False|FALSE))$"). | |
| 198 firstMatch(scalar.value); | |
| 199 if (match == null) return null; | |
| 200 return new YamlScalar.internal( | |
| 201 match.group(1) != null, scalar.span, scalar.style); | |
| 202 } | |
| 203 | |
| 204 /// Parses an integer scalar. | |
| 205 YamlScalar _parseInt(ScalarEvent scalar) { | |
| 206 var match = new RegExp(r"^[-+]?[0-9]+$").firstMatch(scalar.value); | |
| 207 if (match != null) { | |
| 208 return new YamlScalar.internal( | |
| 209 int.parse(match.group(0)), scalar.span, scalar.style); | |
| 210 } | |
| 211 | |
| 212 match = new RegExp(r"^0o([0-7]+)$").firstMatch(scalar.value); | |
| 213 if (match != null) { | |
| 214 var n = int.parse(match.group(1), radix: 8); | |
| 215 return new YamlScalar.internal(n, scalar.span, scalar.style); | |
| 216 } | |
| 217 | |
| 218 match = new RegExp(r"^0x[0-9a-fA-F]+$").firstMatch(scalar.value); | |
| 219 if (match != null) { | |
| 220 return new YamlScalar.internal( | |
| 221 int.parse(match.group(0)), scalar.span, scalar.style); | |
| 222 } | |
| 223 | |
| 224 return null; | |
| 225 } | |
| 226 | |
| 227 /// Parses a floating-point scalar. | |
| 228 YamlScalar _parseFloat(ScalarEvent scalar) { | |
| 229 var match = new RegExp( | |
| 230 r"^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$"). | |
| 231 firstMatch(scalar.value); | |
| 232 if (match != null) { | |
| 233 // YAML allows floats of the form "0.", but Dart does not. Fix up those | |
| 234 // floats by removing the trailing dot. | |
| 235 var matchStr = match.group(0).replaceAll(new RegExp(r"\.$"), ""); | |
| 236 return new YamlScalar.internal( | |
| 237 double.parse(matchStr), scalar.span, scalar.style); | |
| 238 } | |
| 239 | |
| 240 match = new RegExp(r"^([+-]?)\.(inf|Inf|INF)$").firstMatch(scalar.value); | |
| 241 if (match != null) { | |
| 242 var value = match.group(1) == "-" ? -double.INFINITY : double.INFINITY; | |
| 243 return new YamlScalar.internal(value, scalar.span, scalar.style); | |
| 244 } | |
| 245 | |
| 246 match = new RegExp(r"^\.(nan|NaN|NAN)$").firstMatch(scalar.value); | |
| 247 if (match != null) { | |
| 248 return new YamlScalar.internal(double.NAN, scalar.span, scalar.style); | |
| 249 } | |
| 250 | |
| 251 return null; | |
| 252 } | |
| 253 | |
| 254 /// Parses a string scalar. | |
| 255 YamlScalar _parseString(ScalarEvent scalar) => | |
| 256 new YamlScalar.internal(scalar.value, scalar.span, scalar.style); | |
| 257 } | |
| OLD | NEW |