UNPKG

@aurelia/expression-parser

Version:

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/) [![CircleCI](https://circleci.com/

1,272 lines (1,263 loc) 90.8 kB
import { emptyArray, createImplementationRegister, DI } from '../../../kernel/dist/native-modules/index.mjs'; /* 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'; class CustomExpression { constructor(value) { this.value = value; this.$kind = ekCustom; } evaluate(...params) { return this.value; } assign(...params) { return params; } bind(...params) { // empty } unbind(...params) { // empty } accept(_visitor) { return (void 0); } } class BindingBehaviorExpression { constructor(expression, name, args) { this.expression = expression; this.name = name; this.args = args; this.$kind = ekBindingBehavior; this.key = `_bb_${name}`; } } class ValueConverterExpression { constructor(expression, name, args) { this.expression = expression; this.name = name; this.args = args; this.$kind = ekValueConverter; } } class AssignExpression { constructor(target, value, op = '=') { this.target = target; this.value = value; this.op = op; this.$kind = ekAssign; } } class ConditionalExpression { constructor(condition, yes, no) { this.condition = condition; this.yes = yes; this.no = no; this.$kind = ekConditional; } } class AccessGlobalExpression { constructor(name) { this.name = name; this.$kind = ekAccessGlobal; } } class AccessThisExpression { constructor(ancestor = 0) { this.ancestor = ancestor; this.$kind = ekAccessThis; } } class AccessBoundaryExpression { constructor() { this.$kind = ekAccessBoundary; } } class AccessScopeExpression { constructor(name, ancestor = 0) { this.name = name; this.ancestor = ancestor; this.$kind = ekAccessScope; } } const isAccessGlobal = (ast) => (ast.$kind === ekAccessGlobal || (ast.$kind === ekAccessMember || ast.$kind === ekAccessKeyed) && ast.accessGlobal); class AccessMemberExpression { constructor(object, name, optional = false) { this.object = object; this.name = name; this.optional = optional; this.$kind = ekAccessMember; this.accessGlobal = isAccessGlobal(object); } } class AccessKeyedExpression { constructor(object, key, optional = false) { this.object = object; this.key = key; this.optional = optional; this.$kind = ekAccessKeyed; this.accessGlobal = isAccessGlobal(object); } } class NewExpression { constructor(func, args) { this.func = func; this.args = args; this.$kind = ekNew; } } class CallScopeExpression { constructor(name, args, ancestor = 0, optional = false) { this.name = name; this.args = args; this.ancestor = ancestor; this.optional = optional; this.$kind = ekCallScope; } } class CallMemberExpression { constructor(object, name, args, optionalMember = false, optionalCall = false) { this.object = object; this.name = name; this.args = args; this.optionalMember = optionalMember; this.optionalCall = optionalCall; this.$kind = ekCallMember; } } class CallFunctionExpression { constructor(func, args, optional = false) { this.func = func; this.args = args; this.optional = optional; this.$kind = ekCallFunction; } } class CallGlobalExpression { constructor(name, args) { this.name = name; this.args = args; this.$kind = ekCallGlobal; } } class BinaryExpression { constructor(operation, left, right) { this.operation = operation; this.left = left; this.right = right; this.$kind = ekBinary; } } class UnaryExpression { constructor(operation, expression, pos = 0) { this.operation = operation; this.expression = expression; this.pos = pos; this.$kind = ekUnary; } } class PrimitiveLiteralExpression { constructor(value) { this.value = value; this.$kind = ekPrimitiveLiteral; } } PrimitiveLiteralExpression.$undefined = new PrimitiveLiteralExpression(void 0); PrimitiveLiteralExpression.$null = new PrimitiveLiteralExpression(null); PrimitiveLiteralExpression.$true = new PrimitiveLiteralExpression(true); PrimitiveLiteralExpression.$false = new PrimitiveLiteralExpression(false); PrimitiveLiteralExpression.$empty = new PrimitiveLiteralExpression(''); class ArrayLiteralExpression { constructor(elements) { this.elements = elements; this.$kind = ekArrayLiteral; } } ArrayLiteralExpression.$empty = new ArrayLiteralExpression(emptyArray); class ObjectLiteralExpression { constructor(keys, values) { this.keys = keys; this.values = values; this.$kind = ekObjectLiteral; } } ObjectLiteralExpression.$empty = new ObjectLiteralExpression(emptyArray, emptyArray); class TemplateExpression { constructor(cooked, expressions = emptyArray) { this.cooked = cooked; this.expressions = expressions; this.$kind = ekTemplate; } } TemplateExpression.$empty = new TemplateExpression(['']); class TaggedTemplateExpression { constructor(cooked, raw, func, expressions = emptyArray) { this.cooked = cooked; this.func = func; this.expressions = expressions; this.$kind = ekTaggedTemplate; cooked.raw = raw; } } class ArrayBindingPattern { // We'll either have elements, or keys+values, but never all 3 constructor(elements) { this.elements = elements; this.$kind = ekArrayBindingPattern; } } class ObjectBindingPattern { // We'll either have elements, or keys+values, but never all 3 constructor(keys, values) { this.keys = keys; this.values = values; this.$kind = ekObjectBindingPattern; } } class BindingIdentifier { constructor(name) { this.name = name; this.$kind = ekBindingIdentifier; } } // https://tc39.github.io/ecma262/#sec-iteration-statements // https://tc39.github.io/ecma262/#sec-for-in-and-for-of-statements class ForOfStatement { constructor(declaration, iterable, semiIdx) { this.declaration = declaration; this.iterable = iterable; this.semiIdx = semiIdx; this.$kind = ekForOfStatement; } } /* * Note: this implementation is far simpler than the one in vCurrent and might be missing important stuff (not sure yet) * so while this implementation is identical to Template and we could reuse that one, we don't want to lock outselves in to potentially the wrong abstraction * but this class might be a candidate for removal if it turns out it does provide all we need */ class Interpolation { constructor(parts, expressions = emptyArray) { this.parts = parts; this.expressions = expressions; this.$kind = ekInterpolation; this.isMulti = expressions.length > 1; this.firstExpression = expressions[0]; } } // spec: https://tc39.es/ecma262/#sec-destructuring-assignment /** This is an internal API */ class DestructuringAssignmentExpression { constructor($kind, list, source, initializer) { this.$kind = $kind; this.list = list; this.source = source; this.initializer = initializer; } } /** This is an internal API */ class DestructuringAssignmentSingleExpression { constructor(target, source, initializer) { this.target = target; this.source = source; this.initializer = initializer; this.$kind = ekDestructuringAssignmentLeaf; } } /** This is an internal API */ class DestructuringAssignmentRestExpression { constructor(target, indexOrProperties) { this.target = target; this.indexOrProperties = indexOrProperties; this.$kind = ekDestructuringAssignmentLeaf; } } class ArrowFunction { constructor(args, body, rest = false) { this.args = args; this.body = body; this.rest = rest; this.$kind = ekArrowFunction; } } /** @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) => new Error(`AUR${safeString(code).padStart(4, '0')}: ${getMessageByCode(code, ...details)}`) ; 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 PrimitiveLiteralExpression.$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 = PrimitiveLiteralExpression.$false; const $true = PrimitiveLiteralExpression.$true; const $null = PrimitiveLiteralExpression.$null; const $undefined = PrimitiveLiteralExpression.$undefined; const $this = new AccessThisExpression(0); const $parent = new AccessThisExpression(1); const boundary = new AccessBoundaryExpression(); 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 = new UnaryExpression(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 : new AccessThisExpression(ancestor); optionalThisTail = true; break primary; } break; default: if ($currentToken & 2097152 /* Token.AccessScopeTerminal */) { result = ancestor === 0 ? $this : ancestor === 1 ? $parent : new AccessThisExpression(ancestor); break primary; } throw invalidMemberExpression(); } } while ($currentToken === 12296 /* Token.ParentScope */); // falls through case 4096 /* Token.Identifier */: { // identifier const id = $tokenValue; if (expressionType === etIsIterator) { result = new BindingIdentifier(id); } else if ($accessGlobal && globalNames.includes(id)) { result = new AccessGlobalExpression(id); } else if ($accessGlobal && id === 'import') { throw unexpectedImportKeyword(); } else { result = new AccessScopeExpression(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 = new ArrowFunction([new BindingIdentifier(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 = new AccessThisExpression($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 = new TemplateExpression([$tokenValue]); $assignable = false; nextToken(); break; case 2163763 /* Token.TemplateContinuation */: result = parseTemplate(expressionType, result, false); break; case 16384 /* Token.StringLiteral */: case 32768 /* Token.NumericLiteral */: result = new PrimitiveLiteralExpression($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 = new NewExpression(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 = new UnaryExpression(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 = new AccessScopeExpression($tokenValue, result.ancestor); nextToken(); } else if ($currentToken === 2688009 /* Token.OpenParen */) { result = new CallFunctionExpression(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 = new AccessScopeExpression($tokenValue, result.ancestor); nextToken(); break; case 12 /* Token.DotDot */: case 13 /* Token.DotDotDot */: throw expectedIdentifier(); case 2688009 /* Token.OpenParen */: result = new CallFunctionExpression(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 = new CallScopeExpression(result.name, parseArguments(), result.ancestor, false); } else if (result.$kind === ekAccessMember) { result = new CallMemberExpression(result.object, result.name, parseArguments(), result.optional, false); } else if (result.$kind === ekAccessGlobal) { result = new CallGlobalExpression(result.name, parseArguments()); } else { result = new CallFunctionExpression(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 :