@aurelia/expression-parser
Version:
[](https://opensource.org/licenses/MIT) [](http://www.typescriptlang.org/) [ • 88.5 kB
JavaScript
import { emptyArray, createImplementationRegister, DI } from '@aurelia/kernel';
/* eslint-disable @typescript-eslint/no-unused-vars */
/** @internal */ const ekAccessThis = 'AccessThis';
/** @internal */ const ekAccessBoundary = 'AccessBoundary';
/** @internal */ const ekAccessGlobal = 'AccessGlobal';
/** @internal */ const ekAccessScope = 'AccessScope';
/** @internal */ const ekArrayLiteral = 'ArrayLiteral';
/** @internal */ const ekObjectLiteral = 'ObjectLiteral';
/** @internal */ const ekPrimitiveLiteral = 'PrimitiveLiteral';
/** @internal */ const ekNew = 'New';
/** @internal */ const ekTemplate = 'Template';
/** @internal */ const ekUnary = 'Unary';
/** @internal */ const ekCallScope = 'CallScope';
/** @internal */ const ekCallMember = 'CallMember';
/** @internal */ const ekCallFunction = 'CallFunction';
/** @internal */ const ekCallGlobal = 'CallGlobal';
/** @internal */ const ekAccessMember = 'AccessMember';
/** @internal */ const ekAccessKeyed = 'AccessKeyed';
/** @internal */ const ekTaggedTemplate = 'TaggedTemplate';
/** @internal */ const ekBinary = 'Binary';
/** @internal */ const ekConditional = 'Conditional';
/** @internal */ const ekAssign = 'Assign';
/** @internal */ const ekArrowFunction = 'ArrowFunction';
/** @internal */ const ekValueConverter = 'ValueConverter';
/** @internal */ const ekBindingBehavior = 'BindingBehavior';
/** @internal */ const ekArrayBindingPattern = 'ArrayBindingPattern';
/** @internal */ const ekObjectBindingPattern = 'ObjectBindingPattern';
/** @internal */ const ekBindingIdentifier = 'BindingIdentifier';
/** @internal */ const ekForOfStatement = 'ForOfStatement';
/** @internal */ const ekInterpolation = 'Interpolation';
/** @internal */ const ekArrayDestructuring = 'ArrayDestructuring';
/** @internal */ const ekObjectDestructuring = 'ObjectDestructuring';
/** @internal */ const ekDestructuringAssignmentLeaf = 'DestructuringAssignmentLeaf';
/** @internal */ const ekCustom = 'Custom';
// Kept as a class because it carries runtime behavior methods (evaluate, assign, etc.)
class CustomExpression {
constructor(value) {
this.value = value;
this.$kind = ekCustom;
}
evaluate(...params) { return this.value; }
assign(...params) { return params; }
bind(...params) { }
unbind(...params) { }
accept(_visitor) { return (void 0); }
}
function createBindingBehaviorExpression(expression, name, args) {
return { $kind: ekBindingBehavior, key: `_bb_${name}`, expression, name, args };
}
function createValueConverterExpression(expression, name, args) {
return { $kind: ekValueConverter, expression, name, args };
}
function createAssignExpression(target, value, op = '=') {
return { $kind: ekAssign, target, value, op };
}
function createConditionalExpression(condition, yes, no) {
return { $kind: ekConditional, condition, yes, no };
}
function createAccessGlobalExpression(name) {
return { $kind: ekAccessGlobal, name };
}
function createAccessThisExpression(ancestor = 0) {
return { $kind: ekAccessThis, ancestor };
}
const AccessBoundary = { $kind: ekAccessBoundary };
function createAccessBoundaryExpression() {
return AccessBoundary;
}
function createAccessScopeExpression(name, ancestor = 0) {
return { $kind: ekAccessScope, name, ancestor };
}
function isAccessGlobal(ast) {
return ast.$kind === ekAccessGlobal || ((ast.$kind === ekAccessMember || ast.$kind === ekAccessKeyed) && ast.accessGlobal);
}
function createAccessMemberExpression(object, name, optional = false) {
return { $kind: ekAccessMember, accessGlobal: isAccessGlobal(object), object, name, optional };
}
function createAccessKeyedExpression(object, key, optional = false) {
return { $kind: ekAccessKeyed, accessGlobal: isAccessGlobal(object), object, key, optional };
}
function createNewExpression(func, args) {
return { $kind: ekNew, func, args };
}
function createCallScopeExpression(name, args, ancestor = 0, optional = false) {
return { $kind: ekCallScope, name, args, ancestor, optional };
}
function createCallMemberExpression(object, name, args, optionalMember = false, optionalCall = false) {
return { $kind: ekCallMember, object, name, args, optionalMember, optionalCall };
}
function createCallFunctionExpression(func, args, optional = false) {
return { $kind: ekCallFunction, func, args, optional };
}
function createCallGlobalExpression(name, args) {
return { $kind: ekCallGlobal, name, args };
}
function createBinaryExpression(operation, left, right) {
return { $kind: ekBinary, operation, left, right };
}
function createUnaryExpression(operation, expression, pos = 0) {
return { $kind: ekUnary, operation, expression, pos };
}
const PrimitiveLiteral = {
$undefined: { $kind: ekPrimitiveLiteral, value: undefined },
$null: { $kind: ekPrimitiveLiteral, value: null },
$true: { $kind: ekPrimitiveLiteral, value: true },
$false: { $kind: ekPrimitiveLiteral, value: false },
$empty: { $kind: ekPrimitiveLiteral, value: '' },
};
function createPrimitiveLiteralExpression(value) {
return { $kind: ekPrimitiveLiteral, value };
}
const ArrayLiteral = {
$empty: { $kind: ekArrayLiteral, elements: emptyArray }
};
function createArrayLiteralExpression(elements) {
return { $kind: ekArrayLiteral, elements };
}
const ObjectLiteral = {
$empty: { $kind: ekObjectLiteral, keys: emptyArray, values: emptyArray }
};
function createObjectLiteralExpression(keys, values) {
return { $kind: ekObjectLiteral, keys, values };
}
const Template = {
$empty: { $kind: ekTemplate, cooked: [''], expressions: emptyArray }
};
function createTemplateExpression(cooked, expressions = emptyArray) {
return { $kind: ekTemplate, cooked, expressions };
}
function createTaggedTemplateExpression(cooked, raw, func, expressions = emptyArray) {
cooked.raw = raw;
return { $kind: ekTaggedTemplate, cooked, func, expressions };
}
function createArrayBindingPattern(elements) {
return { $kind: ekArrayBindingPattern, elements };
}
function createObjectBindingPattern(keys, values) {
return { $kind: ekObjectBindingPattern, keys, values };
}
function createBindingIdentifier(name) {
return { $kind: ekBindingIdentifier, name };
}
function createForOfStatement(declaration, iterable, semiIdx) {
return { $kind: ekForOfStatement, declaration, iterable, semiIdx };
}
function createInterpolation(parts, expressions = emptyArray) {
return {
$kind: ekInterpolation,
isMulti: expressions.length > 1,
firstExpression: expressions[0],
parts,
expressions
};
}
function createDestructuringAssignmentExpression($kind, list, source, initializer) {
return { $kind, list, source, initializer };
}
function createDestructuringAssignmentSingleExpression(target, source, initializer) {
return { $kind: ekDestructuringAssignmentLeaf, target, source, initializer };
}
function createDestructuringAssignmentRestExpression(target, indexOrProperties) {
return { $kind: ekDestructuringAssignmentLeaf, target, indexOrProperties };
}
function createArrowFunction(args, body, rest = false) {
return { $kind: ekArrowFunction, args, body, rest };
}
/** @internal */
const createError = (message) => new Error(message);
/** @internal */
const isString = (v) => typeof v === 'string';
// this is used inside template literal, since TS errs without String(...value)
/** @internal */ const safeString = String;
/** @internal */ const createLookup = () => Object.create(null);
const astVisit = (ast, visitor) => {
switch (ast.$kind) {
case ekAccessKeyed: return visitor.visitAccessKeyed(ast);
case ekAccessMember: return visitor.visitAccessMember(ast);
case ekAccessScope: return visitor.visitAccessScope(ast);
case ekAccessThis: return visitor.visitAccessThis(ast);
case ekAccessBoundary: return visitor.visitAccessBoundary(ast);
case ekArrayBindingPattern: return visitor.visitArrayBindingPattern(ast);
case ekArrayDestructuring: return visitor.visitDestructuringAssignmentExpression(ast);
case ekArrayLiteral: return visitor.visitArrayLiteral(ast);
case ekArrowFunction: return visitor.visitArrowFunction(ast);
case ekAssign: return visitor.visitAssign(ast);
case ekBinary: return visitor.visitBinary(ast);
case ekBindingBehavior: return visitor.visitBindingBehavior(ast);
case ekBindingIdentifier: return visitor.visitBindingIdentifier(ast);
case ekCallFunction: return visitor.visitCallFunction(ast);
case ekCallMember: return visitor.visitCallMember(ast);
case ekCallScope: return visitor.visitCallScope(ast);
case ekConditional: return visitor.visitConditional(ast);
case ekDestructuringAssignmentLeaf: return visitor.visitDestructuringAssignmentSingleExpression(ast);
case ekForOfStatement: return visitor.visitForOfStatement(ast);
case ekInterpolation: return visitor.visitInterpolation(ast);
case ekObjectBindingPattern: return visitor.visitObjectBindingPattern(ast);
case ekObjectDestructuring: return visitor.visitDestructuringAssignmentExpression(ast);
case ekObjectLiteral: return visitor.visitObjectLiteral(ast);
case ekPrimitiveLiteral: return visitor.visitPrimitiveLiteral(ast);
case ekTaggedTemplate: return visitor.visitTaggedTemplate(ast);
case ekTemplate: return visitor.visitTemplate(ast);
case ekUnary: return visitor.visitUnary(ast);
case ekValueConverter: return visitor.visitValueConverter(ast);
case ekCustom: return visitor.visitCustom(ast);
default: {
throw createError(`Trying to visit unknown ast node ${JSON.stringify(ast)}`);
}
}
};
class Unparser {
constructor() {
this.text = '';
}
static unparse(expr) {
const visitor = new Unparser();
astVisit(expr, visitor);
return visitor.text;
}
visitAccessMember(expr) {
astVisit(expr.object, this);
this.text += `${expr.optional ? '?' : ''}.${expr.name}`;
}
visitAccessKeyed(expr) {
astVisit(expr.object, this);
this.text += `${expr.optional ? '?.' : ''}[`;
astVisit(expr.key, this);
this.text += ']';
}
visitAccessThis(expr) {
if (expr.ancestor === 0) {
this.text += '$this';
return;
}
this.text += '$parent';
let i = expr.ancestor - 1;
while (i--) {
this.text += '.$parent';
}
}
visitAccessBoundary(_expr) {
this.text += 'this';
}
visitAccessScope(expr) {
let i = expr.ancestor;
while (i--) {
this.text += '$parent.';
}
this.text += expr.name;
}
visitArrayLiteral(expr) {
const elements = expr.elements;
this.text += '[';
for (let i = 0, length = elements.length; i < length; ++i) {
if (i !== 0) {
this.text += ',';
}
astVisit(elements[i], this);
}
this.text += ']';
}
visitArrowFunction(expr) {
const args = expr.args;
const ii = args.length;
let i = 0;
let text = '(';
let name;
for (; i < ii; ++i) {
name = args[i].name;
if (i > 0) {
text += ', ';
}
if (i < ii - 1) {
text += name;
}
else {
text += expr.rest ? `...${name}` : name;
}
}
this.text += `${text}) => `;
astVisit(expr.body, this);
}
visitObjectLiteral(expr) {
const keys = expr.keys;
const values = expr.values;
this.text += '{';
for (let i = 0, length = keys.length; i < length; ++i) {
if (i !== 0) {
this.text += ',';
}
this.text += `'${keys[i]}':`;
astVisit(values[i], this);
}
this.text += '}';
}
visitPrimitiveLiteral(expr) {
this.text += '(';
if (isString(expr.value)) {
const escaped = expr.value.replace(/'/g, '\\\'');
this.text += `'${escaped}'`;
}
else {
this.text += `${expr.value}`;
}
this.text += ')';
}
visitCallFunction(expr) {
this.text += '(';
astVisit(expr.func, this);
this.text += expr.optional ? '?.' : '';
this.writeArgs(expr.args);
this.text += ')';
}
visitCallMember(expr) {
astVisit(expr.object, this);
this.text += `${expr.optionalMember ? '?.' : ''}.${expr.name}${expr.optionalCall ? '?.' : ''}`;
this.writeArgs(expr.args);
}
visitCallScope(expr) {
let i = expr.ancestor;
while (i--) {
this.text += '$parent.';
}
this.text += `${expr.name}${expr.optional ? '?.' : ''}`;
this.writeArgs(expr.args);
}
visitTemplate(expr) {
const { cooked, expressions } = expr;
const length = expressions.length;
this.text += '`';
this.text += cooked[0];
for (let i = 0; i < length; i++) {
astVisit(expressions[i], this);
this.text += cooked[i + 1];
}
this.text += '`';
}
visitTaggedTemplate(expr) {
const { cooked, expressions } = expr;
const length = expressions.length;
astVisit(expr.func, this);
this.text += '`';
this.text += cooked[0];
for (let i = 0; i < length; i++) {
astVisit(expressions[i], this);
this.text += cooked[i + 1];
}
this.text += '`';
}
visitUnary(expr) {
this.text += `(${expr.operation}`;
if (expr.operation.charCodeAt(0) >= /* a */ 97) {
this.text += ' ';
}
astVisit(expr.expression, this);
this.text += ')';
}
visitBinary(expr) {
this.text += '(';
astVisit(expr.left, this);
if (expr.operation.charCodeAt(0) === /* i */ 105) {
this.text += ` ${expr.operation} `;
}
else {
this.text += expr.operation;
}
astVisit(expr.right, this);
this.text += ')';
}
visitConditional(expr) {
this.text += '(';
astVisit(expr.condition, this);
this.text += '?';
astVisit(expr.yes, this);
this.text += ':';
astVisit(expr.no, this);
this.text += ')';
}
visitAssign(expr) {
this.text += '(';
astVisit(expr.target, this);
this.text += '=';
astVisit(expr.value, this);
this.text += ')';
}
visitValueConverter(expr) {
const args = expr.args;
astVisit(expr.expression, this);
this.text += `|${expr.name}`;
for (let i = 0, length = args.length; i < length; ++i) {
this.text += ':';
astVisit(args[i], this);
}
}
visitBindingBehavior(expr) {
const args = expr.args;
astVisit(expr.expression, this);
this.text += `&${expr.name}`;
for (let i = 0, length = args.length; i < length; ++i) {
this.text += ':';
astVisit(args[i], this);
}
}
visitArrayBindingPattern(expr) {
const elements = expr.elements;
this.text += '[';
for (let i = 0, length = elements.length; i < length; ++i) {
if (i !== 0) {
this.text += ',';
}
astVisit(elements[i], this);
}
this.text += ']';
}
visitObjectBindingPattern(expr) {
const keys = expr.keys;
const values = expr.values;
this.text += '{';
for (let i = 0, length = keys.length; i < length; ++i) {
if (i !== 0) {
this.text += ',';
}
this.text += `'${keys[i]}':`;
astVisit(values[i], this);
}
this.text += '}';
}
visitBindingIdentifier(expr) {
this.text += expr.name;
}
visitForOfStatement(expr) {
astVisit(expr.declaration, this);
this.text += ' of ';
astVisit(expr.iterable, this);
}
visitInterpolation(expr) {
const { parts, expressions } = expr;
const length = expressions.length;
this.text += '${';
this.text += parts[0];
for (let i = 0; i < length; i++) {
astVisit(expressions[i], this);
this.text += parts[i + 1];
}
this.text += '}';
}
visitDestructuringAssignmentExpression(expr) {
const $kind = expr.$kind;
const isObjDes = $kind === ekObjectDestructuring;
this.text += isObjDes ? '{' : '[';
const list = expr.list;
const len = list.length;
let i;
let item;
for (i = 0; i < len; i++) {
item = list[i];
switch (item.$kind) {
case ekDestructuringAssignmentLeaf:
astVisit(item, this);
break;
case ekArrayDestructuring:
case ekObjectDestructuring: {
const source = item.source;
if (source) {
astVisit(source, this);
this.text += ':';
}
astVisit(item, this);
break;
}
}
}
this.text += isObjDes ? '}' : ']';
}
visitDestructuringAssignmentSingleExpression(expr) {
astVisit(expr.source, this);
this.text += ':';
astVisit(expr.target, this);
const initializer = expr.initializer;
if (initializer !== void 0) {
this.text += '=';
astVisit(initializer, this);
}
}
visitDestructuringAssignmentRestExpression(expr) {
this.text += '...';
astVisit(expr.target, this);
}
visitCustom(expr) {
this.text += safeString(expr.value);
}
writeArgs(args) {
this.text += '(';
for (let i = 0, length = args.length; i < length; ++i) {
if (i !== 0) {
this.text += ',';
}
astVisit(args[i], this);
}
this.text += ')';
}
}
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable prefer-template */
/** @internal */
const createMappedError = (code, ...details) => {
const paddedCode = safeString(code).padStart(4, '0');
const message = getMessageByCode(code, ...details);
const link = `https://docs.aurelia.io/developer-guides/error-messages/0151-to-0179/aur${paddedCode}`;
return new Error(`AUR${paddedCode}: ${message}\n\nFor more information, see: ${link}`);
}
;
const errorsMap = {
[99 /* ErrorNames.method_not_implemented */]: 'Method {{0}} not implemented',
[101 /* ErrorNames.ast_behavior_not_found */]: `Ast eval error: binding behavior "{{0}}" could not be found. Did you forget to register it as a dependency?`,
[102 /* ErrorNames.ast_behavior_duplicated */]: `Ast eval error: binding behavior "{{0}}" already applied.`,
[103 /* ErrorNames.ast_converter_not_found */]: `Ast eval error: value converter "{{0}}" could not be found. Did you forget to register it as a dependency?`,
[105 /* ErrorNames.ast_$host_not_found */]: `Ast eval error: unable to find $host context. Did you forget [au-slot] attribute?`,
[106 /* ErrorNames.ast_no_assign_$host */]: `Ast eval error: invalid assignment. "$host" is a reserved keyword.`,
[107 /* ErrorNames.ast_not_a_function */]: `Ast eval error: expression is not a function.`,
[109 /* ErrorNames.ast_unknown_unary_operator */]: `Ast eval error: unknown unary operator: "{{0}}"`,
[108 /* ErrorNames.ast_unknown_binary_operator */]: `Ast eval error: unknown binary operator: "{{0}}"`,
[110 /* ErrorNames.ast_tagged_not_a_function */]: `Ast eval error: left-hand side of tagged template expression is not a function.`,
[111 /* ErrorNames.ast_name_is_not_a_function */]: `Ast eval error: expected "{{0}}" to be a function`,
[112 /* ErrorNames.ast_destruct_null */]: `Ast eval error: cannot use non-object value for destructuring assignment.`,
[151 /* ErrorNames.parse_invalid_start */]: `Expression error: invalid start: "{{0}}"`,
[152 /* ErrorNames.parse_no_spread */]: `Expression error: spread operator is not supported: "{{0}}"`,
[153 /* ErrorNames.parse_expected_identifier */]: `Expression error: expected identifier: "{{0}}"`,
[154 /* ErrorNames.parse_invalid_member_expr */]: `Expression error: invalid member expression: "{{0}}"`,
[155 /* ErrorNames.parse_unexpected_end */]: `Expression error: unexpected end of expression: "{{0}}"`,
[156 /* ErrorNames.parse_unconsumed_token */]: `Expression error: unconsumed token: "{{0}}" at position {{1}} of "{{2}}"`,
[157 /* ErrorNames.parse_invalid_empty */]: `Expression error: invalid empty expression. Empty expression is only valid in event bindings (trigger, delegate, capture etc...)`,
[158 /* ErrorNames.parse_left_hand_side_not_assignable */]: `Expression error: left hand side of expression is not assignable: "{{0}}"`,
[159 /* ErrorNames.parse_expected_converter_identifier */]: `Expression error: expected identifier to come after value converter operator: "{{0}}"`,
[160 /* ErrorNames.parse_expected_behavior_identifier */]: `Expression error: expected identifier to come after binding behavior operator: {{0}}`,
[161 /* ErrorNames.parse_unexpected_keyword_of */]: `Expression error: unexpected keyword "of": "{{0}}"`,
[162 /* ErrorNames.parse_unexpected_keyword_import */]: `Expression error: unexpected keyword "import": "{{0}}"`,
[163 /* ErrorNames.parse_invalid_identifier_in_forof */]: `Expression error: invalid BindingIdentifier at left hand side of "of": "{{0}}" | kind: {{1}}`,
[164 /* ErrorNames.parse_invalid_identifier_object_literal_key */]: `Expression error: invalid or unsupported property definition in object literal: "{{0}}"`,
[165 /* ErrorNames.parse_unterminated_string */]: `Expression error: unterminated quote in string literal: "{{0}}"`,
[166 /* ErrorNames.parse_unterminated_template_string */]: `Expression error: unterminated template string: "{{0}}"`,
[167 /* ErrorNames.parse_missing_expected_token */]: `Expression error: missing expected token "{{0}}" in "{{1}}"`,
[168 /* ErrorNames.parse_unexpected_character */]: `Expression error: unexpected character: "{{0}}"`,
[170 /* ErrorNames.parse_unexpected_token_destructuring */]: `Expression error: unexpected "{{0}}" at position "{{1}}" for destructuring assignment in "{{2}}"`,
[171 /* ErrorNames.parse_unexpected_token_optional_chain */]: `Expression error: unexpected {{0}} at position "{{1}}" for optional chain in "{{2}}"`,
[172 /* ErrorNames.parse_invalid_tag_in_optional_chain */]: `Expression error: invalid tagged template on optional chain in "{{1}}"`,
[173 /* ErrorNames.parse_invalid_arrow_params */]: `Expression error: invalid arrow parameter list in "{{0}}"`,
[174 /* ErrorNames.parse_no_arrow_param_default_value */]: `Expression error: arrow function with default parameters is not supported: "{{0}}"`,
[175 /* ErrorNames.parse_no_arrow_param_destructuring */]: `Expression error: arrow function with destructuring parameters is not supported: "{{0}}"`,
[176 /* ErrorNames.parse_rest_must_be_last */]: `Expression error: rest parameter must be last formal parameter in arrow function: "{{0}}"`,
[178 /* ErrorNames.parse_no_arrow_fn_body */]: `Expression error: arrow function with function body is not supported: "{{0}}"`,
[179 /* ErrorNames.parse_unexpected_double_dot */]: `Expression error: unexpected token '.' at position "{{1}}" in "{{0}}"`,
};
const getMessageByCode = (name, ...details) => {
let cooked = errorsMap[name];
for (let i = 0; i < details.length; ++i) {
const regex = new RegExp(`{{${i}(:.*)?}}`, 'g');
let matches = regex.exec(cooked);
while (matches != null) {
const method = matches[1]?.slice(1);
let value = details[i];
if (value != null) {
switch (method) {
case 'toString':
value = Object.prototype.toString.call(value);
break;
case 'join(!=)':
value = value.join('!=');
break;
case 'element':
value = value === '*' ? 'all elements' : `<${value} />`;
break;
default: {
// property access
if (method?.startsWith('.')) {
value = safeString(value[method.slice(1)]);
}
else {
value = safeString(value);
}
}
}
}
cooked = cooked.slice(0, matches.index) + value + cooked.slice(regex.lastIndex);
matches = regex.exec(cooked);
}
}
return cooked;
};
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
const IExpressionParser = /*@__PURE__*/ DI.createInterface('IExpressionParser');
/**
* A default implementation of the IExpressionParser interface
*/
class ExpressionParser {
constructor() {
/** @internal */ this._expressionLookup = createLookup();
/** @internal */ this._forOfLookup = createLookup();
/** @internal */ this._interpolationLookup = createLookup();
}
parse(expression, expressionType) {
let found;
switch (expressionType) {
case etIsCustom:
return new CustomExpression(expression);
case etInterpolation:
found = this._interpolationLookup[expression];
if (found === void 0) {
found = this._interpolationLookup[expression] = this.$parse(expression, expressionType);
}
return found;
case etIsIterator:
found = this._forOfLookup[expression];
if (found === void 0) {
found = this._forOfLookup[expression] = this.$parse(expression, expressionType);
}
return found;
default: {
if (expression.length === 0) {
if (expressionType === etIsFunction || expressionType === etIsProperty) {
return PrimitiveLiteral.$empty;
}
throw invalidEmptyExpression();
}
found = this._expressionLookup[expression];
if (found === void 0) {
found = this._expressionLookup[expression] = this.$parse(expression, expressionType);
}
return found;
}
}
}
/** @internal */
$parse(expression, expressionType) {
$input = expression;
$index = 0;
$length = expression.length;
$scopeDepth = 0;
$startIndex = 0;
$currentToken = 6291456 /* Token.EOF */;
$tokenValue = '';
$currentChar = $charCodeAt(0);
$assignable = true;
$optional = false;
$accessGlobal = true;
$semicolonIndex = -1;
return parse(61 /* Precedence.Variadic */, expressionType === void 0 ? etIsProperty : expressionType);
}
}
ExpressionParser.register = createImplementationRegister(IExpressionParser);
function unescapeCode(code) {
switch (code) {
case 98 /* Char.LowerB */: return 8 /* Char.Backspace */;
case 116 /* Char.LowerT */: return 9 /* Char.Tab */;
case 110 /* Char.LowerN */: return 10 /* Char.LineFeed */;
case 118 /* Char.LowerV */: return 11 /* Char.VerticalTab */;
case 102 /* Char.LowerF */: return 12 /* Char.FormFeed */;
case 114 /* Char.LowerR */: return 13 /* Char.CarriageReturn */;
case 34 /* Char.DoubleQuote */: return 34 /* Char.DoubleQuote */;
case 39 /* Char.SingleQuote */: return 39 /* Char.SingleQuote */;
case 92 /* Char.Backslash */: return 92 /* Char.Backslash */;
default: return code;
}
}
const $false = PrimitiveLiteral.$false;
const $true = PrimitiveLiteral.$true;
const $null = PrimitiveLiteral.$null;
const $undefined = PrimitiveLiteral.$undefined;
const $this = createAccessThisExpression(0);
const $parent = createAccessThisExpression(1);
const boundary = createAccessBoundaryExpression();
const etNone = 'None';
const etInterpolation = 'Interpolation';
const etIsIterator = 'IsIterator';
const etIsChainable = 'IsChainable';
const etIsFunction = 'IsFunction';
const etIsProperty = 'IsProperty';
const etIsCustom = 'IsCustom';
let $input = '';
let $index = 0;
let $length = 0;
let $scopeDepth = 0;
let $startIndex = 0;
let $currentToken = 6291456 /* Token.EOF */;
let $tokenValue = '';
let $currentChar;
let $assignable = true;
let $optional = false;
let $accessGlobal = true;
let $semicolonIndex = -1;
const stringFromCharCode = String.fromCharCode;
const $charCodeAt = (index) => $input.charCodeAt(index);
const $tokenRaw = () => $input.slice($startIndex, $index);
const globalNames = ('Infinity NaN isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent' +
' Array BigInt Boolean Date Map Number Object RegExp Set String JSON Math Intl').split(' ');
function parseExpression(input, expressionType) {
$input = input;
$index = 0;
$length = input.length;
$scopeDepth = 0;
$startIndex = 0;
$currentToken = 6291456 /* Token.EOF */;
$tokenValue = '';
$currentChar = $charCodeAt(0);
$assignable = true;
$optional = false;
$accessGlobal = true;
$semicolonIndex = -1;
return parse(61 /* Precedence.Variadic */, expressionType === void 0 ? etIsProperty : expressionType);
}
// This is performance-critical code which follows a subset of the well-known ES spec.
// Knowing the spec, or parsers in general, will help with understanding this code and it is therefore not the
// single source of information for being able to figure it out.
// It generally does not need to change unless the spec changes or spec violations are found, or optimization
// opportunities are found (which would likely not fix these warnings in any case).
// It's therefore not considered to have any tangible impact on the maintainability of the code base.
// For reference, most of the parsing logic is based on: https://tc39.github.io/ecma262/#sec-ecmascript-language-expressions
// eslint-disable-next-line max-lines-per-function
function parse(minPrecedence, expressionType) {
if (expressionType === etIsCustom) {
return new CustomExpression($input);
}
if ($index === 0) {
if (expressionType === etInterpolation) {
return parseInterpolation();
}
nextToken();
if ($currentToken & 4194304 /* Token.ExpressionTerminal */) {
throw invalidStartOfExpression();
}
}
$assignable = 577 /* Precedence.Binary */ > minPrecedence;
$optional = false;
$accessGlobal = 579 /* Precedence.LeftHandSide */ > minPrecedence;
let optionalThisTail = false;
let result = void 0;
let ancestor = 0;
if ($currentToken & 131072 /* Token.UnaryOp */) {
/**
* parseUnaryExpression
*
* https://tc39.github.io/ecma262/#sec-unary-operators
*
* UnaryExpression :
* 1. LeftHandSideExpression
* 2. void UnaryExpression
* 3. typeof UnaryExpression
* 4. + UnaryExpression
* 5. - UnaryExpression
* 6. ! UnaryExpression
* 7. ++ UnaryExpression
* 8. -- UnaryExpression
*
* IsValidAssignmentTarget
* 2,3,4,5,6,7,8 = false
* 1 = see parseLeftHandSideExpression
*
* Note: technically we should throw on +++ / ---, but there's nothing to gain from that
*/
const op = TokenValues[$currentToken & 63 /* Token.Type */];
nextToken();
result = createUnaryExpression(op, parse(579 /* Precedence.LeftHandSide */, expressionType));
$assignable = false;
}
else {
/**
* parsePrimaryExpression
*
* https://tc39.github.io/ecma262/#sec-primary-expression
*
* PrimaryExpression :
* 1. this
* 2. IdentifierName
* 3. Literal
* 4. ArrayLiteralExpression
* 5. ObjectLiteralExpression
* 6. TemplateLiteral
* 7. ParenthesizedExpression
*
* Literal :
* NullLiteral
* BooleanLiteral
* NumericLiteral
* StringLiteral
*
* ParenthesizedExpression :
* ( AssignmentExpression )
*
* IsValidAssignmentTarget
* 1,3,4,5,6,7 = false
* 2 = true
*/
primary: switch ($currentToken) {
case 12296 /* Token.ParentScope */: // $parent
ancestor = $scopeDepth;
$assignable = false;
$accessGlobal = false;
do {
nextToken();
++ancestor;
switch ($currentToken) {
case 65547 /* Token.Dot */:
nextToken();
if (($currentToken & 12288 /* Token.IdentifierName */) === 0) {
throw expectedIdentifier();
}
break;
case 12 /* Token.DotDot */:
case 13 /* Token.DotDotDot */:
throw expectedIdentifier();
case 2162702 /* Token.QuestionDot */:
$optional = true;
nextToken();
if (($currentToken & 12288 /* Token.IdentifierName */) === 0) {
result = ancestor === 0 ? $this : ancestor === 1 ? $parent : createAccessThisExpression(ancestor);
optionalThisTail = true;
break primary;
}
break;
default:
if ($currentToken & 2097152 /* Token.AccessScopeTerminal */) {
result = ancestor === 0 ? $this : ancestor === 1 ? $parent : createAccessThisExpression(ancestor);
break primary;
}
throw invalidMemberExpression();
}
} while ($currentToken === 12296 /* Token.ParentScope */);
// falls through
case 4096 /* Token.Identifier */: { // identifier
const id = $tokenValue;
if (expressionType === etIsIterator) {
result = createBindingIdentifier(id);
}
else if ($accessGlobal && globalNames.includes(id)) {
result = createAccessGlobalExpression(id);
}
else if ($accessGlobal && id === 'import') {
throw unexpectedImportKeyword();
}
else {
result = createAccessScopeExpression(id, ancestor);
}
$assignable = !$optional;
nextToken();
if (consumeOpt(53 /* Token.Arrow */)) {
if ($currentToken === 524298 /* Token.OpenBrace */) {
throw functionBodyInArrowFn();
}
const _optional = $optional;
const _scopeDepth = $scopeDepth;
++$scopeDepth;
const body = parse(62 /* Precedence.Assign */, etNone);
$optional = _optional;
$scopeDepth = _scopeDepth;
$assignable = false;
result = createArrowFunction([createBindingIdentifier(id)], body);
}
break;
}
case 12 /* Token.DotDot */:
throw unexpectedDoubleDot();
case 13 /* Token.DotDotDot */:
throw invalidSpreadOp();
case 12293 /* Token.ThisScope */: // $this
$assignable = false;
nextToken();
switch ($scopeDepth) {
case 0:
result = $this;
break;
case 1:
result = $parent;
break;
default:
result = createAccessThisExpression($scopeDepth);
break;
}
break;
case 12294 /* Token.AccessBoundary */: // this
$assignable = false;
nextToken();
result = boundary;
break;
case 2688009 /* Token.OpenParen */:
result = parseCoverParenthesizedExpressionAndArrowParameterList(expressionType);
break;
case 2688020 /* Token.OpenBracket */:
result = $input.search(/\s+of\s+/) > $index ? parseArrayDestructuring() : parseArrayLiteralExpression(expressionType);
break;
case 524298 /* Token.OpenBrace */:
result = parseObjectLiteralExpression(expressionType);
break;
case 2163762 /* Token.TemplateTail */:
result = createTemplateExpression([$tokenValue]);
$assignable = false;
nextToken();
break;
case 2163763 /* Token.TemplateContinuation */:
result = parseTemplate(expressionType, result, false);
break;
case 16384 /* Token.StringLiteral */:
case 32768 /* Token.NumericLiteral */:
result = createPrimitiveLiteralExpression($tokenValue);
$assignable = false;
nextToken();
break;
case 8194 /* Token.NullKeyword */:
case 8195 /* Token.UndefinedKeyword */:
case 8193 /* Token.TrueKeyword */:
case 8192 /* Token.FalseKeyword */:
result = TokenValues[$currentToken & 63 /* Token.Type */];
$assignable = false;
nextToken();
break;
case 8196 /* Token.NewKeyword */: {
nextToken();
const callee = parse(578 /* Precedence.Member */, expressionType);
let args;
if ($currentToken === 2688009 /* Token.OpenParen */) {
args = parseArguments();
}
else {
args = [];
nextToken();
}
result = createNewExpression(callee, args);
$assignable = false;
break;
}
default:
if ($index >= $length) {
throw unexpectedEndOfExpression();
}
else {
throw unconsumedToken();
}
}
if (expressionType === etIsIterator) {
return parseForOfStatement(result);
}
switch ($currentToken) {
case 2228282 /* Token.PlusPlus */:
case 2228283 /* Token.MinusMinus */:
result = createUnaryExpression(TokenValues[$currentToken & 63 /* Token.Type */], result, 1);
nextToken();
$assignable = false;
break;
}
if (579 /* Precedence.LeftHandSide */ < minPrecedence) {
return result;
}
if ($currentToken === 12 /* Token.DotDot */ || $currentToken === 13 /* Token.DotDotDot */) {
throw expectedIdentifier();
}
if (result.$kind === ekAccessThis) {
switch ($currentToken) {
case 2162702 /* Token.QuestionDot */:
$optional = true;
$assignable = false;
nextToken();
if (($currentToken & 13312 /* Token.OptionalSuffix */) === 0) {
throw unexpectedTokenInOptionalChain();
}
if ($currentToken & 12288 /* Token.IdentifierName */) {
result = createAccessScopeExpression($tokenValue, result.ancestor);
nextToken();
}
else if ($currentToken === 2688009 /* Token.OpenParen */) {
result = createCallFunctionExpression(result, parseArguments(), true);
}
else if ($currentToken === 2688020 /* Token.OpenBracket */) {
result = parseKeyedExpression(result, true);
}
else {
throw invalidTaggedTemplateOnOptionalChain();
}
break;
case 65547 /* Token.Dot */:
$assignable = !$optional;
nextToken();
if (($currentToken & 12288 /* Token.IdentifierName */) === 0) {
throw expectedIdentifier();
}
result = createAccessScopeExpression($tokenValue, result.ancestor);
nextToken();
break;
case 12 /* Token.DotDot */:
case 13 /* Token.DotDotDot */:
throw expectedIdentifier();
case 2688009 /* Token.OpenParen */:
result = createCallFunctionExpression(result, parseArguments(), optionalThisTail);
break;
case 2688020 /* Token.OpenBracket */:
result = parseKeyedExpression(result, optionalThisTail);
break;
case 2163762 /* Token.TemplateTail */:
result = createTemplateTail(result);
break;
case 2163763 /* Token.TemplateContinuation */:
result = parseTemplate(expressionType, result, true);
break;
}
}
/**
* parseMemberExpression (Token.Dot, Token.OpenBracket, Token.TemplateContinuation)
*
* MemberExpression :
* 1. PrimaryExpression
* 2. MemberExpression [ AssignmentExpression ]
* 3. MemberExpression . IdentifierName
* 4. MemberExpression TemplateLiteral
*
* IsValidAssignmentTarget
* 1,4 = false
* 2,3 = true
*
*
* parseCallExpression (Token.OpenParen)
* CallExpression :
* 1. MemberExpression Arguments
* 2. CallExpression Arguments
* 3. CallExpression [ AssignmentExpression ]
* 4. CallExpression . IdentifierName
* 5. CallExpression TemplateLiteral
*
* IsValidAssignmentTarget
* 1,2,5 = false
* 3,4 = true
*/
while (($currentToken & 65536 /* Token.LeftHandSide */) > 0) {
switch ($currentToken) {
case 2162702 /* Token.QuestionDot */:
result = parseOptionalChainLHS(result);
break;
case 65547 /* Token.Dot */:
nextToken();
if (($currentToken & 12288 /* Token.IdentifierName */) === 0) {
throw expectedIdentifier();
}
result = parseMemberExpressionLHS(result, false);
break;
case 12 /* Token.DotDot */:
case 13 /* Token.DotDotDot */:
throw expectedIdentifier();
case 2688009 /* Token.OpenParen */:
if (578 /* Precedence.Member */ === minPrecedence) {
return result;
}
if (result.$kind === ekAccessScope) {
result = createCallScopeExpression(result.name, parseArguments(), result.ancestor, false);
}
else if (result.$kind === ekAccessMember) {
result = createCallMemberExpression(result.object, result.name, parseArguments(), result.optional, false);
}
else if (result.$kind === ekAccessGlobal) {
result = createCallGlobalExpression(result.name, parseArguments());
}
else {
result = createCallFunctionExpression(result, parseArguments(), false);
}
break;
case 2688020 /* Token.OpenBracket */:
result = parseKeyedExpression(result, false);
break;
case 2163762 /* Token.TemplateTail */:
if ($optional) {
throw invalidTaggedTemplateOnOptionalChain();
}
result = createTemplateTail(result);
break;
case 2163763 /* Token.TemplateContinuation */:
if ($optional) {
throw invalidTaggedTemplateOnOptionalChain();
}
result = parseTemplate(expressionType, result, true);
break;
}
}
}
if ($currentToken === 12 /* Token.DotDot */ || $currentToken === 13 /* Token.DotDotDot */) {
throw expectedIdentifier();
}
if (577 /* Precedence.Binary */ < minPrecedence) {
return result;
}
/**
* parseBinaryExpression
*
* https://tc39.github.io/ecma262/#sec-multiplicative-operators
*
* MultiplicativeExpression : (local precedence 6)
* UnaryExpression
* MultiplicativeExpression * / % UnaryExpression
*
* AdditiveExpression : (local precedence 5)
* MultiplicativeExpression
* AdditiveExpression + - MultiplicativeExpression
*
* RelationalExpression : (local precedence 4)
* AdditiveExpression
* RelationalExpression < > <= >= instanceof in AdditiveExpression
*
* EqualityExpression : (local precedence 3)
* RelationalExpression
* EqualityExpression == != === !== RelationalExpression
*
* LogicalANDExpression : (local precedence 2)
* EqualityExpression
* LogicalANDExpression && EqualityExpression
*
* LogicalORExpression : (local precedence 1)
* LogicalANDExpression
* LogicalORExpression || LogicalANDExpression
*
* CoalesceExpression :
* CoalesceExpressionHead ?? BitwiseORExpression
*
* CoalesceExpressionHead :
* CoelesceExpression
* BitwiseORExpression
*
* ShortCircuitExpression :
* LogicalORExpression
* CoalesceExpression
*/
while (($currentToken & 262144 /* Token.BinaryOp */) > 0) {
const opToken = $currentToken;
if ((opToken & 960 /* Token.Precedence */) <= minPrecedence) {
break;
}
nextToken();
result = createBinaryExpression(TokenValues[opToken & 63 /* Token.Type */], result, parse(opToken & 960 /* Token.Precedence */, expressionType));
$assignable = false;
}
if (63 /* Precedence.Conditional */ < minPrecedence) {
return result;
}
/**
* parseConditionalExpression
* https://tc39.github.io/ecma262/#prod-ConditionalExpression
*
* ConditionalExpression :
* 1. ShortCircuitExpression
* 2. ShortCircuitExpression ? AssignmentExpression : AssignmentExpression
*
* IsValidAssignmentTarget
* 1,2 = false
*/
if (consumeOpt(6291480 /* Token.Question */)) {
const yes = parse(62 /* Precedence.Assign */, expressionType);
consume(6291478 /* Token.Colon */);
result = createConditionalExpression(result, yes, parse(62 /* Precedence.Assign */, expressionType));
$assignable = false;
}
if (62 /* Precedence.Assign */ < minPrecedence) {
return result;
}
/**
* parseAssignmentExpression
*
* https://tc39.github.io/ecma262/#prod-AssignmentExpression
* Note: AssignmentExpression here is equivalent to ES Expression because we don't parse the comma operator
*
* AssignmentExpression :
* 1. ConditionalExpression
* 2. LeftHandSideExpression = AssignmentExpression
* 3. LeftHandSideExpression AssignmentOperator AssignmentExpression
*
* IsValidAssignmentTarget
* 1,2 = false
*/
switch ($currentToken) {
case 4194352 /* Token.Equals */:
case 4194358 /* Token.PlusEquals */:
case 4194359 /* Token.MinusEquals */:
case 4194360 /* Token.AsteriskEquals */:
case 4194361 /* Token.SlashEquals */: {
if (!$assignable) {
throw lhsNotAssignable();
}
const op = TokenValues[$currentToken & 63 /* Token.Type */];
nextToken();
result = createAssignExpression(result, parse(62 /* Precedence.Assign */, expressionType), op);
break;
}
}
if (61 /* Precedence.Variadic */ < minPrecedence) {
return result;
}
/**
* parseValueConverter
*/
while (consumeOpt(6291482 /* Token.Bar */)) {
if ($currentToken === 6291456 /* Token.EOF */) {
throw expectedValueC