123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954 |
- 'use strict';
- var cst = require('./cst.js');
- var lexer = require('./lexer.js');
- function includesToken(list, type) {
- for (let i = 0; i < list.length; ++i)
- if (list[i].type === type)
- return true;
- return false;
- }
- function findNonEmptyIndex(list) {
- for (let i = 0; i < list.length; ++i) {
- switch (list[i].type) {
- case 'space':
- case 'comment':
- case 'newline':
- break;
- default:
- return i;
- }
- }
- return -1;
- }
- function isFlowToken(token) {
- switch (token?.type) {
- case 'alias':
- case 'scalar':
- case 'single-quoted-scalar':
- case 'double-quoted-scalar':
- case 'flow-collection':
- return true;
- default:
- return false;
- }
- }
- function getPrevProps(parent) {
- switch (parent.type) {
- case 'document':
- return parent.start;
- case 'block-map': {
- const it = parent.items[parent.items.length - 1];
- return it.sep ?? it.start;
- }
- case 'block-seq':
- return parent.items[parent.items.length - 1].start;
- /* istanbul ignore next should not happen */
- default:
- return [];
- }
- }
- /** Note: May modify input array */
- function getFirstKeyStartProps(prev) {
- if (prev.length === 0)
- return [];
- let i = prev.length;
- loop: while (--i >= 0) {
- switch (prev[i].type) {
- case 'doc-start':
- case 'explicit-key-ind':
- case 'map-value-ind':
- case 'seq-item-ind':
- case 'newline':
- break loop;
- }
- }
- while (prev[++i]?.type === 'space') {
- /* loop */
- }
- return prev.splice(i, prev.length);
- }
- function fixFlowSeqItems(fc) {
- if (fc.start.type === 'flow-seq-start') {
- for (const it of fc.items) {
- if (it.sep &&
- !it.value &&
- !includesToken(it.start, 'explicit-key-ind') &&
- !includesToken(it.sep, 'map-value-ind')) {
- if (it.key)
- it.value = it.key;
- delete it.key;
- if (isFlowToken(it.value)) {
- if (it.value.end)
- Array.prototype.push.apply(it.value.end, it.sep);
- else
- it.value.end = it.sep;
- }
- else
- Array.prototype.push.apply(it.start, it.sep);
- delete it.sep;
- }
- }
- }
- }
- /**
- * A YAML concrete syntax tree (CST) parser
- *
- * ```ts
- * const src: string = ...
- * for (const token of new Parser().parse(src)) {
- * // token: Token
- * }
- * ```
- *
- * To use the parser with a user-provided lexer:
- *
- * ```ts
- * function* parse(source: string, lexer: Lexer) {
- * const parser = new Parser()
- * for (const lexeme of lexer.lex(source))
- * yield* parser.next(lexeme)
- * yield* parser.end()
- * }
- *
- * const src: string = ...
- * const lexer = new Lexer()
- * for (const token of parse(src, lexer)) {
- * // token: Token
- * }
- * ```
- */
- class Parser {
- /**
- * @param onNewLine - If defined, called separately with the start position of
- * each new line (in `parse()`, including the start of input).
- */
- constructor(onNewLine) {
- /** If true, space and sequence indicators count as indentation */
- this.atNewLine = true;
- /** If true, next token is a scalar value */
- this.atScalar = false;
- /** Current indentation level */
- this.indent = 0;
- /** Current offset since the start of parsing */
- this.offset = 0;
- /** On the same line with a block map key */
- this.onKeyLine = false;
- /** Top indicates the node that's currently being built */
- this.stack = [];
- /** The source of the current token, set in parse() */
- this.source = '';
- /** The type of the current token, set in parse() */
- this.type = '';
- // Must be defined after `next()`
- this.lexer = new lexer.Lexer();
- this.onNewLine = onNewLine;
- }
- /**
- * Parse `source` as a YAML stream.
- * If `incomplete`, a part of the last line may be left as a buffer for the next call.
- *
- * Errors are not thrown, but yielded as `{ type: 'error', message }` tokens.
- *
- * @returns A generator of tokens representing each directive, document, and other structure.
- */
- *parse(source, incomplete = false) {
- if (this.onNewLine && this.offset === 0)
- this.onNewLine(0);
- for (const lexeme of this.lexer.lex(source, incomplete))
- yield* this.next(lexeme);
- if (!incomplete)
- yield* this.end();
- }
- /**
- * Advance the parser by the `source` of one lexical token.
- */
- *next(source) {
- this.source = source;
- if (process.env.LOG_TOKENS)
- console.log('|', cst.prettyToken(source));
- if (this.atScalar) {
- this.atScalar = false;
- yield* this.step();
- this.offset += source.length;
- return;
- }
- const type = cst.tokenType(source);
- if (!type) {
- const message = `Not a YAML token: ${source}`;
- yield* this.pop({ type: 'error', offset: this.offset, message, source });
- this.offset += source.length;
- }
- else if (type === 'scalar') {
- this.atNewLine = false;
- this.atScalar = true;
- this.type = 'scalar';
- }
- else {
- this.type = type;
- yield* this.step();
- switch (type) {
- case 'newline':
- this.atNewLine = true;
- this.indent = 0;
- if (this.onNewLine)
- this.onNewLine(this.offset + source.length);
- break;
- case 'space':
- if (this.atNewLine && source[0] === ' ')
- this.indent += source.length;
- break;
- case 'explicit-key-ind':
- case 'map-value-ind':
- case 'seq-item-ind':
- if (this.atNewLine)
- this.indent += source.length;
- break;
- case 'doc-mode':
- case 'flow-error-end':
- return;
- default:
- this.atNewLine = false;
- }
- this.offset += source.length;
- }
- }
- /** Call at end of input to push out any remaining constructions */
- *end() {
- while (this.stack.length > 0)
- yield* this.pop();
- }
- get sourceToken() {
- const st = {
- type: this.type,
- offset: this.offset,
- indent: this.indent,
- source: this.source
- };
- return st;
- }
- *step() {
- const top = this.peek(1);
- if (this.type === 'doc-end' && (!top || top.type !== 'doc-end')) {
- while (this.stack.length > 0)
- yield* this.pop();
- this.stack.push({
- type: 'doc-end',
- offset: this.offset,
- source: this.source
- });
- return;
- }
- if (!top)
- return yield* this.stream();
- switch (top.type) {
- case 'document':
- return yield* this.document(top);
- case 'alias':
- case 'scalar':
- case 'single-quoted-scalar':
- case 'double-quoted-scalar':
- return yield* this.scalar(top);
- case 'block-scalar':
- return yield* this.blockScalar(top);
- case 'block-map':
- return yield* this.blockMap(top);
- case 'block-seq':
- return yield* this.blockSequence(top);
- case 'flow-collection':
- return yield* this.flowCollection(top);
- case 'doc-end':
- return yield* this.documentEnd(top);
- }
- /* istanbul ignore next should not happen */
- yield* this.pop();
- }
- peek(n) {
- return this.stack[this.stack.length - n];
- }
- *pop(error) {
- const token = error ?? this.stack.pop();
- /* istanbul ignore if should not happen */
- if (!token) {
- const message = 'Tried to pop an empty stack';
- yield { type: 'error', offset: this.offset, source: '', message };
- }
- else if (this.stack.length === 0) {
- yield token;
- }
- else {
- const top = this.peek(1);
- if (token.type === 'block-scalar') {
- // Block scalars use their parent rather than header indent
- token.indent = 'indent' in top ? top.indent : 0;
- }
- else if (token.type === 'flow-collection' && top.type === 'document') {
- // Ignore all indent for top-level flow collections
- token.indent = 0;
- }
- if (token.type === 'flow-collection')
- fixFlowSeqItems(token);
- switch (top.type) {
- case 'document':
- top.value = token;
- break;
- case 'block-scalar':
- top.props.push(token); // error
- break;
- case 'block-map': {
- const it = top.items[top.items.length - 1];
- if (it.value) {
- top.items.push({ start: [], key: token, sep: [] });
- this.onKeyLine = true;
- return;
- }
- else if (it.sep) {
- it.value = token;
- }
- else {
- Object.assign(it, { key: token, sep: [] });
- this.onKeyLine = !includesToken(it.start, 'explicit-key-ind');
- return;
- }
- break;
- }
- case 'block-seq': {
- const it = top.items[top.items.length - 1];
- if (it.value)
- top.items.push({ start: [], value: token });
- else
- it.value = token;
- break;
- }
- case 'flow-collection': {
- const it = top.items[top.items.length - 1];
- if (!it || it.value)
- top.items.push({ start: [], key: token, sep: [] });
- else if (it.sep)
- it.value = token;
- else
- Object.assign(it, { key: token, sep: [] });
- return;
- }
- /* istanbul ignore next should not happen */
- default:
- yield* this.pop();
- yield* this.pop(token);
- }
- if ((top.type === 'document' ||
- top.type === 'block-map' ||
- top.type === 'block-seq') &&
- (token.type === 'block-map' || token.type === 'block-seq')) {
- const last = token.items[token.items.length - 1];
- if (last &&
- !last.sep &&
- !last.value &&
- last.start.length > 0 &&
- findNonEmptyIndex(last.start) === -1 &&
- (token.indent === 0 ||
- last.start.every(st => st.type !== 'comment' || st.indent < token.indent))) {
- if (top.type === 'document')
- top.end = last.start;
- else
- top.items.push({ start: last.start });
- token.items.splice(-1, 1);
- }
- }
- }
- }
- *stream() {
- switch (this.type) {
- case 'directive-line':
- yield { type: 'directive', offset: this.offset, source: this.source };
- return;
- case 'byte-order-mark':
- case 'space':
- case 'comment':
- case 'newline':
- yield this.sourceToken;
- return;
- case 'doc-mode':
- case 'doc-start': {
- const doc = {
- type: 'document',
- offset: this.offset,
- start: []
- };
- if (this.type === 'doc-start')
- doc.start.push(this.sourceToken);
- this.stack.push(doc);
- return;
- }
- }
- yield {
- type: 'error',
- offset: this.offset,
- message: `Unexpected ${this.type} token in YAML stream`,
- source: this.source
- };
- }
- *document(doc) {
- if (doc.value)
- return yield* this.lineEnd(doc);
- switch (this.type) {
- case 'doc-start': {
- if (findNonEmptyIndex(doc.start) !== -1) {
- yield* this.pop();
- yield* this.step();
- }
- else
- doc.start.push(this.sourceToken);
- return;
- }
- case 'anchor':
- case 'tag':
- case 'space':
- case 'comment':
- case 'newline':
- doc.start.push(this.sourceToken);
- return;
- }
- const bv = this.startBlockValue(doc);
- if (bv)
- this.stack.push(bv);
- else {
- yield {
- type: 'error',
- offset: this.offset,
- message: `Unexpected ${this.type} token in YAML document`,
- source: this.source
- };
- }
- }
- *scalar(scalar) {
- if (this.type === 'map-value-ind') {
- const prev = getPrevProps(this.peek(2));
- const start = getFirstKeyStartProps(prev);
- let sep;
- if (scalar.end) {
- sep = scalar.end;
- sep.push(this.sourceToken);
- delete scalar.end;
- }
- else
- sep = [this.sourceToken];
- const map = {
- type: 'block-map',
- offset: scalar.offset,
- indent: scalar.indent,
- items: [{ start, key: scalar, sep }]
- };
- this.onKeyLine = true;
- this.stack[this.stack.length - 1] = map;
- }
- else
- yield* this.lineEnd(scalar);
- }
- *blockScalar(scalar) {
- switch (this.type) {
- case 'space':
- case 'comment':
- case 'newline':
- scalar.props.push(this.sourceToken);
- return;
- case 'scalar':
- scalar.source = this.source;
- // block-scalar source includes trailing newline
- this.atNewLine = true;
- this.indent = 0;
- if (this.onNewLine) {
- let nl = this.source.indexOf('\n') + 1;
- while (nl !== 0) {
- this.onNewLine(this.offset + nl);
- nl = this.source.indexOf('\n', nl) + 1;
- }
- }
- yield* this.pop();
- break;
- /* istanbul ignore next should not happen */
- default:
- yield* this.pop();
- yield* this.step();
- }
- }
- *blockMap(map) {
- const it = map.items[map.items.length - 1];
- // it.sep is true-ish if pair already has key or : separator
- switch (this.type) {
- case 'newline':
- this.onKeyLine = false;
- if (it.value) {
- const end = 'end' in it.value ? it.value.end : undefined;
- const last = Array.isArray(end) ? end[end.length - 1] : undefined;
- if (last?.type === 'comment')
- end?.push(this.sourceToken);
- else
- map.items.push({ start: [this.sourceToken] });
- }
- else if (it.sep) {
- it.sep.push(this.sourceToken);
- }
- else {
- it.start.push(this.sourceToken);
- }
- return;
- case 'space':
- case 'comment':
- if (it.value) {
- map.items.push({ start: [this.sourceToken] });
- }
- else if (it.sep) {
- it.sep.push(this.sourceToken);
- }
- else {
- if (this.atIndentedComment(it.start, map.indent)) {
- const prev = map.items[map.items.length - 2];
- const end = prev?.value?.end;
- if (Array.isArray(end)) {
- Array.prototype.push.apply(end, it.start);
- end.push(this.sourceToken);
- map.items.pop();
- return;
- }
- }
- it.start.push(this.sourceToken);
- }
- return;
- }
- if (this.indent >= map.indent) {
- const atNextItem = !this.onKeyLine && this.indent === map.indent && it.sep;
- // For empty nodes, assign newline-separated not indented empty tokens to following node
- let start = [];
- if (atNextItem && it.sep && !it.value) {
- const nl = [];
- for (let i = 0; i < it.sep.length; ++i) {
- const st = it.sep[i];
- switch (st.type) {
- case 'newline':
- nl.push(i);
- break;
- case 'space':
- break;
- case 'comment':
- if (st.indent > map.indent)
- nl.length = 0;
- break;
- default:
- nl.length = 0;
- }
- }
- if (nl.length >= 2)
- start = it.sep.splice(nl[1]);
- }
- switch (this.type) {
- case 'anchor':
- case 'tag':
- if (atNextItem || it.value) {
- start.push(this.sourceToken);
- map.items.push({ start });
- this.onKeyLine = true;
- }
- else if (it.sep) {
- it.sep.push(this.sourceToken);
- }
- else {
- it.start.push(this.sourceToken);
- }
- return;
- case 'explicit-key-ind':
- if (!it.sep && !includesToken(it.start, 'explicit-key-ind')) {
- it.start.push(this.sourceToken);
- }
- else if (atNextItem || it.value) {
- start.push(this.sourceToken);
- map.items.push({ start });
- }
- else {
- this.stack.push({
- type: 'block-map',
- offset: this.offset,
- indent: this.indent,
- items: [{ start: [this.sourceToken] }]
- });
- }
- this.onKeyLine = true;
- return;
- case 'map-value-ind':
- if (includesToken(it.start, 'explicit-key-ind')) {
- if (!it.sep) {
- if (includesToken(it.start, 'newline')) {
- Object.assign(it, { key: null, sep: [this.sourceToken] });
- }
- else {
- const start = getFirstKeyStartProps(it.start);
- this.stack.push({
- type: 'block-map',
- offset: this.offset,
- indent: this.indent,
- items: [{ start, key: null, sep: [this.sourceToken] }]
- });
- }
- }
- else if (it.value) {
- map.items.push({ start: [], key: null, sep: [this.sourceToken] });
- }
- else if (includesToken(it.sep, 'map-value-ind')) {
- this.stack.push({
- type: 'block-map',
- offset: this.offset,
- indent: this.indent,
- items: [{ start, key: null, sep: [this.sourceToken] }]
- });
- }
- else if (isFlowToken(it.key) &&
- !includesToken(it.sep, 'newline')) {
- const start = getFirstKeyStartProps(it.start);
- const key = it.key;
- const sep = it.sep;
- sep.push(this.sourceToken);
- // @ts-expect-error type guard is wrong here
- delete it.key, delete it.sep;
- this.stack.push({
- type: 'block-map',
- offset: this.offset,
- indent: this.indent,
- items: [{ start, key, sep }]
- });
- }
- else if (start.length > 0) {
- // Not actually at next item
- it.sep = it.sep.concat(start, this.sourceToken);
- }
- else {
- it.sep.push(this.sourceToken);
- }
- }
- else {
- if (!it.sep) {
- Object.assign(it, { key: null, sep: [this.sourceToken] });
- }
- else if (it.value || atNextItem) {
- map.items.push({ start, key: null, sep: [this.sourceToken] });
- }
- else if (includesToken(it.sep, 'map-value-ind')) {
- this.stack.push({
- type: 'block-map',
- offset: this.offset,
- indent: this.indent,
- items: [{ start: [], key: null, sep: [this.sourceToken] }]
- });
- }
- else {
- it.sep.push(this.sourceToken);
- }
- }
- this.onKeyLine = true;
- return;
- case 'alias':
- case 'scalar':
- case 'single-quoted-scalar':
- case 'double-quoted-scalar': {
- const fs = this.flowScalar(this.type);
- if (atNextItem || it.value) {
- map.items.push({ start, key: fs, sep: [] });
- this.onKeyLine = true;
- }
- else if (it.sep) {
- this.stack.push(fs);
- }
- else {
- Object.assign(it, { key: fs, sep: [] });
- this.onKeyLine = true;
- }
- return;
- }
- default: {
- const bv = this.startBlockValue(map);
- if (bv) {
- if (atNextItem &&
- bv.type !== 'block-seq' &&
- includesToken(it.start, 'explicit-key-ind')) {
- map.items.push({ start });
- }
- this.stack.push(bv);
- return;
- }
- }
- }
- }
- yield* this.pop();
- yield* this.step();
- }
- *blockSequence(seq) {
- const it = seq.items[seq.items.length - 1];
- switch (this.type) {
- case 'newline':
- if (it.value) {
- const end = 'end' in it.value ? it.value.end : undefined;
- const last = Array.isArray(end) ? end[end.length - 1] : undefined;
- if (last?.type === 'comment')
- end?.push(this.sourceToken);
- else
- seq.items.push({ start: [this.sourceToken] });
- }
- else
- it.start.push(this.sourceToken);
- return;
- case 'space':
- case 'comment':
- if (it.value)
- seq.items.push({ start: [this.sourceToken] });
- else {
- if (this.atIndentedComment(it.start, seq.indent)) {
- const prev = seq.items[seq.items.length - 2];
- const end = prev?.value?.end;
- if (Array.isArray(end)) {
- Array.prototype.push.apply(end, it.start);
- end.push(this.sourceToken);
- seq.items.pop();
- return;
- }
- }
- it.start.push(this.sourceToken);
- }
- return;
- case 'anchor':
- case 'tag':
- if (it.value || this.indent <= seq.indent)
- break;
- it.start.push(this.sourceToken);
- return;
- case 'seq-item-ind':
- if (this.indent !== seq.indent)
- break;
- if (it.value || includesToken(it.start, 'seq-item-ind'))
- seq.items.push({ start: [this.sourceToken] });
- else
- it.start.push(this.sourceToken);
- return;
- }
- if (this.indent > seq.indent) {
- const bv = this.startBlockValue(seq);
- if (bv) {
- this.stack.push(bv);
- return;
- }
- }
- yield* this.pop();
- yield* this.step();
- }
- *flowCollection(fc) {
- const it = fc.items[fc.items.length - 1];
- if (this.type === 'flow-error-end') {
- let top;
- do {
- yield* this.pop();
- top = this.peek(1);
- } while (top && top.type === 'flow-collection');
- }
- else if (fc.end.length === 0) {
- switch (this.type) {
- case 'comma':
- case 'explicit-key-ind':
- if (!it || it.sep)
- fc.items.push({ start: [this.sourceToken] });
- else
- it.start.push(this.sourceToken);
- return;
- case 'map-value-ind':
- if (!it || it.value)
- fc.items.push({ start: [], key: null, sep: [this.sourceToken] });
- else if (it.sep)
- it.sep.push(this.sourceToken);
- else
- Object.assign(it, { key: null, sep: [this.sourceToken] });
- return;
- case 'space':
- case 'comment':
- case 'newline':
- case 'anchor':
- case 'tag':
- if (!it || it.value)
- fc.items.push({ start: [this.sourceToken] });
- else if (it.sep)
- it.sep.push(this.sourceToken);
- else
- it.start.push(this.sourceToken);
- return;
- case 'alias':
- case 'scalar':
- case 'single-quoted-scalar':
- case 'double-quoted-scalar': {
- const fs = this.flowScalar(this.type);
- if (!it || it.value)
- fc.items.push({ start: [], key: fs, sep: [] });
- else if (it.sep)
- this.stack.push(fs);
- else
- Object.assign(it, { key: fs, sep: [] });
- return;
- }
- case 'flow-map-end':
- case 'flow-seq-end':
- fc.end.push(this.sourceToken);
- return;
- }
- const bv = this.startBlockValue(fc);
- /* istanbul ignore else should not happen */
- if (bv)
- this.stack.push(bv);
- else {
- yield* this.pop();
- yield* this.step();
- }
- }
- else {
- const parent = this.peek(2);
- if (parent.type === 'block-map' &&
- ((this.type === 'map-value-ind' && parent.indent === fc.indent) ||
- (this.type === 'newline' &&
- !parent.items[parent.items.length - 1].sep))) {
- yield* this.pop();
- yield* this.step();
- }
- else if (this.type === 'map-value-ind' &&
- parent.type !== 'flow-collection') {
- const prev = getPrevProps(parent);
- const start = getFirstKeyStartProps(prev);
- fixFlowSeqItems(fc);
- const sep = fc.end.splice(1, fc.end.length);
- sep.push(this.sourceToken);
- const map = {
- type: 'block-map',
- offset: fc.offset,
- indent: fc.indent,
- items: [{ start, key: fc, sep }]
- };
- this.onKeyLine = true;
- this.stack[this.stack.length - 1] = map;
- }
- else {
- yield* this.lineEnd(fc);
- }
- }
- }
- flowScalar(type) {
- if (this.onNewLine) {
- let nl = this.source.indexOf('\n') + 1;
- while (nl !== 0) {
- this.onNewLine(this.offset + nl);
- nl = this.source.indexOf('\n', nl) + 1;
- }
- }
- return {
- type,
- offset: this.offset,
- indent: this.indent,
- source: this.source
- };
- }
- startBlockValue(parent) {
- switch (this.type) {
- case 'alias':
- case 'scalar':
- case 'single-quoted-scalar':
- case 'double-quoted-scalar':
- return this.flowScalar(this.type);
- case 'block-scalar-header':
- return {
- type: 'block-scalar',
- offset: this.offset,
- indent: this.indent,
- props: [this.sourceToken],
- source: ''
- };
- case 'flow-map-start':
- case 'flow-seq-start':
- return {
- type: 'flow-collection',
- offset: this.offset,
- indent: this.indent,
- start: this.sourceToken,
- items: [],
- end: []
- };
- case 'seq-item-ind':
- return {
- type: 'block-seq',
- offset: this.offset,
- indent: this.indent,
- items: [{ start: [this.sourceToken] }]
- };
- case 'explicit-key-ind': {
- this.onKeyLine = true;
- const prev = getPrevProps(parent);
- const start = getFirstKeyStartProps(prev);
- start.push(this.sourceToken);
- return {
- type: 'block-map',
- offset: this.offset,
- indent: this.indent,
- items: [{ start }]
- };
- }
- case 'map-value-ind': {
- this.onKeyLine = true;
- const prev = getPrevProps(parent);
- const start = getFirstKeyStartProps(prev);
- return {
- type: 'block-map',
- offset: this.offset,
- indent: this.indent,
- items: [{ start, key: null, sep: [this.sourceToken] }]
- };
- }
- }
- return null;
- }
- atIndentedComment(start, indent) {
- if (this.type !== 'comment')
- return false;
- if (this.indent <= indent)
- return false;
- return start.every(st => st.type === 'newline' || st.type === 'space');
- }
- *documentEnd(docEnd) {
- if (this.type !== 'doc-mode') {
- if (docEnd.end)
- docEnd.end.push(this.sourceToken);
- else
- docEnd.end = [this.sourceToken];
- if (this.type === 'newline')
- yield* this.pop();
- }
- }
- *lineEnd(token) {
- switch (this.type) {
- case 'comma':
- case 'doc-start':
- case 'doc-end':
- case 'flow-seq-end':
- case 'flow-map-end':
- case 'map-value-ind':
- yield* this.pop();
- yield* this.step();
- break;
- case 'newline':
- this.onKeyLine = false;
- // fallthrough
- case 'space':
- case 'comment':
- default:
- // all other values are errors
- if (token.end)
- token.end.push(this.sourceToken);
- else
- token.end = [this.sourceToken];
- if (this.type === 'newline')
- yield* this.pop();
- }
- }
- }
- exports.Parser = Parser;
|