4 var common = require('./common');
5 var YAMLException = require('./exception');
6 var Mark = require('./mark');
7 var DEFAULT_SAFE_SCHEMA = require('./schema/default_safe');
8 var DEFAULT_FULL_SCHEMA = require('./schema/default_full');
11 var _hasOwnProperty = Object.prototype.hasOwnProperty;
14 var CONTEXT_FLOW_IN = 1;
15 var CONTEXT_FLOW_OUT = 2;
16 var CONTEXT_BLOCK_IN = 3;
17 var CONTEXT_BLOCK_OUT = 4;
20 var CHOMPING_CLIP = 1;
21 var CHOMPING_STRIP = 2;
22 var CHOMPING_KEEP = 3;
25 var PATTERN_NON_PRINTABLE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uD800-\uDFFF\uFFFE\uFFFF]/;
26 var PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/;
27 var PATTERN_FLOW_INDICATORS = /[,\[\]\{\}]/;
28 var PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i;
29 var PATTERN_TAG_URI = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i;
33 return (c === 0x0A/* LF */) || (c === 0x0D/* CR */);
36 function is_WHITE_SPACE(c) {
37 return (c === 0x09/* Tab */) || (c === 0x20/* Space */);
40 function is_WS_OR_EOL(c) {
41 return (c === 0x09/* Tab */) ||
42 (c === 0x20/* Space */) ||
43 (c === 0x0A/* LF */) ||
47 function is_FLOW_INDICATOR(c) {
48 return 0x2C/* , */ === c ||
55 function fromHexCode(c) {
58 if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {
63 if ((0x61/* a */ <= lc) && (lc <= 0x66/* f */)) {
64 return lc - 0x61 + 10;
70 function escapedHexLen(c) {
71 if (c === 0x78/* x */) { return 2; }
72 if (c === 0x75/* u */) { return 4; }
73 if (c === 0x55/* U */) { return 8; }
77 function fromDecimalCode(c) {
78 if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {
85 function simpleEscapeSequence(c) {
86 return (c === 0x30/* 0 */) ? '\x00' :
87 (c === 0x61/* a */) ? '\x07' :
88 (c === 0x62/* b */) ? '\x08' :
89 (c === 0x74/* t */) ? '\x09' :
90 (c === 0x09/* Tab */) ? '\x09' :
91 (c === 0x6E/* n */) ? '\x0A' :
92 (c === 0x76/* v */) ? '\x0B' :
93 (c === 0x66/* f */) ? '\x0C' :
94 (c === 0x72/* r */) ? '\x0D' :
95 (c === 0x65/* e */) ? '\x1B' :
96 (c === 0x20/* Space */) ? ' ' :
97 (c === 0x22/* " */) ? '\x22' :
98 (c === 0x2F/* / */) ? '/' :
99 (c === 0x5C/* \ */) ? '\x5C' :
100 (c === 0x4E/* N */) ? '\x85' :
101 (c === 0x5F/* _ */) ? '\xA0' :
102 (c === 0x4C/* L */) ? '\u2028' :
103 (c === 0x50/* P */) ? '\u2029' : '';
106 function charFromCodepoint(c) {
108 return String.fromCharCode(c);
110 // Encode UTF-16 surrogate pair
111 // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF
112 return String.fromCharCode(((c - 0x010000) >> 10) + 0xD800,
113 ((c - 0x010000) & 0x03FF) + 0xDC00);
117 var simpleEscapeCheck = new Array(256); // integer, for fast access
118 var simpleEscapeMap = new Array(256);
119 for (var i = 0; i < 256; i++) {
120 simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0;
121 simpleEscapeMap[i] = simpleEscapeSequence(i);
125 function State(input, options) {
128 this.filename = options['filename'] || null;
129 this.schema = options['schema'] || DEFAULT_FULL_SCHEMA;
130 this.onWarning = options['onWarning'] || null;
131 this.legacy = options['legacy'] || false;
133 this.implicitTypes = this.schema.compiledImplicit;
134 this.typeMap = this.schema.compiledTypeMap;
136 this.length = input.length;
146 this.checkLineBreaks;
157 function generateError(state, message) {
158 return new YAMLException(
160 new Mark(state.filename, state.input, state.position, state.line, (state.position - state.lineStart)));
163 function throwError(state, message) {
164 throw generateError(state, message);
167 function throwWarning(state, message) {
168 var error = generateError(state, message);
170 if (state.onWarning) {
171 state.onWarning.call(null, error);
178 var directiveHandlers = {
180 'YAML': function handleYamlDirective(state, name, args) {
182 var match, major, minor;
184 if (null !== state.version) {
185 throwError(state, 'duplication of %YAML directive');
188 if (1 !== args.length) {
189 throwError(state, 'YAML directive accepts exactly one argument');
192 match = /^([0-9]+)\.([0-9]+)$/.exec(args[0]);
194 if (null === match) {
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 (1 !== minor && 2 !== minor) {
209 throwWarning(state, 'unsupported YAML version of the document');
213 'TAG': function handleTagDirective(state, name, args) {
217 if (2 !== args.length) {
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 (!(0x09 === _character ||
253 0x20 <= _character && _character <= 0x10FFFF)) {
254 throwError(state, 'expected valid JSON character');
259 state.result += _result;
263 function mergeMappings(state, destination, source) {
264 var sourceKeys, key, index, quantity;
266 if (!common.isObject(source)) {
267 throwError(state, 'cannot merge mappings; the provided source object is unacceptable');
270 sourceKeys = Object.keys(source);
272 for (index = 0, quantity = sourceKeys.length; index < quantity; index += 1) {
273 key = sourceKeys[index];
275 if (!_hasOwnProperty.call(destination, key)) {
276 destination[key] = source[key];
281 function storeMappingPair(state, _result, keyTag, keyNode, valueNode) {
284 keyNode = String(keyNode);
286 if (null === _result) {
290 if ('tag:yaml.org,2002:merge' === keyTag) {
291 if (Array.isArray(valueNode)) {
292 for (index = 0, quantity = valueNode.length; index < quantity; index += 1) {
293 mergeMappings(state, _result, valueNode[index]);
296 mergeMappings(state, _result, valueNode);
299 _result[keyNode] = valueNode;
305 function readLineBreak(state) {
308 ch = state.input.charCodeAt(state.position);
310 if (0x0A/* LF */ === ch) {
312 } else if (0x0D/* CR */ === ch) {
314 if (0x0A/* LF */ === state.input.charCodeAt(state.position)) {
318 throwError(state, 'a line break is expected');
322 state.lineStart = state.position;
325 function skipSeparationSpace(state, allowComments, checkIndent) {
327 ch = state.input.charCodeAt(state.position);
330 while (is_WHITE_SPACE(ch)) {
331 ch = state.input.charCodeAt(++state.position);
334 if (allowComments && 0x23/* # */ === ch) {
336 ch = state.input.charCodeAt(++state.position);
337 } while (ch !== 0x0A/* LF */ && ch !== 0x0D/* CR */ && 0 !== ch);
341 readLineBreak(state);
343 ch = state.input.charCodeAt(state.position);
345 state.lineIndent = 0;
347 while (0x20/* Space */ === ch) {
349 ch = state.input.charCodeAt(++state.position);
356 if (-1 !== checkIndent && 0 !== lineBreaks && state.lineIndent < checkIndent) {
357 throwWarning(state, 'deficient indentation');
363 function testDocumentSeparator(state) {
364 var _position = state.position,
367 ch = state.input.charCodeAt(_position);
369 // Condition state.position === state.lineStart is tested
370 // in parent on each call, for efficiency. No needs to test here again.
371 if ((0x2D/* - */ === ch || 0x2E/* . */ === ch) &&
372 state.input.charCodeAt(_position + 1) === ch &&
373 state.input.charCodeAt(_position+ 2) === ch) {
377 ch = state.input.charCodeAt(_position);
379 if (ch === 0 || is_WS_OR_EOL(ch)) {
387 function writeFoldedLines(state, count) {
390 } else if (count > 1) {
391 state.result += common.repeat('\n', count - 1);
396 function readPlainScalar(state, nodeIndent, withinFlowCollection) {
406 _result = state.result,
409 ch = state.input.charCodeAt(state.position);
411 if (is_WS_OR_EOL(ch) ||
412 is_FLOW_INDICATOR(ch) ||
413 0x23/* # */ === ch ||
414 0x26/* & */ === ch ||
415 0x2A/* * */ === ch ||
416 0x21/* ! */ === ch ||
417 0x7C/* | */ === ch ||
418 0x3E/* > */ === ch ||
419 0x27/* ' */ === ch ||
420 0x22/* " */ === ch ||
421 0x25/* % */ === ch ||
422 0x40/* @ */ === ch ||
423 0x60/* ` */ === ch) {
427 if (0x3F/* ? */ === ch || 0x2D/* - */ === ch) {
428 following = state.input.charCodeAt(state.position + 1);
430 if (is_WS_OR_EOL(following) ||
431 withinFlowCollection && is_FLOW_INDICATOR(following)) {
436 state.kind = 'scalar';
438 captureStart = captureEnd = state.position;
439 hasPendingContent = false;
442 if (0x3A/* : */ === ch) {
443 following = state.input.charCodeAt(state.position+1);
445 if (is_WS_OR_EOL(following) ||
446 withinFlowCollection && is_FLOW_INDICATOR(following)) {
450 } else if (0x23/* # */ === ch) {
451 preceding = state.input.charCodeAt(state.position - 1);
453 if (is_WS_OR_EOL(preceding)) {
457 } else if ((state.position === state.lineStart && testDocumentSeparator(state)) ||
458 withinFlowCollection && is_FLOW_INDICATOR(ch)) {
461 } else if (is_EOL(ch)) {
463 _lineStart = state.lineStart;
464 _lineIndent = state.lineIndent;
465 skipSeparationSpace(state, false, -1);
467 if (state.lineIndent >= nodeIndent) {
468 hasPendingContent = true;
469 ch = state.input.charCodeAt(state.position);
472 state.position = captureEnd;
474 state.lineStart = _lineStart;
475 state.lineIndent = _lineIndent;
480 if (hasPendingContent) {
481 captureSegment(state, captureStart, captureEnd, false);
482 writeFoldedLines(state, state.line - _line);
483 captureStart = captureEnd = state.position;
484 hasPendingContent = false;
487 if (!is_WHITE_SPACE(ch)) {
488 captureEnd = state.position + 1;
491 ch = state.input.charCodeAt(++state.position);
494 captureSegment(state, captureStart, captureEnd, false);
500 state.result = _result;
505 function readSingleQuotedScalar(state, nodeIndent) {
507 captureStart, captureEnd;
509 ch = state.input.charCodeAt(state.position);
511 if (0x27/* ' */ !== ch) {
515 state.kind = 'scalar';
518 captureStart = captureEnd = state.position;
520 while (0 !== (ch = state.input.charCodeAt(state.position))) {
521 if (0x27/* ' */ === ch) {
522 captureSegment(state, captureStart, state.position, true);
523 ch = state.input.charCodeAt(++state.position);
525 if (0x27/* ' */ === ch) {
526 captureStart = captureEnd = state.position;
532 } else if (is_EOL(ch)) {
533 captureSegment(state, captureStart, captureEnd, true);
534 writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));
535 captureStart = captureEnd = state.position;
537 } else if (state.position === state.lineStart && testDocumentSeparator(state)) {
538 throwError(state, 'unexpected end of the document within a single quoted scalar');
542 captureEnd = state.position;
546 throwError(state, 'unexpected end of the stream within a single quoted scalar');
549 function readDoubleQuotedScalar(state, nodeIndent) {
557 ch = state.input.charCodeAt(state.position);
559 if (0x22/* " */ !== ch) {
563 state.kind = 'scalar';
566 captureStart = captureEnd = state.position;
568 while (0 !== (ch = state.input.charCodeAt(state.position))) {
569 if (0x22/* " */ === ch) {
570 captureSegment(state, captureStart, state.position, true);
574 } else if (0x5C/* \ */ === ch) {
575 captureSegment(state, captureStart, state.position, true);
576 ch = state.input.charCodeAt(++state.position);
579 skipSeparationSpace(state, false, nodeIndent);
581 //TODO: rework to inline fn with no type cast?
582 } else if (ch < 256 && simpleEscapeCheck[ch]) {
583 state.result += simpleEscapeMap[ch];
586 } else if ((tmp = escapedHexLen(ch)) > 0) {
590 for (; hexLength > 0; hexLength--) {
591 ch = state.input.charCodeAt(++state.position);
593 if ((tmp = fromHexCode(ch)) >= 0) {
594 hexResult = (hexResult << 4) + tmp;
597 throwError(state, 'expected hexadecimal character');
601 state.result += charFromCodepoint(hexResult);
606 throwError(state, 'unknown escape sequence');
609 captureStart = captureEnd = state.position;
611 } else if (is_EOL(ch)) {
612 captureSegment(state, captureStart, captureEnd, true);
613 writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));
614 captureStart = captureEnd = state.position;
616 } else if (state.position === state.lineStart && testDocumentSeparator(state)) {
617 throwError(state, 'unexpected end of the document within a double quoted scalar');
621 captureEnd = state.position;
625 throwError(state, 'unexpected end of the stream within a double quoted scalar');
628 function readFlowCollection(state, nodeIndent) {
633 _anchor = state.anchor,
644 ch = state.input.charCodeAt(state.position);
646 if (ch === 0x5B/* [ */) {
647 terminator = 0x5D/* ] */;
650 } else if (ch === 0x7B/* { */) {
651 terminator = 0x7D/* } */;
658 if (null !== state.anchor) {
659 state.anchorMap[state.anchor] = _result;
662 ch = state.input.charCodeAt(++state.position);
665 skipSeparationSpace(state, true, nodeIndent);
667 ch = state.input.charCodeAt(state.position);
669 if (ch === terminator) {
672 state.anchor = _anchor;
673 state.kind = isMapping ? 'mapping' : 'sequence';
674 state.result = _result;
676 } else if (!readNext) {
677 throwError(state, 'missed comma between flow collection entries');
680 keyTag = keyNode = valueNode = null;
681 isPair = isExplicitPair = false;
683 if (0x3F/* ? */ === ch) {
684 following = state.input.charCodeAt(state.position + 1);
686 if (is_WS_OR_EOL(following)) {
687 isPair = isExplicitPair = true;
689 skipSeparationSpace(state, true, nodeIndent);
694 composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);
696 keyNode = state.result;
697 skipSeparationSpace(state, true, nodeIndent);
699 ch = state.input.charCodeAt(state.position);
701 if ((isExplicitPair || state.line === _line) && 0x3A/* : */ === ch) {
703 ch = state.input.charCodeAt(++state.position);
704 skipSeparationSpace(state, true, nodeIndent);
705 composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);
706 valueNode = state.result;
710 storeMappingPair(state, _result, keyTag, keyNode, valueNode);
712 _result.push(storeMappingPair(state, null, keyTag, keyNode, valueNode));
714 _result.push(keyNode);
717 skipSeparationSpace(state, true, nodeIndent);
719 ch = state.input.charCodeAt(state.position);
721 if (0x2C/* , */ === ch) {
723 ch = state.input.charCodeAt(++state.position);
729 throwError(state, 'unexpected end of the stream within a flow collection');
732 function readBlockScalar(state, nodeIndent) {
735 chomping = CHOMPING_CLIP,
736 detectedIndent = false,
737 textIndent = nodeIndent,
739 atMoreIndented = false,
743 ch = state.input.charCodeAt(state.position);
745 if (ch === 0x7C/* | */) {
747 } else if (ch === 0x3E/* > */) {
753 state.kind = 'scalar';
757 ch = state.input.charCodeAt(++state.position);
759 if (0x2B/* + */ === ch || 0x2D/* - */ === ch) {
760 if (CHOMPING_CLIP === chomping) {
761 chomping = (0x2B/* + */ === ch) ? CHOMPING_KEEP : CHOMPING_STRIP;
763 throwError(state, 'repeat of a chomping mode identifier');
766 } else if ((tmp = fromDecimalCode(ch)) >= 0) {
768 throwError(state, 'bad explicit indentation width of a block scalar; it cannot be less than one');
769 } else if (!detectedIndent) {
770 textIndent = nodeIndent + tmp - 1;
771 detectedIndent = true;
773 throwError(state, 'repeat of an indentation width identifier');
781 if (is_WHITE_SPACE(ch)) {
782 do { ch = state.input.charCodeAt(++state.position); }
783 while (is_WHITE_SPACE(ch));
785 if (0x23/* # */ === ch) {
786 do { ch = state.input.charCodeAt(++state.position); }
787 while (!is_EOL(ch) && (0 !== ch));
792 readLineBreak(state);
793 state.lineIndent = 0;
795 ch = state.input.charCodeAt(state.position);
797 while ((!detectedIndent || state.lineIndent < textIndent) &&
798 (0x20/* Space */ === ch)) {
800 ch = state.input.charCodeAt(++state.position);
803 if (!detectedIndent && state.lineIndent > textIndent) {
804 textIndent = state.lineIndent;
812 // End of the scalar.
813 if (state.lineIndent < textIndent) {
815 // Perform the chomping.
816 if (chomping === CHOMPING_KEEP) {
817 state.result += common.repeat('\n', emptyLines);
818 } else if (chomping === CHOMPING_CLIP) {
819 if (detectedIndent) { // i.e. only if the scalar is not empty.
820 state.result += '\n';
824 // Break this `while` cycle and go to the funciton's epilogue.
828 // Folded style: use fancy rules to handle line breaks.
831 // Lines starting with white space characters (more-indented lines) are not folded.
832 if (is_WHITE_SPACE(ch)) {
833 atMoreIndented = true;
834 state.result += common.repeat('\n', emptyLines + 1);
836 // End of more-indented block.
837 } else if (atMoreIndented) {
838 atMoreIndented = false;
839 state.result += common.repeat('\n', emptyLines + 1);
841 // Just one line break - perceive as the same line.
842 } else if (0 === emptyLines) {
843 if (detectedIndent) { // i.e. only if we have already read some scalar content.
847 // Several line breaks - perceive as different lines.
849 state.result += common.repeat('\n', emptyLines);
852 // Literal style: just add exact number of line breaks between content lines.
855 // If current line isn't the first one - count line break from the last content line.
856 if (detectedIndent) {
857 state.result += common.repeat('\n', emptyLines + 1);
859 // In case of the first content line - count only empty lines.
861 state.result += common.repeat('\n', emptyLines);
865 detectedIndent = true;
867 captureStart = state.position;
869 while (!is_EOL(ch) && (0 !== ch))
870 { ch = state.input.charCodeAt(++state.position); }
872 captureSegment(state, captureStart, state.position, false);
878 function readBlockSequence(state, nodeIndent) {
881 _anchor = state.anchor,
887 if (null !== state.anchor) {
888 state.anchorMap[state.anchor] = _result;
891 ch = state.input.charCodeAt(state.position);
895 if (0x2D/* - */ !== ch) {
899 following = state.input.charCodeAt(state.position + 1);
901 if (!is_WS_OR_EOL(following)) {
908 if (skipSeparationSpace(state, true, -1)) {
909 if (state.lineIndent <= nodeIndent) {
911 ch = state.input.charCodeAt(state.position);
917 composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true);
918 _result.push(state.result);
919 skipSeparationSpace(state, true, -1);
921 ch = state.input.charCodeAt(state.position);
923 if ((state.line === _line || state.lineIndent > nodeIndent) && (0 !== ch)) {
924 throwError(state, 'bad indentation of a sequence entry');
925 } else if (state.lineIndent < nodeIndent) {
932 state.anchor = _anchor;
933 state.kind = 'sequence';
934 state.result = _result;
941 function readBlockMapping(state, nodeIndent, flowIndent) {
946 _anchor = state.anchor,
951 atExplicitKey = false,
955 if (null !== state.anchor) {
956 state.anchorMap[state.anchor] = _result;
959 ch = state.input.charCodeAt(state.position);
962 following = state.input.charCodeAt(state.position + 1);
963 _line = state.line; // Save the current line.
966 // Explicit notation case. There are two separate blocks:
967 // first for the key (denoted by "?") and second for the value (denoted by ":")
969 if ((0x3F/* ? */ === ch || 0x3A/* : */ === ch) && is_WS_OR_EOL(following)) {
971 if (0x3F/* ? */ === ch) {
973 storeMappingPair(state, _result, keyTag, keyNode, null);
974 keyTag = keyNode = valueNode = null;
978 atExplicitKey = true;
981 } else if (atExplicitKey) {
982 // i.e. 0x3A/* : */ === character after the explicit key.
983 atExplicitKey = false;
987 throwError(state, 'incomplete explicit mapping pair; a key node is missed');
994 // Implicit notation case. Flow-style node as the key first, then ":", and the value.
996 } else if (composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) {
998 if (state.line === _line) {
999 ch = state.input.charCodeAt(state.position);
1001 while (is_WHITE_SPACE(ch)) {
1002 ch = state.input.charCodeAt(++state.position);
1005 if (0x3A/* : */ === ch) {
1006 ch = state.input.charCodeAt(++state.position);
1008 if (!is_WS_OR_EOL(ch)) {
1009 throwError(state, 'a whitespace character is expected after the key-value separator within a block mapping');
1012 if (atExplicitKey) {
1013 storeMappingPair(state, _result, keyTag, keyNode, null);
1014 keyTag = keyNode = valueNode = null;
1018 atExplicitKey = false;
1019 allowCompact = false;
1021 keyNode = state.result;
1023 } else if (detected) {
1024 throwError(state, 'can not read an implicit mapping pair; a colon is missed');
1028 state.anchor = _anchor;
1029 return true; // Keep the result of `composeNode`.
1032 } else if (detected) {
1033 throwError(state, 'can not read a block mapping entry; a multiline key may not be an implicit key');
1037 state.anchor = _anchor;
1038 return true; // Keep the result of `composeNode`.
1042 break; // Reading is done. Go to the epilogue.
1046 // Common reading code for both explicit and implicit notations.
1048 if (state.line === _line || state.lineIndent > nodeIndent) {
1049 if (composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact)) {
1050 if (atExplicitKey) {
1051 keyNode = state.result;
1053 valueNode = state.result;
1057 if (!atExplicitKey) {
1058 storeMappingPair(state, _result, keyTag, keyNode, valueNode);
1059 keyTag = keyNode = valueNode = null;
1062 skipSeparationSpace(state, true, -1);
1063 ch = state.input.charCodeAt(state.position);
1066 if (state.lineIndent > nodeIndent && (0 !== ch)) {
1067 throwError(state, 'bad indentation of a mapping entry');
1068 } else if (state.lineIndent < nodeIndent) {
1077 // Special case: last mapping's node contains only the key in explicit notation.
1078 if (atExplicitKey) {
1079 storeMappingPair(state, _result, keyTag, keyNode, null);
1082 // Expose the resulting mapping.
1085 state.anchor = _anchor;
1086 state.kind = 'mapping';
1087 state.result = _result;
1093 function readTagProperty(state) {
1101 ch = state.input.charCodeAt(state.position);
1103 if (0x21/* ! */ !== ch) {
1107 if (null !== state.tag) {
1108 throwError(state, 'duplication of a tag property');
1111 ch = state.input.charCodeAt(++state.position);
1113 if (0x3C/* < */ === ch) {
1115 ch = state.input.charCodeAt(++state.position);
1117 } else if (0x21/* ! */ === ch) {
1120 ch = state.input.charCodeAt(++state.position);
1126 _position = state.position;
1129 do { ch = state.input.charCodeAt(++state.position); }
1130 while (0 !== ch && 0x3E/* > */ !== ch);
1132 if (state.position < state.length) {
1133 tagName = state.input.slice(_position, state.position);
1134 ch = state.input.charCodeAt(++state.position);
1136 throwError(state, 'unexpected end of the stream within a verbatim tag');
1139 while (0 !== ch && !is_WS_OR_EOL(ch)) {
1141 if (0x21/* ! */ === ch) {
1143 tagHandle = state.input.slice(_position - 1, state.position + 1);
1145 if (!PATTERN_TAG_HANDLE.test(tagHandle)) {
1146 throwError(state, 'named tag handle cannot contain such characters');
1150 _position = state.position + 1;
1152 throwError(state, 'tag suffix cannot contain exclamation marks');
1156 ch = state.input.charCodeAt(++state.position);
1159 tagName = state.input.slice(_position, state.position);
1161 if (PATTERN_FLOW_INDICATORS.test(tagName)) {
1162 throwError(state, 'tag suffix cannot contain flow indicator characters');
1166 if (tagName && !PATTERN_TAG_URI.test(tagName)) {
1167 throwError(state, 'tag name cannot contain such characters: ' + tagName);
1171 state.tag = tagName;
1173 } else if (_hasOwnProperty.call(state.tagMap, tagHandle)) {
1174 state.tag = state.tagMap[tagHandle] + tagName;
1176 } else if ('!' === tagHandle) {
1177 state.tag = '!' + tagName;
1179 } else if ('!!' === tagHandle) {
1180 state.tag = 'tag:yaml.org,2002:' + tagName;
1183 throwError(state, 'undeclared tag handle "' + tagHandle + '"');
1189 function readAnchorProperty(state) {
1193 ch = state.input.charCodeAt(state.position);
1195 if (0x26/* & */ !== ch) {
1199 if (null !== state.anchor) {
1200 throwError(state, 'duplication of an anchor property');
1203 ch = state.input.charCodeAt(++state.position);
1204 _position = state.position;
1206 while (0 !== ch && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {
1207 ch = state.input.charCodeAt(++state.position);
1210 if (state.position === _position) {
1211 throwError(state, 'name of an anchor node must contain at least one character');
1214 state.anchor = state.input.slice(_position, state.position);
1218 function readAlias(state) {
1219 var _position, alias,
1221 input = state.input,
1224 ch = state.input.charCodeAt(state.position);
1226 if (0x2A/* * */ !== ch) {
1230 ch = state.input.charCodeAt(++state.position);
1231 _position = state.position;
1233 while (0 !== ch && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {
1234 ch = state.input.charCodeAt(++state.position);
1237 if (state.position === _position) {
1238 throwError(state, 'name of an alias node must contain at least one character');
1241 alias = state.input.slice(_position, state.position);
1243 if (!state.anchorMap.hasOwnProperty(alias)) {
1244 throwError(state, 'unidentified alias "' + alias + '"');
1247 state.result = state.anchorMap[alias];
1248 skipSeparationSpace(state, true, -1);
1252 function composeNode(state, parentIndent, nodeContext, allowToSeek, allowCompact) {
1253 var allowBlockStyles,
1255 allowBlockCollections,
1256 indentStatus = 1, // 1: this>parent, 0: this=parent, -1: this<parent
1267 state.anchor = null;
1269 state.result = null;
1271 allowBlockStyles = allowBlockScalars = allowBlockCollections =
1272 CONTEXT_BLOCK_OUT === nodeContext ||
1273 CONTEXT_BLOCK_IN === nodeContext;
1276 if (skipSeparationSpace(state, true, -1)) {
1279 if (state.lineIndent > parentIndent) {
1281 } else if (state.lineIndent === parentIndent) {
1283 } else if (state.lineIndent < parentIndent) {
1289 if (1 === indentStatus) {
1290 while (readTagProperty(state) || readAnchorProperty(state)) {
1291 if (skipSeparationSpace(state, true, -1)) {
1293 allowBlockCollections = allowBlockStyles;
1295 if (state.lineIndent > parentIndent) {
1297 } else if (state.lineIndent === parentIndent) {
1299 } else if (state.lineIndent < parentIndent) {
1303 allowBlockCollections = false;
1308 if (allowBlockCollections) {
1309 allowBlockCollections = atNewLine || allowCompact;
1312 if (1 === indentStatus || CONTEXT_BLOCK_OUT === nodeContext) {
1313 if (CONTEXT_FLOW_IN === nodeContext || CONTEXT_FLOW_OUT === nodeContext) {
1314 flowIndent = parentIndent;
1316 flowIndent = parentIndent + 1;
1319 blockIndent = state.position - state.lineStart;
1321 if (1 === indentStatus) {
1322 if (allowBlockCollections &&
1323 (readBlockSequence(state, blockIndent) ||
1324 readBlockMapping(state, blockIndent, flowIndent)) ||
1325 readFlowCollection(state, flowIndent)) {
1328 if ((allowBlockScalars && readBlockScalar(state, flowIndent)) ||
1329 readSingleQuotedScalar(state, flowIndent) ||
1330 readDoubleQuotedScalar(state, flowIndent)) {
1333 } else if (readAlias(state)) {
1336 if (null !== state.tag || null !== state.anchor) {
1337 throwError(state, 'alias node should not have any properties');
1340 } else if (readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext)) {
1343 if (null === state.tag) {
1348 if (null !== state.anchor) {
1349 state.anchorMap[state.anchor] = state.result;
1352 } else if (0 === indentStatus) {
1353 // Special case: block sequences are allowed to have same indentation level as the parent.
1354 // http://www.yaml.org/spec/1.2/spec.html#id2799784
1355 hasContent = allowBlockCollections && readBlockSequence(state, blockIndent);
1359 if (null !== state.tag && '!' !== state.tag) {
1360 if ('?' === state.tag) {
1361 for (typeIndex = 0, typeQuantity = state.implicitTypes.length;
1362 typeIndex < typeQuantity;
1364 type = state.implicitTypes[typeIndex];
1366 // Implicit resolving is not allowed for non-scalar types, and '?'
1367 // non-specific tag is only assigned to plain scalars. So, it isn't
1368 // needed to check for 'kind' conformity.
1370 if (type.resolve(state.result)) { // `state.result` updated in resolver if matched
1371 state.result = type.construct(state.result);
1372 state.tag = type.tag;
1373 if (null !== state.anchor) {
1374 state.anchorMap[state.anchor] = state.result;
1379 } else if (_hasOwnProperty.call(state.typeMap, state.tag)) {
1380 type = state.typeMap[state.tag];
1382 if (null !== state.result && type.kind !== state.kind) {
1383 throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be "' + type.kind + '", not "' + state.kind + '"');
1386 if (!type.resolve(state.result)) { // `state.result` updated in resolver if matched
1387 throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag');
1389 state.result = type.construct(state.result);
1390 if (null !== state.anchor) {
1391 state.anchorMap[state.anchor] = state.result;
1395 throwWarning(state, 'unknown tag !<' + state.tag + '>');
1399 return null !== state.tag || null !== state.anchor || hasContent;
1402 function readDocument(state) {
1403 var documentStart = state.position,
1407 hasDirectives = false,
1410 state.version = null;
1411 state.checkLineBreaks = state.legacy;
1413 state.anchorMap = {};
1415 while (0 !== (ch = state.input.charCodeAt(state.position))) {
1416 skipSeparationSpace(state, true, -1);
1418 ch = state.input.charCodeAt(state.position);
1420 if (state.lineIndent > 0 || 0x25/* % */ !== ch) {
1424 hasDirectives = true;
1425 ch = state.input.charCodeAt(++state.position);
1426 _position = state.position;
1428 while (0 !== ch && !is_WS_OR_EOL(ch)) {
1429 ch = state.input.charCodeAt(++state.position);
1432 directiveName = state.input.slice(_position, state.position);
1435 if (directiveName.length < 1) {
1436 throwError(state, 'directive name must not be less than one character in length');
1440 while (is_WHITE_SPACE(ch)) {
1441 ch = state.input.charCodeAt(++state.position);
1444 if (0x23/* # */ === ch) {
1445 do { ch = state.input.charCodeAt(++state.position); }
1446 while (0 !== ch && !is_EOL(ch));
1454 _position = state.position;
1456 while (0 !== ch && !is_WS_OR_EOL(ch)) {
1457 ch = state.input.charCodeAt(++state.position);
1460 directiveArgs.push(state.input.slice(_position, state.position));
1464 readLineBreak(state);
1467 if (_hasOwnProperty.call(directiveHandlers, directiveName)) {
1468 directiveHandlers[directiveName](state, directiveName, directiveArgs);
1470 throwWarning(state, 'unknown document directive "' + directiveName + '"');
1474 skipSeparationSpace(state, true, -1);
1476 if (0 === state.lineIndent &&
1477 0x2D/* - */ === state.input.charCodeAt(state.position) &&
1478 0x2D/* - */ === state.input.charCodeAt(state.position + 1) &&
1479 0x2D/* - */ === state.input.charCodeAt(state.position + 2)) {
1480 state.position += 3;
1481 skipSeparationSpace(state, true, -1);
1483 } else if (hasDirectives) {
1484 throwError(state, 'directives end mark is expected');
1487 composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true);
1488 skipSeparationSpace(state, true, -1);
1490 if (state.checkLineBreaks &&
1491 PATTERN_NON_ASCII_LINE_BREAKS.test(state.input.slice(documentStart, state.position))) {
1492 throwWarning(state, 'non-ASCII line breaks are interpreted as content');
1495 state.documents.push(state.result);
1497 if (state.position === state.lineStart && testDocumentSeparator(state)) {
1499 if (0x2E/* . */ === state.input.charCodeAt(state.position)) {
1500 state.position += 3;
1501 skipSeparationSpace(state, true, -1);
1506 if (state.position < (state.length - 1)) {
1507 throwError(state, 'end of the stream or a document separator is expected');
1514 function loadDocuments(input, options) {
1515 input = String(input);
1516 options = options || {};
1518 if (0 !== input.length &&
1519 0x0A/* LF */ !== input.charCodeAt(input.length - 1) &&
1520 0x0D/* CR */ !== input.charCodeAt(input.length - 1)) {
1524 var state = new State(input, options);
1526 if (PATTERN_NON_PRINTABLE.test(state.input)) {
1527 throwError(state, 'the stream contains non-printable characters');
1530 // Use 0 as string terminator. That significantly simplifies bounds check.
1531 state.input += '\0';
1533 while (0x20/* Space */ === state.input.charCodeAt(state.position)) {
1534 state.lineIndent += 1;
1535 state.position += 1;
1538 while (state.position < (state.length - 1)) {
1539 readDocument(state);
1542 return state.documents;
1546 function loadAll(input, iterator, options) {
1547 var documents = loadDocuments(input, options), index, length;
1549 for (index = 0, length = documents.length; index < length; index += 1) {
1550 iterator(documents[index]);
1555 function load(input, options) {
1556 var documents = loadDocuments(input, options), index, length;
1558 if (0 === documents.length) {
1560 } else if (1 === documents.length) {
1561 return documents[0];
1563 throw new YAMLException('expected a single document in the stream, but found more');
1568 function safeLoadAll(input, output, options) {
1569 loadAll(input, output, common.extend({ schema: DEFAULT_SAFE_SCHEMA }, options));
1573 function safeLoad(input, options) {
1574 return load(input, common.extend({ schema: DEFAULT_SAFE_SCHEMA }, options));
1578 module.exports.loadAll = loadAll;
1579 module.exports.load = load;
1580 module.exports.safeLoadAll = safeLoadAll;
1581 module.exports.safeLoad = safeLoad;