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

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

Issue 1325053002: - Remove scanning by regular expression and redundant scanning. (Closed) Base URL: git@github.com:dart-lang/yaml.git@master
Patch Set: Updated to df28edda6846b052d75aea0585b8d8aa250d96f2 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 | « no previous file | no next file » | 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:source_span/source_span.dart'; 7 import 'package:source_span/source_span.dart';
8 8
9 import 'equality.dart'; 9 import 'equality.dart';
10 import 'event.dart'; 10 import 'event.dart';
11 import 'parser.dart'; 11 import 'parser.dart';
12 import 'yaml_document.dart'; 12 import 'yaml_document.dart';
13 import 'yaml_exception.dart'; 13 import 'yaml_exception.dart';
14 import 'yaml_node.dart'; 14 import 'yaml_node.dart';
15 15
16 /// Matches YAML null. 16 // A singleton class which corresponds to no value having been scanned.
17 final _nullRegExp = new RegExp(r"^(null|Null|NULL|~|)$"); 17 class _NoValue {
18 18 const _NoValue();
19 /// Matches a YAML bool. 19 }
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 20
41 /// A loader that reads [Event]s emitted by a [Parser] and emits 21 /// A loader that reads [Event]s emitted by a [Parser] and emits
42 /// [YamlDocument]s. 22 /// [YamlDocument]s.
43 /// 23 ///
44 /// This is based on the libyaml loader, available at 24 /// This is based on the libyaml loader, available at
45 /// https://github.com/yaml/libyaml/blob/master/src/loader.c. The license for 25 /// https://github.com/yaml/libyaml/blob/master/src/loader.c. The license for
46 /// that is available in ../../libyaml-license.txt. 26 /// that is available in ../../libyaml-license.txt.
47 class Loader { 27 class Loader {
48 /// The underlying [Parser] that generates [Event]s. 28 /// The underlying [Parser] that generates [Event]s.
49 final Parser _parser; 29 final Parser _parser;
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
126 var alias = _aliases[event.name]; 106 var alias = _aliases[event.name];
127 if (alias != null) return alias; 107 if (alias != null) return alias;
128 108
129 throw new YamlException("Undefined alias.", event.span); 109 throw new YamlException("Undefined alias.", event.span);
130 } 110 }
131 111
132 /// Composes a scalar node. 112 /// Composes a scalar node.
133 YamlNode _loadScalar(ScalarEvent scalar) { 113 YamlNode _loadScalar(ScalarEvent scalar) {
134 var node; 114 var node;
135 if (scalar.tag == "!") { 115 if (scalar.tag == "!") {
136 node = _parseString(scalar); 116 node = new YamlScalar.internal(scalar.value, scalar.span, scalar.style);
137 } else if (scalar.tag != null) { 117 } else if (scalar.tag != null) {
138 node = _parseByTag(scalar); 118 node = _scanByTag(scalar);
139 } else { 119 } else {
140 node = _parseNull(scalar); 120 node = _scanScalar(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 } 121 }
146 122
147 _registerAnchor(scalar.anchor, node); 123 _registerAnchor(scalar.anchor, node);
148 return node; 124 return node;
149 } 125 }
150 126
151 /// Composes a sequence node. 127 /// Composes a sequence node.
152 YamlNode _loadSequence(SequenceStartEvent firstEvent) { 128 YamlNode _loadSequence(SequenceStartEvent firstEvent) {
153 if (firstEvent.tag != "!" && firstEvent.tag != null && 129 if (firstEvent.tag != "!" && firstEvent.tag != null &&
154 firstEvent.tag != "tag:yaml.org,2002:seq") { 130 firstEvent.tag != "tag:yaml.org,2002:seq") {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
187 var key = _loadNode(event); 163 var key = _loadNode(event);
188 var value = _loadNode(_parser.parse()); 164 var value = _loadNode(_parser.parse());
189 children[key] = value; 165 children[key] = value;
190 event = _parser.parse(); 166 event = _parser.parse();
191 } 167 }
192 168
193 setSpan(node, firstEvent.span.expand(event.span)); 169 setSpan(node, firstEvent.span.expand(event.span));
194 return node; 170 return node;
195 } 171 }
196 172
173 // Value returned by the scanning functions if no valid value was scanned.
174 static const _kNoValue = const _NoValue();
175
197 /// Parses a scalar according to its tag name. 176 /// Parses a scalar according to its tag name.
198 YamlScalar _parseByTag(ScalarEvent scalar) { 177 YamlScalar _scanByTag(ScalarEvent scalar) {
178 var str = scalar.value;
179 var len = str.length;
180 var ch0 = (len == 0) ? null : str.codeUnitAt(0);
181
182 var value = _kNoValue;
199 switch (scalar.tag) { 183 switch (scalar.tag) {
200 case "tag:yaml.org,2002:null": return _parseNull(scalar); 184 case "tag:yaml.org,2002:null":
201 case "tag:yaml.org,2002:bool": return _parseBool(scalar); 185 value = _scanNull(str, ch0, len);
202 case "tag:yaml.org,2002:int": return _parseInt(scalar); 186 break;
203 case "tag:yaml.org,2002:float": return _parseFloat(scalar); 187 case "tag:yaml.org,2002:bool":
204 case "tag:yaml.org,2002:str": return _parseString(scalar); 188 value = _scanBool(str, ch0, len);
189 break;
190 case "tag:yaml.org,2002:int":
191 value = _scanNumber(str, ch0, len, true, false);
192 break;
193 case "tag:yaml.org,2002:float":
194 value = _scanNumber(str, ch0, len, false, true);
195 break;
196 case "tag:yaml.org,2002:str":
197 value = str;
198 break;
199 default:
200 throw new YamlException('Undefined tag: ${scalar.tag}.', scalar.span);
205 } 201 }
206 throw new YamlException('Undefined tag: ${scalar.tag}.', scalar.span); 202 if (value != _kNoValue) {
207 } 203 return new YamlScalar.internal(value, scalar.span, scalar.style);
208
209 /// Parses a null scalar.
210 YamlScalar _parseNull(ScalarEvent scalar) {
211 // TODO(nweiz): add ScalarStyle and implicit metadata to the scalars.
212 if (_nullRegExp.hasMatch(scalar.value)) {
213 return new YamlScalar.internal(null, scalar.span, scalar.style);
214 } else {
215 return null;
216 } 204 }
217 }
218
219 /// Parses a boolean scalar.
220 YamlScalar _parseBool(ScalarEvent scalar) {
221 var match = _boolRegExp.firstMatch(scalar.value);
222 if (match == null) return null;
223 return new YamlScalar.internal(
224 match.group(1) != null, scalar.span, scalar.style);
225 }
226
227 /// Parses an integer scalar.
228 YamlScalar _parseInt(ScalarEvent scalar) {
229 var match = _decimalIntRegExp.firstMatch(scalar.value);
230 if (match != null) {
231 return new YamlScalar.internal(
232 int.parse(match.group(0)), scalar.span, scalar.style);
233 }
234
235 match = _octalIntRegExp.firstMatch(scalar.value);
236 if (match != null) {
237 var n = int.parse(match.group(1), radix: 8);
238 return new YamlScalar.internal(n, scalar.span, scalar.style);
239 }
240
241 match = _hexIntRegExp.firstMatch(scalar.value);
242 if (match != null) {
243 return new YamlScalar.internal(
244 int.parse(match.group(0)), scalar.span, scalar.style);
245 }
246
247 return null; 205 return null;
248 } 206 }
249 207
250 /// Parses a floating-point scalar. 208 /// Code unit values.
251 YamlScalar _parseFloat(ScalarEvent scalar) { 209 static const _plus = 0x2B;
252 var match = _floatRegExp.firstMatch(scalar.value); 210 static const _minus = 0x2D;
253 if (match != null) { 211 static const _dot = 0x2E;
254 // YAML allows floats of the form "0.", but Dart does not. Fix up those 212 static const _0 = 0x30;
255 // floats by removing the trailing dot. 213 static const _9 = 0x39;
256 var matchStr = match.group(0).replaceAll(new RegExp(r"\.$"), ""); 214 static const _F = 0x46;
257 return new YamlScalar.internal( 215 static const _N = 0x4E;
258 double.parse(matchStr), scalar.span, scalar.style); 216 static const _T = 0x54;
217 static const _f = 0x66;
218 static const _n = 0x6E;
219 static const _o = 0x6F;
220 static const _t = 0x74;
221 static const _x = 0x78;
222 static const _tilde = 0x7E;
223
224 /// Scan a scalar event's value.
225 YamlScalar _scanScalar(ScalarEvent scalar) {
226 final str = scalar.value;
227 final len = str.length;
228 final span = scalar.span;
229 final style = scalar.style;
230
231 var value = _kNoValue;
232
233 // Quickly check for the empty string.
234 if (len == 0) {
235 // Empty string is equivalent to null.
236 value = null;
237 } else {
238 // Dispatch on the first character.
239 var ch0 = str.codeUnitAt(0);
240 if ((ch0 == _dot) || (ch0 == _plus) || (ch0 == _minus) ||
241 ((ch0 >= _0) && (ch0 <= _9))) {
242 // Recognize numbers (either int or double).
243 value = _scanNumber(str, ch0, len, true, true);
244 } else if ((len == 4) && ((ch0 == _n) || (ch0 == _N))) {
245 value = _scanNull(str, ch0, len);
246 } else if (((len == 4) && ((ch0 == _t) || (ch0 == _T))) ||
247 ((len == 5) && ((ch0 == _f) || (ch0 == _F)))) {
248 value = _scanBool(str, ch0, len);
249 } else if ((len == 1) && (ch0 == _tilde)) {
250 // Special case of null be written as "~'.
251 value = null;
252 }
259 } 253 }
260 254
261 match = _infinityRegExp.firstMatch(scalar.value); 255 // If no value was found, set to the String value.
262 if (match != null) { 256 if (value == _kNoValue) {
263 var value = match.group(1) == "-" ? -double.INFINITY : double.INFINITY; 257 value = str;
264 return new YamlScalar.internal(value, scalar.span, scalar.style);
265 } 258 }
266 259 return new YamlScalar.internal(value, span, style);
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 } 260 }
274 261
275 /// Parses a string scalar. 262 /// Scan the different versions of the null literal.
276 YamlScalar _parseString(ScalarEvent scalar) => 263 _scanNull(String str, int ch0, int len) {
277 new YamlScalar.internal(scalar.value, scalar.span, scalar.style); 264 if (len == 4) {
265 if (((ch0 == _n) && (str == "null")) ||
266 ((ch0 == _N) && ((str == "Null") || (str == "NULL")))) {
267 return null;
268 }
269 } else if ((len == 0) || ((len == 1) && (ch0 == _tilde))) {
270 return null;
271 }
272 return _kNoValue;
273 }
274
275 /// Scan a boolean.
276 _scanBool(String str, int ch0, int len) {
277 if (len == 4) {
278 if (((ch0 == _t) && (str == "true")) ||
279 ((ch0 == _T) && ((str == "True") || (str == "TRUE")))) {
280 return true;
281 }
282 } else if (len == 5) {
283 if (((ch0 == _f) && (str == "false")) ||
284 ((ch0 == _F) && ((str == "False") || (str == "FALSE")))) {
285 return false;
286 }
287 }
288 return _kNoValue;
289 }
290
291
292 /// Helper which returns the known "no value" when number parsing fails.
293 static var _onNumError = (_) => null;
294
295 /// Scan a number. Handles both integers and floats within one function if
296 /// possible avoid rescanning in the general case.
297 _scanNumber(String str, int ch0, int len, bool allow_int, bool allow_float) {
298 // Quick check for single digit integers.
299 if (len == 1) {
300 var val = ch0 - _0;
301 if ((val >= 0) && (val <= 9)) {
302 return val;
303 }
304 return _kNoValue;
305 }
306 // Assume we are scanning a nicely formed number.
307 var ch1 = str.codeUnitAt(1);
308 if (allow_int && (ch0 == _0) && ((ch1 == _x) || (ch1 == _o))) {
309 // Scanning hex or octal integers.
310 var sub_str = str.substring(2);
311 if (ch1 == _x) {
312 var result = int.parse(sub_str, radix:16, onError: _onNumError);
313 return (result != null) ? result : _kNoValue;
314 }
315 var result = int.parse(sub_str, radix: 8, onError: _onNumError);
316 return (result != null) ? result : _kNoValue;
317 } else if (((ch0 >= _0) && (ch0 <= _9)) ||
318 (((ch0 == _plus) || (ch0 == _minus)) &&
319 (ch1 >= _0) && (ch1 <= _9))) {
320 // Either starting with a number or starting with a sign and number.
321 var result = null;
322 if (allow_int) {
323 // Possible hex or octal numbers are handled above, so only allow
324 // radix 10 here.
325 result = int.parse(str, radix: 10, onError: _onNumError);
326 }
327 // If we failed to scan an int, try to scan as a double.
328 if (allow_float && (result == null)) {
329 result = double.parse(str, _onNumError);
330 }
331 return (result != null) ? result : _kNoValue;
332 } else if (allow_float) {
333 // Not the only possibility is to scan a float starting with a dot or
334 // a sign and a dot. As well as the signed/unsigned infinity values and
335 // not-a-numbers.
336 if (((ch0 == _dot) && (ch1 >= _0) && (ch1 <= _9)) ||
337 ((ch0 == _minus) || (ch0 == _plus)) && (ch1 == _dot)) {
338 // Starting with a . and a number or a sign followed by a dot.
339 if (len == 5) {
340 if (str == "+.inf" || str == "+.Inf" || str == "+.INF") {
341 return double.INFINITY;
342 } else if (str == "-.inf" || str == "-.Inf" || str == "-.INF") {
343 return -double.INFINITY;
344 }
345 }
346 var result = double.parse(str, _onNumError);
347 return (result != null) ? result : _kNoValue;
348 } else if ((len == 4) && (ch0 == _dot)) {
349 if (str == ".inf" || str == ".Inf" || str == ".INF") {
350 return double.INFINITY;
351 } else if (str == ".nan" || str == ".NaN" || str == ".NAN") {
352 return double.NAN;
353 }
354 }
355 }
356 return _kNoValue;
357 }
278 } 358 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698