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

Side by Side Diff: yaml/lib/src/loader.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 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 unified diff | Download patch
« no previous file with comments | « yaml/lib/src/event.dart ('k') | yaml/lib/src/null_span.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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:charcode/ascii.dart';
8 import 'package:source_span/source_span.dart';
9
10 import 'equality.dart';
11 import 'event.dart';
12 import 'parser.dart';
13 import 'yaml_document.dart';
14 import 'yaml_exception.dart';
15 import 'yaml_node.dart';
16
17 /// A loader that reads [Event]s emitted by a [Parser] and emits
18 /// [YamlDocument]s.
19 ///
20 /// This is based on the libyaml loader, available at
21 /// https://github.com/yaml/libyaml/blob/master/src/loader.c. The license for
22 /// that is available in ../../libyaml-license.txt.
23 class Loader {
24 /// The underlying [Parser] that generates [Event]s.
25 final Parser _parser;
26
27 /// Aliases by the alias name.
28 final _aliases = new Map<String, YamlNode>();
29
30 /// The span of the entire stream emitted so far.
31 FileSpan get span => _span;
32 FileSpan _span;
33
34 /// Creates a loader that loads [source].
35 ///
36 /// [sourceUrl] can be a String or a [Uri].
37 Loader(String source, {sourceUrl})
38 : _parser = new Parser(source, sourceUrl: sourceUrl) {
39 var event = _parser.parse();
40 _span = event.span;
41 assert(event.type == EventType.STREAM_START);
42 }
43
44 /// Loads the next document from the stream.
45 ///
46 /// If there are no more documents, returns `null`.
47 YamlDocument load() {
48 if (_parser.isDone) return null;
49
50 var event = _parser.parse();
51 if (event.type == EventType.STREAM_END) {
52 _span = _span.expand(event.span);
53 return null;
54 }
55
56 var document = _loadDocument(event);
57 _span = _span.expand(document.span);
58 _aliases.clear();
59 return document;
60 }
61
62 /// Composes a document object.
63 YamlDocument _loadDocument(DocumentStartEvent firstEvent) {
64 var contents = _loadNode(_parser.parse());
65
66 var lastEvent = _parser.parse();
67 assert(lastEvent.type == EventType.DOCUMENT_END);
68
69 return new YamlDocument.internal(
70 contents,
71 firstEvent.span.expand(lastEvent.span),
72 firstEvent.versionDirective,
73 firstEvent.tagDirectives,
74 startImplicit: firstEvent.isImplicit,
75 endImplicit: lastEvent.isImplicit);
76 }
77
78 /// Composes a node.
79 YamlNode _loadNode(Event firstEvent) {
80 switch (firstEvent.type) {
81 case EventType.ALIAS: return _loadAlias(firstEvent);
82 case EventType.SCALAR: return _loadScalar(firstEvent);
83 case EventType.SEQUENCE_START: return _loadSequence(firstEvent);
84 case EventType.MAPPING_START: return _loadMapping(firstEvent);
85 default: throw "Unreachable";
86 }
87 }
88
89 /// Registers an anchor.
90 void _registerAnchor(String anchor, YamlNode node) {
91 if (anchor == null) return;
92
93 // libyaml throws an error for duplicate anchors, but example 7.1 makes it
94 // clear that they should be overridden:
95 // http://yaml.org/spec/1.2/spec.html#id2786448.
96
97 _aliases[anchor] = node;
98 }
99
100 /// Composes a node corresponding to an alias.
101 YamlNode _loadAlias(AliasEvent event) {
102 var alias = _aliases[event.name];
103 if (alias != null) return alias;
104
105 throw new YamlException("Undefined alias.", event.span);
106 }
107
108 /// Composes a scalar node.
109 YamlNode _loadScalar(ScalarEvent scalar) {
110 var node;
111 if (scalar.tag == "!") {
112 node = new YamlScalar.internal(scalar.value, scalar);
113 } else if (scalar.tag != null) {
114 node = _parseByTag(scalar);
115 } else {
116 node = _parseScalar(scalar);
117 }
118
119 _registerAnchor(scalar.anchor, node);
120 return node;
121 }
122
123 /// Composes a sequence node.
124 YamlNode _loadSequence(SequenceStartEvent firstEvent) {
125 if (firstEvent.tag != "!" && firstEvent.tag != null &&
126 firstEvent.tag != "tag:yaml.org,2002:seq") {
127 throw new YamlException("Invalid tag for sequence.", firstEvent.span);
128 }
129
130 var children = [];
131 var node = new YamlList.internal(
132 children, firstEvent.span, firstEvent.style);
133 _registerAnchor(firstEvent.anchor, node);
134
135 var event = _parser.parse();
136 while (event.type != EventType.SEQUENCE_END) {
137 children.add(_loadNode(event));
138 event = _parser.parse();
139 }
140
141 setSpan(node, firstEvent.span.expand(event.span));
142 return node;
143 }
144
145 /// Composes a mapping node.
146 YamlNode _loadMapping(MappingStartEvent firstEvent) {
147 if (firstEvent.tag != "!" && firstEvent.tag != null &&
148 firstEvent.tag != "tag:yaml.org,2002:map") {
149 throw new YamlException("Invalid tag for mapping.", firstEvent.span);
150 }
151
152 var children = deepEqualsMap();
153 var node = new YamlMap.internal(
154 children, firstEvent.span, firstEvent.style);
155 _registerAnchor(firstEvent.anchor, node);
156
157 var event = _parser.parse();
158 while (event.type != EventType.MAPPING_END) {
159 var key = _loadNode(event);
160 var value = _loadNode(_parser.parse());
161 children[key] = value;
162 event = _parser.parse();
163 }
164
165 setSpan(node, firstEvent.span.expand(event.span));
166 return node;
167 }
168
169 /// Parses a scalar according to its tag name.
170 YamlScalar _parseByTag(ScalarEvent scalar) {
171 switch (scalar.tag) {
172 case "tag:yaml.org,2002:null":
173 var result = _parseNull(scalar);
174 if (result != null) return result;
175 throw new YamlException("Invalid null scalar.", scalar.span);
176 case "tag:yaml.org,2002:bool":
177 var result = _parseBool(scalar);
178 if (result != null) return result;
179 throw new YamlException("Invalid bool scalar.", scalar.span);
180 case "tag:yaml.org,2002:int":
181 var result = _parseNumber(scalar, allowFloat: false);
182 if (result != null) return result;
183 throw new YamlException("Invalid int scalar.", scalar.span);
184 case "tag:yaml.org,2002:float":
185 var result = _parseNumber(scalar, allowInt: false);
186 if (result != null) return result;
187 throw new YamlException("Invalid float scalar.", scalar.span);
188 case "tag:yaml.org,2002:str":
189 return new YamlScalar.internal(scalar.value, scalar);
190 default:
191 throw new YamlException('Undefined tag: ${scalar.tag}.', scalar.span);
192 }
193 }
194
195 /// Parses [scalar], which may be one of several types.
196 YamlScalar _parseScalar(ScalarEvent scalar) =>
197 _tryParseScalar(scalar) ?? new YamlScalar.internal(scalar.value, scalar);
198
199 /// Tries to parse [scalar].
200 ///
201 /// If parsing fails, this returns `null`, indicating that the scalar should
202 /// be parsed as a string.
203 YamlScalar _tryParseScalar(ScalarEvent scalar) {
204 // Quickly check for the empty string, which means null.
205 var length = scalar.value.length;
206 if (length == 0) return new YamlScalar.internal(null, scalar);
207
208 // Dispatch on the first character.
209 var firstChar = scalar.value.codeUnitAt(0);
210 switch (firstChar) {
211 case $dot:
212 case $plus:
213 case $minus:
214 return _parseNumber(scalar);
215 case $n:
216 case $N:
217 return length == 4 ? _parseNull(scalar) : null;
218 case $t:
219 case $T:
220 return length == 4 ? _parseBool(scalar) : null;
221 case $f:
222 case $F:
223 return length == 5 ? _parseBool(scalar) : null;
224 case $tilde:
225 return length == 1 ? new YamlScalar.internal(null, scalar) : null;
226 default:
227 if (firstChar >= $0 && firstChar <= $9) return _parseNumber(scalar);
228 return null;
229 }
230 }
231
232 /// Parse a null scalar.
233 ///
234 /// Returns a Dart `null` if parsing fails.
235 YamlScalar _parseNull(ScalarEvent scalar) {
236 switch (scalar.value) {
237 case "":
238 case "null":
239 case "Null":
240 case "NULL":
241 case "~":
242 return new YamlScalar.internal(null, scalar);
243 default:
244 return null;
245 }
246 }
247
248 /// Parse a boolean scalar.
249 ///
250 /// Returns `null` if parsing fails.
251 YamlScalar _parseBool(ScalarEvent scalar) {
252 switch (scalar.value) {
253 case "true":
254 case "True":
255 case "TRUE":
256 return new YamlScalar.internal(true, scalar);
257 case "false":
258 case "False":
259 case "FALSE":
260 return new YamlScalar.internal(false, scalar);
261 default:
262 return null;
263 }
264 }
265
266 /// Parses a numeric scalar.
267 ///
268 /// Returns `null` if parsing fails.
269 YamlNode _parseNumber(ScalarEvent scalar, {bool allowInt: true,
270 bool allowFloat: true}) {
271 var value = _parseNumberValue(scalar.value,
272 allowInt: allowInt, allowFloat: allowFloat);
273 return value == null ? null : new YamlScalar.internal(value, scalar);
274 }
275
276 /// Parses the value of a number.
277 ///
278 /// Returns the number if it's parsed successfully, or `null` if it's not.
279 num _parseNumberValue(String contents, {bool allowInt: true,
280 bool allowFloat: true}) {
281 assert(allowInt || allowFloat);
282
283 var firstChar = contents.codeUnitAt(0);
284 var length = contents.length;
285
286 // Quick check for single digit integers.
287 if (allowInt && length == 1) {
288 var value = firstChar - $0;
289 return value >= 0 && value <= 9 ? value : null;
290 }
291
292 var secondChar = contents.codeUnitAt(1);
293
294 // Hexadecimal or octal integers.
295 if (allowInt && firstChar == $0) {
296 // int.parse supports 0x natively.
297 if (secondChar == $x) return int.parse(contents, onError: (_) => null);
298
299 if (secondChar == $o) {
300 var afterRadix = contents.substring(2);
301 return int.parse(afterRadix, radix: 8, onError: (_) => null);
302 }
303 }
304
305 // Int or float starting with a digit or a +/- sign.
306 if ((firstChar >= $0 && firstChar <= $9) ||
307 ((firstChar == $plus || firstChar == $minus) &&
308 secondChar >= $0 && secondChar <= $9)) {
309 // Try to parse an int or, failing that, a double.
310 var result = null;
311 if (allowInt) {
312 // Pass "radix: 10" explicitly to ensure that "-0x10", which is valid
313 // Dart but invalid YAML, doesn't get parsed.
314 result = int.parse(contents, radix: 10, onError: (_) => null);
315 }
316
317 if (allowFloat) result ??= double.parse(contents, (_) => null);
318 return result;
319 }
320
321 if (!allowFloat) return null;
322
323 // Now the only possibility is to parse a float starting with a dot or a
324 // sign and a dot, or the signed/unsigned infinity values and not-a-numbers.
325 if ((firstChar == $dot && secondChar >= $0 && secondChar <= $9) ||
326 (firstChar == $minus || firstChar == $plus) && secondChar == $dot) {
327 // Starting with a . and a number or a sign followed by a dot.
328 if (length == 5) {
329 switch (contents) {
330 case "+.inf":
331 case "+.Inf":
332 case "+.INF":
333 return double.INFINITY;
334 case "-.inf":
335 case "-.Inf":
336 case "-.INF":
337 return -double.INFINITY;
338 }
339 }
340
341 return double.parse(contents, (_) => null);
342 }
343
344 if (length == 4 && firstChar == $dot) {
345 switch (contents) {
346 case ".inf":
347 case ".Inf":
348 case ".INF":
349 return double.INFINITY;
350 case ".nan":
351 case ".NaN":
352 case ".NAN":
353 return double.NAN;
354 }
355 }
356
357 return null;
358 }
359 }
OLDNEW
« no previous file with comments | « yaml/lib/src/event.dart ('k') | yaml/lib/src/null_span.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698