source: node_modules/yaml/dist/parse/parser.js

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 33.7 KB
RevLine 
[d24f17c]1'use strict';
2
3var cst = require('./cst.js');
4var lexer = require('./lexer.js');
5
6function includesToken(list, type) {
7 for (let i = 0; i < list.length; ++i)
8 if (list[i].type === type)
9 return true;
10 return false;
11}
12function findNonEmptyIndex(list) {
13 for (let i = 0; i < list.length; ++i) {
14 switch (list[i].type) {
15 case 'space':
16 case 'comment':
17 case 'newline':
18 break;
19 default:
20 return i;
21 }
22 }
23 return -1;
24}
25function isFlowToken(token) {
26 switch (token?.type) {
27 case 'alias':
28 case 'scalar':
29 case 'single-quoted-scalar':
30 case 'double-quoted-scalar':
31 case 'flow-collection':
32 return true;
33 default:
34 return false;
35 }
36}
37function getPrevProps(parent) {
38 switch (parent.type) {
39 case 'document':
40 return parent.start;
41 case 'block-map': {
42 const it = parent.items[parent.items.length - 1];
43 return it.sep ?? it.start;
44 }
45 case 'block-seq':
46 return parent.items[parent.items.length - 1].start;
47 /* istanbul ignore next should not happen */
48 default:
49 return [];
50 }
51}
52/** Note: May modify input array */
53function getFirstKeyStartProps(prev) {
54 if (prev.length === 0)
55 return [];
56 let i = prev.length;
57 loop: while (--i >= 0) {
58 switch (prev[i].type) {
59 case 'doc-start':
60 case 'explicit-key-ind':
61 case 'map-value-ind':
62 case 'seq-item-ind':
63 case 'newline':
64 break loop;
65 }
66 }
67 while (prev[++i]?.type === 'space') {
68 /* loop */
69 }
70 return prev.splice(i, prev.length);
71}
72function fixFlowSeqItems(fc) {
73 if (fc.start.type === 'flow-seq-start') {
74 for (const it of fc.items) {
75 if (it.sep &&
76 !it.value &&
77 !includesToken(it.start, 'explicit-key-ind') &&
78 !includesToken(it.sep, 'map-value-ind')) {
79 if (it.key)
80 it.value = it.key;
81 delete it.key;
82 if (isFlowToken(it.value)) {
83 if (it.value.end)
84 Array.prototype.push.apply(it.value.end, it.sep);
85 else
86 it.value.end = it.sep;
87 }
88 else
89 Array.prototype.push.apply(it.start, it.sep);
90 delete it.sep;
91 }
92 }
93 }
94}
95/**
96 * A YAML concrete syntax tree (CST) parser
97 *
98 * ```ts
99 * const src: string = ...
100 * for (const token of new Parser().parse(src)) {
101 * // token: Token
102 * }
103 * ```
104 *
105 * To use the parser with a user-provided lexer:
106 *
107 * ```ts
108 * function* parse(source: string, lexer: Lexer) {
109 * const parser = new Parser()
110 * for (const lexeme of lexer.lex(source))
111 * yield* parser.next(lexeme)
112 * yield* parser.end()
113 * }
114 *
115 * const src: string = ...
116 * const lexer = new Lexer()
117 * for (const token of parse(src, lexer)) {
118 * // token: Token
119 * }
120 * ```
121 */
122class Parser {
123 /**
124 * @param onNewLine - If defined, called separately with the start position of
125 * each new line (in `parse()`, including the start of input).
126 */
127 constructor(onNewLine) {
128 /** If true, space and sequence indicators count as indentation */
129 this.atNewLine = true;
130 /** If true, next token is a scalar value */
131 this.atScalar = false;
132 /** Current indentation level */
133 this.indent = 0;
134 /** Current offset since the start of parsing */
135 this.offset = 0;
136 /** On the same line with a block map key */
137 this.onKeyLine = false;
138 /** Top indicates the node that's currently being built */
139 this.stack = [];
140 /** The source of the current token, set in parse() */
141 this.source = '';
142 /** The type of the current token, set in parse() */
143 this.type = '';
144 // Must be defined after `next()`
145 this.lexer = new lexer.Lexer();
146 this.onNewLine = onNewLine;
147 }
148 /**
149 * Parse `source` as a YAML stream.
150 * If `incomplete`, a part of the last line may be left as a buffer for the next call.
151 *
152 * Errors are not thrown, but yielded as `{ type: 'error', message }` tokens.
153 *
154 * @returns A generator of tokens representing each directive, document, and other structure.
155 */
156 *parse(source, incomplete = false) {
157 if (this.onNewLine && this.offset === 0)
158 this.onNewLine(0);
159 for (const lexeme of this.lexer.lex(source, incomplete))
160 yield* this.next(lexeme);
161 if (!incomplete)
162 yield* this.end();
163 }
164 /**
165 * Advance the parser by the `source` of one lexical token.
166 */
167 *next(source) {
168 this.source = source;
169 if (process.env.LOG_TOKENS)
170 console.log('|', cst.prettyToken(source));
171 if (this.atScalar) {
172 this.atScalar = false;
173 yield* this.step();
174 this.offset += source.length;
175 return;
176 }
177 const type = cst.tokenType(source);
178 if (!type) {
179 const message = `Not a YAML token: ${source}`;
180 yield* this.pop({ type: 'error', offset: this.offset, message, source });
181 this.offset += source.length;
182 }
183 else if (type === 'scalar') {
184 this.atNewLine = false;
185 this.atScalar = true;
186 this.type = 'scalar';
187 }
188 else {
189 this.type = type;
190 yield* this.step();
191 switch (type) {
192 case 'newline':
193 this.atNewLine = true;
194 this.indent = 0;
195 if (this.onNewLine)
196 this.onNewLine(this.offset + source.length);
197 break;
198 case 'space':
199 if (this.atNewLine && source[0] === ' ')
200 this.indent += source.length;
201 break;
202 case 'explicit-key-ind':
203 case 'map-value-ind':
204 case 'seq-item-ind':
205 if (this.atNewLine)
206 this.indent += source.length;
207 break;
208 case 'doc-mode':
209 case 'flow-error-end':
210 return;
211 default:
212 this.atNewLine = false;
213 }
214 this.offset += source.length;
215 }
216 }
217 /** Call at end of input to push out any remaining constructions */
218 *end() {
219 while (this.stack.length > 0)
220 yield* this.pop();
221 }
222 get sourceToken() {
223 const st = {
224 type: this.type,
225 offset: this.offset,
226 indent: this.indent,
227 source: this.source
228 };
229 return st;
230 }
231 *step() {
232 const top = this.peek(1);
233 if (this.type === 'doc-end' && (!top || top.type !== 'doc-end')) {
234 while (this.stack.length > 0)
235 yield* this.pop();
236 this.stack.push({
237 type: 'doc-end',
238 offset: this.offset,
239 source: this.source
240 });
241 return;
242 }
243 if (!top)
244 return yield* this.stream();
245 switch (top.type) {
246 case 'document':
247 return yield* this.document(top);
248 case 'alias':
249 case 'scalar':
250 case 'single-quoted-scalar':
251 case 'double-quoted-scalar':
252 return yield* this.scalar(top);
253 case 'block-scalar':
254 return yield* this.blockScalar(top);
255 case 'block-map':
256 return yield* this.blockMap(top);
257 case 'block-seq':
258 return yield* this.blockSequence(top);
259 case 'flow-collection':
260 return yield* this.flowCollection(top);
261 case 'doc-end':
262 return yield* this.documentEnd(top);
263 }
264 /* istanbul ignore next should not happen */
265 yield* this.pop();
266 }
267 peek(n) {
268 return this.stack[this.stack.length - n];
269 }
270 *pop(error) {
271 const token = error ?? this.stack.pop();
272 /* istanbul ignore if should not happen */
273 if (!token) {
274 const message = 'Tried to pop an empty stack';
275 yield { type: 'error', offset: this.offset, source: '', message };
276 }
277 else if (this.stack.length === 0) {
278 yield token;
279 }
280 else {
281 const top = this.peek(1);
282 if (token.type === 'block-scalar') {
283 // Block scalars use their parent rather than header indent
284 token.indent = 'indent' in top ? top.indent : 0;
285 }
286 else if (token.type === 'flow-collection' && top.type === 'document') {
287 // Ignore all indent for top-level flow collections
288 token.indent = 0;
289 }
290 if (token.type === 'flow-collection')
291 fixFlowSeqItems(token);
292 switch (top.type) {
293 case 'document':
294 top.value = token;
295 break;
296 case 'block-scalar':
297 top.props.push(token); // error
298 break;
299 case 'block-map': {
300 const it = top.items[top.items.length - 1];
301 if (it.value) {
302 top.items.push({ start: [], key: token, sep: [] });
303 this.onKeyLine = true;
304 return;
305 }
306 else if (it.sep) {
307 it.value = token;
308 }
309 else {
310 Object.assign(it, { key: token, sep: [] });
311 this.onKeyLine = !includesToken(it.start, 'explicit-key-ind');
312 return;
313 }
314 break;
315 }
316 case 'block-seq': {
317 const it = top.items[top.items.length - 1];
318 if (it.value)
319 top.items.push({ start: [], value: token });
320 else
321 it.value = token;
322 break;
323 }
324 case 'flow-collection': {
325 const it = top.items[top.items.length - 1];
326 if (!it || it.value)
327 top.items.push({ start: [], key: token, sep: [] });
328 else if (it.sep)
329 it.value = token;
330 else
331 Object.assign(it, { key: token, sep: [] });
332 return;
333 }
334 /* istanbul ignore next should not happen */
335 default:
336 yield* this.pop();
337 yield* this.pop(token);
338 }
339 if ((top.type === 'document' ||
340 top.type === 'block-map' ||
341 top.type === 'block-seq') &&
342 (token.type === 'block-map' || token.type === 'block-seq')) {
343 const last = token.items[token.items.length - 1];
344 if (last &&
345 !last.sep &&
346 !last.value &&
347 last.start.length > 0 &&
348 findNonEmptyIndex(last.start) === -1 &&
349 (token.indent === 0 ||
350 last.start.every(st => st.type !== 'comment' || st.indent < token.indent))) {
351 if (top.type === 'document')
352 top.end = last.start;
353 else
354 top.items.push({ start: last.start });
355 token.items.splice(-1, 1);
356 }
357 }
358 }
359 }
360 *stream() {
361 switch (this.type) {
362 case 'directive-line':
363 yield { type: 'directive', offset: this.offset, source: this.source };
364 return;
365 case 'byte-order-mark':
366 case 'space':
367 case 'comment':
368 case 'newline':
369 yield this.sourceToken;
370 return;
371 case 'doc-mode':
372 case 'doc-start': {
373 const doc = {
374 type: 'document',
375 offset: this.offset,
376 start: []
377 };
378 if (this.type === 'doc-start')
379 doc.start.push(this.sourceToken);
380 this.stack.push(doc);
381 return;
382 }
383 }
384 yield {
385 type: 'error',
386 offset: this.offset,
387 message: `Unexpected ${this.type} token in YAML stream`,
388 source: this.source
389 };
390 }
391 *document(doc) {
392 if (doc.value)
393 return yield* this.lineEnd(doc);
394 switch (this.type) {
395 case 'doc-start': {
396 if (findNonEmptyIndex(doc.start) !== -1) {
397 yield* this.pop();
398 yield* this.step();
399 }
400 else
401 doc.start.push(this.sourceToken);
402 return;
403 }
404 case 'anchor':
405 case 'tag':
406 case 'space':
407 case 'comment':
408 case 'newline':
409 doc.start.push(this.sourceToken);
410 return;
411 }
412 const bv = this.startBlockValue(doc);
413 if (bv)
414 this.stack.push(bv);
415 else {
416 yield {
417 type: 'error',
418 offset: this.offset,
419 message: `Unexpected ${this.type} token in YAML document`,
420 source: this.source
421 };
422 }
423 }
424 *scalar(scalar) {
425 if (this.type === 'map-value-ind') {
426 const prev = getPrevProps(this.peek(2));
427 const start = getFirstKeyStartProps(prev);
428 let sep;
429 if (scalar.end) {
430 sep = scalar.end;
431 sep.push(this.sourceToken);
432 delete scalar.end;
433 }
434 else
435 sep = [this.sourceToken];
436 const map = {
437 type: 'block-map',
438 offset: scalar.offset,
439 indent: scalar.indent,
440 items: [{ start, key: scalar, sep }]
441 };
442 this.onKeyLine = true;
443 this.stack[this.stack.length - 1] = map;
444 }
445 else
446 yield* this.lineEnd(scalar);
447 }
448 *blockScalar(scalar) {
449 switch (this.type) {
450 case 'space':
451 case 'comment':
452 case 'newline':
453 scalar.props.push(this.sourceToken);
454 return;
455 case 'scalar':
456 scalar.source = this.source;
457 // block-scalar source includes trailing newline
458 this.atNewLine = true;
459 this.indent = 0;
460 if (this.onNewLine) {
461 let nl = this.source.indexOf('\n') + 1;
462 while (nl !== 0) {
463 this.onNewLine(this.offset + nl);
464 nl = this.source.indexOf('\n', nl) + 1;
465 }
466 }
467 yield* this.pop();
468 break;
469 /* istanbul ignore next should not happen */
470 default:
471 yield* this.pop();
472 yield* this.step();
473 }
474 }
475 *blockMap(map) {
476 const it = map.items[map.items.length - 1];
477 // it.sep is true-ish if pair already has key or : separator
478 switch (this.type) {
479 case 'newline':
480 this.onKeyLine = false;
481 if (it.value) {
482 const end = 'end' in it.value ? it.value.end : undefined;
483 const last = Array.isArray(end) ? end[end.length - 1] : undefined;
484 if (last?.type === 'comment')
485 end?.push(this.sourceToken);
486 else
487 map.items.push({ start: [this.sourceToken] });
488 }
489 else if (it.sep) {
490 it.sep.push(this.sourceToken);
491 }
492 else {
493 it.start.push(this.sourceToken);
494 }
495 return;
496 case 'space':
497 case 'comment':
498 if (it.value) {
499 map.items.push({ start: [this.sourceToken] });
500 }
501 else if (it.sep) {
502 it.sep.push(this.sourceToken);
503 }
504 else {
505 if (this.atIndentedComment(it.start, map.indent)) {
506 const prev = map.items[map.items.length - 2];
507 const end = prev?.value?.end;
508 if (Array.isArray(end)) {
509 Array.prototype.push.apply(end, it.start);
510 end.push(this.sourceToken);
511 map.items.pop();
512 return;
513 }
514 }
515 it.start.push(this.sourceToken);
516 }
517 return;
518 }
519 if (this.indent >= map.indent) {
520 const atNextItem = !this.onKeyLine && this.indent === map.indent && it.sep;
521 // For empty nodes, assign newline-separated not indented empty tokens to following node
522 let start = [];
523 if (atNextItem && it.sep && !it.value) {
524 const nl = [];
525 for (let i = 0; i < it.sep.length; ++i) {
526 const st = it.sep[i];
527 switch (st.type) {
528 case 'newline':
529 nl.push(i);
530 break;
531 case 'space':
532 break;
533 case 'comment':
534 if (st.indent > map.indent)
535 nl.length = 0;
536 break;
537 default:
538 nl.length = 0;
539 }
540 }
541 if (nl.length >= 2)
542 start = it.sep.splice(nl[1]);
543 }
544 switch (this.type) {
545 case 'anchor':
546 case 'tag':
547 if (atNextItem || it.value) {
548 start.push(this.sourceToken);
549 map.items.push({ start });
550 this.onKeyLine = true;
551 }
552 else if (it.sep) {
553 it.sep.push(this.sourceToken);
554 }
555 else {
556 it.start.push(this.sourceToken);
557 }
558 return;
559 case 'explicit-key-ind':
560 if (!it.sep && !includesToken(it.start, 'explicit-key-ind')) {
561 it.start.push(this.sourceToken);
562 }
563 else if (atNextItem || it.value) {
564 start.push(this.sourceToken);
565 map.items.push({ start });
566 }
567 else {
568 this.stack.push({
569 type: 'block-map',
570 offset: this.offset,
571 indent: this.indent,
572 items: [{ start: [this.sourceToken] }]
573 });
574 }
575 this.onKeyLine = true;
576 return;
577 case 'map-value-ind':
578 if (includesToken(it.start, 'explicit-key-ind')) {
579 if (!it.sep) {
580 if (includesToken(it.start, 'newline')) {
581 Object.assign(it, { key: null, sep: [this.sourceToken] });
582 }
583 else {
584 const start = getFirstKeyStartProps(it.start);
585 this.stack.push({
586 type: 'block-map',
587 offset: this.offset,
588 indent: this.indent,
589 items: [{ start, key: null, sep: [this.sourceToken] }]
590 });
591 }
592 }
593 else if (it.value) {
594 map.items.push({ start: [], key: null, sep: [this.sourceToken] });
595 }
596 else if (includesToken(it.sep, 'map-value-ind')) {
597 this.stack.push({
598 type: 'block-map',
599 offset: this.offset,
600 indent: this.indent,
601 items: [{ start, key: null, sep: [this.sourceToken] }]
602 });
603 }
604 else if (isFlowToken(it.key) &&
605 !includesToken(it.sep, 'newline')) {
606 const start = getFirstKeyStartProps(it.start);
607 const key = it.key;
608 const sep = it.sep;
609 sep.push(this.sourceToken);
610 // @ts-expect-error type guard is wrong here
611 delete it.key, delete it.sep;
612 this.stack.push({
613 type: 'block-map',
614 offset: this.offset,
615 indent: this.indent,
616 items: [{ start, key, sep }]
617 });
618 }
619 else if (start.length > 0) {
620 // Not actually at next item
621 it.sep = it.sep.concat(start, this.sourceToken);
622 }
623 else {
624 it.sep.push(this.sourceToken);
625 }
626 }
627 else {
628 if (!it.sep) {
629 Object.assign(it, { key: null, sep: [this.sourceToken] });
630 }
631 else if (it.value || atNextItem) {
632 map.items.push({ start, key: null, sep: [this.sourceToken] });
633 }
634 else if (includesToken(it.sep, 'map-value-ind')) {
635 this.stack.push({
636 type: 'block-map',
637 offset: this.offset,
638 indent: this.indent,
639 items: [{ start: [], key: null, sep: [this.sourceToken] }]
640 });
641 }
642 else {
643 it.sep.push(this.sourceToken);
644 }
645 }
646 this.onKeyLine = true;
647 return;
648 case 'alias':
649 case 'scalar':
650 case 'single-quoted-scalar':
651 case 'double-quoted-scalar': {
652 const fs = this.flowScalar(this.type);
653 if (atNextItem || it.value) {
654 map.items.push({ start, key: fs, sep: [] });
655 this.onKeyLine = true;
656 }
657 else if (it.sep) {
658 this.stack.push(fs);
659 }
660 else {
661 Object.assign(it, { key: fs, sep: [] });
662 this.onKeyLine = true;
663 }
664 return;
665 }
666 default: {
667 const bv = this.startBlockValue(map);
668 if (bv) {
669 if (atNextItem &&
670 bv.type !== 'block-seq' &&
671 includesToken(it.start, 'explicit-key-ind')) {
672 map.items.push({ start });
673 }
674 this.stack.push(bv);
675 return;
676 }
677 }
678 }
679 }
680 yield* this.pop();
681 yield* this.step();
682 }
683 *blockSequence(seq) {
684 const it = seq.items[seq.items.length - 1];
685 switch (this.type) {
686 case 'newline':
687 if (it.value) {
688 const end = 'end' in it.value ? it.value.end : undefined;
689 const last = Array.isArray(end) ? end[end.length - 1] : undefined;
690 if (last?.type === 'comment')
691 end?.push(this.sourceToken);
692 else
693 seq.items.push({ start: [this.sourceToken] });
694 }
695 else
696 it.start.push(this.sourceToken);
697 return;
698 case 'space':
699 case 'comment':
700 if (it.value)
701 seq.items.push({ start: [this.sourceToken] });
702 else {
703 if (this.atIndentedComment(it.start, seq.indent)) {
704 const prev = seq.items[seq.items.length - 2];
705 const end = prev?.value?.end;
706 if (Array.isArray(end)) {
707 Array.prototype.push.apply(end, it.start);
708 end.push(this.sourceToken);
709 seq.items.pop();
710 return;
711 }
712 }
713 it.start.push(this.sourceToken);
714 }
715 return;
716 case 'anchor':
717 case 'tag':
718 if (it.value || this.indent <= seq.indent)
719 break;
720 it.start.push(this.sourceToken);
721 return;
722 case 'seq-item-ind':
723 if (this.indent !== seq.indent)
724 break;
725 if (it.value || includesToken(it.start, 'seq-item-ind'))
726 seq.items.push({ start: [this.sourceToken] });
727 else
728 it.start.push(this.sourceToken);
729 return;
730 }
731 if (this.indent > seq.indent) {
732 const bv = this.startBlockValue(seq);
733 if (bv) {
734 this.stack.push(bv);
735 return;
736 }
737 }
738 yield* this.pop();
739 yield* this.step();
740 }
741 *flowCollection(fc) {
742 const it = fc.items[fc.items.length - 1];
743 if (this.type === 'flow-error-end') {
744 let top;
745 do {
746 yield* this.pop();
747 top = this.peek(1);
748 } while (top && top.type === 'flow-collection');
749 }
750 else if (fc.end.length === 0) {
751 switch (this.type) {
752 case 'comma':
753 case 'explicit-key-ind':
754 if (!it || it.sep)
755 fc.items.push({ start: [this.sourceToken] });
756 else
757 it.start.push(this.sourceToken);
758 return;
759 case 'map-value-ind':
760 if (!it || it.value)
761 fc.items.push({ start: [], key: null, sep: [this.sourceToken] });
762 else if (it.sep)
763 it.sep.push(this.sourceToken);
764 else
765 Object.assign(it, { key: null, sep: [this.sourceToken] });
766 return;
767 case 'space':
768 case 'comment':
769 case 'newline':
770 case 'anchor':
771 case 'tag':
772 if (!it || it.value)
773 fc.items.push({ start: [this.sourceToken] });
774 else if (it.sep)
775 it.sep.push(this.sourceToken);
776 else
777 it.start.push(this.sourceToken);
778 return;
779 case 'alias':
780 case 'scalar':
781 case 'single-quoted-scalar':
782 case 'double-quoted-scalar': {
783 const fs = this.flowScalar(this.type);
784 if (!it || it.value)
785 fc.items.push({ start: [], key: fs, sep: [] });
786 else if (it.sep)
787 this.stack.push(fs);
788 else
789 Object.assign(it, { key: fs, sep: [] });
790 return;
791 }
792 case 'flow-map-end':
793 case 'flow-seq-end':
794 fc.end.push(this.sourceToken);
795 return;
796 }
797 const bv = this.startBlockValue(fc);
798 /* istanbul ignore else should not happen */
799 if (bv)
800 this.stack.push(bv);
801 else {
802 yield* this.pop();
803 yield* this.step();
804 }
805 }
806 else {
807 const parent = this.peek(2);
808 if (parent.type === 'block-map' &&
809 ((this.type === 'map-value-ind' && parent.indent === fc.indent) ||
810 (this.type === 'newline' &&
811 !parent.items[parent.items.length - 1].sep))) {
812 yield* this.pop();
813 yield* this.step();
814 }
815 else if (this.type === 'map-value-ind' &&
816 parent.type !== 'flow-collection') {
817 const prev = getPrevProps(parent);
818 const start = getFirstKeyStartProps(prev);
819 fixFlowSeqItems(fc);
820 const sep = fc.end.splice(1, fc.end.length);
821 sep.push(this.sourceToken);
822 const map = {
823 type: 'block-map',
824 offset: fc.offset,
825 indent: fc.indent,
826 items: [{ start, key: fc, sep }]
827 };
828 this.onKeyLine = true;
829 this.stack[this.stack.length - 1] = map;
830 }
831 else {
832 yield* this.lineEnd(fc);
833 }
834 }
835 }
836 flowScalar(type) {
837 if (this.onNewLine) {
838 let nl = this.source.indexOf('\n') + 1;
839 while (nl !== 0) {
840 this.onNewLine(this.offset + nl);
841 nl = this.source.indexOf('\n', nl) + 1;
842 }
843 }
844 return {
845 type,
846 offset: this.offset,
847 indent: this.indent,
848 source: this.source
849 };
850 }
851 startBlockValue(parent) {
852 switch (this.type) {
853 case 'alias':
854 case 'scalar':
855 case 'single-quoted-scalar':
856 case 'double-quoted-scalar':
857 return this.flowScalar(this.type);
858 case 'block-scalar-header':
859 return {
860 type: 'block-scalar',
861 offset: this.offset,
862 indent: this.indent,
863 props: [this.sourceToken],
864 source: ''
865 };
866 case 'flow-map-start':
867 case 'flow-seq-start':
868 return {
869 type: 'flow-collection',
870 offset: this.offset,
871 indent: this.indent,
872 start: this.sourceToken,
873 items: [],
874 end: []
875 };
876 case 'seq-item-ind':
877 return {
878 type: 'block-seq',
879 offset: this.offset,
880 indent: this.indent,
881 items: [{ start: [this.sourceToken] }]
882 };
883 case 'explicit-key-ind': {
884 this.onKeyLine = true;
885 const prev = getPrevProps(parent);
886 const start = getFirstKeyStartProps(prev);
887 start.push(this.sourceToken);
888 return {
889 type: 'block-map',
890 offset: this.offset,
891 indent: this.indent,
892 items: [{ start }]
893 };
894 }
895 case 'map-value-ind': {
896 this.onKeyLine = true;
897 const prev = getPrevProps(parent);
898 const start = getFirstKeyStartProps(prev);
899 return {
900 type: 'block-map',
901 offset: this.offset,
902 indent: this.indent,
903 items: [{ start, key: null, sep: [this.sourceToken] }]
904 };
905 }
906 }
907 return null;
908 }
909 atIndentedComment(start, indent) {
910 if (this.type !== 'comment')
911 return false;
912 if (this.indent <= indent)
913 return false;
914 return start.every(st => st.type === 'newline' || st.type === 'space');
915 }
916 *documentEnd(docEnd) {
917 if (this.type !== 'doc-mode') {
918 if (docEnd.end)
919 docEnd.end.push(this.sourceToken);
920 else
921 docEnd.end = [this.sourceToken];
922 if (this.type === 'newline')
923 yield* this.pop();
924 }
925 }
926 *lineEnd(token) {
927 switch (this.type) {
928 case 'comma':
929 case 'doc-start':
930 case 'doc-end':
931 case 'flow-seq-end':
932 case 'flow-map-end':
933 case 'map-value-ind':
934 yield* this.pop();
935 yield* this.step();
936 break;
937 case 'newline':
938 this.onKeyLine = false;
939 // fallthrough
940 case 'space':
941 case 'comment':
942 default:
943 // all other values are errors
944 if (token.end)
945 token.end.push(this.sourceToken);
946 else
947 token.end = [this.sourceToken];
948 if (this.type === 'newline')
949 yield* this.pop();
950 }
951 }
952}
953
954exports.Parser = Parser;
Note: See TracBrowser for help on using the repository browser.