Chromium Code Reviews| 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 { | |
|
Bob Nystrom
2014/10/31 20:03:27
The separation between Loader and Parser is intere
nweiz
2014/11/04 22:19:36
It might make sense to expose the parser on its ow
| |
| 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 parser that loads [source]. | |
|
Bob Nystrom
2014/10/31 20:03:27
"loader".
nweiz
2014/11/04 22:19:36
Done.
| |
| 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 YamlDocument load() { | |
| 45 if (_parser.isDone) return null; | |
|
Bob Nystrom
2014/10/31 20:03:27
Document this.
nweiz
2014/11/04 22:19:36
Done.
| |
| 46 | |
| 47 var event = _parser.parse(); | |
| 48 if (event.type == EventType.STREAM_END) { | |
| 49 _span = _span.expand(event.span); | |
| 50 return null; | |
| 51 } | |
| 52 | |
| 53 var document = _loadDocument(event); | |
| 54 _span = _span.expand(document.span); | |
| 55 _aliases.clear(); | |
| 56 return document; | |
| 57 } | |
| 58 | |
| 59 /// Composes a document object. | |
| 60 YamlDocument _loadDocument(DocumentStartEvent firstEvent) { | |
| 61 var contents = _loadNode(_parser.parse()); | |
| 62 | |
| 63 var lastEvent = _parser.parse(); | |
| 64 assert(lastEvent.type == EventType.DOCUMENT_END); | |
| 65 | |
| 66 return new YamlDocument.internal( | |
| 67 contents, | |
| 68 firstEvent.span.expand(lastEvent.span), | |
| 69 firstEvent.versionDirective, | |
| 70 firstEvent.tagDirectives, | |
| 71 startImplicit: firstEvent.implicit, | |
| 72 endImplicit: lastEvent.implicit); | |
| 73 } | |
| 74 | |
| 75 /// Composes a node. | |
| 76 YamlNode _loadNode(Event firstEvent) { | |
| 77 switch (firstEvent.type) { | |
| 78 case EventType.ALIAS: return _loadAlias(firstEvent); | |
| 79 case EventType.SCALAR: return _loadScalar(firstEvent); | |
| 80 case EventType.SEQUENCE_START: return _loadSequence(firstEvent); | |
| 81 case EventType.MAPPING_START: return _loadMapping(firstEvent); | |
| 82 default: throw "Unreachable"; | |
| 83 } | |
| 84 } | |
| 85 | |
| 86 /// Registers an anchor. | |
| 87 void _registerAnchor(String anchor, YamlNode node) { | |
| 88 if (anchor == null) return; | |
| 89 | |
| 90 // libyaml throws an error for duplicate anchors, but example 7.1 makes it | |
| 91 // clear that they should be overridden: | |
| 92 // http://yaml.org/spec/1.2/spec.html#id2786448. | |
| 93 | |
| 94 _aliases[anchor] = node; | |
| 95 } | |
| 96 | |
| 97 /// Composes a node corresponding to an alias. | |
| 98 YamlNode _loadAlias(AliasEvent event) { | |
| 99 var alias = _aliases[event.name]; | |
| 100 if (alias != null) return alias; | |
| 101 | |
| 102 throw new YamlException("Undefined alias.", event.span); | |
| 103 } | |
| 104 | |
| 105 /// Composes a scalar node. | |
| 106 YamlNode _loadScalar(ScalarEvent scalar) { | |
| 107 var node; | |
| 108 if (scalar.tag == "!") { | |
| 109 node = _parseString(scalar); | |
| 110 } else if (scalar.tag == null) { | |
| 111 node = _parseNull(scalar); | |
| 112 if (node == null) node = _parseBool(scalar); | |
| 113 if (node == null) node = _parseInt(scalar); | |
| 114 if (node == null) node = _parseFloat(scalar); | |
| 115 if (node == null) node = _parseString(scalar); | |
| 116 } else { | |
| 117 node = _parseByTag(scalar); | |
|
Bob Nystrom
2014/10/31 20:03:27
I think it would read a little clearer to do this
nweiz
2014/11/04 22:19:36
Done.
| |
| 118 } | |
| 119 | |
| 120 _registerAnchor(scalar.anchor, node); | |
| 121 return node; | |
| 122 } | |
| 123 | |
| 124 /// Composes a sequence node. | |
| 125 YamlNode _loadSequence(SequenceStartEvent firstEvent) { | |
| 126 if (firstEvent.tag != "!" && firstEvent.tag != null && | |
| 127 firstEvent.tag != "tag:yaml.org,2002:seq") { | |
| 128 throw new YamlException("Invalid tag for sequence.", firstEvent.span); | |
| 129 } | |
| 130 | |
| 131 var children = []; | |
| 132 var node = new YamlList.internal( | |
| 133 children, firstEvent.span, firstEvent.style); | |
| 134 _registerAnchor(firstEvent.anchor, node); | |
| 135 | |
| 136 var event = _parser.parse(); | |
| 137 while (event.type != EventType.SEQUENCE_END) { | |
| 138 children.add(_loadNode(event)); | |
| 139 event = _parser.parse(); | |
| 140 } | |
| 141 | |
| 142 setSpan(node, firstEvent.span.expand(event.span)); | |
| 143 return node; | |
| 144 } | |
| 145 | |
| 146 /// Composes a mapping node. | |
| 147 YamlNode _loadMapping(MappingStartEvent firstEvent) { | |
| 148 if (firstEvent.tag != "!" && firstEvent.tag != null && | |
| 149 firstEvent.tag != "tag:yaml.org,2002:map") { | |
| 150 throw new YamlException("Invalid tag for mapping.", firstEvent.span); | |
| 151 } | |
| 152 | |
| 153 var children = deepEqualsMap(); | |
| 154 var node = new YamlMap.internal( | |
| 155 children, firstEvent.span, firstEvent.style); | |
| 156 _registerAnchor(firstEvent.anchor, node); | |
| 157 | |
| 158 var event = _parser.parse(); | |
| 159 while (event.type != EventType.MAPPING_END) { | |
| 160 var key = _loadNode(event); | |
| 161 var value = _loadNode(_parser.parse()); | |
| 162 children[key] = value; | |
| 163 event = _parser.parse(); | |
| 164 } | |
| 165 | |
| 166 setSpan(node, firstEvent.span.expand(event.span)); | |
| 167 return node; | |
| 168 } | |
| 169 | |
| 170 /// Parses a scalar according to its tag name. | |
| 171 YamlScalar _parseByTag(ScalarEvent scalar) { | |
| 172 switch (scalar.tag) { | |
| 173 case "tag:yaml.org,2002:null": return _parseNull(scalar); | |
| 174 case "tag:yaml.org,2002:bool": return _parseBool(scalar); | |
| 175 case "tag:yaml.org,2002:int": return _parseInt(scalar); | |
| 176 case "tag:yaml.org,2002:float": return _parseFloat(scalar); | |
| 177 case "tag:yaml.org,2002:str": return _parseString(scalar); | |
| 178 } | |
| 179 throw new YamlException('Undefined tag: ${scalar.tag}.', scalar.span); | |
| 180 } | |
| 181 | |
| 182 /// Parses a null scalar. | |
| 183 YamlScalar _parseNull(ScalarEvent scalar) { | |
| 184 // TODO(nweiz): stop using regexps. | |
| 185 // TODO(nweiz): add ScalarStyle and implicit metadata to the scalars. | |
| 186 if (new RegExp(r"^(null|Null|NULL|~|)$").hasMatch(scalar.value)) { | |
| 187 return new YamlScalar.internal(null, scalar.span, scalar.style); | |
| 188 } else { | |
| 189 return null; | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 /// Parses a boolean scalar. | |
| 194 YamlScalar _parseBool(ScalarEvent scalar) { | |
| 195 var match = new RegExp(r"^(?:(true|True|TRUE)|(false|False|FALSE))$"). | |
| 196 firstMatch(scalar.value); | |
| 197 if (match == null) return null; | |
| 198 return new YamlScalar.internal( | |
| 199 match.group(1) != null, scalar.span, scalar.style); | |
| 200 } | |
| 201 | |
| 202 /// Parses an integer scalar. | |
| 203 YamlScalar _parseInt(ScalarEvent scalar) { | |
| 204 var match = new RegExp(r"^[-+]?[0-9]+$").firstMatch(scalar.value); | |
|
Bob Nystrom
2014/10/31 20:03:27
Is "0000123" allowed?
I wonder if it's better to
nweiz
2014/11/04 22:19:36
Yes.
| |
| 205 if (match != null) { | |
| 206 return new YamlScalar.internal( | |
| 207 int.parse(match.group(0)), scalar.span, scalar.style); | |
| 208 } | |
| 209 | |
| 210 match = new RegExp(r"^0o([0-7]+)$").firstMatch(scalar.value); | |
| 211 if (match != null) { | |
| 212 var n = int.parse(match.group(1), radix: 8); | |
| 213 return new YamlScalar.internal(n, scalar.span, scalar.style); | |
| 214 } | |
| 215 | |
| 216 match = new RegExp(r"^0x[0-9a-fA-F]+$").firstMatch(scalar.value); | |
| 217 if (match != null) { | |
| 218 return new YamlScalar.internal( | |
| 219 int.parse(match.group(0)), scalar.span, scalar.style); | |
| 220 } | |
| 221 | |
| 222 return null; | |
| 223 } | |
| 224 | |
| 225 /// Parses a floating-point scalar. | |
| 226 YamlScalar _parseFloat(ScalarEvent scalar) { | |
| 227 var match = new RegExp( | |
| 228 r"^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$"). | |
| 229 firstMatch(scalar.value); | |
| 230 if (match != null) { | |
| 231 // YAML allows floats of the form "0.", but Dart does not. Fix up those | |
| 232 // floats by removing the trailing dot. | |
| 233 var matchStr = match.group(0).replaceAll(new RegExp(r"\.$"), ""); | |
| 234 return new YamlScalar.internal( | |
| 235 double.parse(matchStr), scalar.span, scalar.style); | |
| 236 } | |
| 237 | |
| 238 match = new RegExp(r"^([+-]?)\.(inf|Inf|INF)$").firstMatch(scalar.value); | |
| 239 if (match != null) { | |
| 240 var value = match.group(1) == "-" ? -double.INFINITY : double.INFINITY; | |
| 241 return new YamlScalar.internal(value, scalar.span, scalar.style); | |
| 242 } | |
| 243 | |
| 244 match = new RegExp(r"^\.(nan|NaN|NAN)$").firstMatch(scalar.value); | |
| 245 if (match != null) { | |
| 246 return new YamlScalar.internal(double.NAN, scalar.span, scalar.style); | |
| 247 } | |
| 248 | |
| 249 return null; | |
| 250 } | |
| 251 | |
| 252 /// Parses a string scalar. | |
| 253 YamlScalar _parseString(ScalarEvent scalar) => | |
| 254 new YamlScalar.internal(scalar.value, scalar.span, scalar.style); | |
| 255 } | |
| OLD | NEW |