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

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

Issue 689513002: Rewrite the pkg/yaml parser. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Update string_scanner dependency. Created 6 years, 1 month 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 | Annotate | Revision Log
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: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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698