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