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

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

Issue 10153004: Add a basic YAML processor. Much of the language is still unimplemented. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review changes Created 8 years, 8 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/yaml/model.dart ('k') | utils/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 /**
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 final TAB = 0x9;
22 static final LF = 0xA;
23 static final CR = 0xD;
24 static final SP = 0x20;
25 static final TILDE = 0x7E;
26 static final NEL = 0x85;
27 static final HYPHEN = 0x2D;
28 static final QUESTION_MARK = 0x3F;
29 static final COLON = 0x3A;
30 static final COMMA = 0x2C;
31 static final LEFT_BRACKET = 0x5B;
32 static final RIGHT_BRACKET = 0x5D;
33 static final LEFT_BRACE = 0x7B;
34 static final RIGHT_BRACE = 0x7D;
35 static final HASH = 0x23;
36 static final AMPERSAND = 0x26;
37 static final ASTERISK = 0x2A;
38 static final EXCLAMATION = 0x21;
39 static final VERTICAL_BAR = 0x7C;
40 static final GREATER_THAN = 0x3E;
41 static final SINGLE_QUOTE = 0x27;
42 static final DOUBLE_QUOTE = 0x22;
43 static final PERCENT = 0x25;
44 static final AT = 0x40;
45 static final GRAVE_ACCENT = 0x60;
46
47 static final NULL = 0x0;
48 static final BELL = 0x7;
49 static final BACKSPACE = 0x8;
50 static final VERTICAL_TAB = 0xB;
51 static final FORM_FEED = 0xC;
52 static final ESCAPE = 0x1B;
53 static final BACKSLASH = 0x5C;
54 static final NBSP = 0xA0;
55 static final LINE_SEPARATOR = 0x2028;
56 static final PARAGRAPH_SEPARATOR = 0x2029;
57
58 static final C_SEQUENCE_ENTRY = 4;
59 static final C_MAPPING_KEY = 5;
60 static final C_MAPPING_VALUE = 6;
61 static final C_COLLECT_ENTRY = 7;
62 static final C_SEQUENCE_START = 8;
63 static final C_SEQUENCE_END = 9;
64 static final C_MAPPING_START = 10;
65 static final C_MAPPING_END = 11;
66 static final C_COMMENT = 12;
67 static final C_ANCHOR = 13;
68 static final C_ALIAS = 14;
69 static final C_TAG = 15;
70 static final C_LITERAL = 16;
71 static final C_FOLDED = 17;
72 static final C_SINGLE_QUOTE = 18;
73 static final C_DOUBLE_QUOTE = 19;
74 static final C_DIRECTIVE = 20;
75 static final C_RESERVED = 21;
76
77 static final BLOCK_OUT = 0;
78 static final BLOCK_IN = 1;
79 static final FLOW_OUT = 2;
80 static final FLOW_IN = 3;
81 static final BLOCK_KEY = 4;
82 static final FLOW_KEY = 5;
83
84 /** The source string being parsed. */
85 final String s;
86
87 /** The current position in the source string. */
88 int pos = 0;
89
90 /** The length of the string being parsed. */
91 final int len;
92
93 /** The current (0-based) line in the source string. */
94 int line = 0;
95
96 /** The current (0-based) column in the source string. */
97 int column = 0;
98
99 /**
100 * Whether we're parsing a bare document (that is, one that doesn't begin with
101 * `---`). Bare documents don't allow `%` immediately following newlines.
102 */
103 bool inBareDocument = false;
104
105 /**
106 * The line number of the farthest position that has been parsed successfully
107 * before backtracking. Used for error reporting.
108 */
109 int farthestLine = 0;
110
111 /**
112 * The column number of the farthest position that has been parsed
113 * successfully before backtracking. Used for error reporting.
114 */
115 int farthestColumn = 0;
116
117 /**
118 * The name of the context of the farthest position that has been parsed
119 * successfully before backtracking. Used for error reporting.
120 */
121 String farthestContext = "document";
122
123 /** A stack of the names of parse contexts. Used for error reporting. */
124 List<String> contextStack;
125
126 _Parser(String s)
127 : this.s = s,
128 len = s.length,
129 contextStack = <String>["document"];
130
131 /**
132 * Return the character at the current position, then move that position
133 * forward one character. Also updates the current line and column numbers.
134 */
135 int next() {
136 if (pos == len) return -1;
137 var char = s.charCodeAt(pos++);
138 if (isBreak(char)) {
139 line++;
140 column = 0;
141 } else {
142 column++;
143 }
144
145 if (farthestLine < line) {
146 farthestLine = line;
147 farthestColumn = column;
148 farthestContext = contextStack.last();
149 } else if (farthestLine == line && farthestColumn < column) {
150 farthestColumn = column;
151 farthestContext = contextStack.last();
152 }
153
154 return char;
155 }
156
157 /**
158 * Returns the character at the current position, or the character [i]
159 * characters after the current position.
160 *
161 * Returns -1 if this would return a character after the end or before the
162 * beginning of the input string.
163 */
164 int peek([int i = 0]) {
165 var peekPos = pos + i;
166 return (peekPos >= len || peekPos < 0) ? -1 : s.charCodeAt(peekPos);
167 }
168
169 /**
170 * The truthiness operator. Returns `false` if [obj] is `null` or `false`,
171 * `true` otherwise.
172 */
173 bool _(obj) => obj != null && obj != false;
Bob Nystrom 2012/04/24 00:53:38 I sent you an LGTM, but I forgot about this one. I
nweiz 2012/04/25 00:26:29 Bob and I talked about this offline, and compromis
174
175 /**
176 * Consumes the current character if it matches [matcher]. Returns the result
177 * of [matcher].
178 */
179 bool consume(bool matcher(int)) {
180 if (matcher(peek())) {
181 next();
182 return true;
183 }
184 return false;
185 }
186
187 /**
188 * Calls [consumer] until it returns a falsey value. Returns a list of all
189 * truthy return values of [consumer], or null if it didn't consume anything.
190 *
191 * Conceptually, repeats a production one or more times.
192 */
193 List oneOrMore(consumer()) {
194 var first = consumer();
195 if (!_(first)) return null;
196 var out = [first];
197 while (true) {
198 var el = consumer();
199 if (!_(el)) return out;
200 out.add(el);
201 }
202 }
203
204 /**
205 * Calls [consumer] until it returns a falsey value. Returns a list of all
206 * truthy return values of [consumer], or the empty list if it didn't consume
207 * anything.
208 *
209 * Conceptually, repeats a production any number of times.
210 */
211 List zeroOrMore(consumer()) {
212 var out = [];
213 var oldPos = pos;
214 while (true) {
215 var el = consumer();
216 if (!_(el) || oldPos == pos) return out;
217 oldPos = pos;
218 out.add(el);
219 }
220 }
221
222 /**
223 * Just calls [consumer] and returns its result. Used to make it explicit that
224 * a production is intended to be optional.
225 */
226 zeroOrOne(consumer()) => consumer();
227
228 /**
229 * Calls each function in [consumers] until one returns a truthy value, then
230 * returns that.
231 */
232 or(List<Function> consumers) {
233 for (var c in consumers) {
234 var res = c();
235 if (_(res)) return res;
236 }
237 return null;
238 }
239
240 /**
241 * Calls [consumer] and returns its result, but rolls back the parser state if
242 * [consumer] returns a falsey value.
243 */
244 transaction(consumer()) {
245 int oldPos = pos, oldLine = line, oldColumn = column;
246 var res = consumer();
247 if (_(res)) return res;
248
249 pos = oldPos;
250 line = oldLine;
251 column = oldColumn;
252 return res;
253 }
254
255 /**
256 * Consumes [n] characters matching [matcher], or none if there isn't a
257 * complete match. The first argument to [matcher] is the character code, the
258 * second is the index (from 0 to [n] - 1).
259 *
260 * Returns whether or not the characters were consumed.
261 */
262 bool nAtOnce(int n, bool matcher(int c, int i)) => transaction(() {
263 for (int i = 0; i < n; i++) {
264 if (!consume((c) => matcher(c, i))) return false;
265 }
266 return true;
267 });
268
269 /**
270 * Consumes the exact characters in [str], or nothing.
271 *
272 * Returns whether or not the string was consumed.
273 */
274 bool rawString(String str) =>
275 nAtOnce(str.length, (c, i) => str.charCodeAt(i) == c);
276
277 /**
278 * Consumes and returns a string of characters matching [matcher], or null if
279 * there are no such characters.
280 */
281 String stringOf(bool matcher(int)) =>
282 captureString(() => oneOrMore(() => consume(matcher)));
283
284 /**
285 * Calls [consumer] and returns the string that was consumed while doing so,
286 * or null if [consumer] returned a falsey value. Automatically wraps
287 * [consumer] in `transaction`.
288 */
289 String captureString(consumer()) {
290 int start = pos;
291 var res = transaction(consumer);
292 if (!_(res)) return null;
293 return s.substring(start, pos);
294 }
295
296 /**
297 * Adds a tag and an anchor to [node], if they're defined.
298 */
299 _Node addProps(_Node node, _Pair<_Tag, String> props) {
300 if (_(props.first)) node.tag = props.first;
301 if (_(props.last)) node.anchor = props.last;
302 return node;
303 }
304
305 /** Creates a MappingNode from [pairs]. */
306 _MappingNode map(List<_Pair<_Node, _Node>> pairs) {
307 var content = new Map<_Node, _Node>();
308 pairs.forEach((pair) { content[pair.first] = pair.last; });
309 return new _MappingNode("?", content);
310 }
311
312 /** Runs [fn] in a context named [name]. Used for error reporting. */
313 context(String name, fn()) {
314 try {
315 contextStack.add(name);
316 return fn();
317 } finally {
318 var popped = contextStack.removeLast();
319 assert(popped == name);
320 }
321 }
322
323 /** Throws an error with additional context information. */
324 error(String message) {
325 // Line and column should be one-based.
326 throw new SyntaxError(line + 1, column + 1,
327 "$message (in $farthestContext)");
328 }
329
330 /**
331 * If [result] is falsey, throws an error saying that [expected] was
332 * expected.
333 */
334 expect(result, String expected) {
335 if (_(result)) return result;
336 error("expected $expected");
337 }
338
339 /**
340 * Throws an error saying that the parse failed. Uses [farthestLine],
341 * [farthestColumn], and [farthestContext] to provide additional information.
342 */
343 parseFailed() {
344 throw new SyntaxError(farthestLine + 1, farthestColumn + 1,
345 "invalid YAML in $farthestContext");
346 }
347
348 /** Returns the number of spaces after the current position. */
349 int countIndentation() {
350 var i = 0;
351 while (peek(i) == SP) i++;
352 return i;
353 }
354
355 /** Returns whether the current position is at the beginning of a line. */
356 bool get atStartOfLine() => column == 0;
357
358 /** Returns whether the current position is at the end of the input. */
359 bool get atEndOfFile() => pos == len;
360
361 /**
362 * Given an indicator character, returns the type of that indicator (or null
363 * if the indicator isn't found.
364 */
365 int indicatorType(int char) {
366 switch (char) {
367 case HYPHEN: return C_SEQUENCE_ENTRY;
368 case QUESTION_MARK: return C_MAPPING_KEY;
369 case COLON: return C_MAPPING_VALUE;
370 case COMMA: return C_COLLECT_ENTRY;
371 case LEFT_BRACKET: return C_SEQUENCE_START;
372 case RIGHT_BRACKET: return C_SEQUENCE_END;
373 case LEFT_BRACE: return C_MAPPING_START;
374 case RIGHT_BRACE: return C_MAPPING_END;
375 case HASH: return C_COMMENT;
376 case AMPERSAND: return C_ANCHOR;
377 case ASTERISK: return C_ALIAS;
378 case EXCLAMATION: return C_TAG;
379 case VERTICAL_BAR: return C_LITERAL;
380 case GREATER_THAN: return C_FOLDED;
381 case SINGLE_QUOTE: return C_SINGLE_QUOTE;
382 case DOUBLE_QUOTE: return C_DOUBLE_QUOTE;
383 case PERCENT: return C_DIRECTIVE;
384 case AT:
385 case GRAVE_ACCENT:
386 return C_RESERVED;
387 default: return null;
388 }
389 }
390
391 // 1
392 bool isPrintable(int char) {
393 return char == TAB ||
394 char == LF ||
395 char == CR ||
396 (char >= SP && char <= TILDE) ||
397 char == NEL ||
398 (char >= 0xA0 && char <= 0xD7FF) ||
399 (char >= 0xE000 && char <= 0xFFFD) ||
400 (char >= 0x10000 && char <= 0x10FFFF);
401 }
402
403 // 22
404 bool c_indicator(int type) => consume((c) => indicatorType(c) == type);
405
406 // 23
407 bool isFlowIndicator(int char) {
408 var indicator = indicatorType(char);
409 return indicator == C_COLLECT_ENTRY ||
410 indicator == C_SEQUENCE_START ||
411 indicator == C_SEQUENCE_END ||
412 indicator == C_MAPPING_START ||
413 indicator == C_MAPPING_END;
414 }
415
416 // 26
417 bool isBreak(int char) => char == LF || char == CR;
418
419 // 27
420 bool isNonBreak(int char) => isPrintable(char) && !isBreak(char);
421
422 // 30
423 bool b_non_content() => consume(isBreak);
424
425 // 33
426 bool isSpace(int char) => char == SP || char == TAB;
427
428 // 34
429 bool isNonSpace(int char) => isNonBreak(char) && !isSpace(char);
430
431 // 63
432 bool s_indent(int indent) => nAtOnce(indent, (c, i) => c == SP);
433
434 // 66
435 bool s_separateInLine() => transaction(() =>
436 _(oneOrMore(() => consume(isSpace))) || atStartOfLine);
437
438 // 69
439 bool s_flowLinePrefix(int indent) {
440 if (!s_indent(indent)) return false;
441 zeroOrOne(s_separateInLine);
442 return true;
443 }
444
445 // 74
446 bool s_flowFolded(int indent) => false; // TODO(nweiz): implement
447
448 // 75
449 bool c_nb_commentText() {
450 if (!c_indicator(C_COMMENT)) return false;
451 zeroOrMore(() => consume(isNonBreak));
452 return true;
453 }
454
455 // 76
456 bool b_comment() => atEndOfFile || b_non_content();
457
458 // 77
459 bool s_b_comment() {
460 if (s_separateInLine()) {
461 zeroOrOne(c_nb_commentText);
462 }
463 return b_comment();
464 }
465
466 // 78
467 bool l_comment() => transaction(() {
468 if (!s_separateInLine()) return false;
469 zeroOrOne(c_nb_commentText);
470 return b_comment();
471 });
472
473 // 79
474 bool s_l_comments() {
475 if (!s_b_comment() && !atStartOfLine) return false;
476 zeroOrMore(l_comment);
477 return true;
478 }
479
480 // 80
481 bool s_separate(int indent, int ctx) {
482 switch (ctx) {
483 case BLOCK_OUT:
484 case BLOCK_IN:
485 case FLOW_OUT:
486 case FLOW_IN:
487 return s_separateLines(indent);
488 case BLOCK_KEY:
489 case FLOW_KEY:
490 return s_separateInLine();
491 default: throw 'invalid context "$ctx"';
492 }
493 }
494
495 // 81
496 bool s_separateLines(int indent) {
497 return transaction(() => s_l_comments() && s_flowLinePrefix(indent)) ||
498 s_separateInLine();
499 }
500
501 // 82
502 bool l_directive() => false; // TODO(nweiz): implement
503
504 // 96
505 _Pair<_Tag, String> c_ns_properties(int indent, int ctx) {
506 var tag, anchor;
507 tag = c_ns_tagProperty();
508 if (_(tag)) {
509 anchor = transaction(() {
510 if (!s_separate(indent, ctx)) return null;
511 return c_ns_anchorProperty();
512 });
513 return new _Pair<_Tag, String>(tag, anchor);
514 }
515
516 anchor = c_ns_anchorProperty();
517 if (_(anchor)) {
518 tag = transaction(() {
519 if (!s_separate(indent, ctx)) return null;
520 return c_ns_tagProperty();
521 });
522 return new _Pair<_Tag, String>(tag, anchor);
523 }
524
525 return null;
526 }
527
528 // 97
529 _Tag c_ns_tagProperty() => null; // TODO(nweiz): implement
530
531 // 101
532 String c_ns_anchorProperty() => null; // TODO(nweiz): implement
533
534 // 102
535 bool isAnchorChar(int char) => isNonSpace(char) && !isFlowIndicator(char);
536
537 // 103
538 String ns_anchorName() =>
539 captureString(() => oneOrMore(() => consume(isAnchorChar)));
540
541 // 104
542 _Node c_ns_aliasNode() {
543 if (!c_indicator(C_ALIAS)) return null;
544 var name = expect(ns_anchorName(), 'anchor name');
545 return new _AliasNode(name);
546 }
547
548 // 105
549 _ScalarNode e_scalar() => new _ScalarNode("?", content: "");
550
551 // 106
552 _ScalarNode e_node() => e_scalar();
553
554 // 126
555 bool ns_plainFirst(int ctx) {
556 var char = peek();
557 var indicator = indicatorType(char);
558 if (indicator == C_RESERVED) {
559 error("reserved indicators can't start a plain scalar");
560 }
561 var match = (isNonSpace(char) && indicator == null) ||
562 ((indicator == C_MAPPING_KEY ||
563 indicator == C_MAPPING_VALUE ||
564 indicator == C_SEQUENCE_ENTRY) &&
565 isPlainSafe(ctx, peek(1)));
566
567 if (match) next();
568 return match;
569 }
570
571 // 127
572 bool isPlainSafe(int ctx, int char) {
573 switch (ctx) {
574 case FLOW_OUT:
575 case BLOCK_KEY:
576 // 128
577 return isNonSpace(char);
578 case FLOW_IN:
579 case FLOW_KEY:
580 // 129
581 return isNonSpace(char) && !isFlowIndicator(char);
582 default: throw 'invalid context "$ctx"';
583 }
584 }
585
586 // 130
587 bool ns_plainChar(int ctx) {
588 var char = peek();
589 var indicator = indicatorType(char);
590 var safeChar = isPlainSafe(ctx, char) && indicator != C_MAPPING_VALUE &&
591 indicator != C_COMMENT;
592 var nonCommentHash = isNonSpace(peek(-1)) && indicator == C_COMMENT;
593 var nonMappingColon = indicator == C_MAPPING_VALUE &&
594 isPlainSafe(ctx, peek(1));
595 var match = safeChar || nonCommentHash || nonMappingColon;
596
597 if (match) next();
598 return match;
599 }
600
601 // 131
602 String ns_plain(int indent, int ctx) => context('plain scalar', () {
603 switch (ctx) {
604 case FLOW_OUT:
605 case FLOW_IN:
606 return ns_plainMultiLine(indent, ctx);
607 case BLOCK_KEY:
608 case FLOW_KEY:
609 return ns_plainOneLine(ctx);
610 default: throw 'invalid context "$ctx"';
611 }
612 });
613
614 // 132
615 void nb_ns_plainInLine(int ctx) {
616 zeroOrMore(() => transaction(() {
617 zeroOrMore(() => consume(isSpace));
618 return ns_plainChar(ctx);
619 }));
620 }
621
622 // 133
623 String ns_plainOneLine(int ctx) => captureString(() {
624 if (c_forbidden()) return false;
625 if (!ns_plainFirst(ctx)) return false;
626 nb_ns_plainInLine(ctx);
627 return true;
628 });
629
630 // 134
631 bool s_ns_plainNextLine(int indent, int ctx) => transaction(() {
632 if (c_forbidden()) return false;
633 if (!s_flowFolded(indent)) return false;
634 if (!ns_plainChar(ctx)) return false;
635 nb_ns_plainInLine(ctx);
636 return true;
637 });
638
639 // 135
640 String ns_plainMultiLine(int indent, int ctx) => captureString(() {
641 if (!_(ns_plainOneLine(ctx))) return false;
642 zeroOrMore(() => s_ns_plainNextLine(indent, ctx));
643 return true;
644 });
645
646 // 154
647 _Node c_s_implicitYamlKey(int ctx) => transaction(() {
648 // Indentation parameter is unused in this path
649 var node = ns_flowYamlNode(-10, ctx);
650 if (!_(node)) return null;
651 zeroOrOne(s_separateInLine);
652 return node;
653 });
654
655 // 155
656 _Node c_s_implicitJsonKey(int ctx) => null; // TODO(nweiz): implement
657
658 // 156
659 _Node ns_flowYamlContent(int indent, int ctx) {
660 var str = ns_plain(indent, ctx);
661 if (!_(str)) return null;
662 return new _ScalarNode("?", content: str);
663 }
664
665 // 157
666 // TODO(nweiz): implement
667 _Node ns_flowJsonContent(int indent, int ctx) => null;
668
669 // 158
670 _Node ns_flowContent(int indent, int ctx) => or([
671 () => ns_flowYamlContent(indent, ctx),
672 () => ns_flowJsonContent(indent, ctx)
673 ]);
674
675 // 159
676 _Node ns_flowYamlNode(int indent, int ctx) => or([
677 c_ns_aliasNode,
678 () => ns_flowYamlContent(indent, ctx),
679 () {
680 var props = c_ns_properties(indent, ctx);
681 if (!_(props)) return null;
682 var node = or([
683 () => transaction(() {
684 if (!s_separate(indent, ctx)) return null;
685 return ns_flowYamlContent(indent, ctx);
686 }),
687 e_scalar
688 ]);
689 return addProps(node, props);
690 }
691 ]);
692
693 // 161
694 _Node ns_flowNode(int indent, int ctx) => or([
695 c_ns_aliasNode,
696 () => ns_flowContent(indent, ctx),
697 () => transaction(() {
698 var props = c_ns_properties(indent, ctx);
699 if (!_(props)) return null;
700 var node = or([
701 () => transaction(() => s_separate(indent, ctx) ?
702 ns_flowContent(indent, ctx) : null),
703 e_scalar]);
704 return addProps(node, props);
705 })
706 ]);
707
708 // 170
709 _Node c_l_literal(int indent) => null; // TODO(nweiz); implement
710
711 // 174
712 _Node c_l_folded(int indent) => null; // TODO(nweiz); implement
713
714 // 183
715 _SequenceNode l_blockSequence(int indent) => context('sequence', () {
716 var additionalIndent = countIndentation() - indent;
717 if (additionalIndent <= 0) return null;
718
719 var content = oneOrMore(() => transaction(() {
720 if (!s_indent(indent + additionalIndent)) return null;
721 return c_l_blockSeqEntry(indent + additionalIndent);
722 }));
723 if (!_(content)) return null;
724
725 return new _SequenceNode("?", content);
726 });
727
728 // 184
729 _Node c_l_blockSeqEntry(int indent) => transaction(() {
730 if (!c_indicator(C_SEQUENCE_ENTRY)) return null;
731 if (isNonSpace(peek())) return null;
732
733 return s_l_blockIndented(indent, BLOCK_IN);
734 });
735
736 // 185
737 _Node s_l_blockIndented(int indent, int ctx) {
738 var additionalIndent = countIndentation();
739 return or([
740 () => transaction(() {
741 if (!s_indent(additionalIndent)) return null;
742 return or([
743 () => ns_l_compactSequence(indent + 1 + additionalIndent),
744 () => ns_l_compactMapping(indent + 1 + additionalIndent)]);
745 }),
746 () => s_l_blockNode(indent, ctx),
747 () => s_l_comments() ? e_node() : null]);
748 }
749
750 // 186
751 _Node ns_l_compactSequence(int indent) => context('sequence', () {
752 var first = c_l_blockSeqEntry(indent);
753 if (!_(first)) return null;
754
755 var content = zeroOrMore(() => transaction(() {
756 if (!s_indent(indent)) return null;
757 return c_l_blockSeqEntry(indent);
758 }));
759 content.insertRange(0, 1, first);
760
761 return new _SequenceNode("?", content);
762 });
763
764 // 187
765 _Node l_blockMapping(int indent) => context('mapping', () {
766 var additionalIndent = countIndentation() - indent;
767 if (additionalIndent <= 0) return null;
768
769 var pairs = oneOrMore(() => transaction(() {
770 if (!s_indent(indent + additionalIndent)) return null;
771 return ns_l_blockMapEntry(indent + additionalIndent);
772 }));
773 if (!_(pairs)) return null;
774
775 return map(pairs);
776 });
777
778 // 188
779 _Pair<_Node, _Node> ns_l_blockMapEntry(int indent) => or([
780 () => c_l_blockMapExplicitEntry(indent),
781 () => ns_l_blockMapImplicitEntry(indent)
782 ]);
783
784 // 189
785 // TODO(nweiz): implement
786 _Pair<_Node, _Node> c_l_blockMapExplicitEntry(int indent) => null;
787
788 // 192
789 _Pair<_Node, _Node> ns_l_blockMapImplicitEntry(int indent) => transaction(() {
790 var key = or([ns_s_blockMapImplicitKey, e_node]);
791 var value = c_l_blockMapImplicitValue(indent);
792 return _(value) ? new _Pair<_Node, _Node>(key, value) : null;
793 });
794
795 // 193
796 _Node ns_s_blockMapImplicitKey() => context('mapping key', () => or([
797 () => c_s_implicitJsonKey(BLOCK_KEY),
798 () => c_s_implicitYamlKey(BLOCK_KEY)
799 ]));
800
801 // 194
802 _Node c_l_blockMapImplicitValue(int indent) => context('mapping value', () =>
803 transaction(() {
804 if (!c_indicator(C_MAPPING_VALUE)) return null;
805 return or([
806 () => s_l_blockNode(indent, BLOCK_OUT),
807 () => s_l_comments() ? e_node() : null
808 ]);
809 }));
810
811 // 195
812 _Node ns_l_compactMapping(int indent) => context('mapping', () {
813 var first = ns_l_blockMapEntry(indent);
814 if (!_(first)) return null;
815
816 var pairs = zeroOrMore(() => transaction(() {
817 if (!s_indent(indent)) return null;
818 return ns_l_blockMapEntry(indent);
819 }));
820 pairs.insertRange(0, 1, first);
821
822 return map(pairs);
823 });
824
825 // 196
826 _Node s_l_blockNode(int indent, int ctx) => or([
827 () => s_l_blockInBlock(indent, ctx),
828 () => s_l_flowInBlock(indent)
829 ]);
830
831 // 197
832 _Node s_l_flowInBlock(int indent) => transaction(() {
833 if (!s_separate(indent + 1, FLOW_OUT)) return null;
834 var node = ns_flowNode(indent + 1, FLOW_OUT);
835 if (!_(node)) return null;
836 if (!s_l_comments()) return null;
837 return node;
838 });
839
840 // 198
841 _Node s_l_blockInBlock(int indent, int ctx) => or([
842 () => s_l_blockScalar(indent, ctx),
843 () => s_l_blockCollection(indent, ctx)
844 ]);
845
846 // 199
847 _Node s_l_blockScalar(int indent, int ctx) => transaction(() {
848 if (!s_separate(indent + 1, ctx)) return null;
849 var props = transaction(() {
850 var props = c_ns_properties(indent + 1, ctx);
851 if (!_(props)) return null;
852 if (!s_separate(indent + 1, ctx)) return null;
853 return props;
854 });
855 if (!_(props)) props = new _Pair<_Tag, String>(null, null);
856
857 var node = or([() => c_l_literal(indent), () => c_l_folded(indent)]);
858 if (!_(node)) return null;
859 return addProps(node, props);
860 });
861
862 // 200
863 _Node s_l_blockCollection(int indent, int ctx) => transaction(() {
864 var props = transaction(() {
865 if (!s_separate(indent + 1, ctx)) return null;
866 return c_ns_properties(indent + 1, ctx);
867 });
868 if (!_(props)) props = new _Pair<_Tag, String>(null, null);
869
870 if (!s_l_comments()) return null;
871 return or([
872 () => l_blockSequence(seqSpaces(indent, ctx)),
873 () => l_blockMapping(indent)]);
874 });
875
876 // 201
877 int seqSpaces(int indent, int ctx) => ctx == BLOCK_OUT ? indent - 1 : indent;
878
879 // 202
880 void l_documentPrefix() {
881 zeroOrMore(l_comment);
882 }
883
884 // 203
885 bool c_directivesEnd() => rawString("---");
886
887 // 204
888 bool c_documentEnd() => rawString("...");
889
890 // 205
891 bool l_documentSuffix() => transaction(() {
892 if (!c_documentEnd()) return false;
893 return s_l_comments();
894 });
895
896 // 206
897 bool c_forbidden() {
898 if (!inBareDocument || !atStartOfLine) return false;
899 var forbidden = false;
900 transaction(() {
901 if (!or([c_directivesEnd, c_documentEnd])) return;
902 var char = peek();
903 forbidden = isBreak(char) || isSpace(char) || atEndOfFile;
904 return;
905 });
906 return forbidden;
907 }
908
909 // 207
910 _Node l_bareDocument() {
911 try {
912 inBareDocument = true;
913 return s_l_blockNode(-1, BLOCK_IN);
914 } finally {
915 inBareDocument = false;
916 }
917 }
918
919 // 208
920 _Node l_explicitDocument() {
921 if (!c_directivesEnd()) return null;
922 var doc = l_bareDocument();
923 if (_(doc)) return doc;
924
925 doc = e_node();
926 s_l_comments();
927 return doc;
928 }
929
930 // 209
931 _Node l_directiveDocument() {
932 if (!_(oneOrMore(l_directive))) return null;
933 var doc = l_explicitDocument();
934 if (doc != null) return doc;
935 parseFailed();
936 }
937
938 // 210
939 _Node l_anyDocument() =>
940 or([l_directiveDocument, l_explicitDocument, l_bareDocument]);
941
942 // 211
943 List<_Node> l_yamlStream() {
944 var docs = [];
945 zeroOrMore(l_documentPrefix);
946 var first = zeroOrOne(l_anyDocument);
947 if (!_(first)) first = e_node();
948 docs.add(first);
949
950 zeroOrMore(() {
951 var doc;
952 if (_(oneOrMore(l_documentSuffix))) {
953 zeroOrMore(l_documentPrefix);
954 doc = zeroOrOne(l_anyDocument);
955 } else {
956 zeroOrMore(l_documentPrefix);
957 doc = zeroOrOne(l_explicitDocument);
958 }
959 if (_(doc)) docs.add(doc);
960 return doc;
961 });
962
963 if (!atEndOfFile) parseFailed();
964 return docs;
965 }
966 }
967
968 class SyntaxError extends Error {
969 final int line;
970 final int column;
971
972 SyntaxError(this.line, this.column, String msg) : super(msg);
973
974 String toString() => "Syntax error on line $line, column $column: $msg";
975 }
976
977 /** A pair of values. */
978 class _Pair<E, F> {
979 E first;
980 F last;
981
982 _Pair(this.first, this.last);
983
984 String toString() => '($first, $last)';
985 }
OLDNEW
« no previous file with comments | « utils/yaml/model.dart ('k') | utils/yaml/visitor.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698