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

Side by Side Diff: utils/pub/yaml/parser.dart

Issue 11622011: Restructure YAML package suitable for pub lish (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years 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) 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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * Translates a string of characters into a YAML serialization tree.
7 *
8 * This parser is designed to closely follow the spec. All productions in the
9 * spec are numbered, and the corresponding methods in the parser have the same
10 * numbers. This is certainly not the most efficient way of parsing YAML, but it
11 * is the easiest to write and read in the context of the spec.
12 *
13 * Methods corresponding to productions are also named as in the spec,
14 * translating the name of the method (although not the annotation characters)
15 * into camel-case for dart style.. For example, the spec has a production named
16 * `nb-ns-plain-in-line`, and the method implementing it is named
17 * `nb_ns_plainInLine`. The exception to that rule is methods that just
18 * recognize character classes; these are named `is*`.
19 */
20 class _Parser {
21 static const TAB = 0x9;
22 static const LF = 0xA;
23 static const CR = 0xD;
24 static const SP = 0x20;
25 static const TILDE = 0x7E;
26 static const NEL = 0x85;
27 static const PLUS = 0x2B;
28 static const HYPHEN = 0x2D;
29 static const QUESTION_MARK = 0x3F;
30 static const COLON = 0x3A;
31 static const COMMA = 0x2C;
32 static const LEFT_BRACKET = 0x5B;
33 static const RIGHT_BRACKET = 0x5D;
34 static const LEFT_BRACE = 0x7B;
35 static const RIGHT_BRACE = 0x7D;
36 static const HASH = 0x23;
37 static const AMPERSAND = 0x26;
38 static const ASTERISK = 0x2A;
39 static const EXCLAMATION = 0x21;
40 static const VERTICAL_BAR = 0x7C;
41 static const GREATER_THAN = 0x3E;
42 static const SINGLE_QUOTE = 0x27;
43 static const DOUBLE_QUOTE = 0x22;
44 static const PERCENT = 0x25;
45 static const AT = 0x40;
46 static const GRAVE_ACCENT = 0x60;
47
48 static const NULL = 0x0;
49 static const BELL = 0x7;
50 static const BACKSPACE = 0x8;
51 static const VERTICAL_TAB = 0xB;
52 static const FORM_FEED = 0xC;
53 static const ESCAPE = 0x1B;
54 static const SLASH = 0x2F;
55 static const BACKSLASH = 0x5C;
56 static const UNDERSCORE = 0x5F;
57 static const NBSP = 0xA0;
58 static const LINE_SEPARATOR = 0x2028;
59 static const PARAGRAPH_SEPARATOR = 0x2029;
60
61 static const NUMBER_0 = 0x30;
62 static const NUMBER_9 = 0x39;
63
64 static const LETTER_A = 0x61;
65 static const LETTER_B = 0x62;
66 static const LETTER_E = 0x65;
67 static const LETTER_F = 0x66;
68 static const LETTER_N = 0x6E;
69 static const LETTER_R = 0x72;
70 static const LETTER_T = 0x74;
71 static const LETTER_U = 0x75;
72 static const LETTER_V = 0x76;
73 static const LETTER_X = 0x78;
74
75 static const LETTER_CAP_A = 0x41;
76 static const LETTER_CAP_F = 0x46;
77 static const LETTER_CAP_L = 0x4C;
78 static const LETTER_CAP_N = 0x4E;
79 static const LETTER_CAP_P = 0x50;
80 static const LETTER_CAP_U = 0x55;
81 static const LETTER_CAP_X = 0x58;
82
83 static const C_SEQUENCE_ENTRY = 4;
84 static const C_MAPPING_KEY = 5;
85 static const C_MAPPING_VALUE = 6;
86 static const C_COLLECT_ENTRY = 7;
87 static const C_SEQUENCE_START = 8;
88 static const C_SEQUENCE_END = 9;
89 static const C_MAPPING_START = 10;
90 static const C_MAPPING_END = 11;
91 static const C_COMMENT = 12;
92 static const C_ANCHOR = 13;
93 static const C_ALIAS = 14;
94 static const C_TAG = 15;
95 static const C_LITERAL = 16;
96 static const C_FOLDED = 17;
97 static const C_SINGLE_QUOTE = 18;
98 static const C_DOUBLE_QUOTE = 19;
99 static const C_DIRECTIVE = 20;
100 static const C_RESERVED = 21;
101
102 static const BLOCK_OUT = 0;
103 static const BLOCK_IN = 1;
104 static const FLOW_OUT = 2;
105 static const FLOW_IN = 3;
106 static const BLOCK_KEY = 4;
107 static const FLOW_KEY = 5;
108
109 static const CHOMPING_STRIP = 0;
110 static const CHOMPING_KEEP = 1;
111 static const CHOMPING_CLIP = 2;
112
113 /** The source string being parsed. */
114 final String s;
115
116 /** The current position in the source string. */
117 int pos = 0;
118
119 /** The length of the string being parsed. */
120 final int len;
121
122 /** The current (0-based) line in the source string. */
123 int line = 0;
124
125 /** The current (0-based) column in the source string. */
126 int column = 0;
127
128 /**
129 * Whether we're parsing a bare document (that is, one that doesn't begin with
130 * `---`). Bare documents don't allow `%` immediately following newlines.
131 */
132 bool inBareDocument = false;
133
134 /**
135 * The line number of the farthest position that has been parsed successfully
136 * before backtracking. Used for error reporting.
137 */
138 int farthestLine = 0;
139
140 /**
141 * The column number of the farthest position that has been parsed
142 * successfully before backtracking. Used for error reporting.
143 */
144 int farthestColumn = 0;
145
146 /**
147 * The farthest position in the source string that has been parsed
148 * successfully before backtracking. Used for error reporting.
149 */
150 int farthestPos = 0;
151
152 /**
153 * The name of the context of the farthest position that has been parsed
154 * successfully before backtracking. Used for error reporting.
155 */
156 String farthestContext = "document";
157
158 /** A stack of the names of parse contexts. Used for error reporting. */
159 List<String> contextStack;
160
161 /**
162 * Annotations attached to ranges of the source string that add extra
163 * information to any errors that occur in the annotated range.
164 */
165 _RangeMap<String> errorAnnotations;
166
167 /**
168 * The buffer containing the string currently being captured.
169 */
170 StringBuffer capturedString;
171
172 /**
173 * The beginning of the current section of the captured string.
174 */
175 int captureStart;
176
177 /**
178 * Whether the current string capture is being overridden.
179 */
180 bool capturingAs = false;
181
182 _Parser(String s)
183 : this.s = s,
184 len = s.length,
185 contextStack = <String>["document"],
186 errorAnnotations = new _RangeMap();
187
188 /**
189 * Return the character at the current position, then move that position
190 * forward one character. Also updates the current line and column numbers.
191 */
192 int next() {
193 if (pos == len) return -1;
194 var char = s.charCodeAt(pos++);
195 if (isBreak(char)) {
196 line++;
197 column = 0;
198 } else {
199 column++;
200 }
201
202 if (farthestLine < line) {
203 farthestLine = line;
204 farthestColumn = column;
205 farthestContext = contextStack.last;
206 } else if (farthestLine == line && farthestColumn < column) {
207 farthestColumn = column;
208 farthestContext = contextStack.last;
209 }
210 farthestPos = pos;
211
212 return char;
213 }
214
215 /**
216 * Returns the character at the current position, or the character [i]
217 * characters after the current position.
218 *
219 * Returns -1 if this would return a character after the end or before the
220 * beginning of the input string.
221 */
222 int peek([int i = 0]) {
223 var peekPos = pos + i;
224 return (peekPos >= len || peekPos < 0) ? -1 : s.charCodeAt(peekPos);
225 }
226
227 /**
228 * The truthiness operator. Returns `false` if [obj] is `null` or `false`,
229 * `true` otherwise.
230 */
231 bool truth(obj) => obj != null && obj != false;
232
233 /**
234 * Consumes the current character if it matches [matcher]. Returns the result
235 * of [matcher].
236 */
237 bool consume(bool matcher(int)) {
238 if (matcher(peek())) {
239 next();
240 return true;
241 }
242 return false;
243 }
244
245 /**
246 * Consumes the current character if it equals [char].
247 */
248 bool consumeChar(int char) => consume((c) => c == char);
249
250 /**
251 * Calls [consumer] until it returns a falsey value. Returns a list of all
252 * truthy return values of [consumer], or null if it didn't consume anything.
253 *
254 * Conceptually, repeats a production one or more times.
255 */
256 List oneOrMore(consumer()) {
257 var first = consumer();
258 if (!truth(first)) return null;
259 var out = [first];
260 while (true) {
261 var el = consumer();
262 if (!truth(el)) return out;
263 out.add(el);
264 }
265 return null; // Unreachable.
266 }
267
268 /**
269 * Calls [consumer] until it returns a falsey value. Returns a list of all
270 * truthy return values of [consumer], or the empty list if it didn't consume
271 * anything.
272 *
273 * Conceptually, repeats a production any number of times.
274 */
275 List zeroOrMore(consumer()) {
276 var out = [];
277 var oldPos = pos;
278 while (true) {
279 var el = consumer();
280 if (!truth(el) || oldPos == pos) return out;
281 oldPos = pos;
282 out.add(el);
283 }
284 return null; // Unreachable.
285 }
286
287 /**
288 * Just calls [consumer] and returns its result. Used to make it explicit that
289 * a production is intended to be optional.
290 */
291 zeroOrOne(consumer()) => consumer();
292
293 /**
294 * Calls each function in [consumers] until one returns a truthy value, then
295 * returns that.
296 */
297 or(List<Function> consumers) {
298 for (var c in consumers) {
299 var res = c();
300 if (truth(res)) return res;
301 }
302 return null;
303 }
304
305 /**
306 * Calls [consumer] and returns its result, but rolls back the parser state if
307 * [consumer] returns a falsey value.
308 */
309 transaction(consumer()) {
310 var oldPos = pos;
311 var oldLine = line;
312 var oldColumn = column;
313 var oldCaptureStart = captureStart;
314 String capturedSoFar = capturedString == null ? null :
315 capturedString.toString();
316 var res = consumer();
317 if (truth(res)) return res;
318
319 pos = oldPos;
320 line = oldLine;
321 column = oldColumn;
322 captureStart = oldCaptureStart;
323 capturedString = capturedSoFar == null ? null :
324 new StringBuffer(capturedSoFar);
325 return res;
326 }
327
328 /**
329 * Consumes [n] characters matching [matcher], or none if there isn't a
330 * complete match. The first argument to [matcher] is the character code, the
331 * second is the index (from 0 to [n] - 1).
332 *
333 * Returns whether or not the characters were consumed.
334 */
335 bool nAtOnce(int n, bool matcher(int c, int i)) => transaction(() {
336 for (int i = 0; i < n; i++) {
337 if (!consume((c) => matcher(c, i))) return false;
338 }
339 return true;
340 });
341
342 /**
343 * Consumes the exact characters in [str], or nothing.
344 *
345 * Returns whether or not the string was consumed.
346 */
347 bool rawString(String str) =>
348 nAtOnce(str.length, (c, i) => str.charCodeAt(i) == c);
349
350 /**
351 * Consumes and returns a string of characters matching [matcher], or null if
352 * there are no such characters.
353 */
354 String stringOf(bool matcher(int)) =>
355 captureString(() => oneOrMore(() => consume(matcher)));
356
357 /**
358 * Calls [consumer] and returns the string that was consumed while doing so,
359 * or null if [consumer] returned a falsey value. Automatically wraps
360 * [consumer] in `transaction`.
361 */
362 String captureString(consumer()) {
363 // captureString calls may not be nested
364 assert(capturedString == null);
365
366 captureStart = pos;
367 capturedString = new StringBuffer();
368 var res = transaction(consumer);
369 if (!truth(res)) {
370 captureStart = null;
371 capturedString = null;
372 return null;
373 }
374
375 flushCapture();
376 var result = capturedString.toString();
377 captureStart = null;
378 capturedString = null;
379 return result;
380 }
381
382 captureAs(String replacement, consumer()) =>
383 captureAndTransform(consumer, (_) => replacement);
384
385 captureAndTransform(consumer(), String transformation(String captured)) {
386 if (capturedString == null) return consumer();
387 if (capturingAs) return consumer();
388
389 flushCapture();
390 capturingAs = true;
391 var res = consumer();
392 capturingAs = false;
393 if (!truth(res)) return res;
394
395 capturedString.add(transformation(s.substring(captureStart, pos)));
396 captureStart = pos;
397 return res;
398 }
399
400 void flushCapture() {
401 capturedString.add(s.substring(captureStart, pos));
402 captureStart = pos;
403 }
404
405 /**
406 * Adds a tag and an anchor to [node], if they're defined.
407 */
408 _Node addProps(_Node node, _Pair<_Tag, String> props) {
409 if (props == null || node == null) return node;
410 if (truth(props.first)) node.tag = props.first;
411 if (truth(props.last)) node.anchor = props.last;
412 return node;
413 }
414
415 /** Creates a MappingNode from [pairs]. */
416 _MappingNode map(List<_Pair<_Node, _Node>> pairs) {
417 var content = new Map<_Node, _Node>();
418 pairs.forEach((pair) => content[pair.first] = pair.last);
419 return new _MappingNode("?", content);
420 }
421
422 /** Runs [fn] in a context named [name]. Used for error reporting. */
423 context(String name, fn()) {
424 try {
425 contextStack.add(name);
426 return fn();
427 } finally {
428 var popped = contextStack.removeLast();
429 assert(popped == name);
430 }
431 }
432
433 /**
434 * Adds [message] as extra information to any errors that occur between the
435 * current position and the position of the cursor after running [fn]. The
436 * cursor is reset after [fn] is run.
437 */
438 annotateError(String message, fn()) {
439 var start = pos;
440 var end;
441 transaction(() {
442 fn();
443 end = pos;
444 return false;
445 });
446 errorAnnotations[new _Range(start, end)] = message;
447 }
448
449 /** Throws an error with additional context information. */
450 error(String message) {
451 // Line and column should be one-based.
452 throw new SyntaxError(line + 1, column + 1,
453 "$message (in $farthestContext)");
454 }
455
456 /**
457 * If [result] is falsey, throws an error saying that [expected] was
458 * expected.
459 */
460 expect(result, String expected) {
461 if (truth(result)) return result;
462 error("expected $expected");
463 }
464
465 /**
466 * Throws an error saying that the parse failed. Uses [farthestLine],
467 * [farthestColumn], and [farthestContext] to provide additional information.
468 */
469 parseFailed() {
470 var message = "invalid YAML in $farthestContext";
471 var extraError = errorAnnotations[farthestPos];
472 if (extraError != null) message = "$message ($extraError)";
473 throw new SyntaxError(farthestLine + 1, farthestColumn + 1, message);
474 }
475
476 /** Returns the number of spaces after the current position. */
477 int countIndentation() {
478 var i = 0;
479 while (peek(i) == SP) i++;
480 return i;
481 }
482
483 /** Returns the indentation for a block scalar. */
484 int blockScalarAdditionalIndentation(_BlockHeader header, int indent) {
485 if (!header.autoDetectIndent) return header.additionalIndent;
486
487 var maxSpaces = 0;
488 var maxSpacesLine = 0;
489 var spaces = 0;
490 transaction(() {
491 do {
492 spaces = captureString(() => zeroOrMore(() => consumeChar(SP))).length;
493 if (spaces > maxSpaces) {
494 maxSpaces = spaces;
495 maxSpacesLine = line;
496 }
497 } while (b_break());
498 return false;
499 });
500
501 // If the next non-empty line isn't indented further than the start of the
502 // block scalar, that means the scalar is going to be empty. Returning any
503 // value > 0 will cause the parser not to consume any text.
504 if (spaces <= indent) return 1;
505
506 // It's an error for a leading empty line to be indented more than the first
507 // non-empty line.
508 if (maxSpaces > spaces) {
509 throw new SyntaxError(maxSpacesLine + 1, maxSpaces,
510 "Leading empty lines may not be indented more than the first "
511 "non-empty line.");
512 }
513
514 return spaces - indent;
515 }
516
517 /** Returns whether the current position is at the beginning of a line. */
518 bool get atStartOfLine => column == 0;
519
520 /** Returns whether the current position is at the end of the input. */
521 bool get atEndOfFile => pos == len;
522
523 /**
524 * Given an indicator character, returns the type of that indicator (or null
525 * if the indicator isn't found.
526 */
527 int indicatorType(int char) {
528 switch (char) {
529 case HYPHEN: return C_SEQUENCE_ENTRY;
530 case QUESTION_MARK: return C_MAPPING_KEY;
531 case COLON: return C_MAPPING_VALUE;
532 case COMMA: return C_COLLECT_ENTRY;
533 case LEFT_BRACKET: return C_SEQUENCE_START;
534 case RIGHT_BRACKET: return C_SEQUENCE_END;
535 case LEFT_BRACE: return C_MAPPING_START;
536 case RIGHT_BRACE: return C_MAPPING_END;
537 case HASH: return C_COMMENT;
538 case AMPERSAND: return C_ANCHOR;
539 case ASTERISK: return C_ALIAS;
540 case EXCLAMATION: return C_TAG;
541 case VERTICAL_BAR: return C_LITERAL;
542 case GREATER_THAN: return C_FOLDED;
543 case SINGLE_QUOTE: return C_SINGLE_QUOTE;
544 case DOUBLE_QUOTE: return C_DOUBLE_QUOTE;
545 case PERCENT: return C_DIRECTIVE;
546 case AT:
547 case GRAVE_ACCENT:
548 return C_RESERVED;
549 default: return null;
550 }
551 }
552
553 // 1
554 bool isPrintable(int char) {
555 return char == TAB ||
556 char == LF ||
557 char == CR ||
558 (char >= SP && char <= TILDE) ||
559 char == NEL ||
560 (char >= 0xA0 && char <= 0xD7FF) ||
561 (char >= 0xE000 && char <= 0xFFFD) ||
562 (char >= 0x10000 && char <= 0x10FFFF);
563 }
564
565 // 2
566 bool isJson(int char) => char == TAB || (char >= SP && char <= 0x10FFFF);
567
568 // 22
569 bool c_indicator(int type) => consume((c) => indicatorType(c) == type);
570
571 // 23
572 bool isFlowIndicator(int char) {
573 var indicator = indicatorType(char);
574 return indicator == C_COLLECT_ENTRY ||
575 indicator == C_SEQUENCE_START ||
576 indicator == C_SEQUENCE_END ||
577 indicator == C_MAPPING_START ||
578 indicator == C_MAPPING_END;
579 }
580
581 // 26
582 bool isBreak(int char) => char == LF || char == CR;
583
584 // 27
585 bool isNonBreak(int char) => isPrintable(char) && !isBreak(char);
586
587 // 28
588 bool b_break() {
589 if (consumeChar(CR)) {
590 zeroOrOne(() => consumeChar(LF));
591 return true;
592 }
593 return consumeChar(LF);
594 }
595
596 // 29
597 bool b_asLineFeed() => captureAs("\n", () => b_break());
598
599 // 30
600 bool b_nonContent() => captureAs("", () => b_break());
601
602 // 33
603 bool isSpace(int char) => char == SP || char == TAB;
604
605 // 34
606 bool isNonSpace(int char) => isNonBreak(char) && !isSpace(char);
607
608 // 35
609 bool isDecDigit(int char) => char >= NUMBER_0 && char <= NUMBER_9;
610
611 // 36
612 bool isHexDigit(int char) {
613 return isDecDigit(char) ||
614 (char >= LETTER_A && char <= LETTER_F) ||
615 (char >= LETTER_CAP_A && char <= LETTER_CAP_F);
616 }
617
618 // 41
619 bool c_escape() => captureAs("", () => consumeChar(BACKSLASH));
620
621 // 42
622 bool ns_escNull() => captureAs("\x00", () => consumeChar(NUMBER_0));
623
624 // 43
625 bool ns_escBell() => captureAs("\x07", () => consumeChar(LETTER_A));
626
627 // 44
628 bool ns_escBackspace() => captureAs("\b", () => consumeChar(LETTER_B));
629
630 // 45
631 bool ns_escHorizontalTab() => captureAs("\t", () {
632 return consume((c) => c == LETTER_T || c == TAB);
633 });
634
635 // 46
636 bool ns_escLineFeed() => captureAs("\n", () => consumeChar(LETTER_N));
637
638 // 47
639 bool ns_escVerticalTab() => captureAs("\v", () => consumeChar(LETTER_V));
640
641 // 48
642 bool ns_escFormFeed() => captureAs("\f", () => consumeChar(LETTER_F));
643
644 // 49
645 bool ns_escCarriageReturn() => captureAs("\r", () => consumeChar(LETTER_R));
646
647 // 50
648 bool ns_escEscape() => captureAs("\x1B", () => consumeChar(LETTER_E));
649
650 // 51
651 bool ns_escSpace() => consumeChar(SP);
652
653 // 52
654 bool ns_escDoubleQuote() => consumeChar(DOUBLE_QUOTE);
655
656 // 53
657 bool ns_escSlash() => consumeChar(SLASH);
658
659 // 54
660 bool ns_escBackslash() => consumeChar(BACKSLASH);
661
662 // 55
663 bool ns_escNextLine() => captureAs("\x85", () => consumeChar(LETTER_CAP_N));
664
665 // 56
666 bool ns_escNonBreakingSpace() =>
667 captureAs("\xA0", () => consumeChar(UNDERSCORE));
668
669 // 57
670 bool ns_escLineSeparator() =>
671 captureAs("\u2028", () => consumeChar(LETTER_CAP_L));
672
673 // 58
674 bool ns_escParagraphSeparator() =>
675 captureAs("\u2029", () => consumeChar(LETTER_CAP_P));
676
677 // 59
678 bool ns_esc8Bit() => ns_escNBit(LETTER_X, 2);
679
680 // 60
681 bool ns_esc16Bit() => ns_escNBit(LETTER_U, 4);
682
683 // 61
684 bool ns_esc32Bit() => ns_escNBit(LETTER_CAP_U, 8);
685
686 // Helper method for 59 - 61
687 bool ns_escNBit(int char, int digits) {
688 if (!captureAs('', () => consumeChar(char))) return false;
689 var captured = captureAndTransform(
690 () => nAtOnce(digits, (c, _) => isHexDigit(c)),
691 (hex) => new String.fromCharCodes([Math.parseInt("0x$hex")]));
692 return expect(captured, "$digits hexidecimal digits");
693 }
694
695 // 62
696 bool c_ns_escChar() => context('escape sequence', () => transaction(() {
697 if (!truth(c_escape())) return false;
698 return truth(or([
699 ns_escNull, ns_escBell, ns_escBackspace, ns_escHorizontalTab,
700 ns_escLineFeed, ns_escVerticalTab, ns_escFormFeed, ns_escCarriageReturn,
701 ns_escEscape, ns_escSpace, ns_escDoubleQuote, ns_escSlash,
702 ns_escBackslash, ns_escNextLine, ns_escNonBreakingSpace,
703 ns_escLineSeparator, ns_escParagraphSeparator, ns_esc8Bit, ns_esc16Bit,
704 ns_esc32Bit
705 ]));
706 }));
707
708 // 63
709 bool s_indent(int indent) {
710 var result = nAtOnce(indent, (c, i) => c == SP);
711 if (peek() == TAB) {
712 annotateError("tab characters are not allowed as indentation in YAML",
713 () => zeroOrMore(() => consume(isSpace)));
714 }
715 return result;
716 }
717
718 // 64
719 bool s_indentLessThan(int indent) {
720 for (int i = 0; i < indent - 1; i++) {
721 if (!consumeChar(SP)) {
722 if (peek() == TAB) {
723 annotateError("tab characters are not allowed as indentation in YAML",
724 () {
725 for (; i < indent - 1; i++) {
726 if (!consume(isSpace)) break;
727 }
728 });
729 }
730 break;
731 }
732 }
733 return true;
734 }
735
736 // 65
737 bool s_indentLessThanOrEqualTo(int indent) => s_indentLessThan(indent + 1);
738
739 // 66
740 bool s_separateInLine() => transaction(() {
741 return captureAs('', () =>
742 truth(oneOrMore(() => consume(isSpace))) || atStartOfLine);
743 });
744
745 // 67
746 bool s_linePrefix(int indent, int ctx) => captureAs("", () {
747 switch (ctx) {
748 case BLOCK_OUT:
749 case BLOCK_IN:
750 return s_blockLinePrefix(indent);
751 case FLOW_OUT:
752 case FLOW_IN:
753 return s_flowLinePrefix(indent);
754 }
755 });
756
757 // 68
758 bool s_blockLinePrefix(int indent) => s_indent(indent);
759
760 // 69
761 bool s_flowLinePrefix(int indent) => captureAs('', () {
762 if (!truth(s_indent(indent))) return false;
763 zeroOrOne(s_separateInLine);
764 return true;
765 });
766
767 // 70
768 bool l_empty(int indent, int ctx) => transaction(() {
769 var start = or([
770 () => s_linePrefix(indent, ctx),
771 () => s_indentLessThan(indent)
772 ]);
773 if (!truth(start)) return false;
774 return b_asLineFeed();
775 });
776
777 // 71
778 bool b_asSpace() => captureAs(" ", () => consume(isBreak));
779
780 // 72
781 bool b_l_trimmed(int indent, int ctx) => transaction(() {
782 if (!truth(b_nonContent())) return false;
783 return truth(oneOrMore(() => captureAs("\n", () => l_empty(indent, ctx))));
784 });
785
786 // 73
787 bool b_l_folded(int indent, int ctx) =>
788 or([() => b_l_trimmed(indent, ctx), b_asSpace]);
789
790 // 74
791 bool s_flowFolded(int indent) => transaction(() {
792 zeroOrOne(s_separateInLine);
793 if (!truth(b_l_folded(indent, FLOW_IN))) return false;
794 return s_flowLinePrefix(indent);
795 });
796
797 // 75
798 bool c_nb_commentText() {
799 if (!truth(c_indicator(C_COMMENT))) return false;
800 zeroOrMore(() => consume(isNonBreak));
801 return true;
802 }
803
804 // 76
805 bool b_comment() => atEndOfFile || b_nonContent();
806
807 // 77
808 bool s_b_comment() {
809 if (truth(s_separateInLine())) {
810 zeroOrOne(c_nb_commentText);
811 }
812 return b_comment();
813 }
814
815 // 78
816 bool l_comment() => transaction(() {
817 if (!truth(s_separateInLine())) return false;
818 zeroOrOne(c_nb_commentText);
819 return b_comment();
820 });
821
822 // 79
823 bool s_l_comments() {
824 if (!truth(s_b_comment()) && !atStartOfLine) return false;
825 zeroOrMore(l_comment);
826 return true;
827 }
828
829 // 80
830 bool s_separate(int indent, int ctx) {
831 switch (ctx) {
832 case BLOCK_OUT:
833 case BLOCK_IN:
834 case FLOW_OUT:
835 case FLOW_IN:
836 return s_separateLines(indent);
837 case BLOCK_KEY:
838 case FLOW_KEY:
839 return s_separateInLine();
840 default: throw 'invalid context "$ctx"';
841 }
842 }
843
844 // 81
845 bool s_separateLines(int indent) {
846 return transaction(() => s_l_comments() && s_flowLinePrefix(indent)) ||
847 s_separateInLine();
848 }
849
850 // 82
851 bool l_directive() => false; // TODO(nweiz): implement
852
853 // 96
854 _Pair<_Tag, String> c_ns_properties(int indent, int ctx) {
855 var tag, anchor;
856 tag = c_ns_tagProperty();
857 if (truth(tag)) {
858 anchor = transaction(() {
859 if (!truth(s_separate(indent, ctx))) return null;
860 return c_ns_anchorProperty();
861 });
862 return new _Pair<_Tag, String>(tag, anchor);
863 }
864
865 anchor = c_ns_anchorProperty();
866 if (truth(anchor)) {
867 tag = transaction(() {
868 if (!truth(s_separate(indent, ctx))) return null;
869 return c_ns_tagProperty();
870 });
871 return new _Pair<_Tag, String>(tag, anchor);
872 }
873
874 return null;
875 }
876
877 // 97
878 _Tag c_ns_tagProperty() => null; // TODO(nweiz): implement
879
880 // 101
881 String c_ns_anchorProperty() => null; // TODO(nweiz): implement
882
883 // 102
884 bool isAnchorChar(int char) => isNonSpace(char) && !isFlowIndicator(char);
885
886 // 103
887 String ns_anchorName() =>
888 captureString(() => oneOrMore(() => consume(isAnchorChar)));
889
890 // 104
891 _Node c_ns_aliasNode() {
892 if (!truth(c_indicator(C_ALIAS))) return null;
893 var name = expect(ns_anchorName(), 'anchor name');
894 return new _AliasNode(name);
895 }
896
897 // 105
898 _ScalarNode e_scalar() => new _ScalarNode("?", content: "");
899
900 // 106
901 _ScalarNode e_node() => e_scalar();
902
903 // 107
904 bool nb_doubleChar() => or([
905 c_ns_escChar,
906 () => consume((c) => isJson(c) && c != BACKSLASH && c != DOUBLE_QUOTE)
907 ]);
908
909 // 108
910 bool ns_doubleChar() => !isSpace(peek()) && truth(nb_doubleChar());
911
912 // 109
913 _Node c_doubleQuoted(int indent, int ctx) => context('string', () {
914 return transaction(() {
915 if (!truth(c_indicator(C_DOUBLE_QUOTE))) return null;
916 var contents = nb_doubleText(indent, ctx);
917 if (!truth(c_indicator(C_DOUBLE_QUOTE))) return null;
918 return new _ScalarNode("!", content: contents);
919 });
920 });
921
922 // 110
923 String nb_doubleText(int indent, int ctx) => captureString(() {
924 switch (ctx) {
925 case FLOW_OUT:
926 case FLOW_IN:
927 nb_doubleMultiLine(indent);
928 break;
929 case BLOCK_KEY:
930 case FLOW_KEY:
931 nb_doubleOneLine();
932 break;
933 }
934 return true;
935 });
936
937 // 111
938 void nb_doubleOneLine() {
939 zeroOrMore(nb_doubleChar);
940 }
941
942 // 112
943 bool s_doubleEscaped(int indent) => transaction(() {
944 zeroOrMore(() => consume(isSpace));
945 if (!captureAs("", () => consumeChar(BACKSLASH))) return false;
946 if (!truth(b_nonContent())) return false;
947 zeroOrMore(() => captureAs("\n", () => l_empty(indent, FLOW_IN)));
948 return s_flowLinePrefix(indent);
949 });
950
951 // 113
952 bool s_doubleBreak(int indent) => or([
953 () => s_doubleEscaped(indent),
954 () => s_flowFolded(indent)
955 ]);
956
957 // 114
958 void nb_ns_doubleInLine() {
959 zeroOrMore(() => transaction(() {
960 zeroOrMore(() => consume(isSpace));
961 return ns_doubleChar();
962 }));
963 }
964
965 // 115
966 bool s_doubleNextLine(int indent) {
967 if (!truth(s_doubleBreak(indent))) return false;
968 zeroOrOne(() {
969 if (!truth(ns_doubleChar())) return;
970 nb_ns_doubleInLine();
971 or([
972 () => s_doubleNextLine(indent),
973 () => zeroOrMore(() => consume(isSpace))
974 ]);
975 });
976 return true;
977 }
978
979 // 116
980 void nb_doubleMultiLine(int indent) {
981 nb_ns_doubleInLine();
982 or([
983 () => s_doubleNextLine(indent),
984 () => zeroOrMore(() => consume(isSpace))
985 ]);
986 }
987
988 // 117
989 bool c_quotedQuote() => captureAs("'", () => rawString("''"));
990
991 // 118
992 bool nb_singleChar() => or([
993 c_quotedQuote,
994 () => consume((c) => isJson(c) && c != SINGLE_QUOTE)
995 ]);
996
997 // 119
998 bool ns_singleChar() => !isSpace(peek()) && truth(nb_singleChar());
999
1000 // 120
1001 _Node c_singleQuoted(int indent, int ctx) => context('string', () {
1002 return transaction(() {
1003 if (!truth(c_indicator(C_SINGLE_QUOTE))) return null;
1004 var contents = nb_singleText(indent, ctx);
1005 if (!truth(c_indicator(C_SINGLE_QUOTE))) return null;
1006 return new _ScalarNode("!", content: contents);
1007 });
1008 });
1009
1010 // 121
1011 String nb_singleText(int indent, int ctx) => captureString(() {
1012 switch (ctx) {
1013 case FLOW_OUT:
1014 case FLOW_IN:
1015 nb_singleMultiLine(indent);
1016 break;
1017 case BLOCK_KEY:
1018 case FLOW_KEY:
1019 nb_singleOneLine(indent);
1020 break;
1021 }
1022 return true;
1023 });
1024
1025 // 122
1026 void nb_singleOneLine(int indent) {
1027 zeroOrMore(nb_singleChar);
1028 }
1029
1030 // 123
1031 void nb_ns_singleInLine() {
1032 zeroOrMore(() => transaction(() {
1033 zeroOrMore(() => consume(isSpace));
1034 return ns_singleChar();
1035 }));
1036 }
1037
1038 // 124
1039 bool s_singleNextLine(int indent) {
1040 if (!truth(s_flowFolded(indent))) return false;
1041 zeroOrOne(() {
1042 if (!truth(ns_singleChar())) return;
1043 nb_ns_singleInLine();
1044 or([
1045 () => s_singleNextLine(indent),
1046 () => zeroOrMore(() => consume(isSpace))
1047 ]);
1048 });
1049 return true;
1050 }
1051
1052 // 125
1053 void nb_singleMultiLine(int indent) {
1054 nb_ns_singleInLine();
1055 or([
1056 () => s_singleNextLine(indent),
1057 () => zeroOrMore(() => consume(isSpace))
1058 ]);
1059 }
1060
1061 // 126
1062 bool ns_plainFirst(int ctx) {
1063 var char = peek();
1064 var indicator = indicatorType(char);
1065 if (indicator == C_RESERVED) {
1066 error("reserved indicators can't start a plain scalar");
1067 }
1068 var match = (isNonSpace(char) && indicator == null) ||
1069 ((indicator == C_MAPPING_KEY ||
1070 indicator == C_MAPPING_VALUE ||
1071 indicator == C_SEQUENCE_ENTRY) &&
1072 isPlainSafe(ctx, peek(1)));
1073
1074 if (match) next();
1075 return match;
1076 }
1077
1078 // 127
1079 bool isPlainSafe(int ctx, int char) {
1080 switch (ctx) {
1081 case FLOW_OUT:
1082 case BLOCK_KEY:
1083 // 128
1084 return isNonSpace(char);
1085 case FLOW_IN:
1086 case FLOW_KEY:
1087 // 129
1088 return isNonSpace(char) && !isFlowIndicator(char);
1089 default: throw 'invalid context "$ctx"';
1090 }
1091 }
1092
1093 // 130
1094 bool ns_plainChar(int ctx) {
1095 var char = peek();
1096 var indicator = indicatorType(char);
1097 var safeChar = isPlainSafe(ctx, char) && indicator != C_MAPPING_VALUE &&
1098 indicator != C_COMMENT;
1099 var nonCommentHash = isNonSpace(peek(-1)) && indicator == C_COMMENT;
1100 var nonMappingColon = indicator == C_MAPPING_VALUE &&
1101 isPlainSafe(ctx, peek(1));
1102 var match = safeChar || nonCommentHash || nonMappingColon;
1103
1104 if (match) next();
1105 return match;
1106 }
1107
1108 // 131
1109 String ns_plain(int indent, int ctx) => context('plain scalar', () {
1110 return captureString(() {
1111 switch (ctx) {
1112 case FLOW_OUT:
1113 case FLOW_IN:
1114 return ns_plainMultiLine(indent, ctx);
1115 case BLOCK_KEY:
1116 case FLOW_KEY:
1117 return ns_plainOneLine(ctx);
1118 default: throw 'invalid context "$ctx"';
1119 }
1120 });
1121 });
1122
1123 // 132
1124 void nb_ns_plainInLine(int ctx) {
1125 zeroOrMore(() => transaction(() {
1126 zeroOrMore(() => consume(isSpace));
1127 return ns_plainChar(ctx);
1128 }));
1129 }
1130
1131 // 133
1132 bool ns_plainOneLine(int ctx) {
1133 if (truth(c_forbidden())) return false;
1134 if (!truth(ns_plainFirst(ctx))) return false;
1135 nb_ns_plainInLine(ctx);
1136 return true;
1137 }
1138
1139 // 134
1140 bool s_ns_plainNextLine(int indent, int ctx) => transaction(() {
1141 if (!truth(s_flowFolded(indent))) return false;
1142 if (truth(c_forbidden())) return false;
1143 if (!truth(ns_plainChar(ctx))) return false;
1144 nb_ns_plainInLine(ctx);
1145 return true;
1146 });
1147
1148 // 135
1149 bool ns_plainMultiLine(int indent, int ctx) {
1150 if (!truth(ns_plainOneLine(ctx))) return false;
1151 zeroOrMore(() => s_ns_plainNextLine(indent, ctx));
1152 return true;
1153 }
1154
1155 // 136
1156 int inFlow(int ctx) {
1157 switch (ctx) {
1158 case FLOW_OUT:
1159 case FLOW_IN:
1160 return FLOW_IN;
1161 case BLOCK_KEY:
1162 case FLOW_KEY:
1163 return FLOW_KEY;
1164 }
1165 }
1166
1167 // 137
1168 _SequenceNode c_flowSequence(int indent, int ctx) => transaction(() {
1169 if (!truth(c_indicator(C_SEQUENCE_START))) return null;
1170 zeroOrOne(() => s_separate(indent, ctx));
1171 var content = zeroOrOne(() => ns_s_flowSeqEntries(indent, inFlow(ctx)));
1172 if (!truth(c_indicator(C_SEQUENCE_END))) return null;
1173 return new _SequenceNode("?", new List<_Node>.from(content));
1174 });
1175
1176 // 138
1177 Collection<_Node> ns_s_flowSeqEntries(int indent, int ctx) {
1178 var first = ns_flowSeqEntry(indent, ctx);
1179 if (!truth(first)) return new Queue<_Node>();
1180 zeroOrOne(() => s_separate(indent, ctx));
1181
1182 var rest;
1183 if (truth(c_indicator(C_COLLECT_ENTRY))) {
1184 zeroOrOne(() => s_separate(indent, ctx));
1185 rest = zeroOrOne(() => ns_s_flowSeqEntries(indent, ctx));
1186 }
1187
1188 if (rest == null) rest = new Queue<_Node>();
1189 rest.addFirst(first);
1190
1191 return rest;
1192 }
1193
1194 // 139
1195 _Node ns_flowSeqEntry(int indent, int ctx) => or([
1196 () => ns_flowPair(indent, ctx),
1197 () => ns_flowNode(indent, ctx)
1198 ]);
1199
1200 // 140
1201 _Node c_flowMapping(int indent, int ctx) {
1202 if (!truth(c_indicator(C_MAPPING_START))) return null;
1203 zeroOrOne(() => s_separate(indent, ctx));
1204 var content = zeroOrOne(() => ns_s_flowMapEntries(indent, inFlow(ctx)));
1205 if (!truth(c_indicator(C_MAPPING_END))) return null;
1206 return new _MappingNode("?", content);
1207 }
1208
1209 // 141
1210 YamlMap ns_s_flowMapEntries(int indent, int ctx) {
1211 var first = ns_flowMapEntry(indent, ctx);
1212 if (!truth(first)) return new YamlMap();
1213 zeroOrOne(() => s_separate(indent, ctx));
1214
1215 var rest;
1216 if (truth(c_indicator(C_COLLECT_ENTRY))) {
1217 zeroOrOne(() => s_separate(indent, ctx));
1218 rest = ns_s_flowMapEntries(indent, ctx);
1219 }
1220
1221 if (rest == null) rest = new YamlMap();
1222
1223 // TODO(nweiz): Duplicate keys should be an error. This includes keys with
1224 // different representations but the same value (e.g. 10 vs 0xa). To make
1225 // this user-friendly we'll probably also want to associate nodes with a
1226 // source range.
1227 if (!rest.containsKey(first.first)) rest[first.first] = first.last;
1228
1229 return rest;
1230 }
1231
1232 // 142
1233 _Pair<_Node, _Node> ns_flowMapEntry(int indent, int ctx) => or([
1234 () => transaction(() {
1235 if (!truth(c_indicator(C_MAPPING_KEY))) return false;
1236 if (!truth(s_separate(indent, ctx))) return false;
1237 return ns_flowMapExplicitEntry(indent, ctx);
1238 }),
1239 () => ns_flowMapImplicitEntry(indent, ctx)
1240 ]);
1241
1242 // 143
1243 _Pair<_Node, _Node> ns_flowMapExplicitEntry(int indent, int ctx) => or([
1244 () => ns_flowMapImplicitEntry(indent, ctx),
1245 () => new _Pair<_Node, _Node>(e_node(), e_node())
1246 ]);
1247
1248 // 144
1249 _Pair<_Node, _Node> ns_flowMapImplicitEntry(int indent, int ctx) => or([
1250 () => ns_flowMapYamlKeyEntry(indent, ctx),
1251 () => c_ns_flowMapEmptyKeyEntry(indent, ctx),
1252 () => c_ns_flowMapJsonKeyEntry(indent, ctx)
1253 ]);
1254
1255 // 145
1256 _Pair<_Node, _Node> ns_flowMapYamlKeyEntry(int indent, int ctx) {
1257 var key = ns_flowYamlNode(indent, ctx);
1258 if (!truth(key)) return null;
1259 var value = or([
1260 () => transaction(() {
1261 zeroOrOne(() => s_separate(indent, ctx));
1262 return c_ns_flowMapSeparateValue(indent, ctx);
1263 }),
1264 e_node
1265 ]);
1266 return new _Pair<_Node, _Node>(key, value);
1267 }
1268
1269 // 146
1270 _Pair<_Node, _Node> c_ns_flowMapEmptyKeyEntry(int indent, int ctx) {
1271 var value = c_ns_flowMapSeparateValue(indent, ctx);
1272 if (!truth(value)) return null;
1273 return new _Pair<_Node, _Node>(e_node(), value);
1274 }
1275
1276 // 147
1277 _Node c_ns_flowMapSeparateValue(int indent, int ctx) => transaction(() {
1278 if (!truth(c_indicator(C_MAPPING_VALUE))) return null;
1279 if (isPlainSafe(ctx, peek())) return null;
1280
1281 return or([
1282 () => transaction(() {
1283 if (!s_separate(indent, ctx)) return null;
1284 return ns_flowNode(indent, ctx);
1285 }),
1286 e_node
1287 ]);
1288 });
1289
1290 // 148
1291 _Pair<_Node, _Node> c_ns_flowMapJsonKeyEntry(int indent, int ctx) {
1292 var key = c_flowJsonNode(indent, ctx);
1293 if (!truth(key)) return null;
1294 var value = or([
1295 () => transaction(() {
1296 zeroOrOne(() => s_separate(indent, ctx));
1297 return c_ns_flowMapAdjacentValue(indent, ctx);
1298 }),
1299 e_node
1300 ]);
1301 return new _Pair<_Node, _Node>(key, value);
1302 }
1303
1304 // 149
1305 _Node c_ns_flowMapAdjacentValue(int indent, int ctx) {
1306 if (!truth(c_indicator(C_MAPPING_VALUE))) return null;
1307 return or([
1308 () => transaction(() {
1309 zeroOrOne(() => s_separate(indent, ctx));
1310 return ns_flowNode(indent, ctx);
1311 }),
1312 e_node
1313 ]);
1314 }
1315
1316 // 150
1317 _Node ns_flowPair(int indent, int ctx) {
1318 var pair = or([
1319 () => transaction(() {
1320 if (!truth(c_indicator(C_MAPPING_KEY))) return null;
1321 if (!truth(s_separate(indent, ctx))) return null;
1322 return ns_flowMapExplicitEntry(indent, ctx);
1323 }),
1324 () => ns_flowPairEntry(indent, ctx)
1325 ]);
1326 if (!truth(pair)) return null;
1327
1328 return map([pair]);
1329 }
1330
1331 // 151
1332 _Pair<_Node, _Node> ns_flowPairEntry(int indent, int ctx) => or([
1333 () => ns_flowPairYamlKeyEntry(indent, ctx),
1334 () => c_ns_flowMapEmptyKeyEntry(indent, ctx),
1335 () => c_ns_flowPairJsonKeyEntry(indent, ctx)
1336 ]);
1337
1338 // 152
1339 _Pair<_Node, _Node> ns_flowPairYamlKeyEntry(int indent, int ctx) =>
1340 transaction(() {
1341 var key = ns_s_implicitYamlKey(FLOW_KEY);
1342 if (!truth(key)) return null;
1343 var value = c_ns_flowMapSeparateValue(indent, ctx);
1344 if (!truth(value)) return null;
1345 return new _Pair<_Node, _Node>(key, value);
1346 });
1347
1348 // 153
1349 _Pair<_Node, _Node> c_ns_flowPairJsonKeyEntry(int indent, int ctx) =>
1350 transaction(() {
1351 var key = c_s_implicitJsonKey(FLOW_KEY);
1352 if (!truth(key)) return null;
1353 var value = c_ns_flowMapAdjacentValue(indent, ctx);
1354 if (!truth(value)) return null;
1355 return new _Pair<_Node, _Node>(key, value);
1356 });
1357
1358 // 154
1359 _Node ns_s_implicitYamlKey(int ctx) => transaction(() {
1360 // TODO(nweiz): this is supposed to be limited to 1024 characters.
1361
1362 // The indentation parameter is "null" since it's unused in this path
1363 var node = ns_flowYamlNode(null, ctx);
1364 if (!truth(node)) return null;
1365 zeroOrOne(s_separateInLine);
1366 return node;
1367 });
1368
1369 // 155
1370 _Node c_s_implicitJsonKey(int ctx) => transaction(() {
1371 // TODO(nweiz): this is supposed to be limited to 1024 characters.
1372
1373 // The indentation parameter is "null" since it's unused in this path
1374 var node = c_flowJsonNode(null, ctx);
1375 if (!truth(node)) return null;
1376 zeroOrOne(s_separateInLine);
1377 return node;
1378 });
1379
1380 // 156
1381 _Node ns_flowYamlContent(int indent, int ctx) {
1382 var str = ns_plain(indent, ctx);
1383 if (!truth(str)) return null;
1384 return new _ScalarNode("?", content: str);
1385 }
1386
1387 // 157
1388 _Node c_flowJsonContent(int indent, int ctx) => or([
1389 () => c_flowSequence(indent, ctx),
1390 () => c_flowMapping(indent, ctx),
1391 () => c_singleQuoted(indent, ctx),
1392 () => c_doubleQuoted(indent, ctx)
1393 ]);
1394
1395 // 158
1396 _Node ns_flowContent(int indent, int ctx) => or([
1397 () => ns_flowYamlContent(indent, ctx),
1398 () => c_flowJsonContent(indent, ctx)
1399 ]);
1400
1401 // 159
1402 _Node ns_flowYamlNode(int indent, int ctx) => or([
1403 c_ns_aliasNode,
1404 () => ns_flowYamlContent(indent, ctx),
1405 () {
1406 var props = c_ns_properties(indent, ctx);
1407 if (!truth(props)) return null;
1408 var node = or([
1409 () => transaction(() {
1410 if (!truth(s_separate(indent, ctx))) return null;
1411 return ns_flowYamlContent(indent, ctx);
1412 }),
1413 e_scalar
1414 ]);
1415 return addProps(node, props);
1416 }
1417 ]);
1418
1419 // 160
1420 _Node c_flowJsonNode(int indent, int ctx) => transaction(() {
1421 var props;
1422 zeroOrOne(() => transaction(() {
1423 props = c_ns_properties(indent, ctx);
1424 if (!truth(props)) return null;
1425 return s_separate(indent, ctx);
1426 }));
1427
1428 return addProps(c_flowJsonContent(indent, ctx), props);
1429 });
1430
1431 // 161
1432 _Node ns_flowNode(int indent, int ctx) => or([
1433 c_ns_aliasNode,
1434 () => ns_flowContent(indent, ctx),
1435 () => transaction(() {
1436 var props = c_ns_properties(indent, ctx);
1437 if (!truth(props)) return null;
1438 var node = or([
1439 () => transaction(() => s_separate(indent, ctx) ?
1440 ns_flowContent(indent, ctx) : null),
1441 e_scalar]);
1442 return addProps(node, props);
1443 })
1444 ]);
1445
1446 // 162
1447 _BlockHeader c_b_blockHeader() => transaction(() {
1448 var indentation = c_indentationIndicator();
1449 var chomping = c_chompingIndicator();
1450 if (!truth(indentation)) indentation = c_indentationIndicator();
1451 if (!truth(s_b_comment())) return null;
1452
1453 return new _BlockHeader(indentation, chomping);
1454 });
1455
1456 // 163
1457 int c_indentationIndicator() {
1458 if (!isDecDigit(peek())) return null;
1459 return next() - NUMBER_0;
1460 }
1461
1462 // 164
1463 int c_chompingIndicator() {
1464 switch (peek()) {
1465 case HYPHEN:
1466 next();
1467 return CHOMPING_STRIP;
1468 case PLUS:
1469 next();
1470 return CHOMPING_KEEP;
1471 default:
1472 return CHOMPING_CLIP;
1473 }
1474 }
1475
1476 // 165
1477 bool b_chompedLast(int chomping) {
1478 if (atEndOfFile) return true;
1479 switch (chomping) {
1480 case CHOMPING_STRIP:
1481 return b_nonContent();
1482 case CHOMPING_CLIP:
1483 case CHOMPING_KEEP:
1484 return b_asLineFeed();
1485 }
1486 }
1487
1488 // 166
1489 void l_chompedEmpty(int indent, int chomping) {
1490 switch (chomping) {
1491 case CHOMPING_STRIP:
1492 case CHOMPING_CLIP:
1493 l_stripEmpty(indent);
1494 break;
1495 case CHOMPING_KEEP:
1496 l_keepEmpty(indent);
1497 break;
1498 }
1499 }
1500
1501 // 167
1502 void l_stripEmpty(int indent) {
1503 captureAs('', () {
1504 zeroOrMore(() => transaction(() {
1505 if (!truth(s_indentLessThanOrEqualTo(indent))) return false;
1506 return b_nonContent();
1507 }));
1508 zeroOrOne(() => l_trailComments(indent));
1509 return true;
1510 });
1511 }
1512
1513 // 168
1514 void l_keepEmpty(int indent) {
1515 zeroOrMore(() => captureAs('\n', () => l_empty(indent, BLOCK_IN)));
1516 zeroOrOne(() => captureAs('', () => l_trailComments(indent)));
1517 }
1518
1519 // 169
1520 bool l_trailComments(int indent) => transaction(() {
1521 if (!truth(s_indentLessThanOrEqualTo(indent))) return false;
1522 if (!truth(c_nb_commentText())) return false;
1523 if (!truth(b_comment())) return false;
1524 zeroOrMore(l_comment);
1525 return true;
1526 });
1527
1528 // 170
1529 _Node c_l_literal(int indent) => transaction(() {
1530 if (!truth(c_indicator(C_LITERAL))) return null;
1531 var header = c_b_blockHeader();
1532 if (!truth(header)) return null;
1533
1534 var additionalIndent = blockScalarAdditionalIndentation(header, indent);
1535 var content = l_literalContent(indent + additionalIndent, header.chomping);
1536 if (!truth(content)) return null;
1537
1538 return new _ScalarNode("!", content: content);
1539 });
1540
1541 // 171
1542 bool l_nb_literalText(int indent) => transaction(() {
1543 zeroOrMore(() => captureAs("\n", () => l_empty(indent, BLOCK_IN)));
1544 if (!truth(captureAs("", () => s_indent(indent)))) return false;
1545 return truth(oneOrMore(() => consume(isNonBreak)));
1546 });
1547
1548 // 172
1549 bool b_nb_literalNext(int indent) => transaction(() {
1550 if (!truth(b_asLineFeed())) return false;
1551 return l_nb_literalText(indent);
1552 });
1553
1554 // 173
1555 String l_literalContent(int indent, int chomping) => captureString(() {
1556 transaction(() {
1557 if (!truth(l_nb_literalText(indent))) return false;
1558 zeroOrMore(() => b_nb_literalNext(indent));
1559 return b_chompedLast(chomping);
1560 });
1561 l_chompedEmpty(indent, chomping);
1562 return true;
1563 });
1564
1565 // 174
1566 _Node c_l_folded(int indent) => transaction(() {
1567 if (!truth(c_indicator(C_FOLDED))) return null;
1568 var header = c_b_blockHeader();
1569 if (!truth(header)) return null;
1570
1571 var additionalIndent = blockScalarAdditionalIndentation(header, indent);
1572 var content = l_foldedContent(indent + additionalIndent, header.chomping);
1573 if (!truth(content)) return null;
1574
1575 return new _ScalarNode("!", content: content);
1576 });
1577
1578 // 175
1579 bool s_nb_foldedText(int indent) => transaction(() {
1580 if (!truth(captureAs('', () => s_indent(indent)))) return false;
1581 if (!truth(consume(isNonSpace))) return false;
1582 zeroOrMore(() => consume(isNonBreak));
1583 return true;
1584 });
1585
1586 // 176
1587 bool l_nb_foldedLines(int indent) {
1588 if (!truth(s_nb_foldedText(indent))) return false;
1589 zeroOrMore(() => transaction(() {
1590 if (!truth(b_l_folded(indent, BLOCK_IN))) return false;
1591 return s_nb_foldedText(indent);
1592 }));
1593 return true;
1594 }
1595
1596 // 177
1597 bool s_nb_spacedText(int indent) => transaction(() {
1598 if (!truth(captureAs('', () => s_indent(indent)))) return false;
1599 if (!truth(consume(isSpace))) return false;
1600 zeroOrMore(() => consume(isNonBreak));
1601 return true;
1602 });
1603
1604 // 178
1605 bool b_l_spaced(int indent) {
1606 if (!truth(b_asLineFeed())) return false;
1607 zeroOrMore(() => captureAs("\n", () => l_empty(indent, BLOCK_IN)));
1608 return true;
1609 }
1610
1611 // 179
1612 bool l_nb_spacedLines(int indent) {
1613 if (!truth(s_nb_spacedText(indent))) return false;
1614 zeroOrMore(() => transaction(() {
1615 if (!truth(b_l_spaced(indent))) return false;
1616 return s_nb_spacedText(indent);
1617 }));
1618 return true;
1619 }
1620
1621 // 180
1622 bool l_nb_sameLines(int indent) => transaction(() {
1623 zeroOrMore(() => captureAs('\n', () => l_empty(indent, BLOCK_IN)));
1624 return or([
1625 () => l_nb_foldedLines(indent),
1626 () => l_nb_spacedLines(indent)
1627 ]);
1628 });
1629
1630 // 181
1631 bool l_nb_diffLines(int indent) {
1632 if (!truth(l_nb_sameLines(indent))) return false;
1633 zeroOrMore(() => transaction(() {
1634 if (!truth(b_asLineFeed())) return false;
1635 return l_nb_sameLines(indent);
1636 }));
1637 return true;
1638 }
1639
1640 // 182
1641 String l_foldedContent(int indent, int chomping) => captureString(() {
1642 transaction(() {
1643 if (!truth(l_nb_diffLines(indent))) return false;
1644 return b_chompedLast(chomping);
1645 });
1646 l_chompedEmpty(indent, chomping);
1647 return true;
1648 });
1649
1650 // 183
1651 _SequenceNode l_blockSequence(int indent) => context('sequence', () {
1652 var additionalIndent = countIndentation() - indent;
1653 if (additionalIndent <= 0) return null;
1654
1655 var content = oneOrMore(() => transaction(() {
1656 if (!truth(s_indent(indent + additionalIndent))) return null;
1657 return c_l_blockSeqEntry(indent + additionalIndent);
1658 }));
1659 if (!truth(content)) return null;
1660
1661 return new _SequenceNode("?", content);
1662 });
1663
1664 // 184
1665 _Node c_l_blockSeqEntry(int indent) => transaction(() {
1666 if (!truth(c_indicator(C_SEQUENCE_ENTRY))) return null;
1667 if (isNonSpace(peek())) return null;
1668
1669 return s_l_blockIndented(indent, BLOCK_IN);
1670 });
1671
1672 // 185
1673 _Node s_l_blockIndented(int indent, int ctx) {
1674 var additionalIndent = countIndentation();
1675 return or([
1676 () => transaction(() {
1677 if (!truth(s_indent(additionalIndent))) return null;
1678 return or([
1679 () => ns_l_compactSequence(indent + 1 + additionalIndent),
1680 () => ns_l_compactMapping(indent + 1 + additionalIndent)]);
1681 }),
1682 () => s_l_blockNode(indent, ctx),
1683 () => s_l_comments() ? e_node() : null]);
1684 }
1685
1686 // 186
1687 _Node ns_l_compactSequence(int indent) => context('sequence', () {
1688 var first = c_l_blockSeqEntry(indent);
1689 if (!truth(first)) return null;
1690
1691 var content = zeroOrMore(() => transaction(() {
1692 if (!truth(s_indent(indent))) return null;
1693 return c_l_blockSeqEntry(indent);
1694 }));
1695 content.insertRange(0, 1, first);
1696
1697 return new _SequenceNode("?", content);
1698 });
1699
1700 // 187
1701 _Node l_blockMapping(int indent) => context('mapping', () {
1702 var additionalIndent = countIndentation() - indent;
1703 if (additionalIndent <= 0) return null;
1704
1705 var pairs = oneOrMore(() => transaction(() {
1706 if (!truth(s_indent(indent + additionalIndent))) return null;
1707 return ns_l_blockMapEntry(indent + additionalIndent);
1708 }));
1709 if (!truth(pairs)) return null;
1710
1711 return map(pairs);
1712 });
1713
1714 // 188
1715 _Pair<_Node, _Node> ns_l_blockMapEntry(int indent) => or([
1716 () => c_l_blockMapExplicitEntry(indent),
1717 () => ns_l_blockMapImplicitEntry(indent)
1718 ]);
1719
1720 // 189
1721 _Pair<_Node, _Node> c_l_blockMapExplicitEntry(int indent) {
1722 var key = c_l_blockMapExplicitKey(indent);
1723 if (!truth(key)) return null;
1724
1725 var value = or([
1726 () => l_blockMapExplicitValue(indent),
1727 e_node
1728 ]);
1729
1730 return new _Pair<_Node, _Node>(key, value);
1731 }
1732
1733 // 190
1734 _Node c_l_blockMapExplicitKey(int indent) => transaction(() {
1735 if (!truth(c_indicator(C_MAPPING_KEY))) return null;
1736 return s_l_blockIndented(indent, BLOCK_OUT);
1737 });
1738
1739 // 191
1740 _Node l_blockMapExplicitValue(int indent) => transaction(() {
1741 if (!truth(s_indent(indent))) return null;
1742 if (!truth(c_indicator(C_MAPPING_VALUE))) return null;
1743 return s_l_blockIndented(indent, BLOCK_OUT);
1744 });
1745
1746 // 192
1747 _Pair<_Node, _Node> ns_l_blockMapImplicitEntry(int indent) => transaction(() {
1748 var key = or([ns_s_blockMapImplicitKey, e_node]);
1749 var value = c_l_blockMapImplicitValue(indent);
1750 return truth(value) ? new _Pair<_Node, _Node>(key, value) : null;
1751 });
1752
1753 // 193
1754 _Node ns_s_blockMapImplicitKey() => context('mapping key', () => or([
1755 () => c_s_implicitJsonKey(BLOCK_KEY),
1756 () => ns_s_implicitYamlKey(BLOCK_KEY)
1757 ]));
1758
1759 // 194
1760 _Node c_l_blockMapImplicitValue(int indent) => context('mapping value', () =>
1761 transaction(() {
1762 if (!truth(c_indicator(C_MAPPING_VALUE))) return null;
1763 return or([
1764 () => s_l_blockNode(indent, BLOCK_OUT),
1765 () => s_l_comments() ? e_node() : null
1766 ]);
1767 }));
1768
1769 // 195
1770 _Node ns_l_compactMapping(int indent) => context('mapping', () {
1771 var first = ns_l_blockMapEntry(indent);
1772 if (!truth(first)) return null;
1773
1774 var pairs = zeroOrMore(() => transaction(() {
1775 if (!truth(s_indent(indent))) return null;
1776 return ns_l_blockMapEntry(indent);
1777 }));
1778 pairs.insertRange(0, 1, first);
1779
1780 return map(pairs);
1781 });
1782
1783 // 196
1784 _Node s_l_blockNode(int indent, int ctx) => or([
1785 () => s_l_blockInBlock(indent, ctx),
1786 () => s_l_flowInBlock(indent)
1787 ]);
1788
1789 // 197
1790 _Node s_l_flowInBlock(int indent) => transaction(() {
1791 if (!truth(s_separate(indent + 1, FLOW_OUT))) return null;
1792 var node = ns_flowNode(indent + 1, FLOW_OUT);
1793 if (!truth(node)) return null;
1794 if (!truth(s_l_comments())) return null;
1795 return node;
1796 });
1797
1798 // 198
1799 _Node s_l_blockInBlock(int indent, int ctx) => or([
1800 () => s_l_blockScalar(indent, ctx),
1801 () => s_l_blockCollection(indent, ctx)
1802 ]);
1803
1804 // 199
1805 _Node s_l_blockScalar(int indent, int ctx) => transaction(() {
1806 if (!truth(s_separate(indent + 1, ctx))) return null;
1807 var props = transaction(() {
1808 var props = c_ns_properties(indent + 1, ctx);
1809 if (!truth(props)) return null;
1810 if (!truth(s_separate(indent + 1, ctx))) return null;
1811 return props;
1812 });
1813
1814 var node = or([() => c_l_literal(indent), () => c_l_folded(indent)]);
1815 if (!truth(node)) return null;
1816 return addProps(node, props);
1817 });
1818
1819 // 200
1820 _Node s_l_blockCollection(int indent, int ctx) => transaction(() {
1821 var props = transaction(() {
1822 if (!truth(s_separate(indent + 1, ctx))) return null;
1823 return c_ns_properties(indent + 1, ctx);
1824 });
1825
1826 if (!truth(s_l_comments())) return null;
1827 return or([
1828 () => l_blockSequence(seqSpaces(indent, ctx)),
1829 () => l_blockMapping(indent)]);
1830 });
1831
1832 // 201
1833 int seqSpaces(int indent, int ctx) => ctx == BLOCK_OUT ? indent - 1 : indent;
1834
1835 // 202
1836 void l_documentPrefix() {
1837 zeroOrMore(l_comment);
1838 }
1839
1840 // 203
1841 bool c_directivesEnd() => rawString("---");
1842
1843 // 204
1844 bool c_documentEnd() => rawString("...");
1845
1846 // 205
1847 bool l_documentSuffix() => transaction(() {
1848 if (!truth(c_documentEnd())) return false;
1849 return s_l_comments();
1850 });
1851
1852 // 206
1853 bool c_forbidden() {
1854 if (!inBareDocument || !atStartOfLine) return false;
1855 var forbidden = false;
1856 transaction(() {
1857 if (!truth(or([c_directivesEnd, c_documentEnd]))) return;
1858 var char = peek();
1859 forbidden = isBreak(char) || isSpace(char) || atEndOfFile;
1860 return;
1861 });
1862 return forbidden;
1863 }
1864
1865 // 207
1866 _Node l_bareDocument() {
1867 try {
1868 inBareDocument = true;
1869 return s_l_blockNode(-1, BLOCK_IN);
1870 } finally {
1871 inBareDocument = false;
1872 }
1873 }
1874
1875 // 208
1876 _Node l_explicitDocument() {
1877 if (!truth(c_directivesEnd())) return null;
1878 var doc = l_bareDocument();
1879 if (truth(doc)) return doc;
1880
1881 doc = e_node();
1882 s_l_comments();
1883 return doc;
1884 }
1885
1886 // 209
1887 _Node l_directiveDocument() {
1888 if (!truth(oneOrMore(l_directive))) return null;
1889 var doc = l_explicitDocument();
1890 if (doc != null) return doc;
1891 parseFailed();
1892 return null; // Unreachable.
1893 }
1894
1895 // 210
1896 _Node l_anyDocument() =>
1897 or([l_directiveDocument, l_explicitDocument, l_bareDocument]);
1898
1899 // 211
1900 List<_Node> l_yamlStream() {
1901 var docs = [];
1902 zeroOrMore(l_documentPrefix);
1903 var first = zeroOrOne(l_anyDocument);
1904 if (!truth(first)) first = e_node();
1905 docs.add(first);
1906
1907 zeroOrMore(() {
1908 var doc;
1909 if (truth(oneOrMore(l_documentSuffix))) {
1910 zeroOrMore(l_documentPrefix);
1911 doc = zeroOrOne(l_anyDocument);
1912 } else {
1913 zeroOrMore(l_documentPrefix);
1914 doc = zeroOrOne(l_explicitDocument);
1915 }
1916 if (truth(doc)) docs.add(doc);
1917 return doc;
1918 });
1919
1920 if (!atEndOfFile) parseFailed();
1921 return docs;
1922 }
1923 }
1924
1925 class SyntaxError extends YamlException {
1926 final int line;
1927 final int column;
1928
1929 SyntaxError(this.line, this.column, String msg) : super(msg);
1930
1931 String toString() => "Syntax error on line $line, column $column: $msg";
1932 }
1933
1934 /** A pair of values. */
1935 class _Pair<E, F> {
1936 E first;
1937 F last;
1938
1939 _Pair(this.first, this.last);
1940
1941 String toString() => '($first, $last)';
1942 }
1943
1944 /** The information in the header for a block scalar. */
1945 class _BlockHeader {
1946 final int additionalIndent;
1947 final int chomping;
1948
1949 _BlockHeader(this.additionalIndent, this.chomping);
1950
1951 bool get autoDetectIndent => additionalIndent == null;
1952 }
1953
1954 /**
1955 * A range of characters in the YAML document, from [start] to [end] (inclusive) .
1956 */
1957 class _Range {
1958 /** The first character in the range. */
1959 final int start;
1960
1961 /** The last character in the range. */
1962 final int end;
1963
1964 _Range(this.start, this.end);
1965
1966 /** Returns whether or not [pos] lies within this range. */
1967 bool contains(int pos) => pos >= start && pos <= end;
1968 }
1969
1970 /**
1971 * A map that associates [E] values with [_Range]s. It's efficient to create new
1972 * associations, but finding the value associated with a position is more
1973 * expensive.
1974 */
1975 class _RangeMap<E> {
1976 /** The ranges and their associated elements. */
1977 final List<_Pair<_Range, E>> contents;
1978
1979 _RangeMap() : this.contents = <_Pair<_Range, E>>[];
1980
1981 /**
1982 * Returns the value associated with the range in which [pos] lies, or null if
1983 * there is no such range. If there's more than one such range, the most
1984 * recently set one is used.
1985 */
1986 E operator[](int pos) {
1987 // Iterate backwards through contents so the more recent range takes
1988 // precedence. TODO(nweiz): clean this up when issue 2804 is fixed.
1989 for (var i = contents.length - 1; i >= 0; i--) {
1990 var pair = contents[i];
1991 if (pair.first.contains(pos)) return pair.last;
1992 }
1993 return null;
1994 }
1995
1996 /** Associates [value] with [range]. */
1997 operator[]=(_Range range, E value) =>
1998 contents.add(new _Pair<_Range, E>(range, value));
1999 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698