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

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

Issue 308743007: Use SpanScanner to emit better YAML parse errors. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: code review Created 6 years, 6 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 | Annotate | Revision Log
« no previous file with comments | « pkg/yaml/CHANGELOG.md ('k') | pkg/yaml/lib/yaml.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) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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.parser; 5 library yaml.parser;
6 6
7 import 'dart:collection'; 7 import 'dart:collection';
8 8
9 import 'package:string_scanner/string_scanner.dart';
10
9 import 'model.dart'; 11 import 'model.dart';
10 import 'yaml_exception.dart'; 12 import 'yaml_exception.dart';
11 import 'yaml_map.dart'; 13 import 'yaml_map.dart';
12 14
13 /// Translates a string of characters into a YAML serialization tree. 15 /// Translates a string of characters into a YAML serialization tree.
14 /// 16 ///
15 /// This parser is designed to closely follow the spec. All productions in the 17 /// This parser is designed to closely follow the spec. All productions in the
16 /// spec are numbered, and the corresponding methods in the parser have the same 18 /// spec are numbered, and the corresponding methods in the parser have the same
17 /// numbers. This is certainly not the most efficient way of parsing YAML, but 19 /// numbers. This is certainly not the most efficient way of parsing YAML, but
18 /// it is the easiest to write and read in the context of the spec. 20 /// it is the easiest to write and read in the context of the spec.
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
109 static const BLOCK_IN = 1; 111 static const BLOCK_IN = 1;
110 static const FLOW_OUT = 2; 112 static const FLOW_OUT = 2;
111 static const FLOW_IN = 3; 113 static const FLOW_IN = 3;
112 static const BLOCK_KEY = 4; 114 static const BLOCK_KEY = 4;
113 static const FLOW_KEY = 5; 115 static const FLOW_KEY = 5;
114 116
115 static const CHOMPING_STRIP = 0; 117 static const CHOMPING_STRIP = 0;
116 static const CHOMPING_KEEP = 1; 118 static const CHOMPING_KEEP = 1;
117 static const CHOMPING_CLIP = 2; 119 static const CHOMPING_CLIP = 2;
118 120
119 /// The source string being parsed. 121 /// The scanner that's used to scan through the document.
120 final String _s; 122 final SpanScanner _scanner;
121
122 /// The current position in the source string.
123 int _pos = 0;
124
125 /// The length of the string being parsed.
126 final int _len;
127
128 /// The current (0-based) line in the source string.
129 int _line = 0;
130
131 /// The current (0-based) column in the source string.
132 int _column = 0;
133 123
134 /// Whether we're parsing a bare document (that is, one that doesn't begin 124 /// Whether we're parsing a bare document (that is, one that doesn't begin
135 /// with `---`). Bare documents don't allow `%` immediately following 125 /// with `---`). Bare documents don't allow `%` immediately following
136 /// newlines. 126 /// newlines.
137 bool _inBareDocument = false; 127 bool _inBareDocument = false;
138 128
139 /// The line number of the farthest position that has been parsed successfully 129 /// The state of the scanner when it was the farthest in the document it's
140 /// before backtracking. Used for error reporting. 130 /// been.
141 int _farthestLine = 0; 131 LineScannerState _farthestState;
142
143 /// The column number of the farthest position that has been parsed
144 /// successfully before backtracking. Used for error reporting.
145 int _farthestColumn = 0;
146
147 /// The farthest position in the source string that has been parsed
148 /// successfully before backtracking. Used for error reporting.
149 int _farthestPos = 0;
150 132
151 /// The name of the context of the farthest position that has been parsed 133 /// The name of the context of the farthest position that has been parsed
152 /// successfully before backtracking. Used for error reporting. 134 /// successfully before backtracking. Used for error reporting.
153 String _farthestContext = "document"; 135 String _farthestContext = "document";
154 136
155 /// A stack of the names of parse contexts. Used for error reporting. 137 /// A stack of the names of parse contexts. Used for error reporting.
156 List<String> _contextStack; 138 final _contextStack = <String>["document"];
157 139
158 /// Annotations attached to ranges of the source string that add extra 140 /// Annotations attached to ranges of the source string that add extra
159 /// information to any errors that occur in the annotated range. 141 /// information to any errors that occur in the annotated range.
160 _RangeMap<String> _errorAnnotations; 142 final _errorAnnotations = new _RangeMap<String>();
161 143
162 /// The buffer containing the string currently being captured. 144 /// The buffer containing the string currently being captured.
163 StringBuffer _capturedString; 145 StringBuffer _capturedString;
164 146
165 /// The beginning of the current section of the captured string. 147 /// The beginning of the current section of the captured string.
166 int _captureStart; 148 int _captureStart;
167 149
168 /// Whether the current string capture is being overridden. 150 /// Whether the current string capture is being overridden.
169 bool _capturingAs = false; 151 bool _capturingAs = false;
170 152
171 Parser(String s) 153 Parser(String yaml, String sourceName)
172 : this._s = s, 154 : _scanner = new SpanScanner(yaml, sourceName) {
173 _len = s.length, 155 _farthestState = _scanner.state;
174 _contextStack = <String>["document"], 156 }
175 _errorAnnotations = new _RangeMap();
176 157
177 /// Return the character at the current position, then move that position 158 /// Returns the character at the current position, then moves that position
178 /// forward one character. Also updates the current line and column numbers. 159 /// forward one character.
179 int next() { 160 int next() => _scanner.readChar();
180 if (_pos == _len) return -1;
181 var char = _s.codeUnitAt(_pos++);
182 if (isBreak(char)) {
183 _line++;
184 _column = 0;
185 } else {
186 _column++;
187 }
188
189 if (_farthestLine < _line) {
190 _farthestLine = _line;
191 _farthestColumn = _column;
192 _farthestContext = _contextStack.last;
193 } else if (_farthestLine == _line && _farthestColumn < _column) {
194 _farthestColumn = _column;
195 _farthestContext = _contextStack.last;
196 }
197 _farthestPos = _pos;
198
199 return char;
200 }
201 161
202 /// Returns the code unit at the current position, or the character [i] 162 /// Returns the code unit at the current position, or the character [i]
203 /// characters after the current position. 163 /// characters after the current position.
204 /// 164 int peek([int i = 0]) => _scanner.peekChar(i);
205 /// Returns -1 if this would return a character after the end or before the
206 /// beginning of the input string.
207 int peek([int i = 0]) {
208 var peekPos = _pos + i;
209 return (peekPos >= _len || peekPos < 0) ? -1 : _s.codeUnitAt(peekPos);
210 }
211 165
212 /// The truthiness operator. Returns `false` if [obj] is `null` or `false`, 166 /// The truthiness operator. Returns `false` if [obj] is `null` or `false`,
213 /// `true` otherwise. 167 /// `true` otherwise.
214 bool truth(obj) => obj != null && obj != false; 168 bool truth(obj) => obj != null && obj != false;
215 169
216 /// Consumes the current character if it matches [matcher]. Returns the result 170 /// Consumes the current character if it matches [matcher]. Returns the result
217 /// of [matcher]. 171 /// of [matcher].
218 bool consume(bool matcher(int)) { 172 bool consume(bool matcher(int)) {
219 if (matcher(peek())) { 173 if (matcher(peek())) {
220 next(); 174 next();
(...skipping 21 matching lines...) Expand all
242 return null; // Unreachable. 196 return null; // Unreachable.
243 } 197 }
244 198
245 /// Calls [consumer] until it returns a falsey value. Returns a list of all 199 /// Calls [consumer] until it returns a falsey value. Returns a list of all
246 /// truthy return values of [consumer], or the empty list if it didn't consume 200 /// truthy return values of [consumer], or the empty list if it didn't consume
247 /// anything. 201 /// anything.
248 /// 202 ///
249 /// Conceptually, repeats a production any number of times. 203 /// Conceptually, repeats a production any number of times.
250 List zeroOrMore(consumer()) { 204 List zeroOrMore(consumer()) {
251 var out = []; 205 var out = [];
252 var oldPos = _pos; 206 var oldPos = _scanner.position;
253 while (true) { 207 while (true) {
254 var el = consumer(); 208 var el = consumer();
255 if (!truth(el) || oldPos == _pos) return out; 209 if (!truth(el) || oldPos == _scanner.position) return out;
256 oldPos = _pos; 210 oldPos = _scanner.position;
257 out.add(el); 211 out.add(el);
258 } 212 }
259 return null; // Unreachable. 213 return null; // Unreachable.
260 } 214 }
261 215
262 /// Just calls [consumer] and returns its result. Used to make it explicit 216 /// Just calls [consumer] and returns its result. Used to make it explicit
263 /// that a production is intended to be optional. 217 /// that a production is intended to be optional.
264 zeroOrOne(consumer()) => consumer(); 218 zeroOrOne(consumer()) => consumer();
265 219
266 /// Calls each function in [consumers] until one returns a truthy value, then 220 /// Calls each function in [consumers] until one returns a truthy value, then
267 /// returns that. 221 /// returns that.
268 or(List<Function> consumers) { 222 or(List<Function> consumers) {
269 for (var c in consumers) { 223 for (var c in consumers) {
270 var res = c(); 224 var res = c();
271 if (truth(res)) return res; 225 if (truth(res)) return res;
272 } 226 }
273 return null; 227 return null;
274 } 228 }
275 229
276 /// Calls [consumer] and returns its result, but rolls back the parser state 230 /// Calls [consumer] and returns its result, but rolls back the parser state
277 /// if [consumer] returns a falsey value. 231 /// if [consumer] returns a falsey value.
278 transaction(consumer()) { 232 transaction(consumer()) {
279 var oldPos = _pos; 233 var oldState = _scanner.state;
280 var oldLine = _line;
281 var oldColumn = _column;
282 var oldCaptureStart = _captureStart; 234 var oldCaptureStart = _captureStart;
283 String capturedSoFar = _capturedString == null ? null : 235 String capturedSoFar = _capturedString == null ? null :
284 _capturedString.toString(); 236 _capturedString.toString();
285 var res = consumer(); 237 var res = consumer();
238 _refreshFarthestState();
286 if (truth(res)) return res; 239 if (truth(res)) return res;
287 240
288 _pos = oldPos; 241 _scanner.state = oldState;
289 _line = oldLine;
290 _column = oldColumn;
291 _captureStart = oldCaptureStart; 242 _captureStart = oldCaptureStart;
292 _capturedString = capturedSoFar == null ? null : 243 _capturedString = capturedSoFar == null ? null :
293 new StringBuffer(capturedSoFar); 244 new StringBuffer(capturedSoFar);
294 return res; 245 return res;
295 } 246 }
296 247
297 /// Consumes [n] characters matching [matcher], or none if there isn't a 248 /// Consumes [n] characters matching [matcher], or none if there isn't a
298 /// complete match. The first argument to [matcher] is the character code, the 249 /// complete match. The first argument to [matcher] is the character code, the
299 /// second is the index (from 0 to [n] - 1). 250 /// second is the index (from 0 to [n] - 1).
300 /// 251 ///
(...skipping 16 matching lines...) Expand all
317 String stringOf(bool matcher(int)) => 268 String stringOf(bool matcher(int)) =>
318 captureString(() => oneOrMore(() => consume(matcher))); 269 captureString(() => oneOrMore(() => consume(matcher)));
319 270
320 /// Calls [consumer] and returns the string that was consumed while doing so, 271 /// Calls [consumer] and returns the string that was consumed while doing so,
321 /// or null if [consumer] returned a falsey value. Automatically wraps 272 /// or null if [consumer] returned a falsey value. Automatically wraps
322 /// [consumer] in `transaction`. 273 /// [consumer] in `transaction`.
323 String captureString(consumer()) { 274 String captureString(consumer()) {
324 // captureString calls may not be nested 275 // captureString calls may not be nested
325 assert(_capturedString == null); 276 assert(_capturedString == null);
326 277
327 _captureStart = _pos; 278 _captureStart = _scanner.position;
328 _capturedString = new StringBuffer(); 279 _capturedString = new StringBuffer();
329 var res = transaction(consumer); 280 var res = transaction(consumer);
330 if (!truth(res)) { 281 if (!truth(res)) {
331 _captureStart = null; 282 _captureStart = null;
332 _capturedString = null; 283 _capturedString = null;
333 return null; 284 return null;
334 } 285 }
335 286
336 flushCapture(); 287 flushCapture();
337 var result = _capturedString.toString(); 288 var result = _capturedString.toString();
338 _captureStart = null; 289 _captureStart = null;
339 _capturedString = null; 290 _capturedString = null;
340 return result; 291 return result;
341 } 292 }
342 293
343 captureAs(String replacement, consumer()) => 294 captureAs(String replacement, consumer()) =>
344 captureAndTransform(consumer, (_) => replacement); 295 captureAndTransform(consumer, (_) => replacement);
345 296
346 captureAndTransform(consumer(), String transformation(String captured)) { 297 captureAndTransform(consumer(), String transformation(String captured)) {
347 if (_capturedString == null) return consumer(); 298 if (_capturedString == null) return consumer();
348 if (_capturingAs) return consumer(); 299 if (_capturingAs) return consumer();
349 300
350 flushCapture(); 301 flushCapture();
351 _capturingAs = true; 302 _capturingAs = true;
352 var res = consumer(); 303 var res = consumer();
353 _capturingAs = false; 304 _capturingAs = false;
354 if (!truth(res)) return res; 305 if (!truth(res)) return res;
355 306
356 _capturedString.write(transformation(_s.substring(_captureStart, _pos))); 307 _capturedString.write(transformation(
357 _captureStart = _pos; 308 _scanner.string.substring(_captureStart, _scanner.position)));
309 _captureStart = _scanner.position;
358 return res; 310 return res;
359 } 311 }
360 312
361 void flushCapture() { 313 void flushCapture() {
362 _capturedString.write(_s.substring(_captureStart, _pos)); 314 _capturedString.write(_scanner.string.substring(
363 _captureStart = _pos; 315 _captureStart, _scanner.position));
316 _captureStart = _scanner.position;
364 } 317 }
365 318
366 /// Adds a tag and an anchor to [node], if they're defined. 319 /// Adds a tag and an anchor to [node], if they're defined.
367 Node addProps(Node node, _Pair<Tag, String> props) { 320 Node addProps(Node node, _Pair<Tag, String> props) {
368 if (props == null || node == null) return node; 321 if (props == null || node == null) return node;
369 if (truth(props.first)) node.tag = props.first; 322 if (truth(props.first)) node.tag = props.first;
370 if (truth(props.last)) node.anchor = props.last; 323 if (truth(props.last)) node.anchor = props.last;
371 return node; 324 return node;
372 } 325 }
373 326
(...skipping 12 matching lines...) Expand all
386 } finally { 339 } finally {
387 var popped = _contextStack.removeLast(); 340 var popped = _contextStack.removeLast();
388 assert(popped == name); 341 assert(popped == name);
389 } 342 }
390 } 343 }
391 344
392 /// Adds [message] as extra information to any errors that occur between the 345 /// Adds [message] as extra information to any errors that occur between the
393 /// current position and the position of the cursor after running [fn]. The 346 /// current position and the position of the cursor after running [fn]. The
394 /// cursor is reset after [fn] is run. 347 /// cursor is reset after [fn] is run.
395 annotateError(String message, fn()) { 348 annotateError(String message, fn()) {
396 var start = _pos; 349 var start = _scanner.position;
397 var end; 350 var end;
398 transaction(() { 351 transaction(() {
399 fn(); 352 fn();
400 end = _pos; 353 end = _scanner.position;
401 return false; 354 return false;
402 }); 355 });
403 _errorAnnotations[new _Range(start, end)] = message; 356 _errorAnnotations[new _Range(start, end)] = message;
404 } 357 }
405 358
406 /// Throws an error with additional context information. 359 /// Throws an error with additional context information.
407 error(String message) { 360 void error(String message) =>
408 // Line and column should be one-based. 361 _scanner.error("$message (in $_farthestContext).");
409 throw new SyntaxError(_line + 1, _column + 1,
410 "$message (in $_farthestContext).");
411 }
412 362
413 /// If [result] is falsey, throws an error saying that [expected] was 363 /// If [result] is falsey, throws an error saying that [expected] was
414 /// expected. 364 /// expected.
415 expect(result, String expected) { 365 expect(result, String expected) {
416 if (truth(result)) return result; 366 if (truth(result)) return result;
417 error("Expected $expected"); 367 error("Expected $expected");
418 } 368 }
419 369
420 /// Throws an error saying that the parse failed. Uses [_farthestLine], 370 /// Throws an error saying that the parse failed.
421 /// [_farthestColumn], and [_farthestContext] to provide additional 371 ///
372 /// Uses [_farthestState] and [_farthestContext] to provide additional
422 /// information. 373 /// information.
423 parseFailed() { 374 parseFailed() {
424 var message = "Invalid YAML in $_farthestContext"; 375 var message = "Invalid YAML in $_farthestContext";
425 var extraError = _errorAnnotations[_farthestPos]; 376 _refreshFarthestState();
377 _scanner.state = _farthestState;
378
379 var extraError = _errorAnnotations[_scanner.position];
426 if (extraError != null) message = "$message ($extraError)"; 380 if (extraError != null) message = "$message ($extraError)";
427 throw new SyntaxError(_farthestLine + 1, _farthestColumn + 1, "$message."); 381 _scanner.error("$message.");
382 }
383
384 /// Update [_farthestState] if the scanner is farther than it's been before.
385 void _refreshFarthestState() {
386 if (_scanner.position <= _farthestState.position) return;
387 _farthestState = _scanner.state;
428 } 388 }
429 389
430 /// Returns the number of spaces after the current position. 390 /// Returns the number of spaces after the current position.
431 int countIndentation() { 391 int countIndentation() {
432 var i = 0; 392 var i = 0;
433 while (peek(i) == SP) i++; 393 while (peek(i) == SP) i++;
434 return i; 394 return i;
435 } 395 }
436 396
437 /// Returns the indentation for a block scalar. 397 /// Returns the indentation for a block scalar.
438 int blockScalarAdditionalIndentation(_BlockHeader header, int indent) { 398 int blockScalarAdditionalIndentation(_BlockHeader header, int indent) {
439 if (!header.autoDetectIndent) return header.additionalIndent; 399 if (!header.autoDetectIndent) return header.additionalIndent;
440 400
441 var maxSpaces = 0; 401 var maxSpaces = 0;
442 var maxSpacesLine = 0;
443 var spaces = 0; 402 var spaces = 0;
444 transaction(() { 403 transaction(() {
445 do { 404 do {
446 spaces = captureString(() => zeroOrMore(() => consumeChar(SP))).length; 405 spaces = captureString(() => zeroOrMore(() => consumeChar(SP))).length;
447 if (spaces > maxSpaces) { 406 if (spaces > maxSpaces) maxSpaces = spaces;
448 maxSpaces = spaces;
449 maxSpacesLine = _line;
450 }
451 } while (b_break()); 407 } while (b_break());
452 return false; 408 return false;
453 }); 409 });
454 410
455 // If the next non-empty line isn't indented further than the start of the 411 // If the next non-empty line isn't indented further than the start of the
456 // block scalar, that means the scalar is going to be empty. Returning any 412 // block scalar, that means the scalar is going to be empty. Returning any
457 // value > 0 will cause the parser not to consume any text. 413 // value > 0 will cause the parser not to consume any text.
458 if (spaces <= indent) return 1; 414 if (spaces <= indent) return 1;
459 415
460 // It's an error for a leading empty line to be indented more than the first 416 // It's an error for a leading empty line to be indented more than the first
461 // non-empty line. 417 // non-empty line.
462 if (maxSpaces > spaces) { 418 if (maxSpaces > spaces) {
463 throw new SyntaxError(maxSpacesLine + 1, maxSpaces, 419 _scanner.error("Leading empty lines may not be indented more than the "
464 "Leading empty lines may not be indented more than the first " 420 "first non-empty line.");
465 "non-empty line.");
466 } 421 }
467 422
468 return spaces - indent; 423 return spaces - indent;
469 } 424 }
470 425
471 /// Returns whether the current position is at the beginning of a line. 426 /// Returns whether the current position is at the beginning of a line.
472 bool get atStartOfLine => _column == 0; 427 bool get atStartOfLine => _scanner.column == 0;
473
474 /// Returns whether the current position is at the end of the input.
475 bool get atEndOfFile => _pos == _len;
476 428
477 /// Given an indicator character, returns the type of that indicator (or null 429 /// Given an indicator character, returns the type of that indicator (or null
478 /// if the indicator isn't found. 430 /// if the indicator isn't found.
479 int indicatorType(int char) { 431 int indicatorType(int char) {
480 switch (char) { 432 switch (char) {
481 case HYPHEN: return C_SEQUENCE_ENTRY; 433 case HYPHEN: return C_SEQUENCE_ENTRY;
482 case QUESTION_MARK: return C_MAPPING_KEY; 434 case QUESTION_MARK: return C_MAPPING_KEY;
483 case COLON: return C_MAPPING_VALUE; 435 case COLON: return C_MAPPING_VALUE;
484 case COMMA: return C_COLLECT_ENTRY; 436 case COMMA: return C_COLLECT_ENTRY;
485 case LEFT_BRACKET: return C_SEQUENCE_START; 437 case LEFT_BRACKET: return C_SEQUENCE_START;
(...skipping 11 matching lines...) Expand all
497 case PERCENT: return C_DIRECTIVE; 449 case PERCENT: return C_DIRECTIVE;
498 case AT: 450 case AT:
499 case GRAVE_ACCENT: 451 case GRAVE_ACCENT:
500 return C_RESERVED; 452 return C_RESERVED;
501 default: return null; 453 default: return null;
502 } 454 }
503 } 455 }
504 456
505 // 1 457 // 1
506 bool isPrintable(int char) { 458 bool isPrintable(int char) {
459 if (char == null) return false;
507 return char == TAB || 460 return char == TAB ||
508 char == LF || 461 char == LF ||
509 char == CR || 462 char == CR ||
510 (char >= SP && char <= TILDE) || 463 (char >= SP && char <= TILDE) ||
511 char == NEL || 464 char == NEL ||
512 (char >= 0xA0 && char <= 0xD7FF) || 465 (char >= 0xA0 && char <= 0xD7FF) ||
513 (char >= 0xE000 && char <= 0xFFFD) || 466 (char >= 0xE000 && char <= 0xFFFD) ||
514 (char >= 0x10000 && char <= 0x10FFFF); 467 (char >= 0x10000 && char <= 0x10FFFF);
515 } 468 }
516 469
517 // 2 470 // 2
518 bool isJson(int char) => char == TAB || (char >= SP && char <= 0x10FFFF); 471 bool isJson(int char) => char != null &&
472 (char == TAB || (char >= SP && char <= 0x10FFFF));
519 473
520 // 22 474 // 22
521 bool c_indicator(int type) => consume((c) => indicatorType(c) == type); 475 bool c_indicator(int type) => consume((c) => indicatorType(c) == type);
522 476
523 // 23 477 // 23
524 bool isFlowIndicator(int char) { 478 bool isFlowIndicator(int char) {
525 var indicator = indicatorType(char); 479 var indicator = indicatorType(char);
526 return indicator == C_COLLECT_ENTRY || 480 return indicator == C_COLLECT_ENTRY ||
527 indicator == C_SEQUENCE_START || 481 indicator == C_SEQUENCE_START ||
528 indicator == C_SEQUENCE_END || 482 indicator == C_SEQUENCE_END ||
(...skipping 22 matching lines...) Expand all
551 // 30 505 // 30
552 bool b_nonContent() => captureAs("", () => b_break()); 506 bool b_nonContent() => captureAs("", () => b_break());
553 507
554 // 33 508 // 33
555 bool isSpace(int char) => char == SP || char == TAB; 509 bool isSpace(int char) => char == SP || char == TAB;
556 510
557 // 34 511 // 34
558 bool isNonSpace(int char) => isNonBreak(char) && !isSpace(char); 512 bool isNonSpace(int char) => isNonBreak(char) && !isSpace(char);
559 513
560 // 35 514 // 35
561 bool isDecDigit(int char) => char >= NUMBER_0 && char <= NUMBER_9; 515 bool isDecDigit(int char) => char != null && char >= NUMBER_0 &&
516 char <= NUMBER_9;
562 517
563 // 36 518 // 36
564 bool isHexDigit(int char) { 519 bool isHexDigit(int char) {
520 if (char == null) return false;
565 return isDecDigit(char) || 521 return isDecDigit(char) ||
566 (char >= LETTER_A && char <= LETTER_F) || 522 (char >= LETTER_A && char <= LETTER_F) ||
567 (char >= LETTER_CAP_A && char <= LETTER_CAP_F); 523 (char >= LETTER_CAP_A && char <= LETTER_CAP_F);
568 } 524 }
569 525
570 // 41 526 // 41
571 bool c_escape() => captureAs("", () => consumeChar(BACKSLASH)); 527 bool c_escape() => captureAs("", () => consumeChar(BACKSLASH));
572 528
573 // 42 529 // 42
574 bool ns_escNull() => captureAs("\x00", () => consumeChar(NUMBER_0)); 530 bool ns_escNull() => captureAs("\x00", () => consumeChar(NUMBER_0));
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
747 }); 703 });
748 704
749 // 75 705 // 75
750 bool c_nb_commentText() { 706 bool c_nb_commentText() {
751 if (!truth(c_indicator(C_COMMENT))) return false; 707 if (!truth(c_indicator(C_COMMENT))) return false;
752 zeroOrMore(() => consume(isNonBreak)); 708 zeroOrMore(() => consume(isNonBreak));
753 return true; 709 return true;
754 } 710 }
755 711
756 // 76 712 // 76
757 bool b_comment() => atEndOfFile || b_nonContent(); 713 bool b_comment() => _scanner.isDone || b_nonContent();
758 714
759 // 77 715 // 77
760 bool s_b_comment() { 716 bool s_b_comment() {
761 if (truth(s_separateInLine())) { 717 if (truth(s_separateInLine())) {
762 zeroOrOne(c_nb_commentText); 718 zeroOrOne(c_nb_commentText);
763 } 719 }
764 return b_comment(); 720 return b_comment();
765 } 721 }
766 722
767 // 78 723 // 78
(...skipping 653 matching lines...) Expand 10 before | Expand all | Expand 10 after
1421 case PLUS: 1377 case PLUS:
1422 next(); 1378 next();
1423 return CHOMPING_KEEP; 1379 return CHOMPING_KEEP;
1424 default: 1380 default:
1425 return CHOMPING_CLIP; 1381 return CHOMPING_CLIP;
1426 } 1382 }
1427 } 1383 }
1428 1384
1429 // 165 1385 // 165
1430 bool b_chompedLast(int chomping) { 1386 bool b_chompedLast(int chomping) {
1431 if (atEndOfFile) return true; 1387 if (_scanner.isDone) return true;
1432 switch (chomping) { 1388 switch (chomping) {
1433 case CHOMPING_STRIP: 1389 case CHOMPING_STRIP:
1434 return b_nonContent(); 1390 return b_nonContent();
1435 case CHOMPING_CLIP: 1391 case CHOMPING_CLIP:
1436 case CHOMPING_KEEP: 1392 case CHOMPING_KEEP:
1437 return b_asLineFeed(); 1393 return b_asLineFeed();
1438 } 1394 }
1439 throw "unreachable"; 1395 throw "unreachable";
1440 } 1396 }
1441 1397
(...skipping 361 matching lines...) Expand 10 before | Expand all | Expand 10 after
1803 return s_l_comments(); 1759 return s_l_comments();
1804 }); 1760 });
1805 1761
1806 // 206 1762 // 206
1807 bool c_forbidden() { 1763 bool c_forbidden() {
1808 if (!_inBareDocument || !atStartOfLine) return false; 1764 if (!_inBareDocument || !atStartOfLine) return false;
1809 var forbidden = false; 1765 var forbidden = false;
1810 transaction(() { 1766 transaction(() {
1811 if (!truth(or([c_directivesEnd, c_documentEnd]))) return; 1767 if (!truth(or([c_directivesEnd, c_documentEnd]))) return;
1812 var char = peek(); 1768 var char = peek();
1813 forbidden = isBreak(char) || isSpace(char) || atEndOfFile; 1769 forbidden = isBreak(char) || isSpace(char) || _scanner.isDone;
1814 return; 1770 return;
1815 }); 1771 });
1816 return forbidden; 1772 return forbidden;
1817 } 1773 }
1818 1774
1819 // 207 1775 // 207
1820 Node l_bareDocument() { 1776 Node l_bareDocument() {
1821 try { 1777 try {
1822 _inBareDocument = true; 1778 _inBareDocument = true;
1823 return s_l_blockNode(-1, BLOCK_IN); 1779 return s_l_blockNode(-1, BLOCK_IN);
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1864 zeroOrMore(l_documentPrefix); 1820 zeroOrMore(l_documentPrefix);
1865 doc = zeroOrOne(l_anyDocument); 1821 doc = zeroOrOne(l_anyDocument);
1866 } else { 1822 } else {
1867 zeroOrMore(l_documentPrefix); 1823 zeroOrMore(l_documentPrefix);
1868 doc = zeroOrOne(l_explicitDocument); 1824 doc = zeroOrOne(l_explicitDocument);
1869 } 1825 }
1870 if (truth(doc)) docs.add(doc); 1826 if (truth(doc)) docs.add(doc);
1871 return doc; 1827 return doc;
1872 }); 1828 });
1873 1829
1874 if (!atEndOfFile) parseFailed(); 1830 if (!_scanner.isDone) parseFailed();
1875 return docs; 1831 return docs;
1876 } 1832 }
1877 } 1833 }
1878 1834
1879 class SyntaxError extends YamlException {
1880 final int _line;
1881 final int _column;
1882
1883 SyntaxError(this._line, this._column, String msg) : super(msg);
1884
1885 String toString() => "Syntax error on line $_line, column $_column: "
1886 "${super.toString()}";
1887 }
1888
1889 /// A pair of values. 1835 /// A pair of values.
1890 class _Pair<E, F> { 1836 class _Pair<E, F> {
1891 E first; 1837 E first;
1892 F last; 1838 F last;
1893 1839
1894 _Pair(this.first, this.last); 1840 _Pair(this.first, this.last);
1895 1841
1896 String toString() => '($first, $last)'; 1842 String toString() => '($first, $last)';
1897 } 1843 }
1898 1844
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1939 for (var pair in _contents.reversed) { 1885 for (var pair in _contents.reversed) {
1940 if (pair.first.contains(pos)) return pair.last; 1886 if (pair.first.contains(pos)) return pair.last;
1941 } 1887 }
1942 return null; 1888 return null;
1943 } 1889 }
1944 1890
1945 /// Associates [value] with [range]. 1891 /// Associates [value] with [range].
1946 operator[]=(_Range range, E value) => 1892 operator[]=(_Range range, E value) =>
1947 _contents.add(new _Pair<_Range, E>(range, value)); 1893 _contents.add(new _Pair<_Range, E>(range, value));
1948 } 1894 }
OLDNEW
« no previous file with comments | « pkg/yaml/CHANGELOG.md ('k') | pkg/yaml/lib/yaml.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698