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

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

Issue 1329763002: Don't use regular expressions in Loader. (Closed) Base URL: git@github.com:dart-lang/yaml@master
Patch Set: Code review changes Created 5 years, 3 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 | « CHANGELOG.md ('k') | lib/src/yaml_node.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library yaml.loader; 5 library yaml.loader;
6 6
7 import 'package:charcode/ascii.dart';
7 import 'package:source_span/source_span.dart'; 8 import 'package:source_span/source_span.dart';
8 9
9 import 'equality.dart'; 10 import 'equality.dart';
10 import 'event.dart'; 11 import 'event.dart';
11 import 'parser.dart'; 12 import 'parser.dart';
12 import 'yaml_document.dart'; 13 import 'yaml_document.dart';
13 import 'yaml_exception.dart'; 14 import 'yaml_exception.dart';
14 import 'yaml_node.dart'; 15 import 'yaml_node.dart';
15 16
16 /// Matches YAML null.
17 final _nullRegExp = new RegExp(r"^(null|Null|NULL|~|)$");
18
19 /// Matches a YAML bool.
20 final _boolRegExp = new RegExp(r"^(?:(true|True|TRUE)|(false|False|FALSE))$");
21
22 /// Matches a YAML decimal integer like `+1234`.
23 final _decimalIntRegExp = new RegExp(r"^[-+]?[0-9]+$");
24
25 /// Matches a YAML octal integer like `0o123`.
26 final _octalIntRegExp = new RegExp(r"^0o([0-7]+)$");
27
28 /// Matches a YAML hexidecimal integer like `0x123abc`.
29 final _hexIntRegExp = new RegExp(r"^0x[0-9a-fA-F]+$");
30
31 /// Matches a YAML floating point number like `12.34+e56`.
32 final _floatRegExp = new RegExp(
33 r"^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$");
34
35 /// Matches YAML infinity.
36 final _infinityRegExp = new RegExp(r"^([+-]?)\.(inf|Inf|INF)$");
37
38 /// Matches YAML NaN.
39 final _nanRegExp = new RegExp(r"^\.(nan|NaN|NAN)$");
40
41 /// A loader that reads [Event]s emitted by a [Parser] and emits 17 /// A loader that reads [Event]s emitted by a [Parser] and emits
42 /// [YamlDocument]s. 18 /// [YamlDocument]s.
43 /// 19 ///
44 /// This is based on the libyaml loader, available at 20 /// This is based on the libyaml loader, available at
45 /// https://github.com/yaml/libyaml/blob/master/src/loader.c. The license for 21 /// https://github.com/yaml/libyaml/blob/master/src/loader.c. The license for
46 /// that is available in ../../libyaml-license.txt. 22 /// that is available in ../../libyaml-license.txt.
47 class Loader { 23 class Loader {
48 /// The underlying [Parser] that generates [Event]s. 24 /// The underlying [Parser] that generates [Event]s.
49 final Parser _parser; 25 final Parser _parser;
50 26
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
126 var alias = _aliases[event.name]; 102 var alias = _aliases[event.name];
127 if (alias != null) return alias; 103 if (alias != null) return alias;
128 104
129 throw new YamlException("Undefined alias.", event.span); 105 throw new YamlException("Undefined alias.", event.span);
130 } 106 }
131 107
132 /// Composes a scalar node. 108 /// Composes a scalar node.
133 YamlNode _loadScalar(ScalarEvent scalar) { 109 YamlNode _loadScalar(ScalarEvent scalar) {
134 var node; 110 var node;
135 if (scalar.tag == "!") { 111 if (scalar.tag == "!") {
136 node = _parseString(scalar); 112 node = new YamlScalar.internal(scalar.value, scalar);
137 } else if (scalar.tag != null) { 113 } else if (scalar.tag != null) {
138 node = _parseByTag(scalar); 114 node = _parseByTag(scalar);
139 } else { 115 } else {
140 node = _parseNull(scalar); 116 node = _parseScalar(scalar);
141 if (node == null) node = _parseBool(scalar);
142 if (node == null) node = _parseInt(scalar);
143 if (node == null) node = _parseFloat(scalar);
144 if (node == null) node = _parseString(scalar);
145 } 117 }
146 118
147 _registerAnchor(scalar.anchor, node); 119 _registerAnchor(scalar.anchor, node);
148 return node; 120 return node;
149 } 121 }
150 122
151 /// Composes a sequence node. 123 /// Composes a sequence node.
152 YamlNode _loadSequence(SequenceStartEvent firstEvent) { 124 YamlNode _loadSequence(SequenceStartEvent firstEvent) {
153 if (firstEvent.tag != "!" && firstEvent.tag != null && 125 if (firstEvent.tag != "!" && firstEvent.tag != null &&
154 firstEvent.tag != "tag:yaml.org,2002:seq") { 126 firstEvent.tag != "tag:yaml.org,2002:seq") {
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 event = _parser.parse(); 162 event = _parser.parse();
191 } 163 }
192 164
193 setSpan(node, firstEvent.span.expand(event.span)); 165 setSpan(node, firstEvent.span.expand(event.span));
194 return node; 166 return node;
195 } 167 }
196 168
197 /// Parses a scalar according to its tag name. 169 /// Parses a scalar according to its tag name.
198 YamlScalar _parseByTag(ScalarEvent scalar) { 170 YamlScalar _parseByTag(ScalarEvent scalar) {
199 switch (scalar.tag) { 171 switch (scalar.tag) {
200 case "tag:yaml.org,2002:null": return _parseNull(scalar); 172 case "tag:yaml.org,2002:null":
201 case "tag:yaml.org,2002:bool": return _parseBool(scalar); 173 var result = _parseNull(scalar);
202 case "tag:yaml.org,2002:int": return _parseInt(scalar); 174 if (result != null) return result;
203 case "tag:yaml.org,2002:float": return _parseFloat(scalar); 175 throw new YamlException("Invalid null scalar.", scalar.span);
204 case "tag:yaml.org,2002:str": return _parseString(scalar); 176 case "tag:yaml.org,2002:bool":
205 } 177 var result = _parseBool(scalar);
206 throw new YamlException('Undefined tag: ${scalar.tag}.', scalar.span); 178 if (result != null) return result;
207 } 179 throw new YamlException("Invalid bool scalar.", scalar.span);
208 180 case "tag:yaml.org,2002:int":
209 /// Parses a null scalar. 181 var result = _parseNumber(scalar, allowFloat: false);
210 YamlScalar _parseNull(ScalarEvent scalar) { 182 if (result != null) return result;
211 // TODO(nweiz): add ScalarStyle and implicit metadata to the scalars. 183 throw new YamlException("Invalid int scalar.", scalar.span);
212 if (_nullRegExp.hasMatch(scalar.value)) { 184 case "tag:yaml.org,2002:float":
213 return new YamlScalar.internal(null, scalar.span, scalar.style); 185 var result = _parseNumber(scalar, allowInt: false);
214 } else { 186 if (result != null) return result;
215 return null; 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);
216 } 192 }
217 } 193 }
218 194
219 /// Parses a boolean scalar. 195 /// Parses [scalar], which may be one of several types.
220 YamlScalar _parseBool(ScalarEvent scalar) { 196 YamlScalar _parseScalar(ScalarEvent scalar) =>
221 var match = _boolRegExp.firstMatch(scalar.value); 197 _tryParseScalar(scalar) ?? new YamlScalar.internal(scalar.value, scalar);
222 if (match == null) return null; 198
223 return new YamlScalar.internal( 199 /// Tries to parse [scalar].
224 match.group(1) != null, scalar.span, scalar.style); 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 }
225 } 230 }
226 231
227 /// Parses an integer scalar. 232 /// Parse a null scalar.
228 YamlScalar _parseInt(ScalarEvent scalar) { 233 ///
229 var match = _decimalIntRegExp.firstMatch(scalar.value); 234 /// Returns a Dart `null` if parsing fails.
230 if (match != null) { 235 YamlScalar _parseNull(ScalarEvent scalar) {
231 return new YamlScalar.internal( 236 switch (scalar.value) {
232 int.parse(match.group(0)), scalar.span, scalar.style); 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;
233 } 290 }
234 291
235 match = _octalIntRegExp.firstMatch(scalar.value); 292 var secondChar = contents.codeUnitAt(1);
236 if (match != null) { 293
237 var n = int.parse(match.group(1), radix: 8); 294 // Hexadecimal or octal integers.
238 return new YamlScalar.internal(n, scalar.span, scalar.style); 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 }
239 } 303 }
240 304
241 match = _hexIntRegExp.firstMatch(scalar.value); 305 // Int or float starting with a digit or a +/- sign.
242 if (match != null) { 306 if ((firstChar >= $0 && firstChar <= $9) ||
243 return new YamlScalar.internal( 307 ((firstChar == $plus || firstChar == $minus) &&
244 int.parse(match.group(0)), scalar.span, scalar.style); 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 }
245 } 355 }
246 356
247 return null; 357 return null;
248 } 358 }
249
250 /// Parses a floating-point scalar.
251 YamlScalar _parseFloat(ScalarEvent scalar) {
252 var match = _floatRegExp.firstMatch(scalar.value);
253 if (match != null) {
254 // YAML allows floats of the form "0.", but Dart does not. Fix up those
255 // floats by removing the trailing dot.
256 var matchStr = match.group(0).replaceAll(new RegExp(r"\.$"), "");
257 return new YamlScalar.internal(
258 double.parse(matchStr), scalar.span, scalar.style);
259 }
260
261 match = _infinityRegExp.firstMatch(scalar.value);
262 if (match != null) {
263 var value = match.group(1) == "-" ? -double.INFINITY : double.INFINITY;
264 return new YamlScalar.internal(value, scalar.span, scalar.style);
265 }
266
267 match = _nanRegExp.firstMatch(scalar.value);
268 if (match != null) {
269 return new YamlScalar.internal(double.NAN, scalar.span, scalar.style);
270 }
271
272 return null;
273 }
274
275 /// Parses a string scalar.
276 YamlScalar _parseString(ScalarEvent scalar) =>
277 new YamlScalar.internal(scalar.value, scalar.span, scalar.style);
278 } 359 }
OLDNEW
« no previous file with comments | « CHANGELOG.md ('k') | lib/src/yaml_node.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698