UNPKG

47.1 kBJavaScriptView Raw
1'use strict';
2
3/*eslint-disable max-len,no-use-before-define*/
4
5var common = require('./common');
6var YAMLException = require('./exception');
7var makeSnippet = require('./snippet');
8var DEFAULT_SCHEMA = require('./schema/default');
9
10
11var _hasOwnProperty = Object.prototype.hasOwnProperty;
12
13
14var CONTEXT_FLOW_IN = 1;
15var CONTEXT_FLOW_OUT = 2;
16var CONTEXT_BLOCK_IN = 3;
17var CONTEXT_BLOCK_OUT = 4;
18
19
20var CHOMPING_CLIP = 1;
21var CHOMPING_STRIP = 2;
22var CHOMPING_KEEP = 3;
23
24
25var PATTERN_NON_PRINTABLE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/;
26var PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/;
27var PATTERN_FLOW_INDICATORS = /[,\[\]\{\}]/;
28var PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i;
29var PATTERN_TAG_URI = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i;
30
31
32function _class(obj) { return Object.prototype.toString.call(obj); }
33
34function is_EOL(c) {
35 return (c === 0x0A/* LF */) || (c === 0x0D/* CR */);
36}
37
38function is_WHITE_SPACE(c) {
39 return (c === 0x09/* Tab */) || (c === 0x20/* Space */);
40}
41
42function is_WS_OR_EOL(c) {
43 return (c === 0x09/* Tab */) ||
44 (c === 0x20/* Space */) ||
45 (c === 0x0A/* LF */) ||
46 (c === 0x0D/* CR */);
47}
48
49function is_FLOW_INDICATOR(c) {
50 return c === 0x2C/* , */ ||
51 c === 0x5B/* [ */ ||
52 c === 0x5D/* ] */ ||
53 c === 0x7B/* { */ ||
54 c === 0x7D/* } */;
55}
56
57function fromHexCode(c) {
58 var lc;
59
60 if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {
61 return c - 0x30;
62 }
63
64 /*eslint-disable no-bitwise*/
65 lc = c | 0x20;
66
67 if ((0x61/* a */ <= lc) && (lc <= 0x66/* f */)) {
68 return lc - 0x61 + 10;
69 }
70
71 return -1;
72}
73
74function escapedHexLen(c) {
75 if (c === 0x78/* x */) { return 2; }
76 if (c === 0x75/* u */) { return 4; }
77 if (c === 0x55/* U */) { return 8; }
78 return 0;
79}
80
81function fromDecimalCode(c) {
82 if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {
83 return c - 0x30;
84 }
85
86 return -1;
87}
88
89function simpleEscapeSequence(c) {
90 /* eslint-disable indent */
91 return (c === 0x30/* 0 */) ? '\x00' :
92 (c === 0x61/* a */) ? '\x07' :
93 (c === 0x62/* b */) ? '\x08' :
94 (c === 0x74/* t */) ? '\x09' :
95 (c === 0x09/* Tab */) ? '\x09' :
96 (c === 0x6E/* n */) ? '\x0A' :
97 (c === 0x76/* v */) ? '\x0B' :
98 (c === 0x66/* f */) ? '\x0C' :
99 (c === 0x72/* r */) ? '\x0D' :
100 (c === 0x65/* e */) ? '\x1B' :
101 (c === 0x20/* Space */) ? ' ' :
102 (c === 0x22/* " */) ? '\x22' :
103 (c === 0x2F/* / */) ? '/' :
104 (c === 0x5C/* \ */) ? '\x5C' :
105 (c === 0x4E/* N */) ? '\x85' :
106 (c === 0x5F/* _ */) ? '\xA0' :
107 (c === 0x4C/* L */) ? '\u2028' :
108 (c === 0x50/* P */) ? '\u2029' : '';
109}
110
111function charFromCodepoint(c) {
112 if (c <= 0xFFFF) {
113 return String.fromCharCode(c);
114 }
115 // Encode UTF-16 surrogate pair
116 // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF
117 return String.fromCharCode(
118 ((c - 0x010000) >> 10) + 0xD800,
119 ((c - 0x010000) & 0x03FF) + 0xDC00
120 );
121}
122
123var simpleEscapeCheck = new Array(256); // integer, for fast access
124var simpleEscapeMap = new Array(256);
125for (var i = 0; i < 256; i++) {
126 simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0;
127 simpleEscapeMap[i] = simpleEscapeSequence(i);
128}
129
130
131function State(input, options) {
132 this.input = input;
133
134 this.filename = options['filename'] || null;
135 this.schema = options['schema'] || DEFAULT_SCHEMA;
136 this.onWarning = options['onWarning'] || null;
137 // (Hidden) Remove? makes the loader to expect YAML 1.1 documents
138 // if such documents have no explicit %YAML directive
139 this.legacy = options['legacy'] || false;
140
141 this.json = options['json'] || false;
142 this.listener = options['listener'] || null;
143
144 this.implicitTypes = this.schema.compiledImplicit;
145 this.typeMap = this.schema.compiledTypeMap;
146
147 this.length = input.length;
148 this.position = 0;
149 this.line = 0;
150 this.lineStart = 0;
151 this.lineIndent = 0;
152
153 // position of first leading tab in the current line,
154 // used to make sure there are no tabs in the indentation
155 this.firstTabInLine = -1;
156
157 this.documents = [];
158
159 /*
160 this.version;
161 this.checkLineBreaks;
162 this.tagMap;
163 this.anchorMap;
164 this.tag;
165 this.anchor;
166 this.kind;
167 this.result;*/
168
169}
170
171
172function generateError(state, message) {
173 var mark = {
174 name: state.filename,
175 buffer: state.input.slice(0, -1), // omit trailing \0
176 position: state.position,
177 line: state.line,
178 column: state.position - state.lineStart
179 };
180
181 mark.snippet = makeSnippet(mark);
182
183 return new YAMLException(message, mark);
184}
185
186function throwError(state, message) {
187 throw generateError(state, message);
188}
189
190function throwWarning(state, message) {
191 if (state.onWarning) {
192 state.onWarning.call(null, generateError(state, message));
193 }
194}
195
196
197var directiveHandlers = {
198
199 YAML: function handleYamlDirective(state, name, args) {
200
201 var match, major, minor;
202
203 if (state.version !== null) {
204 throwError(state, 'duplication of %YAML directive');
205 }
206
207 if (args.length !== 1) {
208 throwError(state, 'YAML directive accepts exactly one argument');
209 }
210
211 match = /^([0-9]+)\.([0-9]+)$/.exec(args[0]);
212
213 if (match === null) {
214 throwError(state, 'ill-formed argument of the YAML directive');
215 }
216
217 major = parseInt(match[1], 10);
218 minor = parseInt(match[2], 10);
219
220 if (major !== 1) {
221 throwError(state, 'unacceptable YAML version of the document');
222 }
223
224 state.version = args[0];
225 state.checkLineBreaks = (minor < 2);
226
227 if (minor !== 1 && minor !== 2) {
228 throwWarning(state, 'unsupported YAML version of the document');
229 }
230 },
231
232 TAG: function handleTagDirective(state, name, args) {
233
234 var handle, prefix;
235
236 if (args.length !== 2) {
237 throwError(state, 'TAG directive accepts exactly two arguments');
238 }
239
240 handle = args[0];
241 prefix = args[1];
242
243 if (!PATTERN_TAG_HANDLE.test(handle)) {
244 throwError(state, 'ill-formed tag handle (first argument) of the TAG directive');
245 }
246
247 if (_hasOwnProperty.call(state.tagMap, handle)) {
248 throwError(state, 'there is a previously declared suffix for "' + handle + '" tag handle');
249 }
250
251 if (!PATTERN_TAG_URI.test(prefix)) {
252 throwError(state, 'ill-formed tag prefix (second argument) of the TAG directive');
253 }
254
255 try {
256 prefix = decodeURIComponent(prefix);
257 } catch (err) {
258 throwError(state, 'tag prefix is malformed: ' + prefix);
259 }
260
261 state.tagMap[handle] = prefix;
262 }
263};
264
265
266function captureSegment(state, start, end, checkJson) {
267 var _position, _length, _character, _result;
268
269 if (start < end) {
270 _result = state.input.slice(start, end);
271
272 if (checkJson) {
273 for (_position = 0, _length = _result.length; _position < _length; _position += 1) {
274 _character = _result.charCodeAt(_position);
275 if (!(_character === 0x09 ||
276 (0x20 <= _character && _character <= 0x10FFFF))) {
277 throwError(state, 'expected valid JSON character');
278 }
279 }
280 } else if (PATTERN_NON_PRINTABLE.test(_result)) {
281 throwError(state, 'the stream contains non-printable characters');
282 }
283
284 state.result += _result;
285 }
286}
287
288function mergeMappings(state, destination, source, overridableKeys) {
289 var sourceKeys, key, index, quantity;
290
291 if (!common.isObject(source)) {
292 throwError(state, 'cannot merge mappings; the provided source object is unacceptable');
293 }
294
295 sourceKeys = Object.keys(source);
296
297 for (index = 0, quantity = sourceKeys.length; index < quantity; index += 1) {
298 key = sourceKeys[index];
299
300 if (!_hasOwnProperty.call(destination, key)) {
301 destination[key] = source[key];
302 overridableKeys[key] = true;
303 }
304 }
305}
306
307function storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode,
308 startLine, startLineStart, startPos) {
309
310 var index, quantity;
311
312 // The output is a plain object here, so keys can only be strings.
313 // We need to convert keyNode to a string, but doing so can hang the process
314 // (deeply nested arrays that explode exponentially using aliases).
315 if (Array.isArray(keyNode)) {
316 keyNode = Array.prototype.slice.call(keyNode);
317
318 for (index = 0, quantity = keyNode.length; index < quantity; index += 1) {
319 if (Array.isArray(keyNode[index])) {
320 throwError(state, 'nested arrays are not supported inside keys');
321 }
322
323 if (typeof keyNode === 'object' && _class(keyNode[index]) === '[object Object]') {
324 keyNode[index] = '[object Object]';
325 }
326 }
327 }
328
329 // Avoid code execution in load() via toString property
330 // (still use its own toString for arrays, timestamps,
331 // and whatever user schema extensions happen to have @@toStringTag)
332 if (typeof keyNode === 'object' && _class(keyNode) === '[object Object]') {
333 keyNode = '[object Object]';
334 }
335
336
337 keyNode = String(keyNode);
338
339 if (_result === null) {
340 _result = {};
341 }
342
343 if (keyTag === 'tag:yaml.org,2002:merge') {
344 if (Array.isArray(valueNode)) {
345 for (index = 0, quantity = valueNode.length; index < quantity; index += 1) {
346 mergeMappings(state, _result, valueNode[index], overridableKeys);
347 }
348 } else {
349 mergeMappings(state, _result, valueNode, overridableKeys);
350 }
351 } else {
352 if (!state.json &&
353 !_hasOwnProperty.call(overridableKeys, keyNode) &&
354 _hasOwnProperty.call(_result, keyNode)) {
355 state.line = startLine || state.line;
356 state.lineStart = startLineStart || state.lineStart;
357 state.position = startPos || state.position;
358 throwError(state, 'duplicated mapping key');
359 }
360
361 // used for this specific key only because Object.defineProperty is slow
362 if (keyNode === '__proto__') {
363 Object.defineProperty(_result, keyNode, {
364 configurable: true,
365 enumerable: true,
366 writable: true,
367 value: valueNode
368 });
369 } else {
370 _result[keyNode] = valueNode;
371 }
372 delete overridableKeys[keyNode];
373 }
374
375 return _result;
376}
377
378function readLineBreak(state) {
379 var ch;
380
381 ch = state.input.charCodeAt(state.position);
382
383 if (ch === 0x0A/* LF */) {
384 state.position++;
385 } else if (ch === 0x0D/* CR */) {
386 state.position++;
387 if (state.input.charCodeAt(state.position) === 0x0A/* LF */) {
388 state.position++;
389 }
390 } else {
391 throwError(state, 'a line break is expected');
392 }
393
394 state.line += 1;
395 state.lineStart = state.position;
396 state.firstTabInLine = -1;
397}
398
399function skipSeparationSpace(state, allowComments, checkIndent) {
400 var lineBreaks = 0,
401 ch = state.input.charCodeAt(state.position);
402
403 while (ch !== 0) {
404 while (is_WHITE_SPACE(ch)) {
405 if (ch === 0x09/* Tab */ && state.firstTabInLine === -1) {
406 state.firstTabInLine = state.position;
407 }
408 ch = state.input.charCodeAt(++state.position);
409 }
410
411 if (allowComments && ch === 0x23/* # */) {
412 do {
413 ch = state.input.charCodeAt(++state.position);
414 } while (ch !== 0x0A/* LF */ && ch !== 0x0D/* CR */ && ch !== 0);
415 }
416
417 if (is_EOL(ch)) {
418 readLineBreak(state);
419
420 ch = state.input.charCodeAt(state.position);
421 lineBreaks++;
422 state.lineIndent = 0;
423
424 while (ch === 0x20/* Space */) {
425 state.lineIndent++;
426 ch = state.input.charCodeAt(++state.position);
427 }
428 } else {
429 break;
430 }
431 }
432
433 if (checkIndent !== -1 && lineBreaks !== 0 && state.lineIndent < checkIndent) {
434 throwWarning(state, 'deficient indentation');
435 }
436
437 return lineBreaks;
438}
439
440function testDocumentSeparator(state) {
441 var _position = state.position,
442 ch;
443
444 ch = state.input.charCodeAt(_position);
445
446 // Condition state.position === state.lineStart is tested
447 // in parent on each call, for efficiency. No needs to test here again.
448 if ((ch === 0x2D/* - */ || ch === 0x2E/* . */) &&
449 ch === state.input.charCodeAt(_position + 1) &&
450 ch === state.input.charCodeAt(_position + 2)) {
451
452 _position += 3;
453
454 ch = state.input.charCodeAt(_position);
455
456 if (ch === 0 || is_WS_OR_EOL(ch)) {
457 return true;
458 }
459 }
460
461 return false;
462}
463
464function writeFoldedLines(state, count) {
465 if (count === 1) {
466 state.result += ' ';
467 } else if (count > 1) {
468 state.result += common.repeat('\n', count - 1);
469 }
470}
471
472
473function readPlainScalar(state, nodeIndent, withinFlowCollection) {
474 var preceding,
475 following,
476 captureStart,
477 captureEnd,
478 hasPendingContent,
479 _line,
480 _lineStart,
481 _lineIndent,
482 _kind = state.kind,
483 _result = state.result,
484 ch;
485
486 ch = state.input.charCodeAt(state.position);
487
488 if (is_WS_OR_EOL(ch) ||
489 is_FLOW_INDICATOR(ch) ||
490 ch === 0x23/* # */ ||
491 ch === 0x26/* & */ ||
492 ch === 0x2A/* * */ ||
493 ch === 0x21/* ! */ ||
494 ch === 0x7C/* | */ ||
495 ch === 0x3E/* > */ ||
496 ch === 0x27/* ' */ ||
497 ch === 0x22/* " */ ||
498 ch === 0x25/* % */ ||
499 ch === 0x40/* @ */ ||
500 ch === 0x60/* ` */) {
501 return false;
502 }
503
504 if (ch === 0x3F/* ? */ || ch === 0x2D/* - */) {
505 following = state.input.charCodeAt(state.position + 1);
506
507 if (is_WS_OR_EOL(following) ||
508 withinFlowCollection && is_FLOW_INDICATOR(following)) {
509 return false;
510 }
511 }
512
513 state.kind = 'scalar';
514 state.result = '';
515 captureStart = captureEnd = state.position;
516 hasPendingContent = false;
517
518 while (ch !== 0) {
519 if (ch === 0x3A/* : */) {
520 following = state.input.charCodeAt(state.position + 1);
521
522 if (is_WS_OR_EOL(following) ||
523 withinFlowCollection && is_FLOW_INDICATOR(following)) {
524 break;
525 }
526
527 } else if (ch === 0x23/* # */) {
528 preceding = state.input.charCodeAt(state.position - 1);
529
530 if (is_WS_OR_EOL(preceding)) {
531 break;
532 }
533
534 } else if ((state.position === state.lineStart && testDocumentSeparator(state)) ||
535 withinFlowCollection && is_FLOW_INDICATOR(ch)) {
536 break;
537
538 } else if (is_EOL(ch)) {
539 _line = state.line;
540 _lineStart = state.lineStart;
541 _lineIndent = state.lineIndent;
542 skipSeparationSpace(state, false, -1);
543
544 if (state.lineIndent >= nodeIndent) {
545 hasPendingContent = true;
546 ch = state.input.charCodeAt(state.position);
547 continue;
548 } else {
549 state.position = captureEnd;
550 state.line = _line;
551 state.lineStart = _lineStart;
552 state.lineIndent = _lineIndent;
553 break;
554 }
555 }
556
557 if (hasPendingContent) {
558 captureSegment(state, captureStart, captureEnd, false);
559 writeFoldedLines(state, state.line - _line);
560 captureStart = captureEnd = state.position;
561 hasPendingContent = false;
562 }
563
564 if (!is_WHITE_SPACE(ch)) {
565 captureEnd = state.position + 1;
566 }
567
568 ch = state.input.charCodeAt(++state.position);
569 }
570
571 captureSegment(state, captureStart, captureEnd, false);
572
573 if (state.result) {
574 return true;
575 }
576
577 state.kind = _kind;
578 state.result = _result;
579 return false;
580}
581
582function readSingleQuotedScalar(state, nodeIndent) {
583 var ch,
584 captureStart, captureEnd;
585
586 ch = state.input.charCodeAt(state.position);
587
588 if (ch !== 0x27/* ' */) {
589 return false;
590 }
591
592 state.kind = 'scalar';
593 state.result = '';
594 state.position++;
595 captureStart = captureEnd = state.position;
596
597 while ((ch = state.input.charCodeAt(state.position)) !== 0) {
598 if (ch === 0x27/* ' */) {
599 captureSegment(state, captureStart, state.position, true);
600 ch = state.input.charCodeAt(++state.position);
601
602 if (ch === 0x27/* ' */) {
603 captureStart = state.position;
604 state.position++;
605 captureEnd = state.position;
606 } else {
607 return true;
608 }
609
610 } else if (is_EOL(ch)) {
611 captureSegment(state, captureStart, captureEnd, true);
612 writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));
613 captureStart = captureEnd = state.position;
614
615 } else if (state.position === state.lineStart && testDocumentSeparator(state)) {
616 throwError(state, 'unexpected end of the document within a single quoted scalar');
617
618 } else {
619 state.position++;
620 captureEnd = state.position;
621 }
622 }
623
624 throwError(state, 'unexpected end of the stream within a single quoted scalar');
625}
626
627function readDoubleQuotedScalar(state, nodeIndent) {
628 var captureStart,
629 captureEnd,
630 hexLength,
631 hexResult,
632 tmp,
633 ch;
634
635 ch = state.input.charCodeAt(state.position);
636
637 if (ch !== 0x22/* " */) {
638 return false;
639 }
640
641 state.kind = 'scalar';
642 state.result = '';
643 state.position++;
644 captureStart = captureEnd = state.position;
645
646 while ((ch = state.input.charCodeAt(state.position)) !== 0) {
647 if (ch === 0x22/* " */) {
648 captureSegment(state, captureStart, state.position, true);
649 state.position++;
650 return true;
651
652 } else if (ch === 0x5C/* \ */) {
653 captureSegment(state, captureStart, state.position, true);
654 ch = state.input.charCodeAt(++state.position);
655
656 if (is_EOL(ch)) {
657 skipSeparationSpace(state, false, nodeIndent);
658
659 // TODO: rework to inline fn with no type cast?
660 } else if (ch < 256 && simpleEscapeCheck[ch]) {
661 state.result += simpleEscapeMap[ch];
662 state.position++;
663
664 } else if ((tmp = escapedHexLen(ch)) > 0) {
665 hexLength = tmp;
666 hexResult = 0;
667
668 for (; hexLength > 0; hexLength--) {
669 ch = state.input.charCodeAt(++state.position);
670
671 if ((tmp = fromHexCode(ch)) >= 0) {
672 hexResult = (hexResult << 4) + tmp;
673
674 } else {
675 throwError(state, 'expected hexadecimal character');
676 }
677 }
678
679 state.result += charFromCodepoint(hexResult);
680
681 state.position++;
682
683 } else {
684 throwError(state, 'unknown escape sequence');
685 }
686
687 captureStart = captureEnd = state.position;
688
689 } else if (is_EOL(ch)) {
690 captureSegment(state, captureStart, captureEnd, true);
691 writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));
692 captureStart = captureEnd = state.position;
693
694 } else if (state.position === state.lineStart && testDocumentSeparator(state)) {
695 throwError(state, 'unexpected end of the document within a double quoted scalar');
696
697 } else {
698 state.position++;
699 captureEnd = state.position;
700 }
701 }
702
703 throwError(state, 'unexpected end of the stream within a double quoted scalar');
704}
705
706function readFlowCollection(state, nodeIndent) {
707 var readNext = true,
708 _line,
709 _lineStart,
710 _pos,
711 _tag = state.tag,
712 _result,
713 _anchor = state.anchor,
714 following,
715 terminator,
716 isPair,
717 isExplicitPair,
718 isMapping,
719 overridableKeys = Object.create(null),
720 keyNode,
721 keyTag,
722 valueNode,
723 ch;
724
725 ch = state.input.charCodeAt(state.position);
726
727 if (ch === 0x5B/* [ */) {
728 terminator = 0x5D;/* ] */
729 isMapping = false;
730 _result = [];
731 } else if (ch === 0x7B/* { */) {
732 terminator = 0x7D;/* } */
733 isMapping = true;
734 _result = {};
735 } else {
736 return false;
737 }
738
739 if (state.anchor !== null) {
740 state.anchorMap[state.anchor] = _result;
741 }
742
743 ch = state.input.charCodeAt(++state.position);
744
745 while (ch !== 0) {
746 skipSeparationSpace(state, true, nodeIndent);
747
748 ch = state.input.charCodeAt(state.position);
749
750 if (ch === terminator) {
751 state.position++;
752 state.tag = _tag;
753 state.anchor = _anchor;
754 state.kind = isMapping ? 'mapping' : 'sequence';
755 state.result = _result;
756 return true;
757 } else if (!readNext) {
758 throwError(state, 'missed comma between flow collection entries');
759 } else if (ch === 0x2C/* , */) {
760 // "flow collection entries can never be completely empty", as per YAML 1.2, section 7.4
761 throwError(state, "expected the node content, but found ','");
762 }
763
764 keyTag = keyNode = valueNode = null;
765 isPair = isExplicitPair = false;
766
767 if (ch === 0x3F/* ? */) {
768 following = state.input.charCodeAt(state.position + 1);
769
770 if (is_WS_OR_EOL(following)) {
771 isPair = isExplicitPair = true;
772 state.position++;
773 skipSeparationSpace(state, true, nodeIndent);
774 }
775 }
776
777 _line = state.line; // Save the current line.
778 _lineStart = state.lineStart;
779 _pos = state.position;
780 composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);
781 keyTag = state.tag;
782 keyNode = state.result;
783 skipSeparationSpace(state, true, nodeIndent);
784
785 ch = state.input.charCodeAt(state.position);
786
787 if ((isExplicitPair || state.line === _line) && ch === 0x3A/* : */) {
788 isPair = true;
789 ch = state.input.charCodeAt(++state.position);
790 skipSeparationSpace(state, true, nodeIndent);
791 composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);
792 valueNode = state.result;
793 }
794
795 if (isMapping) {
796 storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _line, _lineStart, _pos);
797 } else if (isPair) {
798 _result.push(storeMappingPair(state, null, overridableKeys, keyTag, keyNode, valueNode, _line, _lineStart, _pos));
799 } else {
800 _result.push(keyNode);
801 }
802
803 skipSeparationSpace(state, true, nodeIndent);
804
805 ch = state.input.charCodeAt(state.position);
806
807 if (ch === 0x2C/* , */) {
808 readNext = true;
809 ch = state.input.charCodeAt(++state.position);
810 } else {
811 readNext = false;
812 }
813 }
814
815 throwError(state, 'unexpected end of the stream within a flow collection');
816}
817
818function readBlockScalar(state, nodeIndent) {
819 var captureStart,
820 folding,
821 chomping = CHOMPING_CLIP,
822 didReadContent = false,
823 detectedIndent = false,
824 textIndent = nodeIndent,
825 emptyLines = 0,
826 atMoreIndented = false,
827 tmp,
828 ch;
829
830 ch = state.input.charCodeAt(state.position);
831
832 if (ch === 0x7C/* | */) {
833 folding = false;
834 } else if (ch === 0x3E/* > */) {
835 folding = true;
836 } else {
837 return false;
838 }
839
840 state.kind = 'scalar';
841 state.result = '';
842
843 while (ch !== 0) {
844 ch = state.input.charCodeAt(++state.position);
845
846 if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
847 if (CHOMPING_CLIP === chomping) {
848 chomping = (ch === 0x2B/* + */) ? CHOMPING_KEEP : CHOMPING_STRIP;
849 } else {
850 throwError(state, 'repeat of a chomping mode identifier');
851 }
852
853 } else if ((tmp = fromDecimalCode(ch)) >= 0) {
854 if (tmp === 0) {
855 throwError(state, 'bad explicit indentation width of a block scalar; it cannot be less than one');
856 } else if (!detectedIndent) {
857 textIndent = nodeIndent + tmp - 1;
858 detectedIndent = true;
859 } else {
860 throwError(state, 'repeat of an indentation width identifier');
861 }
862
863 } else {
864 break;
865 }
866 }
867
868 if (is_WHITE_SPACE(ch)) {
869 do { ch = state.input.charCodeAt(++state.position); }
870 while (is_WHITE_SPACE(ch));
871
872 if (ch === 0x23/* # */) {
873 do { ch = state.input.charCodeAt(++state.position); }
874 while (!is_EOL(ch) && (ch !== 0));
875 }
876 }
877
878 while (ch !== 0) {
879 readLineBreak(state);
880 state.lineIndent = 0;
881
882 ch = state.input.charCodeAt(state.position);
883
884 while ((!detectedIndent || state.lineIndent < textIndent) &&
885 (ch === 0x20/* Space */)) {
886 state.lineIndent++;
887 ch = state.input.charCodeAt(++state.position);
888 }
889
890 if (!detectedIndent && state.lineIndent > textIndent) {
891 textIndent = state.lineIndent;
892 }
893
894 if (is_EOL(ch)) {
895 emptyLines++;
896 continue;
897 }
898
899 // End of the scalar.
900 if (state.lineIndent < textIndent) {
901
902 // Perform the chomping.
903 if (chomping === CHOMPING_KEEP) {
904 state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines);
905 } else if (chomping === CHOMPING_CLIP) {
906 if (didReadContent) { // i.e. only if the scalar is not empty.
907 state.result += '\n';
908 }
909 }
910
911 // Break this `while` cycle and go to the funciton's epilogue.
912 break;
913 }
914
915 // Folded style: use fancy rules to handle line breaks.
916 if (folding) {
917
918 // Lines starting with white space characters (more-indented lines) are not folded.
919 if (is_WHITE_SPACE(ch)) {
920 atMoreIndented = true;
921 // except for the first content line (cf. Example 8.1)
922 state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines);
923
924 // End of more-indented block.
925 } else if (atMoreIndented) {
926 atMoreIndented = false;
927 state.result += common.repeat('\n', emptyLines + 1);
928
929 // Just one line break - perceive as the same line.
930 } else if (emptyLines === 0) {
931 if (didReadContent) { // i.e. only if we have already read some scalar content.
932 state.result += ' ';
933 }
934
935 // Several line breaks - perceive as different lines.
936 } else {
937 state.result += common.repeat('\n', emptyLines);
938 }
939
940 // Literal style: just add exact number of line breaks between content lines.
941 } else {
942 // Keep all line breaks except the header line break.
943 state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines);
944 }
945
946 didReadContent = true;
947 detectedIndent = true;
948 emptyLines = 0;
949 captureStart = state.position;
950
951 while (!is_EOL(ch) && (ch !== 0)) {
952 ch = state.input.charCodeAt(++state.position);
953 }
954
955 captureSegment(state, captureStart, state.position, false);
956 }
957
958 return true;
959}
960
961function readBlockSequence(state, nodeIndent) {
962 var _line,
963 _tag = state.tag,
964 _anchor = state.anchor,
965 _result = [],
966 following,
967 detected = false,
968 ch;
969
970 // there is a leading tab before this token, so it can't be a block sequence/mapping;
971 // it can still be flow sequence/mapping or a scalar
972 if (state.firstTabInLine !== -1) return false;
973
974 if (state.anchor !== null) {
975 state.anchorMap[state.anchor] = _result;
976 }
977
978 ch = state.input.charCodeAt(state.position);
979
980 while (ch !== 0) {
981 if (state.firstTabInLine !== -1) {
982 state.position = state.firstTabInLine;
983 throwError(state, 'tab characters must not be used in indentation');
984 }
985
986 if (ch !== 0x2D/* - */) {
987 break;
988 }
989
990 following = state.input.charCodeAt(state.position + 1);
991
992 if (!is_WS_OR_EOL(following)) {
993 break;
994 }
995
996 detected = true;
997 state.position++;
998
999 if (skipSeparationSpace(state, true, -1)) {
1000 if (state.lineIndent <= nodeIndent) {
1001 _result.push(null);
1002 ch = state.input.charCodeAt(state.position);
1003 continue;
1004 }
1005 }
1006
1007 _line = state.line;
1008 composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true);
1009 _result.push(state.result);
1010 skipSeparationSpace(state, true, -1);
1011
1012 ch = state.input.charCodeAt(state.position);
1013
1014 if ((state.line === _line || state.lineIndent > nodeIndent) && (ch !== 0)) {
1015 throwError(state, 'bad indentation of a sequence entry');
1016 } else if (state.lineIndent < nodeIndent) {
1017 break;
1018 }
1019 }
1020
1021 if (detected) {
1022 state.tag = _tag;
1023 state.anchor = _anchor;
1024 state.kind = 'sequence';
1025 state.result = _result;
1026 return true;
1027 }
1028 return false;
1029}
1030
1031function readBlockMapping(state, nodeIndent, flowIndent) {
1032 var following,
1033 allowCompact,
1034 _line,
1035 _keyLine,
1036 _keyLineStart,
1037 _keyPos,
1038 _tag = state.tag,
1039 _anchor = state.anchor,
1040 _result = {},
1041 overridableKeys = Object.create(null),
1042 keyTag = null,
1043 keyNode = null,
1044 valueNode = null,
1045 atExplicitKey = false,
1046 detected = false,
1047 ch;
1048
1049 // there is a leading tab before this token, so it can't be a block sequence/mapping;
1050 // it can still be flow sequence/mapping or a scalar
1051 if (state.firstTabInLine !== -1) return false;
1052
1053 if (state.anchor !== null) {
1054 state.anchorMap[state.anchor] = _result;
1055 }
1056
1057 ch = state.input.charCodeAt(state.position);
1058
1059 while (ch !== 0) {
1060 if (!atExplicitKey && state.firstTabInLine !== -1) {
1061 state.position = state.firstTabInLine;
1062 throwError(state, 'tab characters must not be used in indentation');
1063 }
1064
1065 following = state.input.charCodeAt(state.position + 1);
1066 _line = state.line; // Save the current line.
1067
1068 //
1069 // Explicit notation case. There are two separate blocks:
1070 // first for the key (denoted by "?") and second for the value (denoted by ":")
1071 //
1072 if ((ch === 0x3F/* ? */ || ch === 0x3A/* : */) && is_WS_OR_EOL(following)) {
1073
1074 if (ch === 0x3F/* ? */) {
1075 if (atExplicitKey) {
1076 storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos);
1077 keyTag = keyNode = valueNode = null;
1078 }
1079
1080 detected = true;
1081 atExplicitKey = true;
1082 allowCompact = true;
1083
1084 } else if (atExplicitKey) {
1085 // i.e. 0x3A/* : */ === character after the explicit key.
1086 atExplicitKey = false;
1087 allowCompact = true;
1088
1089 } else {
1090 throwError(state, 'incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line');
1091 }
1092
1093 state.position += 1;
1094 ch = following;
1095
1096 //
1097 // Implicit notation case. Flow-style node as the key first, then ":", and the value.
1098 //
1099 } else {
1100 _keyLine = state.line;
1101 _keyLineStart = state.lineStart;
1102 _keyPos = state.position;
1103
1104 if (!composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) {
1105 // Neither implicit nor explicit notation.
1106 // Reading is done. Go to the epilogue.
1107 break;
1108 }
1109
1110 if (state.line === _line) {
1111 ch = state.input.charCodeAt(state.position);
1112
1113 while (is_WHITE_SPACE(ch)) {
1114 ch = state.input.charCodeAt(++state.position);
1115 }
1116
1117 if (ch === 0x3A/* : */) {
1118 ch = state.input.charCodeAt(++state.position);
1119
1120 if (!is_WS_OR_EOL(ch)) {
1121 throwError(state, 'a whitespace character is expected after the key-value separator within a block mapping');
1122 }
1123
1124 if (atExplicitKey) {
1125 storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos);
1126 keyTag = keyNode = valueNode = null;
1127 }
1128
1129 detected = true;
1130 atExplicitKey = false;
1131 allowCompact = false;
1132 keyTag = state.tag;
1133 keyNode = state.result;
1134
1135 } else if (detected) {
1136 throwError(state, 'can not read an implicit mapping pair; a colon is missed');
1137
1138 } else {
1139 state.tag = _tag;
1140 state.anchor = _anchor;
1141 return true; // Keep the result of `composeNode`.
1142 }
1143
1144 } else if (detected) {
1145 throwError(state, 'can not read a block mapping entry; a multiline key may not be an implicit key');
1146
1147 } else {
1148 state.tag = _tag;
1149 state.anchor = _anchor;
1150 return true; // Keep the result of `composeNode`.
1151 }
1152 }
1153
1154 //
1155 // Common reading code for both explicit and implicit notations.
1156 //
1157 if (state.line === _line || state.lineIndent > nodeIndent) {
1158 if (atExplicitKey) {
1159 _keyLine = state.line;
1160 _keyLineStart = state.lineStart;
1161 _keyPos = state.position;
1162 }
1163
1164 if (composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact)) {
1165 if (atExplicitKey) {
1166 keyNode = state.result;
1167 } else {
1168 valueNode = state.result;
1169 }
1170 }
1171
1172 if (!atExplicitKey) {
1173 storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _keyLine, _keyLineStart, _keyPos);
1174 keyTag = keyNode = valueNode = null;
1175 }
1176
1177 skipSeparationSpace(state, true, -1);
1178 ch = state.input.charCodeAt(state.position);
1179 }
1180
1181 if ((state.line === _line || state.lineIndent > nodeIndent) && (ch !== 0)) {
1182 throwError(state, 'bad indentation of a mapping entry');
1183 } else if (state.lineIndent < nodeIndent) {
1184 break;
1185 }
1186 }
1187
1188 //
1189 // Epilogue.
1190 //
1191
1192 // Special case: last mapping's node contains only the key in explicit notation.
1193 if (atExplicitKey) {
1194 storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos);
1195 }
1196
1197 // Expose the resulting mapping.
1198 if (detected) {
1199 state.tag = _tag;
1200 state.anchor = _anchor;
1201 state.kind = 'mapping';
1202 state.result = _result;
1203 }
1204
1205 return detected;
1206}
1207
1208function readTagProperty(state) {
1209 var _position,
1210 isVerbatim = false,
1211 isNamed = false,
1212 tagHandle,
1213 tagName,
1214 ch;
1215
1216 ch = state.input.charCodeAt(state.position);
1217
1218 if (ch !== 0x21/* ! */) return false;
1219
1220 if (state.tag !== null) {
1221 throwError(state, 'duplication of a tag property');
1222 }
1223
1224 ch = state.input.charCodeAt(++state.position);
1225
1226 if (ch === 0x3C/* < */) {
1227 isVerbatim = true;
1228 ch = state.input.charCodeAt(++state.position);
1229
1230 } else if (ch === 0x21/* ! */) {
1231 isNamed = true;
1232 tagHandle = '!!';
1233 ch = state.input.charCodeAt(++state.position);
1234
1235 } else {
1236 tagHandle = '!';
1237 }
1238
1239 _position = state.position;
1240
1241 if (isVerbatim) {
1242 do { ch = state.input.charCodeAt(++state.position); }
1243 while (ch !== 0 && ch !== 0x3E/* > */);
1244
1245 if (state.position < state.length) {
1246 tagName = state.input.slice(_position, state.position);
1247 ch = state.input.charCodeAt(++state.position);
1248 } else {
1249 throwError(state, 'unexpected end of the stream within a verbatim tag');
1250 }
1251 } else {
1252 while (ch !== 0 && !is_WS_OR_EOL(ch)) {
1253
1254 if (ch === 0x21/* ! */) {
1255 if (!isNamed) {
1256 tagHandle = state.input.slice(_position - 1, state.position + 1);
1257
1258 if (!PATTERN_TAG_HANDLE.test(tagHandle)) {
1259 throwError(state, 'named tag handle cannot contain such characters');
1260 }
1261
1262 isNamed = true;
1263 _position = state.position + 1;
1264 } else {
1265 throwError(state, 'tag suffix cannot contain exclamation marks');
1266 }
1267 }
1268
1269 ch = state.input.charCodeAt(++state.position);
1270 }
1271
1272 tagName = state.input.slice(_position, state.position);
1273
1274 if (PATTERN_FLOW_INDICATORS.test(tagName)) {
1275 throwError(state, 'tag suffix cannot contain flow indicator characters');
1276 }
1277 }
1278
1279 if (tagName && !PATTERN_TAG_URI.test(tagName)) {
1280 throwError(state, 'tag name cannot contain such characters: ' + tagName);
1281 }
1282
1283 try {
1284 tagName = decodeURIComponent(tagName);
1285 } catch (err) {
1286 throwError(state, 'tag name is malformed: ' + tagName);
1287 }
1288
1289 if (isVerbatim) {
1290 state.tag = tagName;
1291
1292 } else if (_hasOwnProperty.call(state.tagMap, tagHandle)) {
1293 state.tag = state.tagMap[tagHandle] + tagName;
1294
1295 } else if (tagHandle === '!') {
1296 state.tag = '!' + tagName;
1297
1298 } else if (tagHandle === '!!') {
1299 state.tag = 'tag:yaml.org,2002:' + tagName;
1300
1301 } else {
1302 throwError(state, 'undeclared tag handle "' + tagHandle + '"');
1303 }
1304
1305 return true;
1306}
1307
1308function readAnchorProperty(state) {
1309 var _position,
1310 ch;
1311
1312 ch = state.input.charCodeAt(state.position);
1313
1314 if (ch !== 0x26/* & */) return false;
1315
1316 if (state.anchor !== null) {
1317 throwError(state, 'duplication of an anchor property');
1318 }
1319
1320 ch = state.input.charCodeAt(++state.position);
1321 _position = state.position;
1322
1323 while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {
1324 ch = state.input.charCodeAt(++state.position);
1325 }
1326
1327 if (state.position === _position) {
1328 throwError(state, 'name of an anchor node must contain at least one character');
1329 }
1330
1331 state.anchor = state.input.slice(_position, state.position);
1332 return true;
1333}
1334
1335function readAlias(state) {
1336 var _position, alias,
1337 ch;
1338
1339 ch = state.input.charCodeAt(state.position);
1340
1341 if (ch !== 0x2A/* * */) return false;
1342
1343 ch = state.input.charCodeAt(++state.position);
1344 _position = state.position;
1345
1346 while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {
1347 ch = state.input.charCodeAt(++state.position);
1348 }
1349
1350 if (state.position === _position) {
1351 throwError(state, 'name of an alias node must contain at least one character');
1352 }
1353
1354 alias = state.input.slice(_position, state.position);
1355
1356 if (!_hasOwnProperty.call(state.anchorMap, alias)) {
1357 throwError(state, 'unidentified alias "' + alias + '"');
1358 }
1359
1360 state.result = state.anchorMap[alias];
1361 skipSeparationSpace(state, true, -1);
1362 return true;
1363}
1364
1365function composeNode(state, parentIndent, nodeContext, allowToSeek, allowCompact) {
1366 var allowBlockStyles,
1367 allowBlockScalars,
1368 allowBlockCollections,
1369 indentStatus = 1, // 1: this>parent, 0: this=parent, -1: this<parent
1370 atNewLine = false,
1371 hasContent = false,
1372 typeIndex,
1373 typeQuantity,
1374 typeList,
1375 type,
1376 flowIndent,
1377 blockIndent;
1378
1379 if (state.listener !== null) {
1380 state.listener('open', state);
1381 }
1382
1383 state.tag = null;
1384 state.anchor = null;
1385 state.kind = null;
1386 state.result = null;
1387
1388 allowBlockStyles = allowBlockScalars = allowBlockCollections =
1389 CONTEXT_BLOCK_OUT === nodeContext ||
1390 CONTEXT_BLOCK_IN === nodeContext;
1391
1392 if (allowToSeek) {
1393 if (skipSeparationSpace(state, true, -1)) {
1394 atNewLine = true;
1395
1396 if (state.lineIndent > parentIndent) {
1397 indentStatus = 1;
1398 } else if (state.lineIndent === parentIndent) {
1399 indentStatus = 0;
1400 } else if (state.lineIndent < parentIndent) {
1401 indentStatus = -1;
1402 }
1403 }
1404 }
1405
1406 if (indentStatus === 1) {
1407 while (readTagProperty(state) || readAnchorProperty(state)) {
1408 if (skipSeparationSpace(state, true, -1)) {
1409 atNewLine = true;
1410 allowBlockCollections = allowBlockStyles;
1411
1412 if (state.lineIndent > parentIndent) {
1413 indentStatus = 1;
1414 } else if (state.lineIndent === parentIndent) {
1415 indentStatus = 0;
1416 } else if (state.lineIndent < parentIndent) {
1417 indentStatus = -1;
1418 }
1419 } else {
1420 allowBlockCollections = false;
1421 }
1422 }
1423 }
1424
1425 if (allowBlockCollections) {
1426 allowBlockCollections = atNewLine || allowCompact;
1427 }
1428
1429 if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) {
1430 if (CONTEXT_FLOW_IN === nodeContext || CONTEXT_FLOW_OUT === nodeContext) {
1431 flowIndent = parentIndent;
1432 } else {
1433 flowIndent = parentIndent + 1;
1434 }
1435
1436 blockIndent = state.position - state.lineStart;
1437
1438 if (indentStatus === 1) {
1439 if (allowBlockCollections &&
1440 (readBlockSequence(state, blockIndent) ||
1441 readBlockMapping(state, blockIndent, flowIndent)) ||
1442 readFlowCollection(state, flowIndent)) {
1443 hasContent = true;
1444 } else {
1445 if ((allowBlockScalars && readBlockScalar(state, flowIndent)) ||
1446 readSingleQuotedScalar(state, flowIndent) ||
1447 readDoubleQuotedScalar(state, flowIndent)) {
1448 hasContent = true;
1449
1450 } else if (readAlias(state)) {
1451 hasContent = true;
1452
1453 if (state.tag !== null || state.anchor !== null) {
1454 throwError(state, 'alias node should not have any properties');
1455 }
1456
1457 } else if (readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext)) {
1458 hasContent = true;
1459
1460 if (state.tag === null) {
1461 state.tag = '?';
1462 }
1463 }
1464
1465 if (state.anchor !== null) {
1466 state.anchorMap[state.anchor] = state.result;
1467 }
1468 }
1469 } else if (indentStatus === 0) {
1470 // Special case: block sequences are allowed to have same indentation level as the parent.
1471 // http://www.yaml.org/spec/1.2/spec.html#id2799784
1472 hasContent = allowBlockCollections && readBlockSequence(state, blockIndent);
1473 }
1474 }
1475
1476 if (state.tag === null) {
1477 if (state.anchor !== null) {
1478 state.anchorMap[state.anchor] = state.result;
1479 }
1480
1481 } else if (state.tag === '?') {
1482 // Implicit resolving is not allowed for non-scalar types, and '?'
1483 // non-specific tag is only automatically assigned to plain scalars.
1484 //
1485 // We only need to check kind conformity in case user explicitly assigns '?'
1486 // tag, for example like this: "!<?> [0]"
1487 //
1488 if (state.result !== null && state.kind !== 'scalar') {
1489 throwError(state, 'unacceptable node kind for !<?> tag; it should be "scalar", not "' + state.kind + '"');
1490 }
1491
1492 for (typeIndex = 0, typeQuantity = state.implicitTypes.length; typeIndex < typeQuantity; typeIndex += 1) {
1493 type = state.implicitTypes[typeIndex];
1494
1495 if (type.resolve(state.result)) { // `state.result` updated in resolver if matched
1496 state.result = type.construct(state.result);
1497 state.tag = type.tag;
1498 if (state.anchor !== null) {
1499 state.anchorMap[state.anchor] = state.result;
1500 }
1501 break;
1502 }
1503 }
1504 } else if (state.tag !== '!') {
1505 if (_hasOwnProperty.call(state.typeMap[state.kind || 'fallback'], state.tag)) {
1506 type = state.typeMap[state.kind || 'fallback'][state.tag];
1507 } else {
1508 // looking for multi type
1509 type = null;
1510 typeList = state.typeMap.multi[state.kind || 'fallback'];
1511
1512 for (typeIndex = 0, typeQuantity = typeList.length; typeIndex < typeQuantity; typeIndex += 1) {
1513 if (state.tag.slice(0, typeList[typeIndex].tag.length) === typeList[typeIndex].tag) {
1514 type = typeList[typeIndex];
1515 break;
1516 }
1517 }
1518 }
1519
1520 if (!type) {
1521 throwError(state, 'unknown tag !<' + state.tag + '>');
1522 }
1523
1524 if (state.result !== null && type.kind !== state.kind) {
1525 throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be "' + type.kind + '", not "' + state.kind + '"');
1526 }
1527
1528 if (!type.resolve(state.result, state.tag)) { // `state.result` updated in resolver if matched
1529 throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag');
1530 } else {
1531 state.result = type.construct(state.result, state.tag);
1532 if (state.anchor !== null) {
1533 state.anchorMap[state.anchor] = state.result;
1534 }
1535 }
1536 }
1537
1538 if (state.listener !== null) {
1539 state.listener('close', state);
1540 }
1541 return state.tag !== null || state.anchor !== null || hasContent;
1542}
1543
1544function readDocument(state) {
1545 var documentStart = state.position,
1546 _position,
1547 directiveName,
1548 directiveArgs,
1549 hasDirectives = false,
1550 ch;
1551
1552 state.version = null;
1553 state.checkLineBreaks = state.legacy;
1554 state.tagMap = Object.create(null);
1555 state.anchorMap = Object.create(null);
1556
1557 while ((ch = state.input.charCodeAt(state.position)) !== 0) {
1558 skipSeparationSpace(state, true, -1);
1559
1560 ch = state.input.charCodeAt(state.position);
1561
1562 if (state.lineIndent > 0 || ch !== 0x25/* % */) {
1563 break;
1564 }
1565
1566 hasDirectives = true;
1567 ch = state.input.charCodeAt(++state.position);
1568 _position = state.position;
1569
1570 while (ch !== 0 && !is_WS_OR_EOL(ch)) {
1571 ch = state.input.charCodeAt(++state.position);
1572 }
1573
1574 directiveName = state.input.slice(_position, state.position);
1575 directiveArgs = [];
1576
1577 if (directiveName.length < 1) {
1578 throwError(state, 'directive name must not be less than one character in length');
1579 }
1580
1581 while (ch !== 0) {
1582 while (is_WHITE_SPACE(ch)) {
1583 ch = state.input.charCodeAt(++state.position);
1584 }
1585
1586 if (ch === 0x23/* # */) {
1587 do { ch = state.input.charCodeAt(++state.position); }
1588 while (ch !== 0 && !is_EOL(ch));
1589 break;
1590 }
1591
1592 if (is_EOL(ch)) break;
1593
1594 _position = state.position;
1595
1596 while (ch !== 0 && !is_WS_OR_EOL(ch)) {
1597 ch = state.input.charCodeAt(++state.position);
1598 }
1599
1600 directiveArgs.push(state.input.slice(_position, state.position));
1601 }
1602
1603 if (ch !== 0) readLineBreak(state);
1604
1605 if (_hasOwnProperty.call(directiveHandlers, directiveName)) {
1606 directiveHandlers[directiveName](state, directiveName, directiveArgs);
1607 } else {
1608 throwWarning(state, 'unknown document directive "' + directiveName + '"');
1609 }
1610 }
1611
1612 skipSeparationSpace(state, true, -1);
1613
1614 if (state.lineIndent === 0 &&
1615 state.input.charCodeAt(state.position) === 0x2D/* - */ &&
1616 state.input.charCodeAt(state.position + 1) === 0x2D/* - */ &&
1617 state.input.charCodeAt(state.position + 2) === 0x2D/* - */) {
1618 state.position += 3;
1619 skipSeparationSpace(state, true, -1);
1620
1621 } else if (hasDirectives) {
1622 throwError(state, 'directives end mark is expected');
1623 }
1624
1625 composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true);
1626 skipSeparationSpace(state, true, -1);
1627
1628 if (state.checkLineBreaks &&
1629 PATTERN_NON_ASCII_LINE_BREAKS.test(state.input.slice(documentStart, state.position))) {
1630 throwWarning(state, 'non-ASCII line breaks are interpreted as content');
1631 }
1632
1633 state.documents.push(state.result);
1634
1635 if (state.position === state.lineStart && testDocumentSeparator(state)) {
1636
1637 if (state.input.charCodeAt(state.position) === 0x2E/* . */) {
1638 state.position += 3;
1639 skipSeparationSpace(state, true, -1);
1640 }
1641 return;
1642 }
1643
1644 if (state.position < (state.length - 1)) {
1645 throwError(state, 'end of the stream or a document separator is expected');
1646 } else {
1647 return;
1648 }
1649}
1650
1651
1652function loadDocuments(input, options) {
1653 input = String(input);
1654 options = options || {};
1655
1656 if (input.length !== 0) {
1657
1658 // Add tailing `\n` if not exists
1659 if (input.charCodeAt(input.length - 1) !== 0x0A/* LF */ &&
1660 input.charCodeAt(input.length - 1) !== 0x0D/* CR */) {
1661 input += '\n';
1662 }
1663
1664 // Strip BOM
1665 if (input.charCodeAt(0) === 0xFEFF) {
1666 input = input.slice(1);
1667 }
1668 }
1669
1670 var state = new State(input, options);
1671
1672 var nullpos = input.indexOf('\0');
1673
1674 if (nullpos !== -1) {
1675 state.position = nullpos;
1676 throwError(state, 'null byte is not allowed in input');
1677 }
1678
1679 // Use 0 as string terminator. That significantly simplifies bounds check.
1680 state.input += '\0';
1681
1682 while (state.input.charCodeAt(state.position) === 0x20/* Space */) {
1683 state.lineIndent += 1;
1684 state.position += 1;
1685 }
1686
1687 while (state.position < (state.length - 1)) {
1688 readDocument(state);
1689 }
1690
1691 return state.documents;
1692}
1693
1694
1695function loadAll(input, iterator, options) {
1696 if (iterator !== null && typeof iterator === 'object' && typeof options === 'undefined') {
1697 options = iterator;
1698 iterator = null;
1699 }
1700
1701 var documents = loadDocuments(input, options);
1702
1703 if (typeof iterator !== 'function') {
1704 return documents;
1705 }
1706
1707 for (var index = 0, length = documents.length; index < length; index += 1) {
1708 iterator(documents[index]);
1709 }
1710}
1711
1712
1713function load(input, options) {
1714 var documents = loadDocuments(input, options);
1715
1716 if (documents.length === 0) {
1717 /*eslint-disable no-undefined*/
1718 return undefined;
1719 } else if (documents.length === 1) {
1720 return documents[0];
1721 }
1722 throw new YAMLException('expected a single document in the stream, but found more');
1723}
1724
1725
1726module.exports.loadAll = loadAll;
1727module.exports.load = load;