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 |