OLD | NEW |
| (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 } | |
OLD | NEW |