l0bsterssg

node.js static responsive blog post generator
Log | Files | Refs | README

loader.js (44356B)


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