import createDebugger from 'debug'; const debug = createDebugger('cmd-ts:parser'); /** * Create an AST from a token list * * @param tokens A token list, coming from `tokenizer.ts` * @param forceFlag Keys to force as flag. {@see ForceFlag} to read more about it. */ export function parse(tokens, forceFlag) { if (debug.enabled) { const registered = { shortFlags: [...forceFlag.forceFlagShortNames], longFlags: [...forceFlag.forceFlagLongNames], shortOptions: [...forceFlag.forceOptionShortNames], longOptions: [...forceFlag.forceOptionLongNames], }; debug(`Registered:`, JSON.stringify(registered)); } const nodes = []; let index = 0; let forcedPositional = false; function getToken() { return tokens[index++]; } function peekToken() { return tokens[index]; } while (index < tokens.length) { const currentToken = getToken(); if (!currentToken) break; if (currentToken.type === 'argumentDivider') { continue; } if (forcedPositional) { let str = currentToken.raw; let nextToken = getToken(); while (nextToken && (nextToken === null || nextToken === void 0 ? void 0 : nextToken.type) !== 'argumentDivider') { str += nextToken.raw; nextToken = getToken(); } nodes.push({ type: 'positionalArgument', index: currentToken.index, raw: str, }); continue; } if (currentToken.type === 'char') { let str = currentToken.raw; let nextToken = getToken(); while (nextToken && (nextToken === null || nextToken === void 0 ? void 0 : nextToken.type) !== 'argumentDivider') { str += nextToken.raw; nextToken = getToken(); } nodes.push({ type: 'positionalArgument', index: currentToken.index, raw: str, }); continue; } if (currentToken.type === 'longPrefix') { let nextToken = getToken(); if ((nextToken === null || nextToken === void 0 ? void 0 : nextToken.type) === 'argumentDivider') { nodes.push({ type: 'forcePositional', index: currentToken.index, raw: '--', }); forcedPositional = true; continue; } let key = ''; while (nextToken && (nextToken === null || nextToken === void 0 ? void 0 : nextToken.raw) !== '=' && (nextToken === null || nextToken === void 0 ? void 0 : nextToken.type) !== 'argumentDivider') { key += nextToken.raw; nextToken = getToken(); } const parsedValue = parseOptionValue({ key, delimiterToken: nextToken, forceFlag: forceFlag.forceFlagLongNames, getToken, peekToken, forceOption: forceFlag.forceOptionLongNames, }); let raw = `--${key}`; if (parsedValue) { raw += parsedValue.raw; } nodes.push({ type: 'longOption', key, index: currentToken.index, raw, value: parsedValue, }); continue; } if (currentToken.type === 'shortPrefix') { let keys = []; let nextToken = getToken(); if ((nextToken === null || nextToken === void 0 ? void 0 : nextToken.type) === 'argumentDivider' || !nextToken) { nodes.push({ type: 'positionalArgument', index: currentToken.index, raw: '-', }); continue; } while (nextToken && (nextToken === null || nextToken === void 0 ? void 0 : nextToken.type) !== 'argumentDivider' && (nextToken === null || nextToken === void 0 ? void 0 : nextToken.raw) !== '=') { keys.push(nextToken); nextToken = getToken(); } const lastKey = keys.pop(); const parsedValue = parseOptionValue({ key: lastKey.raw, delimiterToken: nextToken, forceFlag: forceFlag.forceFlagShortNames, forceOption: forceFlag.forceOptionShortNames, getToken, peekToken, }); const options = []; for (const key of keys) { options.push({ type: 'shortOption', index: key.index, raw: key.raw, key: key.raw, }); } let lastKeyRaw = lastKey.raw; if (parsedValue) { lastKeyRaw += parsedValue.raw; } options.push({ type: 'shortOption', index: lastKey.index, raw: lastKeyRaw, value: parsedValue, key: lastKey.raw, }); let optionsRaw = `-${keys.map((x) => x.raw).join('')}${lastKey.raw}`; if (parsedValue) { optionsRaw += parsedValue.raw; } const shortOptions = { type: 'shortOptions', index: currentToken.index, raw: optionsRaw, options, }; nodes.push(shortOptions); continue; } index++; continue; } if (debug.enabled) { const objectNodes = nodes.map((node) => ({ [node.type]: node.raw })); debug(`Parsed items:`, JSON.stringify(objectNodes)); } return nodes; } function parseOptionValue(opts) { var _a; let { getToken, delimiterToken, forceFlag, key, forceOption } = opts; const shouldReadKeyAsOption = forceOption.has(key); const shouldReadKeyAsFlag = !shouldReadKeyAsOption && (forceFlag.has(key) || ((_a = opts.peekToken()) === null || _a === void 0 ? void 0 : _a.type) !== 'char'); if (!delimiterToken || (delimiterToken.raw !== '=' && shouldReadKeyAsFlag)) { return; } const delimiter = delimiterToken.raw === '=' ? '=' : ' '; const delimiterIndex = delimiterToken.index; let nextToken = getToken(); if (!nextToken) { return; } let value = ''; const valueIndex = nextToken.index; while (nextToken && (nextToken === null || nextToken === void 0 ? void 0 : nextToken.type) !== 'argumentDivider') { value += nextToken.raw; nextToken = getToken(); } return { type: 'optionValue', index: delimiterToken.index, delimiter: { type: 'delimiter', raw: delimiter, index: delimiterIndex }, node: { type: 'value', raw: value, index: valueIndex }, raw: `${delimiter}${value}`, }; } //# sourceMappingURL=parser.js.map