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

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

Powered by Google App Engine
This is Rietveld 408576698