forked from Fediversity/fediversity.eu
1023 lines
35 KiB
JavaScript
1023 lines
35 KiB
JavaScript
"use strict";Object.defineProperty(exports, "__esModule", {value: true});/* eslint max-len: 0 */
|
|
|
|
// A recursive descent parser operates by defining functions for all
|
|
// syntactic elements, and recursively calling those, each function
|
|
// advancing the input stream and returning an AST node. Precedence
|
|
// of constructs (for example, the fact that `!x[1]` means `!(x[1])`
|
|
// instead of `(!x)[1]` is handled by the fact that the parser
|
|
// function that parses unary prefix operators is called first, and
|
|
// in turn calls the function that parses `[]` subscripts — that
|
|
// way, it'll receive the node for `x[1]` already parsed, and wraps
|
|
// *that* in the unary operator node.
|
|
//
|
|
// Acorn uses an [operator precedence parser][opp] to handle binary
|
|
// operator precedence, because it is much more compact than using
|
|
// the technique outlined above, which uses different, nesting
|
|
// functions to specify precedence, for all of the ten binary
|
|
// precedence levels that JavaScript defines.
|
|
//
|
|
// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var _flow = require('../plugins/flow');
|
|
var _index = require('../plugins/jsx/index');
|
|
var _types = require('../plugins/types');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var _typescript = require('../plugins/typescript');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var _index3 = require('../tokenizer/index');
|
|
var _keywords = require('../tokenizer/keywords');
|
|
var _state = require('../tokenizer/state');
|
|
var _types3 = require('../tokenizer/types');
|
|
var _charcodes = require('../util/charcodes');
|
|
var _identifier = require('../util/identifier');
|
|
var _base = require('./base');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var _lval = require('./lval');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var _statement = require('./statement');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var _util = require('./util');
|
|
|
|
class StopState {
|
|
|
|
constructor(stop) {
|
|
this.stop = stop;
|
|
}
|
|
} exports.StopState = StopState;
|
|
|
|
// ### Expression parsing
|
|
|
|
// These nest, from the most general expression type at the top to
|
|
// 'atomic', nondivisible expression types at the bottom. Most of
|
|
// the functions will simply let the function (s) below them parse,
|
|
// and, *if* the syntactic construct they handle is present, wrap
|
|
// the AST node that the inner parser gave them in another node.
|
|
function parseExpression(noIn = false) {
|
|
parseMaybeAssign(noIn);
|
|
if (_index3.match.call(void 0, _types3.TokenType.comma)) {
|
|
while (_index3.eat.call(void 0, _types3.TokenType.comma)) {
|
|
parseMaybeAssign(noIn);
|
|
}
|
|
}
|
|
} exports.parseExpression = parseExpression;
|
|
|
|
/**
|
|
* noIn is used when parsing a for loop so that we don't interpret a following "in" as the binary
|
|
* operatior.
|
|
* isWithinParens is used to indicate that we're parsing something that might be a comma expression
|
|
* or might be an arrow function or might be a Flow type assertion (which requires explicit parens).
|
|
* In these cases, we should allow : and ?: after the initial "left" part.
|
|
*/
|
|
function parseMaybeAssign(noIn = false, isWithinParens = false) {
|
|
if (_base.isTypeScriptEnabled) {
|
|
return _typescript.tsParseMaybeAssign.call(void 0, noIn, isWithinParens);
|
|
} else if (_base.isFlowEnabled) {
|
|
return _flow.flowParseMaybeAssign.call(void 0, noIn, isWithinParens);
|
|
} else {
|
|
return baseParseMaybeAssign(noIn, isWithinParens);
|
|
}
|
|
} exports.parseMaybeAssign = parseMaybeAssign;
|
|
|
|
// Parse an assignment expression. This includes applications of
|
|
// operators like `+=`.
|
|
// Returns true if the expression was an arrow function.
|
|
function baseParseMaybeAssign(noIn, isWithinParens) {
|
|
if (_index3.match.call(void 0, _types3.TokenType._yield)) {
|
|
parseYield();
|
|
return false;
|
|
}
|
|
|
|
if (_index3.match.call(void 0, _types3.TokenType.parenL) || _index3.match.call(void 0, _types3.TokenType.name) || _index3.match.call(void 0, _types3.TokenType._yield)) {
|
|
_base.state.potentialArrowAt = _base.state.start;
|
|
}
|
|
|
|
const wasArrow = parseMaybeConditional(noIn);
|
|
if (isWithinParens) {
|
|
parseParenItem();
|
|
}
|
|
if (_base.state.type & _types3.TokenType.IS_ASSIGN) {
|
|
_index3.next.call(void 0, );
|
|
parseMaybeAssign(noIn);
|
|
return false;
|
|
}
|
|
return wasArrow;
|
|
} exports.baseParseMaybeAssign = baseParseMaybeAssign;
|
|
|
|
// Parse a ternary conditional (`?:`) operator.
|
|
// Returns true if the expression was an arrow function.
|
|
function parseMaybeConditional(noIn) {
|
|
const wasArrow = parseExprOps(noIn);
|
|
if (wasArrow) {
|
|
return true;
|
|
}
|
|
parseConditional(noIn);
|
|
return false;
|
|
}
|
|
|
|
function parseConditional(noIn) {
|
|
if (_base.isTypeScriptEnabled || _base.isFlowEnabled) {
|
|
_types.typedParseConditional.call(void 0, noIn);
|
|
} else {
|
|
baseParseConditional(noIn);
|
|
}
|
|
}
|
|
|
|
function baseParseConditional(noIn) {
|
|
if (_index3.eat.call(void 0, _types3.TokenType.question)) {
|
|
parseMaybeAssign();
|
|
_util.expect.call(void 0, _types3.TokenType.colon);
|
|
parseMaybeAssign(noIn);
|
|
}
|
|
} exports.baseParseConditional = baseParseConditional;
|
|
|
|
// Start the precedence parser.
|
|
// Returns true if this was an arrow function
|
|
function parseExprOps(noIn) {
|
|
const startTokenIndex = _base.state.tokens.length;
|
|
const wasArrow = parseMaybeUnary();
|
|
if (wasArrow) {
|
|
return true;
|
|
}
|
|
parseExprOp(startTokenIndex, -1, noIn);
|
|
return false;
|
|
}
|
|
|
|
// Parse binary operators with the operator precedence parsing
|
|
// algorithm. `left` is the left-hand side of the operator.
|
|
// `minPrec` provides context that allows the function to stop and
|
|
// defer further parser to one of its callers when it encounters an
|
|
// operator that has a lower precedence than the set it is parsing.
|
|
function parseExprOp(startTokenIndex, minPrec, noIn) {
|
|
if (
|
|
_base.isTypeScriptEnabled &&
|
|
(_types3.TokenType._in & _types3.TokenType.PRECEDENCE_MASK) > minPrec &&
|
|
!_util.hasPrecedingLineBreak.call(void 0, ) &&
|
|
(_util.eatContextual.call(void 0, _keywords.ContextualKeyword._as) || _util.eatContextual.call(void 0, _keywords.ContextualKeyword._satisfies))
|
|
) {
|
|
const oldIsType = _index3.pushTypeContext.call(void 0, 1);
|
|
_typescript.tsParseType.call(void 0, );
|
|
_index3.popTypeContext.call(void 0, oldIsType);
|
|
_index3.rescan_gt.call(void 0, );
|
|
parseExprOp(startTokenIndex, minPrec, noIn);
|
|
return;
|
|
}
|
|
|
|
const prec = _base.state.type & _types3.TokenType.PRECEDENCE_MASK;
|
|
if (prec > 0 && (!noIn || !_index3.match.call(void 0, _types3.TokenType._in))) {
|
|
if (prec > minPrec) {
|
|
const op = _base.state.type;
|
|
_index3.next.call(void 0, );
|
|
if (op === _types3.TokenType.nullishCoalescing) {
|
|
_base.state.tokens[_base.state.tokens.length - 1].nullishStartIndex = startTokenIndex;
|
|
}
|
|
|
|
const rhsStartTokenIndex = _base.state.tokens.length;
|
|
parseMaybeUnary();
|
|
// Extend the right operand of this operator if possible.
|
|
parseExprOp(rhsStartTokenIndex, op & _types3.TokenType.IS_RIGHT_ASSOCIATIVE ? prec - 1 : prec, noIn);
|
|
if (op === _types3.TokenType.nullishCoalescing) {
|
|
_base.state.tokens[startTokenIndex].numNullishCoalesceStarts++;
|
|
_base.state.tokens[_base.state.tokens.length - 1].numNullishCoalesceEnds++;
|
|
}
|
|
// Continue with any future operator holding this expression as the left operand.
|
|
parseExprOp(startTokenIndex, minPrec, noIn);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse unary operators, both prefix and postfix.
|
|
// Returns true if this was an arrow function.
|
|
function parseMaybeUnary() {
|
|
if (_base.isTypeScriptEnabled && !_base.isJSXEnabled && _index3.eat.call(void 0, _types3.TokenType.lessThan)) {
|
|
_typescript.tsParseTypeAssertion.call(void 0, );
|
|
return false;
|
|
}
|
|
if (
|
|
_util.isContextual.call(void 0, _keywords.ContextualKeyword._module) &&
|
|
_index3.lookaheadCharCode.call(void 0, ) === _charcodes.charCodes.leftCurlyBrace &&
|
|
!_util.hasFollowingLineBreak.call(void 0, )
|
|
) {
|
|
parseModuleExpression();
|
|
return false;
|
|
}
|
|
if (_base.state.type & _types3.TokenType.IS_PREFIX) {
|
|
_index3.next.call(void 0, );
|
|
parseMaybeUnary();
|
|
return false;
|
|
}
|
|
|
|
const wasArrow = parseExprSubscripts();
|
|
if (wasArrow) {
|
|
return true;
|
|
}
|
|
while (_base.state.type & _types3.TokenType.IS_POSTFIX && !_util.canInsertSemicolon.call(void 0, )) {
|
|
// The tokenizer calls everything a preincrement, so make it a postincrement when
|
|
// we see it in that context.
|
|
if (_base.state.type === _types3.TokenType.preIncDec) {
|
|
_base.state.type = _types3.TokenType.postIncDec;
|
|
}
|
|
_index3.next.call(void 0, );
|
|
}
|
|
return false;
|
|
} exports.parseMaybeUnary = parseMaybeUnary;
|
|
|
|
// Parse call, dot, and `[]`-subscript expressions.
|
|
// Returns true if this was an arrow function.
|
|
function parseExprSubscripts() {
|
|
const startTokenIndex = _base.state.tokens.length;
|
|
const wasArrow = parseExprAtom();
|
|
if (wasArrow) {
|
|
return true;
|
|
}
|
|
parseSubscripts(startTokenIndex);
|
|
// If there was any optional chain operation, the start token would be marked
|
|
// as such, so also mark the end now.
|
|
if (_base.state.tokens.length > startTokenIndex && _base.state.tokens[startTokenIndex].isOptionalChainStart) {
|
|
_base.state.tokens[_base.state.tokens.length - 1].isOptionalChainEnd = true;
|
|
}
|
|
return false;
|
|
} exports.parseExprSubscripts = parseExprSubscripts;
|
|
|
|
function parseSubscripts(startTokenIndex, noCalls = false) {
|
|
if (_base.isFlowEnabled) {
|
|
_flow.flowParseSubscripts.call(void 0, startTokenIndex, noCalls);
|
|
} else {
|
|
baseParseSubscripts(startTokenIndex, noCalls);
|
|
}
|
|
}
|
|
|
|
function baseParseSubscripts(startTokenIndex, noCalls = false) {
|
|
const stopState = new StopState(false);
|
|
do {
|
|
parseSubscript(startTokenIndex, noCalls, stopState);
|
|
} while (!stopState.stop && !_base.state.error);
|
|
} exports.baseParseSubscripts = baseParseSubscripts;
|
|
|
|
function parseSubscript(startTokenIndex, noCalls, stopState) {
|
|
if (_base.isTypeScriptEnabled) {
|
|
_typescript.tsParseSubscript.call(void 0, startTokenIndex, noCalls, stopState);
|
|
} else if (_base.isFlowEnabled) {
|
|
_flow.flowParseSubscript.call(void 0, startTokenIndex, noCalls, stopState);
|
|
} else {
|
|
baseParseSubscript(startTokenIndex, noCalls, stopState);
|
|
}
|
|
}
|
|
|
|
/** Set 'state.stop = true' to indicate that we should stop parsing subscripts. */
|
|
function baseParseSubscript(
|
|
startTokenIndex,
|
|
noCalls,
|
|
stopState,
|
|
) {
|
|
if (!noCalls && _index3.eat.call(void 0, _types3.TokenType.doubleColon)) {
|
|
parseNoCallExpr();
|
|
stopState.stop = true;
|
|
// Propagate startTokenIndex so that `a::b?.()` will keep `a` as the first token. We may want
|
|
// to revisit this in the future when fully supporting bind syntax.
|
|
parseSubscripts(startTokenIndex, noCalls);
|
|
} else if (_index3.match.call(void 0, _types3.TokenType.questionDot)) {
|
|
_base.state.tokens[startTokenIndex].isOptionalChainStart = true;
|
|
if (noCalls && _index3.lookaheadType.call(void 0, ) === _types3.TokenType.parenL) {
|
|
stopState.stop = true;
|
|
return;
|
|
}
|
|
_index3.next.call(void 0, );
|
|
_base.state.tokens[_base.state.tokens.length - 1].subscriptStartIndex = startTokenIndex;
|
|
|
|
if (_index3.eat.call(void 0, _types3.TokenType.bracketL)) {
|
|
parseExpression();
|
|
_util.expect.call(void 0, _types3.TokenType.bracketR);
|
|
} else if (_index3.eat.call(void 0, _types3.TokenType.parenL)) {
|
|
parseCallExpressionArguments();
|
|
} else {
|
|
parseMaybePrivateName();
|
|
}
|
|
} else if (_index3.eat.call(void 0, _types3.TokenType.dot)) {
|
|
_base.state.tokens[_base.state.tokens.length - 1].subscriptStartIndex = startTokenIndex;
|
|
parseMaybePrivateName();
|
|
} else if (_index3.eat.call(void 0, _types3.TokenType.bracketL)) {
|
|
_base.state.tokens[_base.state.tokens.length - 1].subscriptStartIndex = startTokenIndex;
|
|
parseExpression();
|
|
_util.expect.call(void 0, _types3.TokenType.bracketR);
|
|
} else if (!noCalls && _index3.match.call(void 0, _types3.TokenType.parenL)) {
|
|
if (atPossibleAsync()) {
|
|
// We see "async", but it's possible it's a usage of the name "async". Parse as if it's a
|
|
// function call, and if we see an arrow later, backtrack and re-parse as a parameter list.
|
|
const snapshot = _base.state.snapshot();
|
|
const asyncStartTokenIndex = _base.state.tokens.length;
|
|
_index3.next.call(void 0, );
|
|
_base.state.tokens[_base.state.tokens.length - 1].subscriptStartIndex = startTokenIndex;
|
|
|
|
const callContextId = _base.getNextContextId.call(void 0, );
|
|
|
|
_base.state.tokens[_base.state.tokens.length - 1].contextId = callContextId;
|
|
parseCallExpressionArguments();
|
|
_base.state.tokens[_base.state.tokens.length - 1].contextId = callContextId;
|
|
|
|
if (shouldParseAsyncArrow()) {
|
|
// We hit an arrow, so backtrack and start again parsing function parameters.
|
|
_base.state.restoreFromSnapshot(snapshot);
|
|
stopState.stop = true;
|
|
_base.state.scopeDepth++;
|
|
|
|
_statement.parseFunctionParams.call(void 0, );
|
|
parseAsyncArrowFromCallExpression(asyncStartTokenIndex);
|
|
}
|
|
} else {
|
|
_index3.next.call(void 0, );
|
|
_base.state.tokens[_base.state.tokens.length - 1].subscriptStartIndex = startTokenIndex;
|
|
const callContextId = _base.getNextContextId.call(void 0, );
|
|
_base.state.tokens[_base.state.tokens.length - 1].contextId = callContextId;
|
|
parseCallExpressionArguments();
|
|
_base.state.tokens[_base.state.tokens.length - 1].contextId = callContextId;
|
|
}
|
|
} else if (_index3.match.call(void 0, _types3.TokenType.backQuote)) {
|
|
// Tagged template expression.
|
|
parseTemplate();
|
|
} else {
|
|
stopState.stop = true;
|
|
}
|
|
} exports.baseParseSubscript = baseParseSubscript;
|
|
|
|
function atPossibleAsync() {
|
|
// This was made less strict than the original version to avoid passing around nodes, but it
|
|
// should be safe to have rare false positives here.
|
|
return (
|
|
_base.state.tokens[_base.state.tokens.length - 1].contextualKeyword === _keywords.ContextualKeyword._async &&
|
|
!_util.canInsertSemicolon.call(void 0, )
|
|
);
|
|
} exports.atPossibleAsync = atPossibleAsync;
|
|
|
|
function parseCallExpressionArguments() {
|
|
let first = true;
|
|
while (!_index3.eat.call(void 0, _types3.TokenType.parenR) && !_base.state.error) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
_util.expect.call(void 0, _types3.TokenType.comma);
|
|
if (_index3.eat.call(void 0, _types3.TokenType.parenR)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
parseExprListItem(false);
|
|
}
|
|
} exports.parseCallExpressionArguments = parseCallExpressionArguments;
|
|
|
|
function shouldParseAsyncArrow() {
|
|
return _index3.match.call(void 0, _types3.TokenType.colon) || _index3.match.call(void 0, _types3.TokenType.arrow);
|
|
}
|
|
|
|
function parseAsyncArrowFromCallExpression(startTokenIndex) {
|
|
if (_base.isTypeScriptEnabled) {
|
|
_typescript.tsStartParseAsyncArrowFromCallExpression.call(void 0, );
|
|
} else if (_base.isFlowEnabled) {
|
|
_flow.flowStartParseAsyncArrowFromCallExpression.call(void 0, );
|
|
}
|
|
_util.expect.call(void 0, _types3.TokenType.arrow);
|
|
parseArrowExpression(startTokenIndex);
|
|
}
|
|
|
|
// Parse a no-call expression (like argument of `new` or `::` operators).
|
|
|
|
function parseNoCallExpr() {
|
|
const startTokenIndex = _base.state.tokens.length;
|
|
parseExprAtom();
|
|
parseSubscripts(startTokenIndex, true);
|
|
}
|
|
|
|
// Parse an atomic expression — either a single token that is an
|
|
// expression, an expression started by a keyword like `function` or
|
|
// `new`, or an expression wrapped in punctuation like `()`, `[]`,
|
|
// or `{}`.
|
|
// Returns true if the parsed expression was an arrow function.
|
|
function parseExprAtom() {
|
|
if (_index3.eat.call(void 0, _types3.TokenType.modulo)) {
|
|
// V8 intrinsic expression. Just parse the identifier, and the function invocation is parsed
|
|
// naturally.
|
|
parseIdentifier();
|
|
return false;
|
|
}
|
|
|
|
if (_index3.match.call(void 0, _types3.TokenType.jsxText) || _index3.match.call(void 0, _types3.TokenType.jsxEmptyText)) {
|
|
parseLiteral();
|
|
return false;
|
|
} else if (_index3.match.call(void 0, _types3.TokenType.lessThan) && _base.isJSXEnabled) {
|
|
_base.state.type = _types3.TokenType.jsxTagStart;
|
|
_index.jsxParseElement.call(void 0, );
|
|
_index3.next.call(void 0, );
|
|
return false;
|
|
}
|
|
|
|
const canBeArrow = _base.state.potentialArrowAt === _base.state.start;
|
|
switch (_base.state.type) {
|
|
case _types3.TokenType.slash:
|
|
case _types3.TokenType.assign:
|
|
_index3.retokenizeSlashAsRegex.call(void 0, );
|
|
// Fall through.
|
|
|
|
case _types3.TokenType._super:
|
|
case _types3.TokenType._this:
|
|
case _types3.TokenType.regexp:
|
|
case _types3.TokenType.num:
|
|
case _types3.TokenType.bigint:
|
|
case _types3.TokenType.decimal:
|
|
case _types3.TokenType.string:
|
|
case _types3.TokenType._null:
|
|
case _types3.TokenType._true:
|
|
case _types3.TokenType._false:
|
|
_index3.next.call(void 0, );
|
|
return false;
|
|
|
|
case _types3.TokenType._import:
|
|
_index3.next.call(void 0, );
|
|
if (_index3.match.call(void 0, _types3.TokenType.dot)) {
|
|
// import.meta
|
|
_base.state.tokens[_base.state.tokens.length - 1].type = _types3.TokenType.name;
|
|
_index3.next.call(void 0, );
|
|
parseIdentifier();
|
|
}
|
|
return false;
|
|
|
|
case _types3.TokenType.name: {
|
|
const startTokenIndex = _base.state.tokens.length;
|
|
const functionStart = _base.state.start;
|
|
const contextualKeyword = _base.state.contextualKeyword;
|
|
parseIdentifier();
|
|
if (contextualKeyword === _keywords.ContextualKeyword._await) {
|
|
parseAwait();
|
|
return false;
|
|
} else if (
|
|
contextualKeyword === _keywords.ContextualKeyword._async &&
|
|
_index3.match.call(void 0, _types3.TokenType._function) &&
|
|
!_util.canInsertSemicolon.call(void 0, )
|
|
) {
|
|
_index3.next.call(void 0, );
|
|
_statement.parseFunction.call(void 0, functionStart, false);
|
|
return false;
|
|
} else if (
|
|
canBeArrow &&
|
|
contextualKeyword === _keywords.ContextualKeyword._async &&
|
|
!_util.canInsertSemicolon.call(void 0, ) &&
|
|
_index3.match.call(void 0, _types3.TokenType.name)
|
|
) {
|
|
_base.state.scopeDepth++;
|
|
_lval.parseBindingIdentifier.call(void 0, false);
|
|
_util.expect.call(void 0, _types3.TokenType.arrow);
|
|
// let foo = async bar => {};
|
|
parseArrowExpression(startTokenIndex);
|
|
return true;
|
|
} else if (_index3.match.call(void 0, _types3.TokenType._do) && !_util.canInsertSemicolon.call(void 0, )) {
|
|
_index3.next.call(void 0, );
|
|
_statement.parseBlock.call(void 0, );
|
|
return false;
|
|
}
|
|
|
|
if (canBeArrow && !_util.canInsertSemicolon.call(void 0, ) && _index3.match.call(void 0, _types3.TokenType.arrow)) {
|
|
_base.state.scopeDepth++;
|
|
_lval.markPriorBindingIdentifier.call(void 0, false);
|
|
_util.expect.call(void 0, _types3.TokenType.arrow);
|
|
parseArrowExpression(startTokenIndex);
|
|
return true;
|
|
}
|
|
|
|
_base.state.tokens[_base.state.tokens.length - 1].identifierRole = _index3.IdentifierRole.Access;
|
|
return false;
|
|
}
|
|
|
|
case _types3.TokenType._do: {
|
|
_index3.next.call(void 0, );
|
|
_statement.parseBlock.call(void 0, );
|
|
return false;
|
|
}
|
|
|
|
case _types3.TokenType.parenL: {
|
|
const wasArrow = parseParenAndDistinguishExpression(canBeArrow);
|
|
return wasArrow;
|
|
}
|
|
|
|
case _types3.TokenType.bracketL:
|
|
_index3.next.call(void 0, );
|
|
parseExprList(_types3.TokenType.bracketR, true);
|
|
return false;
|
|
|
|
case _types3.TokenType.braceL:
|
|
parseObj(false, false);
|
|
return false;
|
|
|
|
case _types3.TokenType._function:
|
|
parseFunctionExpression();
|
|
return false;
|
|
|
|
case _types3.TokenType.at:
|
|
_statement.parseDecorators.call(void 0, );
|
|
// Fall through.
|
|
|
|
case _types3.TokenType._class:
|
|
_statement.parseClass.call(void 0, false);
|
|
return false;
|
|
|
|
case _types3.TokenType._new:
|
|
parseNew();
|
|
return false;
|
|
|
|
case _types3.TokenType.backQuote:
|
|
parseTemplate();
|
|
return false;
|
|
|
|
case _types3.TokenType.doubleColon: {
|
|
_index3.next.call(void 0, );
|
|
parseNoCallExpr();
|
|
return false;
|
|
}
|
|
|
|
case _types3.TokenType.hash: {
|
|
const code = _index3.lookaheadCharCode.call(void 0, );
|
|
if (_identifier.IS_IDENTIFIER_START[code] || code === _charcodes.charCodes.backslash) {
|
|
parseMaybePrivateName();
|
|
} else {
|
|
_index3.next.call(void 0, );
|
|
}
|
|
// Smart pipeline topic reference.
|
|
return false;
|
|
}
|
|
|
|
default:
|
|
_util.unexpected.call(void 0, );
|
|
return false;
|
|
}
|
|
} exports.parseExprAtom = parseExprAtom;
|
|
|
|
function parseMaybePrivateName() {
|
|
_index3.eat.call(void 0, _types3.TokenType.hash);
|
|
parseIdentifier();
|
|
}
|
|
|
|
function parseFunctionExpression() {
|
|
const functionStart = _base.state.start;
|
|
parseIdentifier();
|
|
if (_index3.eat.call(void 0, _types3.TokenType.dot)) {
|
|
// function.sent
|
|
parseIdentifier();
|
|
}
|
|
_statement.parseFunction.call(void 0, functionStart, false);
|
|
}
|
|
|
|
function parseLiteral() {
|
|
_index3.next.call(void 0, );
|
|
} exports.parseLiteral = parseLiteral;
|
|
|
|
function parseParenExpression() {
|
|
_util.expect.call(void 0, _types3.TokenType.parenL);
|
|
parseExpression();
|
|
_util.expect.call(void 0, _types3.TokenType.parenR);
|
|
} exports.parseParenExpression = parseParenExpression;
|
|
|
|
// Returns true if this was an arrow expression.
|
|
function parseParenAndDistinguishExpression(canBeArrow) {
|
|
// Assume this is a normal parenthesized expression, but if we see an arrow, we'll bail and
|
|
// start over as a parameter list.
|
|
const snapshot = _base.state.snapshot();
|
|
|
|
const startTokenIndex = _base.state.tokens.length;
|
|
_util.expect.call(void 0, _types3.TokenType.parenL);
|
|
|
|
let first = true;
|
|
|
|
while (!_index3.match.call(void 0, _types3.TokenType.parenR) && !_base.state.error) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
_util.expect.call(void 0, _types3.TokenType.comma);
|
|
if (_index3.match.call(void 0, _types3.TokenType.parenR)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (_index3.match.call(void 0, _types3.TokenType.ellipsis)) {
|
|
_lval.parseRest.call(void 0, false /* isBlockScope */);
|
|
parseParenItem();
|
|
break;
|
|
} else {
|
|
parseMaybeAssign(false, true);
|
|
}
|
|
}
|
|
|
|
_util.expect.call(void 0, _types3.TokenType.parenR);
|
|
|
|
if (canBeArrow && shouldParseArrow()) {
|
|
const wasArrow = parseArrow();
|
|
if (wasArrow) {
|
|
// It was an arrow function this whole time, so start over and parse it as params so that we
|
|
// get proper token annotations.
|
|
_base.state.restoreFromSnapshot(snapshot);
|
|
_base.state.scopeDepth++;
|
|
// Don't specify a context ID because arrow functions don't need a context ID.
|
|
_statement.parseFunctionParams.call(void 0, );
|
|
parseArrow();
|
|
parseArrowExpression(startTokenIndex);
|
|
if (_base.state.error) {
|
|
// Nevermind! This must have been something that looks very much like an
|
|
// arrow function but where its "parameter list" isn't actually a valid
|
|
// parameter list. Force non-arrow parsing.
|
|
// See https://github.com/alangpierce/sucrase/issues/666 for an example.
|
|
_base.state.restoreFromSnapshot(snapshot);
|
|
parseParenAndDistinguishExpression(false);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function shouldParseArrow() {
|
|
return _index3.match.call(void 0, _types3.TokenType.colon) || !_util.canInsertSemicolon.call(void 0, );
|
|
}
|
|
|
|
// Returns whether there was an arrow token.
|
|
function parseArrow() {
|
|
if (_base.isTypeScriptEnabled) {
|
|
return _typescript.tsParseArrow.call(void 0, );
|
|
} else if (_base.isFlowEnabled) {
|
|
return _flow.flowParseArrow.call(void 0, );
|
|
} else {
|
|
return _index3.eat.call(void 0, _types3.TokenType.arrow);
|
|
}
|
|
} exports.parseArrow = parseArrow;
|
|
|
|
function parseParenItem() {
|
|
if (_base.isTypeScriptEnabled || _base.isFlowEnabled) {
|
|
_types.typedParseParenItem.call(void 0, );
|
|
}
|
|
}
|
|
|
|
// New's precedence is slightly tricky. It must allow its argument to
|
|
// be a `[]` or dot subscript expression, but not a call — at least,
|
|
// not without wrapping it in parentheses. Thus, it uses the noCalls
|
|
// argument to parseSubscripts to prevent it from consuming the
|
|
// argument list.
|
|
function parseNew() {
|
|
_util.expect.call(void 0, _types3.TokenType._new);
|
|
if (_index3.eat.call(void 0, _types3.TokenType.dot)) {
|
|
// new.target
|
|
parseIdentifier();
|
|
return;
|
|
}
|
|
parseNewCallee();
|
|
if (_base.isFlowEnabled) {
|
|
_flow.flowStartParseNewArguments.call(void 0, );
|
|
}
|
|
if (_index3.eat.call(void 0, _types3.TokenType.parenL)) {
|
|
parseExprList(_types3.TokenType.parenR);
|
|
}
|
|
}
|
|
|
|
function parseNewCallee() {
|
|
parseNoCallExpr();
|
|
_index3.eat.call(void 0, _types3.TokenType.questionDot);
|
|
}
|
|
|
|
function parseTemplate() {
|
|
// Finish `, read quasi
|
|
_index3.nextTemplateToken.call(void 0, );
|
|
// Finish quasi, read ${
|
|
_index3.nextTemplateToken.call(void 0, );
|
|
while (!_index3.match.call(void 0, _types3.TokenType.backQuote) && !_base.state.error) {
|
|
_util.expect.call(void 0, _types3.TokenType.dollarBraceL);
|
|
parseExpression();
|
|
// Finish }, read quasi
|
|
_index3.nextTemplateToken.call(void 0, );
|
|
// Finish quasi, read either ${ or `
|
|
_index3.nextTemplateToken.call(void 0, );
|
|
}
|
|
_index3.next.call(void 0, );
|
|
} exports.parseTemplate = parseTemplate;
|
|
|
|
// Parse an object literal or binding pattern.
|
|
function parseObj(isPattern, isBlockScope) {
|
|
// Attach a context ID to the object open and close brace and each object key.
|
|
const contextId = _base.getNextContextId.call(void 0, );
|
|
let first = true;
|
|
|
|
_index3.next.call(void 0, );
|
|
_base.state.tokens[_base.state.tokens.length - 1].contextId = contextId;
|
|
|
|
while (!_index3.eat.call(void 0, _types3.TokenType.braceR) && !_base.state.error) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
_util.expect.call(void 0, _types3.TokenType.comma);
|
|
if (_index3.eat.call(void 0, _types3.TokenType.braceR)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
let isGenerator = false;
|
|
if (_index3.match.call(void 0, _types3.TokenType.ellipsis)) {
|
|
const previousIndex = _base.state.tokens.length;
|
|
_lval.parseSpread.call(void 0, );
|
|
if (isPattern) {
|
|
// Mark role when the only thing being spread over is an identifier.
|
|
if (_base.state.tokens.length === previousIndex + 2) {
|
|
_lval.markPriorBindingIdentifier.call(void 0, isBlockScope);
|
|
}
|
|
if (_index3.eat.call(void 0, _types3.TokenType.braceR)) {
|
|
break;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!isPattern) {
|
|
isGenerator = _index3.eat.call(void 0, _types3.TokenType.star);
|
|
}
|
|
|
|
if (!isPattern && _util.isContextual.call(void 0, _keywords.ContextualKeyword._async)) {
|
|
if (isGenerator) _util.unexpected.call(void 0, );
|
|
|
|
parseIdentifier();
|
|
if (
|
|
_index3.match.call(void 0, _types3.TokenType.colon) ||
|
|
_index3.match.call(void 0, _types3.TokenType.parenL) ||
|
|
_index3.match.call(void 0, _types3.TokenType.braceR) ||
|
|
_index3.match.call(void 0, _types3.TokenType.eq) ||
|
|
_index3.match.call(void 0, _types3.TokenType.comma)
|
|
) {
|
|
// This is a key called "async" rather than an async function.
|
|
} else {
|
|
if (_index3.match.call(void 0, _types3.TokenType.star)) {
|
|
_index3.next.call(void 0, );
|
|
isGenerator = true;
|
|
}
|
|
parsePropertyName(contextId);
|
|
}
|
|
} else {
|
|
parsePropertyName(contextId);
|
|
}
|
|
|
|
parseObjPropValue(isPattern, isBlockScope, contextId);
|
|
}
|
|
|
|
_base.state.tokens[_base.state.tokens.length - 1].contextId = contextId;
|
|
} exports.parseObj = parseObj;
|
|
|
|
function isGetterOrSetterMethod(isPattern) {
|
|
// We go off of the next and don't bother checking if the node key is actually "get" or "set".
|
|
// This lets us avoid generating a node, and should only make the validation worse.
|
|
return (
|
|
!isPattern &&
|
|
(_index3.match.call(void 0, _types3.TokenType.string) || // get "string"() {}
|
|
_index3.match.call(void 0, _types3.TokenType.num) || // get 1() {}
|
|
_index3.match.call(void 0, _types3.TokenType.bracketL) || // get ["string"]() {}
|
|
_index3.match.call(void 0, _types3.TokenType.name) || // get foo() {}
|
|
!!(_base.state.type & _types3.TokenType.IS_KEYWORD)) // get debugger() {}
|
|
);
|
|
}
|
|
|
|
// Returns true if this was a method.
|
|
function parseObjectMethod(isPattern, objectContextId) {
|
|
// We don't need to worry about modifiers because object methods can't have optional bodies, so
|
|
// the start will never be used.
|
|
const functionStart = _base.state.start;
|
|
if (_index3.match.call(void 0, _types3.TokenType.parenL)) {
|
|
if (isPattern) _util.unexpected.call(void 0, );
|
|
parseMethod(functionStart, /* isConstructor */ false);
|
|
return true;
|
|
}
|
|
|
|
if (isGetterOrSetterMethod(isPattern)) {
|
|
parsePropertyName(objectContextId);
|
|
parseMethod(functionStart, /* isConstructor */ false);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function parseObjectProperty(isPattern, isBlockScope) {
|
|
if (_index3.eat.call(void 0, _types3.TokenType.colon)) {
|
|
if (isPattern) {
|
|
_lval.parseMaybeDefault.call(void 0, isBlockScope);
|
|
} else {
|
|
parseMaybeAssign(false);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Since there's no colon, we assume this is an object shorthand.
|
|
|
|
// If we're in a destructuring, we've now discovered that the key was actually an assignee, so
|
|
// we need to tag it as a declaration with the appropriate scope. Otherwise, we might need to
|
|
// transform it on access, so mark it as a normal object shorthand.
|
|
let identifierRole;
|
|
if (isPattern) {
|
|
if (_base.state.scopeDepth === 0) {
|
|
identifierRole = _index3.IdentifierRole.ObjectShorthandTopLevelDeclaration;
|
|
} else if (isBlockScope) {
|
|
identifierRole = _index3.IdentifierRole.ObjectShorthandBlockScopedDeclaration;
|
|
} else {
|
|
identifierRole = _index3.IdentifierRole.ObjectShorthandFunctionScopedDeclaration;
|
|
}
|
|
} else {
|
|
identifierRole = _index3.IdentifierRole.ObjectShorthand;
|
|
}
|
|
_base.state.tokens[_base.state.tokens.length - 1].identifierRole = identifierRole;
|
|
|
|
// Regardless of whether we know this to be a pattern or if we're in an ambiguous context, allow
|
|
// parsing as if there's a default value.
|
|
_lval.parseMaybeDefault.call(void 0, isBlockScope, true);
|
|
}
|
|
|
|
function parseObjPropValue(
|
|
isPattern,
|
|
isBlockScope,
|
|
objectContextId,
|
|
) {
|
|
if (_base.isTypeScriptEnabled) {
|
|
_typescript.tsStartParseObjPropValue.call(void 0, );
|
|
} else if (_base.isFlowEnabled) {
|
|
_flow.flowStartParseObjPropValue.call(void 0, );
|
|
}
|
|
const wasMethod = parseObjectMethod(isPattern, objectContextId);
|
|
if (!wasMethod) {
|
|
parseObjectProperty(isPattern, isBlockScope);
|
|
}
|
|
}
|
|
|
|
function parsePropertyName(objectContextId) {
|
|
if (_base.isFlowEnabled) {
|
|
_flow.flowParseVariance.call(void 0, );
|
|
}
|
|
if (_index3.eat.call(void 0, _types3.TokenType.bracketL)) {
|
|
_base.state.tokens[_base.state.tokens.length - 1].contextId = objectContextId;
|
|
parseMaybeAssign();
|
|
_util.expect.call(void 0, _types3.TokenType.bracketR);
|
|
_base.state.tokens[_base.state.tokens.length - 1].contextId = objectContextId;
|
|
} else {
|
|
if (_index3.match.call(void 0, _types3.TokenType.num) || _index3.match.call(void 0, _types3.TokenType.string) || _index3.match.call(void 0, _types3.TokenType.bigint) || _index3.match.call(void 0, _types3.TokenType.decimal)) {
|
|
parseExprAtom();
|
|
} else {
|
|
parseMaybePrivateName();
|
|
}
|
|
|
|
_base.state.tokens[_base.state.tokens.length - 1].identifierRole = _index3.IdentifierRole.ObjectKey;
|
|
_base.state.tokens[_base.state.tokens.length - 1].contextId = objectContextId;
|
|
}
|
|
} exports.parsePropertyName = parsePropertyName;
|
|
|
|
// Parse object or class method.
|
|
function parseMethod(functionStart, isConstructor) {
|
|
const funcContextId = _base.getNextContextId.call(void 0, );
|
|
|
|
_base.state.scopeDepth++;
|
|
const startTokenIndex = _base.state.tokens.length;
|
|
const allowModifiers = isConstructor; // For TypeScript parameter properties
|
|
_statement.parseFunctionParams.call(void 0, allowModifiers, funcContextId);
|
|
parseFunctionBodyAndFinish(functionStart, funcContextId);
|
|
const endTokenIndex = _base.state.tokens.length;
|
|
_base.state.scopes.push(new (0, _state.Scope)(startTokenIndex, endTokenIndex, true));
|
|
_base.state.scopeDepth--;
|
|
} exports.parseMethod = parseMethod;
|
|
|
|
// Parse arrow function expression.
|
|
// If the parameters are provided, they will be converted to an
|
|
// assignable list.
|
|
function parseArrowExpression(startTokenIndex) {
|
|
parseFunctionBody(true);
|
|
const endTokenIndex = _base.state.tokens.length;
|
|
_base.state.scopes.push(new (0, _state.Scope)(startTokenIndex, endTokenIndex, true));
|
|
_base.state.scopeDepth--;
|
|
} exports.parseArrowExpression = parseArrowExpression;
|
|
|
|
function parseFunctionBodyAndFinish(functionStart, funcContextId = 0) {
|
|
if (_base.isTypeScriptEnabled) {
|
|
_typescript.tsParseFunctionBodyAndFinish.call(void 0, functionStart, funcContextId);
|
|
} else if (_base.isFlowEnabled) {
|
|
_flow.flowParseFunctionBodyAndFinish.call(void 0, funcContextId);
|
|
} else {
|
|
parseFunctionBody(false, funcContextId);
|
|
}
|
|
} exports.parseFunctionBodyAndFinish = parseFunctionBodyAndFinish;
|
|
|
|
function parseFunctionBody(allowExpression, funcContextId = 0) {
|
|
const isExpression = allowExpression && !_index3.match.call(void 0, _types3.TokenType.braceL);
|
|
|
|
if (isExpression) {
|
|
parseMaybeAssign();
|
|
} else {
|
|
_statement.parseBlock.call(void 0, true /* isFunctionScope */, funcContextId);
|
|
}
|
|
} exports.parseFunctionBody = parseFunctionBody;
|
|
|
|
// Parses a comma-separated list of expressions, and returns them as
|
|
// an array. `close` is the token type that ends the list, and
|
|
// `allowEmpty` can be turned on to allow subsequent commas with
|
|
// nothing in between them to be parsed as `null` (which is needed
|
|
// for array literals).
|
|
|
|
function parseExprList(close, allowEmpty = false) {
|
|
let first = true;
|
|
while (!_index3.eat.call(void 0, close) && !_base.state.error) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
_util.expect.call(void 0, _types3.TokenType.comma);
|
|
if (_index3.eat.call(void 0, close)) break;
|
|
}
|
|
parseExprListItem(allowEmpty);
|
|
}
|
|
}
|
|
|
|
function parseExprListItem(allowEmpty) {
|
|
if (allowEmpty && _index3.match.call(void 0, _types3.TokenType.comma)) {
|
|
// Empty item; nothing more to parse for this item.
|
|
} else if (_index3.match.call(void 0, _types3.TokenType.ellipsis)) {
|
|
_lval.parseSpread.call(void 0, );
|
|
parseParenItem();
|
|
} else if (_index3.match.call(void 0, _types3.TokenType.question)) {
|
|
// Partial function application proposal.
|
|
_index3.next.call(void 0, );
|
|
} else {
|
|
parseMaybeAssign(false, true);
|
|
}
|
|
}
|
|
|
|
// Parse the next token as an identifier.
|
|
function parseIdentifier() {
|
|
_index3.next.call(void 0, );
|
|
_base.state.tokens[_base.state.tokens.length - 1].type = _types3.TokenType.name;
|
|
} exports.parseIdentifier = parseIdentifier;
|
|
|
|
// Parses await expression inside async function.
|
|
function parseAwait() {
|
|
parseMaybeUnary();
|
|
}
|
|
|
|
// Parses yield expression inside generator.
|
|
function parseYield() {
|
|
_index3.next.call(void 0, );
|
|
if (!_index3.match.call(void 0, _types3.TokenType.semi) && !_util.canInsertSemicolon.call(void 0, )) {
|
|
_index3.eat.call(void 0, _types3.TokenType.star);
|
|
parseMaybeAssign();
|
|
}
|
|
}
|
|
|
|
// https://github.com/tc39/proposal-js-module-blocks
|
|
function parseModuleExpression() {
|
|
_util.expectContextual.call(void 0, _keywords.ContextualKeyword._module);
|
|
_util.expect.call(void 0, _types3.TokenType.braceL);
|
|
// For now, just call parseBlockBody to parse the block. In the future when we
|
|
// implement full support, we'll want to emit scopes and possibly other
|
|
// information.
|
|
_statement.parseBlockBody.call(void 0, _types3.TokenType.braceR);
|
|
}
|