1 | 'use strict';
|
2 |
|
3 | var cst = require('./cst.js');
|
4 | var lexer = require('./lexer.js');
|
5 |
|
6 | function 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 | }
|
12 | function 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 | }
|
25 | function 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 | }
|
37 | function 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 |
|
48 | default:
|
49 | return [];
|
50 | }
|
51 | }
|
52 |
|
53 | function 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 |
|
69 | }
|
70 | return prev.splice(i, prev.length);
|
71 | }
|
72 | function 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 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 | class Parser {
|
123 | |
124 |
|
125 |
|
126 |
|
127 | constructor(onNewLine) {
|
128 |
|
129 | this.atNewLine = true;
|
130 |
|
131 | this.atScalar = false;
|
132 |
|
133 | this.indent = 0;
|
134 |
|
135 | this.offset = 0;
|
136 |
|
137 | this.onKeyLine = false;
|
138 |
|
139 | this.stack = [];
|
140 |
|
141 | this.source = '';
|
142 |
|
143 | this.type = '';
|
144 |
|
145 | this.lexer = new lexer.Lexer();
|
146 | this.onNewLine = onNewLine;
|
147 | }
|
148 | |
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
284 | token.indent = 'indent' in top ? top.indent : 0;
|
285 | }
|
286 | else if (token.type === 'flow-collection' && top.type === 'document') {
|
287 |
|
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);
|
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 = !it.explicitKey;
|
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 |
|
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 |
|
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 |
|
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 |
|
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 atMapIndent = !this.onKeyLine && this.indent === map.indent;
|
521 | const atNextItem = atMapIndent &&
|
522 | (it.sep || it.explicitKey) &&
|
523 | this.type !== 'seq-item-ind';
|
524 |
|
525 | let start = [];
|
526 | if (atNextItem && it.sep && !it.value) {
|
527 | const nl = [];
|
528 | for (let i = 0; i < it.sep.length; ++i) {
|
529 | const st = it.sep[i];
|
530 | switch (st.type) {
|
531 | case 'newline':
|
532 | nl.push(i);
|
533 | break;
|
534 | case 'space':
|
535 | break;
|
536 | case 'comment':
|
537 | if (st.indent > map.indent)
|
538 | nl.length = 0;
|
539 | break;
|
540 | default:
|
541 | nl.length = 0;
|
542 | }
|
543 | }
|
544 | if (nl.length >= 2)
|
545 | start = it.sep.splice(nl[1]);
|
546 | }
|
547 | switch (this.type) {
|
548 | case 'anchor':
|
549 | case 'tag':
|
550 | if (atNextItem || it.value) {
|
551 | start.push(this.sourceToken);
|
552 | map.items.push({ start });
|
553 | this.onKeyLine = true;
|
554 | }
|
555 | else if (it.sep) {
|
556 | it.sep.push(this.sourceToken);
|
557 | }
|
558 | else {
|
559 | it.start.push(this.sourceToken);
|
560 | }
|
561 | return;
|
562 | case 'explicit-key-ind':
|
563 | if (!it.sep && !it.explicitKey) {
|
564 | it.start.push(this.sourceToken);
|
565 | it.explicitKey = true;
|
566 | }
|
567 | else if (atNextItem || it.value) {
|
568 | start.push(this.sourceToken);
|
569 | map.items.push({ start, explicitKey: true });
|
570 | }
|
571 | else {
|
572 | this.stack.push({
|
573 | type: 'block-map',
|
574 | offset: this.offset,
|
575 | indent: this.indent,
|
576 | items: [{ start: [this.sourceToken], explicitKey: true }]
|
577 | });
|
578 | }
|
579 | this.onKeyLine = true;
|
580 | return;
|
581 | case 'map-value-ind':
|
582 | if (it.explicitKey) {
|
583 | if (!it.sep) {
|
584 | if (includesToken(it.start, 'newline')) {
|
585 | Object.assign(it, { key: null, sep: [this.sourceToken] });
|
586 | }
|
587 | else {
|
588 | const start = getFirstKeyStartProps(it.start);
|
589 | this.stack.push({
|
590 | type: 'block-map',
|
591 | offset: this.offset,
|
592 | indent: this.indent,
|
593 | items: [{ start, key: null, sep: [this.sourceToken] }]
|
594 | });
|
595 | }
|
596 | }
|
597 | else if (it.value) {
|
598 | map.items.push({ start: [], key: null, sep: [this.sourceToken] });
|
599 | }
|
600 | else if (includesToken(it.sep, 'map-value-ind')) {
|
601 | this.stack.push({
|
602 | type: 'block-map',
|
603 | offset: this.offset,
|
604 | indent: this.indent,
|
605 | items: [{ start, key: null, sep: [this.sourceToken] }]
|
606 | });
|
607 | }
|
608 | else if (isFlowToken(it.key) &&
|
609 | !includesToken(it.sep, 'newline')) {
|
610 | const start = getFirstKeyStartProps(it.start);
|
611 | const key = it.key;
|
612 | const sep = it.sep;
|
613 | sep.push(this.sourceToken);
|
614 |
|
615 | delete it.key;
|
616 |
|
617 | delete it.sep;
|
618 | this.stack.push({
|
619 | type: 'block-map',
|
620 | offset: this.offset,
|
621 | indent: this.indent,
|
622 | items: [{ start, key, sep }]
|
623 | });
|
624 | }
|
625 | else if (start.length > 0) {
|
626 |
|
627 | it.sep = it.sep.concat(start, this.sourceToken);
|
628 | }
|
629 | else {
|
630 | it.sep.push(this.sourceToken);
|
631 | }
|
632 | }
|
633 | else {
|
634 | if (!it.sep) {
|
635 | Object.assign(it, { key: null, sep: [this.sourceToken] });
|
636 | }
|
637 | else if (it.value || atNextItem) {
|
638 | map.items.push({ start, key: null, sep: [this.sourceToken] });
|
639 | }
|
640 | else if (includesToken(it.sep, 'map-value-ind')) {
|
641 | this.stack.push({
|
642 | type: 'block-map',
|
643 | offset: this.offset,
|
644 | indent: this.indent,
|
645 | items: [{ start: [], key: null, sep: [this.sourceToken] }]
|
646 | });
|
647 | }
|
648 | else {
|
649 | it.sep.push(this.sourceToken);
|
650 | }
|
651 | }
|
652 | this.onKeyLine = true;
|
653 | return;
|
654 | case 'alias':
|
655 | case 'scalar':
|
656 | case 'single-quoted-scalar':
|
657 | case 'double-quoted-scalar': {
|
658 | const fs = this.flowScalar(this.type);
|
659 | if (atNextItem || it.value) {
|
660 | map.items.push({ start, key: fs, sep: [] });
|
661 | this.onKeyLine = true;
|
662 | }
|
663 | else if (it.sep) {
|
664 | this.stack.push(fs);
|
665 | }
|
666 | else {
|
667 | Object.assign(it, { key: fs, sep: [] });
|
668 | this.onKeyLine = true;
|
669 | }
|
670 | return;
|
671 | }
|
672 | default: {
|
673 | const bv = this.startBlockValue(map);
|
674 | if (bv) {
|
675 | if (atMapIndent && bv.type !== 'block-seq') {
|
676 | map.items.push({ start });
|
677 | }
|
678 | this.stack.push(bv);
|
679 | return;
|
680 | }
|
681 | }
|
682 | }
|
683 | }
|
684 | yield* this.pop();
|
685 | yield* this.step();
|
686 | }
|
687 | *blockSequence(seq) {
|
688 | const it = seq.items[seq.items.length - 1];
|
689 | switch (this.type) {
|
690 | case 'newline':
|
691 | if (it.value) {
|
692 | const end = 'end' in it.value ? it.value.end : undefined;
|
693 | const last = Array.isArray(end) ? end[end.length - 1] : undefined;
|
694 | if (last?.type === 'comment')
|
695 | end?.push(this.sourceToken);
|
696 | else
|
697 | seq.items.push({ start: [this.sourceToken] });
|
698 | }
|
699 | else
|
700 | it.start.push(this.sourceToken);
|
701 | return;
|
702 | case 'space':
|
703 | case 'comment':
|
704 | if (it.value)
|
705 | seq.items.push({ start: [this.sourceToken] });
|
706 | else {
|
707 | if (this.atIndentedComment(it.start, seq.indent)) {
|
708 | const prev = seq.items[seq.items.length - 2];
|
709 | const end = prev?.value?.end;
|
710 | if (Array.isArray(end)) {
|
711 | Array.prototype.push.apply(end, it.start);
|
712 | end.push(this.sourceToken);
|
713 | seq.items.pop();
|
714 | return;
|
715 | }
|
716 | }
|
717 | it.start.push(this.sourceToken);
|
718 | }
|
719 | return;
|
720 | case 'anchor':
|
721 | case 'tag':
|
722 | if (it.value || this.indent <= seq.indent)
|
723 | break;
|
724 | it.start.push(this.sourceToken);
|
725 | return;
|
726 | case 'seq-item-ind':
|
727 | if (this.indent !== seq.indent)
|
728 | break;
|
729 | if (it.value || includesToken(it.start, 'seq-item-ind'))
|
730 | seq.items.push({ start: [this.sourceToken] });
|
731 | else
|
732 | it.start.push(this.sourceToken);
|
733 | return;
|
734 | }
|
735 | if (this.indent > seq.indent) {
|
736 | const bv = this.startBlockValue(seq);
|
737 | if (bv) {
|
738 | this.stack.push(bv);
|
739 | return;
|
740 | }
|
741 | }
|
742 | yield* this.pop();
|
743 | yield* this.step();
|
744 | }
|
745 | *flowCollection(fc) {
|
746 | const it = fc.items[fc.items.length - 1];
|
747 | if (this.type === 'flow-error-end') {
|
748 | let top;
|
749 | do {
|
750 | yield* this.pop();
|
751 | top = this.peek(1);
|
752 | } while (top && top.type === 'flow-collection');
|
753 | }
|
754 | else if (fc.end.length === 0) {
|
755 | switch (this.type) {
|
756 | case 'comma':
|
757 | case 'explicit-key-ind':
|
758 | if (!it || it.sep)
|
759 | fc.items.push({ start: [this.sourceToken] });
|
760 | else
|
761 | it.start.push(this.sourceToken);
|
762 | return;
|
763 | case 'map-value-ind':
|
764 | if (!it || it.value)
|
765 | fc.items.push({ start: [], key: null, sep: [this.sourceToken] });
|
766 | else if (it.sep)
|
767 | it.sep.push(this.sourceToken);
|
768 | else
|
769 | Object.assign(it, { key: null, sep: [this.sourceToken] });
|
770 | return;
|
771 | case 'space':
|
772 | case 'comment':
|
773 | case 'newline':
|
774 | case 'anchor':
|
775 | case 'tag':
|
776 | if (!it || it.value)
|
777 | fc.items.push({ start: [this.sourceToken] });
|
778 | else if (it.sep)
|
779 | it.sep.push(this.sourceToken);
|
780 | else
|
781 | it.start.push(this.sourceToken);
|
782 | return;
|
783 | case 'alias':
|
784 | case 'scalar':
|
785 | case 'single-quoted-scalar':
|
786 | case 'double-quoted-scalar': {
|
787 | const fs = this.flowScalar(this.type);
|
788 | if (!it || it.value)
|
789 | fc.items.push({ start: [], key: fs, sep: [] });
|
790 | else if (it.sep)
|
791 | this.stack.push(fs);
|
792 | else
|
793 | Object.assign(it, { key: fs, sep: [] });
|
794 | return;
|
795 | }
|
796 | case 'flow-map-end':
|
797 | case 'flow-seq-end':
|
798 | fc.end.push(this.sourceToken);
|
799 | return;
|
800 | }
|
801 | const bv = this.startBlockValue(fc);
|
802 |
|
803 | if (bv)
|
804 | this.stack.push(bv);
|
805 | else {
|
806 | yield* this.pop();
|
807 | yield* this.step();
|
808 | }
|
809 | }
|
810 | else {
|
811 | const parent = this.peek(2);
|
812 | if (parent.type === 'block-map' &&
|
813 | ((this.type === 'map-value-ind' && parent.indent === fc.indent) ||
|
814 | (this.type === 'newline' &&
|
815 | !parent.items[parent.items.length - 1].sep))) {
|
816 | yield* this.pop();
|
817 | yield* this.step();
|
818 | }
|
819 | else if (this.type === 'map-value-ind' &&
|
820 | parent.type !== 'flow-collection') {
|
821 | const prev = getPrevProps(parent);
|
822 | const start = getFirstKeyStartProps(prev);
|
823 | fixFlowSeqItems(fc);
|
824 | const sep = fc.end.splice(1, fc.end.length);
|
825 | sep.push(this.sourceToken);
|
826 | const map = {
|
827 | type: 'block-map',
|
828 | offset: fc.offset,
|
829 | indent: fc.indent,
|
830 | items: [{ start, key: fc, sep }]
|
831 | };
|
832 | this.onKeyLine = true;
|
833 | this.stack[this.stack.length - 1] = map;
|
834 | }
|
835 | else {
|
836 | yield* this.lineEnd(fc);
|
837 | }
|
838 | }
|
839 | }
|
840 | flowScalar(type) {
|
841 | if (this.onNewLine) {
|
842 | let nl = this.source.indexOf('\n') + 1;
|
843 | while (nl !== 0) {
|
844 | this.onNewLine(this.offset + nl);
|
845 | nl = this.source.indexOf('\n', nl) + 1;
|
846 | }
|
847 | }
|
848 | return {
|
849 | type,
|
850 | offset: this.offset,
|
851 | indent: this.indent,
|
852 | source: this.source
|
853 | };
|
854 | }
|
855 | startBlockValue(parent) {
|
856 | switch (this.type) {
|
857 | case 'alias':
|
858 | case 'scalar':
|
859 | case 'single-quoted-scalar':
|
860 | case 'double-quoted-scalar':
|
861 | return this.flowScalar(this.type);
|
862 | case 'block-scalar-header':
|
863 | return {
|
864 | type: 'block-scalar',
|
865 | offset: this.offset,
|
866 | indent: this.indent,
|
867 | props: [this.sourceToken],
|
868 | source: ''
|
869 | };
|
870 | case 'flow-map-start':
|
871 | case 'flow-seq-start':
|
872 | return {
|
873 | type: 'flow-collection',
|
874 | offset: this.offset,
|
875 | indent: this.indent,
|
876 | start: this.sourceToken,
|
877 | items: [],
|
878 | end: []
|
879 | };
|
880 | case 'seq-item-ind':
|
881 | return {
|
882 | type: 'block-seq',
|
883 | offset: this.offset,
|
884 | indent: this.indent,
|
885 | items: [{ start: [this.sourceToken] }]
|
886 | };
|
887 | case 'explicit-key-ind': {
|
888 | this.onKeyLine = true;
|
889 | const prev = getPrevProps(parent);
|
890 | const start = getFirstKeyStartProps(prev);
|
891 | start.push(this.sourceToken);
|
892 | return {
|
893 | type: 'block-map',
|
894 | offset: this.offset,
|
895 | indent: this.indent,
|
896 | items: [{ start, explicitKey: true }]
|
897 | };
|
898 | }
|
899 | case 'map-value-ind': {
|
900 | this.onKeyLine = true;
|
901 | const prev = getPrevProps(parent);
|
902 | const start = getFirstKeyStartProps(prev);
|
903 | return {
|
904 | type: 'block-map',
|
905 | offset: this.offset,
|
906 | indent: this.indent,
|
907 | items: [{ start, key: null, sep: [this.sourceToken] }]
|
908 | };
|
909 | }
|
910 | }
|
911 | return null;
|
912 | }
|
913 | atIndentedComment(start, indent) {
|
914 | if (this.type !== 'comment')
|
915 | return false;
|
916 | if (this.indent <= indent)
|
917 | return false;
|
918 | return start.every(st => st.type === 'newline' || st.type === 'space');
|
919 | }
|
920 | *documentEnd(docEnd) {
|
921 | if (this.type !== 'doc-mode') {
|
922 | if (docEnd.end)
|
923 | docEnd.end.push(this.sourceToken);
|
924 | else
|
925 | docEnd.end = [this.sourceToken];
|
926 | if (this.type === 'newline')
|
927 | yield* this.pop();
|
928 | }
|
929 | }
|
930 | *lineEnd(token) {
|
931 | switch (this.type) {
|
932 | case 'comma':
|
933 | case 'doc-start':
|
934 | case 'doc-end':
|
935 | case 'flow-seq-end':
|
936 | case 'flow-map-end':
|
937 | case 'map-value-ind':
|
938 | yield* this.pop();
|
939 | yield* this.step();
|
940 | break;
|
941 | case 'newline':
|
942 | this.onKeyLine = false;
|
943 |
|
944 | case 'space':
|
945 | case 'comment':
|
946 | default:
|
947 |
|
948 | if (token.end)
|
949 | token.end.push(this.sourceToken);
|
950 | else
|
951 | token.end = [this.sourceToken];
|
952 | if (this.type === 'newline')
|
953 | yield* this.pop();
|
954 | }
|
955 | }
|
956 | }
|
957 |
|
958 | exports.Parser = Parser;
|