3 /*eslint-disable max-len,no-use-before-define*/
5 var common = require('./common');
6 var YAMLException = require('./exception');
7 var Mark = require('./mark');
8 var DEFAULT_SAFE_SCHEMA = require('./schema/default_safe');
9 var DEFAULT_FULL_SCHEMA = require('./schema/default_full');
12 var _hasOwnProperty = Object.prototype.hasOwnProperty;
15 var CONTEXT_FLOW_IN = 1;
16 var CONTEXT_FLOW_OUT = 2;
17 var CONTEXT_BLOCK_IN = 3;
18 var CONTEXT_BLOCK_OUT = 4;
21 var CHOMPING_CLIP = 1;
22 var CHOMPING_STRIP = 2;
23 var CHOMPING_KEEP = 3;
26 var PATTERN_NON_PRINTABLE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/;
27 var PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/;
28 var PATTERN_FLOW_INDICATORS = /[,\[\]\{\}]/;
29 var PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i;
30 var PATTERN_TAG_URI = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i;
34 return (c === 0x0A/* LF */) || (c === 0x0D/* CR */);
37 function is_WHITE_SPACE(c) {
38 return (c === 0x09/* Tab */) || (c === 0x20/* Space */);
41 function is_WS_OR_EOL(c) {
42 return (c === 0x09/* Tab */) ||
43 (c === 0x20/* Space */) ||
44 (c === 0x0A/* LF */) ||
48 function is_FLOW_INDICATOR(c) {
49 return c === 0x2C/* , */ ||
56 function fromHexCode(c) {
59 if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {
63 /*eslint-disable no-bitwise*/
66 if ((0x61/* a */ <= lc) && (lc <= 0x66/* f */)) {
67 return lc - 0x61 + 10;
73 function escapedHexLen(c) {
74 if (c === 0x78/* x */) { return 2; }
75 if (c === 0x75/* u */) { return 4; }
76 if (c === 0x55/* U */) { return 8; }
80 function fromDecimalCode(c) {
81 if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {
88 function simpleEscapeSequence(c) {
89 return (c === 0x30/* 0 */) ? '\x00' :
90 (c === 0x61/* a */) ? '\x07' :
91 (c === 0x62/* b */) ? '\x08' :
92 (c === 0x74/* t */) ? '\x09' :
93 (c === 0x09/* Tab */) ? '\x09' :
94 (c === 0x6E/* n */) ? '\x0A' :
95 (c === 0x76/* v */) ? '\x0B' :
96 (c === 0x66/* f */) ? '\x0C' :
97 (c === 0x72/* r */) ? '\x0D' :
98 (c === 0x65/* e */) ? '\x1B' :
99 (c === 0x20/* Space */) ? ' ' :
100 (c === 0x22/* " */) ? '\x22' :
101 (c === 0x2F/* / */) ? '/' :
102 (c === 0x5C/* \ */) ? '\x5C' :
103 (c === 0x4E/* N */) ? '\x85' :
104 (c === 0x5F/* _ */) ? '\xA0' :
105 (c === 0x4C/* L */) ? '\u2028' :
106 (c === 0x50/* P */) ? '\u2029' : '';
109 function charFromCodepoint(c) {
111 return String.fromCharCode(c);
113 // Encode UTF-16 surrogate pair
114 // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF
115 return String.fromCharCode(((c - 0x010000) >> 10) + 0xD800,
116 ((c - 0x010000) & 0x03FF) + 0xDC00);
119 var simpleEscapeCheck = new Array(256); // integer, for fast access
120 var simpleEscapeMap = new Array(256);
121 for (var i = 0; i < 256; i++) {
122 simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0;
123 simpleEscapeMap[i] = simpleEscapeSequence(i);
127 function State(input, options) {
130 this.filename = options['filename'] || null;
131 this.schema = options['schema'] || DEFAULT_FULL_SCHEMA;
132 this.onWarning = options['onWarning'] || null;
133 this.legacy = options['legacy'] || false;
134 this.json = options['json'] || false;
135 this.listener = options['listener'] || null;
137 this.implicitTypes = this.schema.compiledImplicit;
138 this.typeMap = this.schema.compiledTypeMap;
140 this.length = input.length;
150 this.checkLineBreaks;
161 function generateError(state, message) {
162 return new YAMLException(
164 new Mark(state.filename, state.input, state.position, state.line, (state.position - state.lineStart)));
167 function throwError(state, message) {
168 throw generateError(state, message);
171 function throwWarning(state, message) {
172 if (state.onWarning) {
173 state.onWarning.call(null, generateError(state, message));
178 var directiveHandlers = {
180 YAML: function handleYamlDirective(state, name, args) {
182 var match, major, minor;
184 if (state.version !== null) {
185 throwError(state, 'duplication of %YAML directive');
188 if (args.length !== 1) {
189 throwError(state, 'YAML directive accepts exactly one argument');
192 match = /^([0-9]+)\.([0-9]+)$/.exec(args[0]);
194 if (match === null) {
195 throwError(state, 'ill-formed argument of the YAML directive');
198 major = parseInt(match[1], 10);
199 minor = parseInt(match[2], 10);
202 throwError(state, 'unacceptable YAML version of the document');
205 state.version = args[0];
206 state.checkLineBreaks = (minor < 2);
208 if (minor !== 1 && minor !== 2) {
209 throwWarning(state, 'unsupported YAML version of the document');
213 TAG: function handleTagDirective(state, name, args) {
217 if (args.length !== 2) {
218 throwError(state, 'TAG directive accepts exactly two arguments');
224 if (!PATTERN_TAG_HANDLE.test(handle)) {
225 throwError(state, 'ill-formed tag handle (first argument) of the TAG directive');
228 if (_hasOwnProperty.call(state.tagMap, handle)) {
229 throwError(state, 'there is a previously declared suffix for "' + handle + '" tag handle');
232 if (!PATTERN_TAG_URI.test(prefix)) {
233 throwError(state, 'ill-formed tag prefix (second argument) of the TAG directive');
236 state.tagMap[handle] = prefix;
241 function captureSegment(state, start, end, checkJson) {
242 var _position, _length, _character, _result;
245 _result = state.input.slice(start, end);
248 for (_position = 0, _length = _result.length;
251 _character = _result.charCodeAt(_position);
252 if (!(_character === 0x09 ||
253 (0x20 <= _character && _character <= 0x10FFFF))) {
254 throwError(state, 'expected valid JSON character');
257 } else if (PATTERN_NON_PRINTABLE.test(_result)) {
258 throwError(state, 'the stream contains non-printable characters');
261 state.result += _result;
265 function mergeMappings(state, destination, source, overridableKeys) {
266 var sourceKeys, key, index, quantity;
268 if (!common.isObject(source)) {
269 throwError(state, 'cannot merge mappings; the provided source object is unacceptable');
272 sourceKeys = Object.keys(source);
274 for (index = 0, quantity = sourceKeys.length; index < quantity; index += 1) {
275 key = sourceKeys[index];
277 if (!_hasOwnProperty.call(destination, key)) {
278 destination[key] = source[key];
279 overridableKeys[key] = true;
284 function storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, startLine, startPos) {
287 keyNode = String(keyNode);
289 if (_result === null) {
293 if (keyTag === 'tag:yaml.org,2002:merge') {
294 if (Array.isArray(valueNode)) {
295 for (index = 0, quantity = valueNode.length; index < quantity; index += 1) {
296 mergeMappings(state, _result, valueNode[index], overridableKeys);
299 mergeMappings(state, _result, valueNode, overridableKeys);
303 !_hasOwnProperty.call(overridableKeys, keyNode) &&
304 _hasOwnProperty.call(_result, keyNode)) {
305 state.line = startLine || state.line;
306 state.position = startPos || state.position;
307 throwError(state, 'duplicated mapping key');
309 _result[keyNode] = valueNode;
310 delete overridableKeys[keyNode];
316 function readLineBreak(state) {
319 ch = state.input.charCodeAt(state.position);
321 if (ch === 0x0A/* LF */) {
323 } else if (ch === 0x0D/* CR */) {
325 if (state.input.charCodeAt(state.position) === 0x0A/* LF */) {
329 throwError(state, 'a line break is expected');
333 state.lineStart = state.position;
336 function skipSeparationSpace(state, allowComments, checkIndent) {
338 ch = state.input.charCodeAt(state.position);
341 while (is_WHITE_SPACE(ch)) {
342 ch = state.input.charCodeAt(++state.position);
345 if (allowComments && ch === 0x23/* # */) {
347 ch = state.input.charCodeAt(++state.position);
348 } while (ch !== 0x0A/* LF */ && ch !== 0x0D/* CR */ && ch !== 0);
352 readLineBreak(state);
354 ch = state.input.charCodeAt(state.position);
356 state.lineIndent = 0;
358 while (ch === 0x20/* Space */) {
360 ch = state.input.charCodeAt(++state.position);
367 if (checkIndent !== -1 && lineBreaks !== 0 && state.lineIndent < checkIndent) {
368 throwWarning(state, 'deficient indentation');
374 function testDocumentSeparator(state) {
375 var _position = state.position,
378 ch = state.input.charCodeAt(_position);
380 // Condition state.position === state.lineStart is tested
381 // in parent on each call, for efficiency. No needs to test here again.
382 if ((ch === 0x2D/* - */ || ch === 0x2E/* . */) &&
383 ch === state.input.charCodeAt(_position + 1) &&
384 ch === state.input.charCodeAt(_position + 2)) {
388 ch = state.input.charCodeAt(_position);
390 if (ch === 0 || is_WS_OR_EOL(ch)) {
398 function writeFoldedLines(state, count) {
401 } else if (count > 1) {
402 state.result += common.repeat('\n', count - 1);
407 function readPlainScalar(state, nodeIndent, withinFlowCollection) {
417 _result = state.result,
420 ch = state.input.charCodeAt(state.position);
422 if (is_WS_OR_EOL(ch) ||
423 is_FLOW_INDICATOR(ch) ||
424 ch === 0x23/* # */ ||
425 ch === 0x26/* & */ ||
426 ch === 0x2A/* * */ ||
427 ch === 0x21/* ! */ ||
428 ch === 0x7C/* | */ ||
429 ch === 0x3E/* > */ ||
430 ch === 0x27/* ' */ ||
431 ch === 0x22/* " */ ||
432 ch === 0x25/* % */ ||
433 ch === 0x40/* @ */ ||
434 ch === 0x60/* ` */) {
438 if (ch === 0x3F/* ? */ || ch === 0x2D/* - */) {
439 following = state.input.charCodeAt(state.position + 1);
441 if (is_WS_OR_EOL(following) ||
442 withinFlowCollection && is_FLOW_INDICATOR(following)) {
447 state.kind = 'scalar';
449 captureStart = captureEnd = state.position;
450 hasPendingContent = false;
453 if (ch === 0x3A/* : */) {
454 following = state.input.charCodeAt(state.position + 1);
456 if (is_WS_OR_EOL(following) ||
457 withinFlowCollection && is_FLOW_INDICATOR(following)) {
461 } else if (ch === 0x23/* # */) {
462 preceding = state.input.charCodeAt(state.position - 1);
464 if (is_WS_OR_EOL(preceding)) {
468 } else if ((state.position === state.lineStart && testDocumentSeparator(state)) ||
469 withinFlowCollection && is_FLOW_INDICATOR(ch)) {
472 } else if (is_EOL(ch)) {
474 _lineStart = state.lineStart;
475 _lineIndent = state.lineIndent;
476 skipSeparationSpace(state, false, -1);
478 if (state.lineIndent >= nodeIndent) {
479 hasPendingContent = true;
480 ch = state.input.charCodeAt(state.position);
483 state.position = captureEnd;
485 state.lineStart = _lineStart;
486 state.lineIndent = _lineIndent;
491 if (hasPendingContent) {
492 captureSegment(state, captureStart, captureEnd, false);
493 writeFoldedLines(state, state.line - _line);
494 captureStart = captureEnd = state.position;
495 hasPendingContent = false;
498 if (!is_WHITE_SPACE(ch)) {
499 captureEnd = state.position + 1;
502 ch = state.input.charCodeAt(++state.position);
505 captureSegment(state, captureStart, captureEnd, false);
512 state.result = _result;
516 function readSingleQuotedScalar(state, nodeIndent) {
518 captureStart, captureEnd;
520 ch = state.input.charCodeAt(state.position);
522 if (ch !== 0x27/* ' */) {
526 state.kind = 'scalar';
529 captureStart = captureEnd = state.position;
531 while ((ch = state.input.charCodeAt(state.position)) !== 0) {
532 if (ch === 0x27/* ' */) {
533 captureSegment(state, captureStart, state.position, true);
534 ch = state.input.charCodeAt(++state.position);
536 if (ch === 0x27/* ' */) {
537 captureStart = state.position;
539 captureEnd = state.position;
544 } else if (is_EOL(ch)) {
545 captureSegment(state, captureStart, captureEnd, true);
546 writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));
547 captureStart = captureEnd = state.position;
549 } else if (state.position === state.lineStart && testDocumentSeparator(state)) {
550 throwError(state, 'unexpected end of the document within a single quoted scalar');
554 captureEnd = state.position;
558 throwError(state, 'unexpected end of the stream within a single quoted scalar');
561 function readDoubleQuotedScalar(state, nodeIndent) {
569 ch = state.input.charCodeAt(state.position);
571 if (ch !== 0x22/* " */) {
575 state.kind = 'scalar';
578 captureStart = captureEnd = state.position;
580 while ((ch = state.input.charCodeAt(state.position)) !== 0) {
581 if (ch === 0x22/* " */) {
582 captureSegment(state, captureStart, state.position, true);
586 } else if (ch === 0x5C/* \ */) {
587 captureSegment(state, captureStart, state.position, true);
588 ch = state.input.charCodeAt(++state.position);
591 skipSeparationSpace(state, false, nodeIndent);
593 // TODO: rework to inline fn with no type cast?
594 } else if (ch < 256 && simpleEscapeCheck[ch]) {
595 state.result += simpleEscapeMap[ch];
598 } else if ((tmp = escapedHexLen(ch)) > 0) {
602 for (; hexLength > 0; hexLength--) {
603 ch = state.input.charCodeAt(++state.position);
605 if ((tmp = fromHexCode(ch)) >= 0) {
606 hexResult = (hexResult << 4) + tmp;
609 throwError(state, 'expected hexadecimal character');
613 state.result += charFromCodepoint(hexResult);
618 throwError(state, 'unknown escape sequence');
621 captureStart = captureEnd = state.position;
623 } else if (is_EOL(ch)) {
624 captureSegment(state, captureStart, captureEnd, true);
625 writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));
626 captureStart = captureEnd = state.position;
628 } else if (state.position === state.lineStart && testDocumentSeparator(state)) {
629 throwError(state, 'unexpected end of the document within a double quoted scalar');
633 captureEnd = state.position;
637 throwError(state, 'unexpected end of the stream within a double quoted scalar');
640 function readFlowCollection(state, nodeIndent) {
645 _anchor = state.anchor,
651 overridableKeys = {},
657 ch = state.input.charCodeAt(state.position);
659 if (ch === 0x5B/* [ */) {
660 terminator = 0x5D;/* ] */
663 } else if (ch === 0x7B/* { */) {
664 terminator = 0x7D;/* } */
671 if (state.anchor !== null) {
672 state.anchorMap[state.anchor] = _result;
675 ch = state.input.charCodeAt(++state.position);
678 skipSeparationSpace(state, true, nodeIndent);
680 ch = state.input.charCodeAt(state.position);
682 if (ch === terminator) {
685 state.anchor = _anchor;
686 state.kind = isMapping ? 'mapping' : 'sequence';
687 state.result = _result;
689 } else if (!readNext) {
690 throwError(state, 'missed comma between flow collection entries');
693 keyTag = keyNode = valueNode = null;
694 isPair = isExplicitPair = false;
696 if (ch === 0x3F/* ? */) {
697 following = state.input.charCodeAt(state.position + 1);
699 if (is_WS_OR_EOL(following)) {
700 isPair = isExplicitPair = true;
702 skipSeparationSpace(state, true, nodeIndent);
707 composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);
709 keyNode = state.result;
710 skipSeparationSpace(state, true, nodeIndent);
712 ch = state.input.charCodeAt(state.position);
714 if ((isExplicitPair || state.line === _line) && ch === 0x3A/* : */) {
716 ch = state.input.charCodeAt(++state.position);
717 skipSeparationSpace(state, true, nodeIndent);
718 composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);
719 valueNode = state.result;
723 storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode);
725 _result.push(storeMappingPair(state, null, overridableKeys, keyTag, keyNode, valueNode));
727 _result.push(keyNode);
730 skipSeparationSpace(state, true, nodeIndent);
732 ch = state.input.charCodeAt(state.position);
734 if (ch === 0x2C/* , */) {
736 ch = state.input.charCodeAt(++state.position);
742 throwError(state, 'unexpected end of the stream within a flow collection');
745 function readBlockScalar(state, nodeIndent) {
748 chomping = CHOMPING_CLIP,
749 didReadContent = false,
750 detectedIndent = false,
751 textIndent = nodeIndent,
753 atMoreIndented = false,
757 ch = state.input.charCodeAt(state.position);
759 if (ch === 0x7C/* | */) {
761 } else if (ch === 0x3E/* > */) {
767 state.kind = 'scalar';
771 ch = state.input.charCodeAt(++state.position);
773 if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
774 if (CHOMPING_CLIP === chomping) {
775 chomping = (ch === 0x2B/* + */) ? CHOMPING_KEEP : CHOMPING_STRIP;
777 throwError(state, 'repeat of a chomping mode identifier');
780 } else if ((tmp = fromDecimalCode(ch)) >= 0) {
782 throwError(state, 'bad explicit indentation width of a block scalar; it cannot be less than one');
783 } else if (!detectedIndent) {
784 textIndent = nodeIndent + tmp - 1;
785 detectedIndent = true;
787 throwError(state, 'repeat of an indentation width identifier');
795 if (is_WHITE_SPACE(ch)) {
796 do { ch = state.input.charCodeAt(++state.position); }
797 while (is_WHITE_SPACE(ch));
799 if (ch === 0x23/* # */) {
800 do { ch = state.input.charCodeAt(++state.position); }
801 while (!is_EOL(ch) && (ch !== 0));
806 readLineBreak(state);
807 state.lineIndent = 0;
809 ch = state.input.charCodeAt(state.position);
811 while ((!detectedIndent || state.lineIndent < textIndent) &&
812 (ch === 0x20/* Space */)) {
814 ch = state.input.charCodeAt(++state.position);
817 if (!detectedIndent && state.lineIndent > textIndent) {
818 textIndent = state.lineIndent;
826 // End of the scalar.
827 if (state.lineIndent < textIndent) {
829 // Perform the chomping.
830 if (chomping === CHOMPING_KEEP) {
831 state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines);
832 } else if (chomping === CHOMPING_CLIP) {
833 if (didReadContent) { // i.e. only if the scalar is not empty.
834 state.result += '\n';
838 // Break this `while` cycle and go to the funciton's epilogue.
842 // Folded style: use fancy rules to handle line breaks.
845 // Lines starting with white space characters (more-indented lines) are not folded.
846 if (is_WHITE_SPACE(ch)) {
847 atMoreIndented = true;
848 // except for the first content line (cf. Example 8.1)
849 state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines);
851 // End of more-indented block.
852 } else if (atMoreIndented) {
853 atMoreIndented = false;
854 state.result += common.repeat('\n', emptyLines + 1);
856 // Just one line break - perceive as the same line.
857 } else if (emptyLines === 0) {
858 if (didReadContent) { // i.e. only if we have already read some scalar content.
862 // Several line breaks - perceive as different lines.
864 state.result += common.repeat('\n', emptyLines);
867 // Literal style: just add exact number of line breaks between content lines.
869 // Keep all line breaks except the header line break.
870 state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines);
873 didReadContent = true;
874 detectedIndent = true;
876 captureStart = state.position;
878 while (!is_EOL(ch) && (ch !== 0)) {
879 ch = state.input.charCodeAt(++state.position);
882 captureSegment(state, captureStart, state.position, false);
888 function readBlockSequence(state, nodeIndent) {
891 _anchor = state.anchor,
897 if (state.anchor !== null) {
898 state.anchorMap[state.anchor] = _result;
901 ch = state.input.charCodeAt(state.position);
905 if (ch !== 0x2D/* - */) {
909 following = state.input.charCodeAt(state.position + 1);
911 if (!is_WS_OR_EOL(following)) {
918 if (skipSeparationSpace(state, true, -1)) {
919 if (state.lineIndent <= nodeIndent) {
921 ch = state.input.charCodeAt(state.position);
927 composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true);
928 _result.push(state.result);
929 skipSeparationSpace(state, true, -1);
931 ch = state.input.charCodeAt(state.position);
933 if ((state.line === _line || state.lineIndent > nodeIndent) && (ch !== 0)) {
934 throwError(state, 'bad indentation of a sequence entry');
935 } else if (state.lineIndent < nodeIndent) {
942 state.anchor = _anchor;
943 state.kind = 'sequence';
944 state.result = _result;
950 function readBlockMapping(state, nodeIndent, flowIndent) {
956 _anchor = state.anchor,
958 overridableKeys = {},
962 atExplicitKey = false,
966 if (state.anchor !== null) {
967 state.anchorMap[state.anchor] = _result;
970 ch = state.input.charCodeAt(state.position);
973 following = state.input.charCodeAt(state.position + 1);
974 _line = state.line; // Save the current line.
975 _pos = state.position;
978 // Explicit notation case. There are two separate blocks:
979 // first for the key (denoted by "?") and second for the value (denoted by ":")
981 if ((ch === 0x3F/* ? */ || ch === 0x3A/* : */) && is_WS_OR_EOL(following)) {
983 if (ch === 0x3F/* ? */) {
985 storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null);
986 keyTag = keyNode = valueNode = null;
990 atExplicitKey = true;
993 } else if (atExplicitKey) {
994 // i.e. 0x3A/* : */ === character after the explicit key.
995 atExplicitKey = false;
999 throwError(state, 'incomplete explicit mapping pair; a key node is missed');
1002 state.position += 1;
1006 // Implicit notation case. Flow-style node as the key first, then ":", and the value.
1008 } else if (composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) {
1010 if (state.line === _line) {
1011 ch = state.input.charCodeAt(state.position);
1013 while (is_WHITE_SPACE(ch)) {
1014 ch = state.input.charCodeAt(++state.position);
1017 if (ch === 0x3A/* : */) {
1018 ch = state.input.charCodeAt(++state.position);
1020 if (!is_WS_OR_EOL(ch)) {
1021 throwError(state, 'a whitespace character is expected after the key-value separator within a block mapping');
1024 if (atExplicitKey) {
1025 storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null);
1026 keyTag = keyNode = valueNode = null;
1030 atExplicitKey = false;
1031 allowCompact = false;
1033 keyNode = state.result;
1035 } else if (detected) {
1036 throwError(state, 'can not read an implicit mapping pair; a colon is missed');
1040 state.anchor = _anchor;
1041 return true; // Keep the result of `composeNode`.
1044 } else if (detected) {
1045 throwError(state, 'can not read a block mapping entry; a multiline key may not be an implicit key');
1049 state.anchor = _anchor;
1050 return true; // Keep the result of `composeNode`.
1054 break; // Reading is done. Go to the epilogue.
1058 // Common reading code for both explicit and implicit notations.
1060 if (state.line === _line || state.lineIndent > nodeIndent) {
1061 if (composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact)) {
1062 if (atExplicitKey) {
1063 keyNode = state.result;
1065 valueNode = state.result;
1069 if (!atExplicitKey) {
1070 storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _line, _pos);
1071 keyTag = keyNode = valueNode = null;
1074 skipSeparationSpace(state, true, -1);
1075 ch = state.input.charCodeAt(state.position);
1078 if (state.lineIndent > nodeIndent && (ch !== 0)) {
1079 throwError(state, 'bad indentation of a mapping entry');
1080 } else if (state.lineIndent < nodeIndent) {
1089 // Special case: last mapping's node contains only the key in explicit notation.
1090 if (atExplicitKey) {
1091 storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null);
1094 // Expose the resulting mapping.
1097 state.anchor = _anchor;
1098 state.kind = 'mapping';
1099 state.result = _result;
1105 function readTagProperty(state) {
1113 ch = state.input.charCodeAt(state.position);
1115 if (ch !== 0x21/* ! */) return false;
1117 if (state.tag !== null) {
1118 throwError(state, 'duplication of a tag property');
1121 ch = state.input.charCodeAt(++state.position);
1123 if (ch === 0x3C/* < */) {
1125 ch = state.input.charCodeAt(++state.position);
1127 } else if (ch === 0x21/* ! */) {
1130 ch = state.input.charCodeAt(++state.position);
1136 _position = state.position;
1139 do { ch = state.input.charCodeAt(++state.position); }
1140 while (ch !== 0 && ch !== 0x3E/* > */);
1142 if (state.position < state.length) {
1143 tagName = state.input.slice(_position, state.position);
1144 ch = state.input.charCodeAt(++state.position);
1146 throwError(state, 'unexpected end of the stream within a verbatim tag');
1149 while (ch !== 0 && !is_WS_OR_EOL(ch)) {
1151 if (ch === 0x21/* ! */) {
1153 tagHandle = state.input.slice(_position - 1, state.position + 1);
1155 if (!PATTERN_TAG_HANDLE.test(tagHandle)) {
1156 throwError(state, 'named tag handle cannot contain such characters');
1160 _position = state.position + 1;
1162 throwError(state, 'tag suffix cannot contain exclamation marks');
1166 ch = state.input.charCodeAt(++state.position);
1169 tagName = state.input.slice(_position, state.position);
1171 if (PATTERN_FLOW_INDICATORS.test(tagName)) {
1172 throwError(state, 'tag suffix cannot contain flow indicator characters');
1176 if (tagName && !PATTERN_TAG_URI.test(tagName)) {
1177 throwError(state, 'tag name cannot contain such characters: ' + tagName);
1181 state.tag = tagName;
1183 } else if (_hasOwnProperty.call(state.tagMap, tagHandle)) {
1184 state.tag = state.tagMap[tagHandle] + tagName;
1186 } else if (tagHandle === '!') {
1187 state.tag = '!' + tagName;
1189 } else if (tagHandle === '!!') {
1190 state.tag = 'tag:yaml.org,2002:' + tagName;
1193 throwError(state, 'undeclared tag handle "' + tagHandle + '"');
1199 function readAnchorProperty(state) {
1203 ch = state.input.charCodeAt(state.position);
1205 if (ch !== 0x26/* & */) return false;
1207 if (state.anchor !== null) {
1208 throwError(state, 'duplication of an anchor property');
1211 ch = state.input.charCodeAt(++state.position);
1212 _position = state.position;
1214 while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {
1215 ch = state.input.charCodeAt(++state.position);
1218 if (state.position === _position) {
1219 throwError(state, 'name of an anchor node must contain at least one character');
1222 state.anchor = state.input.slice(_position, state.position);
1226 function readAlias(state) {
1227 var _position, alias,
1230 ch = state.input.charCodeAt(state.position);
1232 if (ch !== 0x2A/* * */) return false;
1234 ch = state.input.charCodeAt(++state.position);
1235 _position = state.position;
1237 while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {
1238 ch = state.input.charCodeAt(++state.position);
1241 if (state.position === _position) {
1242 throwError(state, 'name of an alias node must contain at least one character');
1245 alias = state.input.slice(_position, state.position);
1247 if (!state.anchorMap.hasOwnProperty(alias)) {
1248 throwError(state, 'unidentified alias "' + alias + '"');
1251 state.result = state.anchorMap[alias];
1252 skipSeparationSpace(state, true, -1);
1256 function composeNode(state, parentIndent, nodeContext, allowToSeek, allowCompact) {
1257 var allowBlockStyles,
1259 allowBlockCollections,
1260 indentStatus = 1, // 1: this>parent, 0: this=parent, -1: this<parent
1269 if (state.listener !== null) {
1270 state.listener('open', state);
1274 state.anchor = null;
1276 state.result = null;
1278 allowBlockStyles = allowBlockScalars = allowBlockCollections =
1279 CONTEXT_BLOCK_OUT === nodeContext ||
1280 CONTEXT_BLOCK_IN === nodeContext;
1283 if (skipSeparationSpace(state, true, -1)) {
1286 if (state.lineIndent > parentIndent) {
1288 } else if (state.lineIndent === parentIndent) {
1290 } else if (state.lineIndent < parentIndent) {
1296 if (indentStatus === 1) {
1297 while (readTagProperty(state) || readAnchorProperty(state)) {
1298 if (skipSeparationSpace(state, true, -1)) {
1300 allowBlockCollections = allowBlockStyles;
1302 if (state.lineIndent > parentIndent) {
1304 } else if (state.lineIndent === parentIndent) {
1306 } else if (state.lineIndent < parentIndent) {
1310 allowBlockCollections = false;
1315 if (allowBlockCollections) {
1316 allowBlockCollections = atNewLine || allowCompact;
1319 if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) {
1320 if (CONTEXT_FLOW_IN === nodeContext || CONTEXT_FLOW_OUT === nodeContext) {
1321 flowIndent = parentIndent;
1323 flowIndent = parentIndent + 1;
1326 blockIndent = state.position - state.lineStart;
1328 if (indentStatus === 1) {
1329 if (allowBlockCollections &&
1330 (readBlockSequence(state, blockIndent) ||
1331 readBlockMapping(state, blockIndent, flowIndent)) ||
1332 readFlowCollection(state, flowIndent)) {
1335 if ((allowBlockScalars && readBlockScalar(state, flowIndent)) ||
1336 readSingleQuotedScalar(state, flowIndent) ||
1337 readDoubleQuotedScalar(state, flowIndent)) {
1340 } else if (readAlias(state)) {
1343 if (state.tag !== null || state.anchor !== null) {
1344 throwError(state, 'alias node should not have any properties');
1347 } else if (readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext)) {
1350 if (state.tag === null) {
1355 if (state.anchor !== null) {
1356 state.anchorMap[state.anchor] = state.result;
1359 } else if (indentStatus === 0) {
1360 // Special case: block sequences are allowed to have same indentation level as the parent.
1361 // http://www.yaml.org/spec/1.2/spec.html#id2799784
1362 hasContent = allowBlockCollections && readBlockSequence(state, blockIndent);
1366 if (state.tag !== null && state.tag !== '!') {
1367 if (state.tag === '?') {
1368 for (typeIndex = 0, typeQuantity = state.implicitTypes.length;
1369 typeIndex < typeQuantity;
1371 type = state.implicitTypes[typeIndex];
1373 // Implicit resolving is not allowed for non-scalar types, and '?'
1374 // non-specific tag is only assigned to plain scalars. So, it isn't
1375 // needed to check for 'kind' conformity.
1377 if (type.resolve(state.result)) { // `state.result` updated in resolver if matched
1378 state.result = type.construct(state.result);
1379 state.tag = type.tag;
1380 if (state.anchor !== null) {
1381 state.anchorMap[state.anchor] = state.result;
1386 } else if (_hasOwnProperty.call(state.typeMap[state.kind || 'fallback'], state.tag)) {
1387 type = state.typeMap[state.kind || 'fallback'][state.tag];
1389 if (state.result !== null && type.kind !== state.kind) {
1390 throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be "' + type.kind + '", not "' + state.kind + '"');
1393 if (!type.resolve(state.result)) { // `state.result` updated in resolver if matched
1394 throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag');
1396 state.result = type.construct(state.result);
1397 if (state.anchor !== null) {
1398 state.anchorMap[state.anchor] = state.result;
1402 throwError(state, 'unknown tag !<' + state.tag + '>');
1406 if (state.listener !== null) {
1407 state.listener('close', state);
1409 return state.tag !== null || state.anchor !== null || hasContent;
1412 function readDocument(state) {
1413 var documentStart = state.position,
1417 hasDirectives = false,
1420 state.version = null;
1421 state.checkLineBreaks = state.legacy;
1423 state.anchorMap = {};
1425 while ((ch = state.input.charCodeAt(state.position)) !== 0) {
1426 skipSeparationSpace(state, true, -1);
1428 ch = state.input.charCodeAt(state.position);
1430 if (state.lineIndent > 0 || ch !== 0x25/* % */) {
1434 hasDirectives = true;
1435 ch = state.input.charCodeAt(++state.position);
1436 _position = state.position;
1438 while (ch !== 0 && !is_WS_OR_EOL(ch)) {
1439 ch = state.input.charCodeAt(++state.position);
1442 directiveName = state.input.slice(_position, state.position);
1445 if (directiveName.length < 1) {
1446 throwError(state, 'directive name must not be less than one character in length');
1450 while (is_WHITE_SPACE(ch)) {
1451 ch = state.input.charCodeAt(++state.position);
1454 if (ch === 0x23/* # */) {
1455 do { ch = state.input.charCodeAt(++state.position); }
1456 while (ch !== 0 && !is_EOL(ch));
1460 if (is_EOL(ch)) break;
1462 _position = state.position;
1464 while (ch !== 0 && !is_WS_OR_EOL(ch)) {
1465 ch = state.input.charCodeAt(++state.position);
1468 directiveArgs.push(state.input.slice(_position, state.position));
1471 if (ch !== 0) readLineBreak(state);
1473 if (_hasOwnProperty.call(directiveHandlers, directiveName)) {
1474 directiveHandlers[directiveName](state, directiveName, directiveArgs);
1476 throwWarning(state, 'unknown document directive "' + directiveName + '"');
1480 skipSeparationSpace(state, true, -1);
1482 if (state.lineIndent === 0 &&
1483 state.input.charCodeAt(state.position) === 0x2D/* - */ &&
1484 state.input.charCodeAt(state.position + 1) === 0x2D/* - */ &&
1485 state.input.charCodeAt(state.position + 2) === 0x2D/* - */) {
1486 state.position += 3;
1487 skipSeparationSpace(state, true, -1);
1489 } else if (hasDirectives) {
1490 throwError(state, 'directives end mark is expected');
1493 composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true);
1494 skipSeparationSpace(state, true, -1);
1496 if (state.checkLineBreaks &&
1497 PATTERN_NON_ASCII_LINE_BREAKS.test(state.input.slice(documentStart, state.position))) {
1498 throwWarning(state, 'non-ASCII line breaks are interpreted as content');
1501 state.documents.push(state.result);
1503 if (state.position === state.lineStart && testDocumentSeparator(state)) {
1505 if (state.input.charCodeAt(state.position) === 0x2E/* . */) {
1506 state.position += 3;
1507 skipSeparationSpace(state, true, -1);
1512 if (state.position < (state.length - 1)) {
1513 throwError(state, 'end of the stream or a document separator is expected');
1520 function loadDocuments(input, options) {
1521 input = String(input);
1522 options = options || {};
1524 if (input.length !== 0) {
1526 // Add tailing `\n` if not exists
1527 if (input.charCodeAt(input.length - 1) !== 0x0A/* LF */ &&
1528 input.charCodeAt(input.length - 1) !== 0x0D/* CR */) {
1533 if (input.charCodeAt(0) === 0xFEFF) {
1534 input = input.slice(1);
1538 var state = new State(input, options);
1540 // Use 0 as string terminator. That significantly simplifies bounds check.
1541 state.input += '\0';
1543 while (state.input.charCodeAt(state.position) === 0x20/* Space */) {
1544 state.lineIndent += 1;
1545 state.position += 1;
1548 while (state.position < (state.length - 1)) {
1549 readDocument(state);
1552 return state.documents;
1556 function loadAll(input, iterator, options) {
1557 var documents = loadDocuments(input, options), index, length;
1559 for (index = 0, length = documents.length; index < length; index += 1) {
1560 iterator(documents[index]);
1565 function load(input, options) {
1566 var documents = loadDocuments(input, options);
1568 if (documents.length === 0) {
1569 /*eslint-disable no-undefined*/
1571 } else if (documents.length === 1) {
1572 return documents[0];
1574 throw new YAMLException('expected a single document in the stream, but found more');
1578 function safeLoadAll(input, output, options) {
1579 loadAll(input, output, common.extend({ schema: DEFAULT_SAFE_SCHEMA }, options));
1583 function safeLoad(input, options) {
1584 return load(input, common.extend({ schema: DEFAULT_SAFE_SCHEMA }, options));
1588 module.exports.loadAll = loadAll;
1589 module.exports.load = load;
1590 module.exports.safeLoadAll = safeLoadAll;
1591 module.exports.safeLoad = safeLoad;