Clone engine262 in /engine262

This commit is contained in:
2020-09-07 10:19:00 +05:30
parent 7688de2e5c
commit 66c1aeb9e0
313 changed files with 46014 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
import { Lexer } from './Lexer.mjs';
export class BaseParser extends Lexer {}
File diff suppressed because it is too large Load Diff
+322
View File
@@ -0,0 +1,322 @@
import { IsSimpleParameterList } from '../static-semantics/all.mjs';
import { getDeclarations } from './Scope.mjs';
import { Token } from './tokens.mjs';
import { IdentifierParser } from './IdentifierParser.mjs';
export const FunctionKind = {
NORMAL: 0,
ASYNC: 1,
};
export class FunctionParser extends IdentifierParser {
// FunctionDeclaration :
// `function` BindingIdentifier `(` FormalParameters `)` `{` FunctionBody `}`
// [+Default] `function` `(` FormalParameters `)` `{` FunctionBody `}`
// FunctionExpression :
// `function` BindingIdentifier? `(` FormalParameters `)` `{` FunctionBody `}`
// GeneratorDeclaration :
// `function` `*` BindingIdentifier `(` FormalParameters `)` `{` GeneratorBody `}`
// [+Default] `function` `*` `(` FormalParameters `)` `{` GeneratorBody `}`
// GeneratorExpression :
// `function` BindingIdentifier? `(` FormalParameters `)` `{` GeneratorBody `}`
// AsyncGeneratorDeclaration :
// `async` `function` `*` BindingIdentifier `(` FormalParameters `)` `{` AsyncGeneratorBody `}`
// [+Default] `async` `function` `*` `(` FormalParameters `)` `{` AsyncGeneratorBody `}`
// AsyncGeneratorExpression :
// `async` `function` BindingIdentifier? `(` FormalParameters `)` `{` AsyncGeneratorBody `}`
// AsyncFunctionDeclaration :
// `async` `function` BindingIdentifier `(` FormalParameters `)` `{` FunctionBody `}`
// [+Default] `async` `function` `(` FormalParameters `)` `{` AsyncFunctionBody `}`
// Async`FunctionExpression :
// `async` `function` BindingIdentifier? `(` FormalParameters `)` `{` AsyncFunctionBody `}`
parseFunction(isExpression, kind) {
const isAsync = kind === FunctionKind.ASYNC;
const node = this.startNode();
if (isAsync) {
this.expect('async');
}
this.expect(Token.FUNCTION);
const isGenerator = this.eat(Token.MUL);
if (!this.test(Token.LPAREN)) {
this.scope.with({
await: isExpression ? false : undefined,
yield: isExpression ? false : undefined,
}, () => {
node.BindingIdentifier = this.parseBindingIdentifier();
});
if (!isExpression) {
this.scope.declare(node.BindingIdentifier, 'function');
}
} else if (isExpression === false && !this.scope.isDefault()) {
this.unexpected();
} else {
node.BindingIdentifier = null;
}
this.scope.with({
default: false,
await: isAsync,
yield: isGenerator,
lexical: true,
variable: true,
variableFunctions: true,
}, () => {
node.FormalParameters = this.parseFormalParameters();
const body = this.parseFunctionBody(isAsync, isGenerator, false);
node[body.type] = body;
if (node.BindingIdentifier) {
if (body.strict && (node.BindingIdentifier.name === 'eval' || node.BindingIdentifier.name === 'arguments')) {
this.raiseEarly('UnexpectedToken', node.BindingIdentifier);
}
if (isExpression) {
if (this.scope.hasYield() && node.BindingIdentifier.name === 'yield') {
this.raiseEarly('UnexpectedToken', node.BindingIdentifier);
}
if (this.scope.hasAwait() && node.BindingIdentifier.name === 'await') {
this.raiseEarly('UnexpectedToken', node.BindingIdentifier);
}
}
}
this.validateFormalParameters(node.FormalParameters, body);
});
const name = `${isAsync ? 'Async' : ''}${isGenerator ? 'Generator' : 'Function'}${isExpression ? 'Expression' : 'Declaration'}`;
return this.finishNode(node, name);
}
validateFormalParameters(parameters, body, wantsUnique = false) {
const isStrict = body.strict;
const hasStrictDirective = body.directives && body.directives.includes('use strict');
if (wantsUnique === false && !IsSimpleParameterList(parameters)) {
wantsUnique = true;
}
if (hasStrictDirective) {
parameters.forEach((p) => {
if (p.type !== 'SingleNameBinding' || p.Initializer) {
this.raiseEarly('UseStrictNonSimpleParameter', p);
}
});
}
const names = new Set();
getDeclarations(parameters)
.forEach((d) => {
if (isStrict) {
if (d.name === 'arguments' || d.name === 'eval') {
this.raiseEarly('UnexpectedToken', d.node);
}
}
if (isStrict || wantsUnique) {
if (names.has(d.name)) {
this.raiseEarly('AlreadyDeclared', d.node, d.name);
} else {
names.add(d.name);
}
}
});
}
convertArrowParameter(node) {
switch (node.type) {
case 'IdentifierReference': {
node.type = 'BindingIdentifier';
const container = this.startNode();
container.BindingIdentifier = node;
container.Initializer = null;
this.scope.declare(node, 'parameter');
return this.finishNode(container, 'SingleNameBinding');
}
case 'BindingRestElement':
this.scope.declare(node, 'parameter');
return node;
case 'Elision':
return node;
case 'ArrayLiteral': {
const wrap = this.startNode();
node.BindingElementList = [];
node.ElementList.forEach((p, i) => {
const c = this.convertArrowParameter(p);
if (c.type === 'BindingRestElement') {
if (i !== node.ElementList.length - 1) {
this.raiseEarly('UnexpectedToken', c);
}
node.BindingRestElement = c;
} else {
node.BindingElementList.push(c);
}
});
delete node.ElementList;
node.type = 'ArrayBindingPattern';
wrap.BindingPattern = node;
wrap.Initializer = null;
return this.finishNode(wrap, 'BindingElement');
}
case 'ObjectLiteral': {
const wrap = this.startNode();
node.BindingPropertyList = [];
node.PropertyDefinitionList.forEach((p) => {
const c = this.convertArrowParameter(p);
if (c.type === 'BindingRestProperty') {
node.BindingRestProperty = c;
} else {
node.BindingPropertyList.push(c);
}
});
delete node.PropertyDefinitionList;
node.type = 'ObjectBindingPattern';
wrap.BindingPattern = node;
wrap.Initializer = null;
return this.finishNode(wrap, 'BindingElement');
}
case 'AssignmentExpression': {
const result = this.convertArrowParameter(node.LeftHandSideExpression);
result.Initializer = node.AssignmentExpression;
return result;
}
case 'CoverInitializedName':
node.type = 'SingleNameBinding';
node.BindingIdentifier = node.IdentifierReference;
node.BindingIdentifier.type = 'BindingIdentifier';
delete node.IdentifierReference;
this.scope.declare(node, 'parameter');
return node;
case 'PropertyDefinition':
if (node.PropertyName === null) {
node.type = 'BindingRestProperty';
node.BindingIdentifier = node.AssignmentExpression;
node.BindingIdentifier.type = 'BindingIdentifier';
} else {
node.type = 'BindingProperty';
node.BindingElement = this.convertArrowParameter(node.AssignmentExpression);
}
this.scope.declare(node, 'parameter');
delete node.AssignmentExpression;
return node;
case 'SpreadElement':
case 'AssignmentRestElement':
node.type = 'BindingRestElement';
if (node.AssignmentExpression.type === 'AssignmentExpression') {
this.raiseEarly('UnexpectedToken', node);
} else if (node.AssignmentExpression.type === 'IdentifierReference') {
node.BindingIdentifier = node.AssignmentExpression;
node.BindingIdentifier.type = 'BindingIdentifier';
} else {
node.BindingPattern = this.convertArrowParameter(node.AssignmentExpression).BindingPattern;
}
this.scope.declare(node, 'parameter');
delete node.AssignmentExpression;
return node;
default:
this.raiseEarly('UnexpectedToken', node);
return node;
}
}
parseArrowFunction(node, { arrowInfo, Arguments }, kind) {
const isAsync = kind === FunctionKind.ASYNC;
this.expect(Token.ARROW);
if (arrowInfo) {
arrowInfo.awaitExpressions.forEach((e) => {
this.raiseEarly('AwaitInFormalParameters', e);
});
arrowInfo.yieldExpressions.forEach((e) => {
this.raiseEarly('YieldInFormalParameters', e);
});
if (isAsync) {
arrowInfo.awaitIdentifiers.forEach((e) => {
this.raiseEarly('AwaitInFormalParameters', e);
});
}
}
this.scope.with({
default: false,
lexical: true,
variable: true,
}, () => {
this.scope.with({
parameters: true,
}, () => {
node.ArrowParameters = Arguments.map((p) => this.convertArrowParameter(p));
});
const body = this.parseConciseBody(isAsync);
this.validateFormalParameters(node.ArrowParameters, body, true);
node[`${isAsync ? 'Async' : ''}ConciseBody`] = body;
});
return this.finishNode(node, `${isAsync ? 'Async' : ''}ArrowFunction`);
}
parseConciseBody(isAsync) {
if (this.test(Token.LBRACE)) {
return this.parseFunctionBody(isAsync, false, true);
}
const asyncBody = this.startNode();
const exprBody = this.startNode();
this.scope.with({ await: isAsync }, () => {
exprBody.AssignmentExpression = this.parseAssignmentExpression();
});
asyncBody.ExpressionBody = this.finishNode(exprBody, 'ExpressionBody');
return this.finishNode(asyncBody, `${isAsync ? 'Async' : ''}ConciseBody`);
}
// FormalParameter : BindingElement
parseFormalParameter() {
return this.parseBindingElement();
}
parseFormalParameters() {
this.expect(Token.LPAREN);
if (this.eat(Token.RPAREN)) {
return [];
}
const params = [];
this.scope.with({ parameters: true }, () => {
while (true) {
if (this.test(Token.ELLIPSIS)) {
const element = this.parseBindingRestElement();
this.scope.declare(element, 'parameter');
params.push(element);
this.expect(Token.RPAREN);
break;
} else {
const formal = this.parseFormalParameter();
this.scope.declare(formal, 'parameter');
params.push(formal);
}
if (this.eat(Token.RPAREN)) {
break;
}
this.expect(Token.COMMA);
if (this.eat(Token.RPAREN)) {
break;
}
}
});
return params;
}
parseUniqueFormalParameters() {
return this.parseFormalParameters();
}
parseFunctionBody(isAsync, isGenerator, isArrow) {
const node = this.startNode();
this.expect(Token.LBRACE);
this.scope.with({
newTarget: isArrow ? undefined : true,
return: true,
await: isAsync,
yield: isGenerator,
label: 'boundary',
}, () => {
node.directives = [];
node.FunctionStatementList = this.parseStatementList(Token.RBRACE, node.directives);
node.strict = node.strict || node.directives.includes('use strict');
});
const name = `${isAsync ? 'Async' : ''}${isGenerator ? 'Generator' : 'Function'}Body`;
return this.finishNode(node, name);
}
}
+138
View File
@@ -0,0 +1,138 @@
import {
Token,
isKeyword,
isReservedWordStrict,
isKeywordRaw,
} from './tokens.mjs';
import { BaseParser } from './BaseParser.mjs';
export class IdentifierParser extends BaseParser {
// IdentifierName
parseIdentifierName() {
const node = this.startNode();
const p = this.peek();
if (p.type === Token.IDENTIFIER
|| p.type === Token.ESCAPED_KEYWORD
|| isKeyword(p.type)) {
node.name = this.next().value;
} else {
this.unexpected();
}
return this.finishNode(node, 'IdentifierName');
}
// BindingIdentifier :
// Identifier
// `yield`
// `await`
parseBindingIdentifier() {
const node = this.startNode();
const token = this.next();
switch (token.type) {
case Token.IDENTIFIER:
node.name = token.value;
break;
case Token.ESCAPED_KEYWORD:
node.name = token.value;
break;
case Token.YIELD:
node.name = 'yield';
break;
case Token.AWAIT:
node.name = 'await';
for (let i = 0; i < this.scope.arrowInfoStack.length; i += 1) {
const arrowInfo = this.scope.arrowInfoStack[i];
if (arrowInfo.isAsync) {
arrowInfo.awaitIdentifiers.push(node);
break;
}
}
break;
default:
this.unexpected(token);
}
if (node.name === 'yield' && (this.scope.hasYield() || this.scope.isModule())) {
this.raiseEarly('UnexpectedReservedWordStrict', token);
}
if (node.name === 'await' && (this.scope.hasAwait() || this.scope.isModule())) {
this.raiseEarly('UnexpectedReservedWordStrict', token);
}
if (this.isStrictMode()) {
if (isReservedWordStrict(node.name)) {
this.raiseEarly('UnexpectedReservedWordStrict', token);
}
if (node.name === 'eval' || node.name === 'arguments') {
this.raiseEarly('UnexpectedEvalOrArguments', token);
}
}
if (node.name !== 'yield'
&& node.name !== 'await'
&& isKeywordRaw(node.name)) {
this.raiseEarly('UnexpectedToken', token);
}
return this.finishNode(node, 'BindingIdentifier');
}
// IdentifierReference :
// Identifier
// [~Yield] `yield`
// [~Await] `await`
parseIdentifierReference() {
const node = this.startNode();
const token = this.next();
switch (token.type) {
case Token.IDENTIFIER:
node.name = token.value;
break;
case Token.ESCAPED_KEYWORD:
node.name = token.value;
break;
case Token.YIELD:
if (this.scope.hasYield()) {
this.unexpected(token);
}
node.name = 'yield';
break;
case Token.AWAIT:
if (this.scope.hasAwait()) {
this.unexpected(token);
}
for (let i = 0; i < this.scope.arrowInfoStack.length; i += 1) {
const arrowInfo = this.scope.arrowInfoStack[i];
if (arrowInfo.isAsync) {
arrowInfo.awaitIdentifiers.push(node);
break;
}
}
node.name = 'await';
break;
default:
this.unexpected(token);
}
if (node.name === 'yield' && (this.scope.hasYield() || this.scope.isModule())) {
this.raiseEarly('UnexpectedReservedWordStrict', token);
}
if (node.name === 'await' && (this.scope.hasAwait() || this.scope.isModule())) {
this.raiseEarly('UnexpectedReservedWordStrict', token);
}
if (this.isStrictMode() && isReservedWordStrict(node.name)) {
this.raiseEarly('UnexpectedReservedWordStrict', token);
}
if (node.name !== 'yield'
&& node.name !== 'await'
&& isKeywordRaw(node.name)) {
this.raiseEarly('UnexpectedToken', token);
}
return this.finishNode(node, 'IdentifierReference');
}
// LabelIdentifier :
// Identifier
// [~Yield] `yield`
// [~Await] `await`
parseLabelIdentifier() {
const node = this.parseIdentifierReference();
node.type = 'LabelIdentifier';
return node;
}
}
+96
View File
@@ -0,0 +1,96 @@
import { StatementParser } from './StatementParser.mjs';
import { Token } from './tokens.mjs';
export class LanguageParser extends StatementParser {
// Script : ScriptBody?
parseScript() {
if (this.feature('hashbang')) {
this.skipHashbangComment();
}
const node = this.startNode();
if (this.eat(Token.EOS)) {
node.ScriptBody = null;
} else {
node.ScriptBody = this.parseScriptBody();
}
return this.finishNode(node, 'Script');
}
// ScriptBody : StatementList
parseScriptBody() {
const node = this.startNode();
this.scope.with({
in: true,
lexical: true,
variable: true,
variableFunctions: true,
}, () => {
const directives = [];
node.StatementList = this.parseStatementList(Token.EOS, directives);
node.strict = directives.includes('use strict');
});
return this.finishNode(node, 'ScriptBody');
}
// Module : ModuleBody?
parseModule() {
if (this.feature('hashbang')) {
this.skipHashbangComment();
}
return this.scope.with({
module: true,
strict: true,
in: true,
importMeta: true,
await: this.feature('top-level-await'),
lexical: true,
variable: true,
}, () => {
const node = this.startNode();
if (this.eat(Token.EOS)) {
node.ModuleBody = null;
} else {
node.ModuleBody = this.parseModuleBody();
}
this.scope.undefinedExports.forEach((importNode, name) => {
this.raiseEarly('ModuleUndefinedExport', importNode, name);
});
node.hasTopLevelAwait = this.state.hasTopLevelAwait;
return this.finishNode(node, 'Module');
});
}
// ModuleBody :
// ModuleItemList
parseModuleBody() {
const node = this.startNode();
node.ModuleItemList = this.parseModuleItemList();
return this.finishNode(node, 'ModuleBody');
}
// ModuleItemList :
// ModuleItem
// ModuleItemList ModuleItem
//
// ModuleItem :
// ImportDeclaration
// ExportDeclaration
// StatementListItem
parseModuleItemList() {
const moduleItemList = [];
while (!this.eat(Token.EOS)) {
switch (this.peek().type) {
case Token.IMPORT:
moduleItemList.push(this.parseImportDeclaration());
break;
case Token.EXPORT:
moduleItemList.push(this.parseExportDeclaration());
break;
default:
moduleItemList.push(this.parseStatementListItem());
break;
}
}
return moduleItemList;
}
}
+896
View File
@@ -0,0 +1,896 @@
import isUnicodeIDStartRegex from 'unicode-13.0.0/Binary_Property/ID_Start/regex';
import isUnicodeIDContinueRegex from 'unicode-13.0.0/Binary_Property/ID_Continue/regex';
import isSpaceSeparatorRegex from 'unicode-13.0.0/General_Category/Space_Separator/regex';
import { surroundingAgent } from '../engine.mjs';
import { UTF16SurrogatePairToCodePoint } from '../static-semantics/all.mjs';
import {
RawTokens,
Token,
TokenNames,
KeywordLookup,
isKeywordRaw,
} from './tokens.mjs';
const isUnicodeIDStart = (c) => c && isUnicodeIDStartRegex.test(c);
const isUnicodeIDContinue = (c) => c && isUnicodeIDContinueRegex.test(c);
export const isDecimalDigit = (c) => c && /\d/u.test(c);
export const isHexDigit = (c) => c && /[\da-f]/ui.test(c);
const isOctalDigit = (c) => c && /[0-7]/u.test(c);
const isBinaryDigit = (c) => (c === '0' || c === '1');
export const isWhitespace = (c) => c && (/[\u0009\u000B\u000C\u0020\u00A0\uFEFF]/u.test(c) || isSpaceSeparatorRegex.test(c)); // eslint-disable-line no-control-regex
export const isLineTerminator = (c) => c && /[\r\n\u2028\u2029]/u.test(c);
const isRegularExpressionFlagPart = (c) => c && (isUnicodeIDContinue(c) || c === '$');
export const isIdentifierStart = (c) => SingleCharTokens[c] === Token.IDENTIFIER || isUnicodeIDStart(c);
export const isIdentifierPart = (c) => SingleCharTokens[c] === Token.IDENTIFIER || c === '\u{200C}' || c === '\u{200D}' || isUnicodeIDContinue(c);
export const isLeadingSurrogate = (cp) => cp >= 0xD800 && cp <= 0xDBFF;
export const isTrailingSurrogate = (cp) => cp >= 0xDC00 && cp <= 0xDFFF;
const SingleCharTokens = {
'__proto__': null,
'0': Token.NUMBER,
'1': Token.NUMBER,
'2': Token.NUMBER,
'3': Token.NUMBER,
'4': Token.NUMBER,
'5': Token.NUMBER,
'6': Token.NUMBER,
'7': Token.NUMBER,
'8': Token.NUMBER,
'9': Token.NUMBER,
'a': Token.IDENTIFIER,
'b': Token.IDENTIFIER,
'c': Token.IDENTIFIER,
'd': Token.IDENTIFIER,
'e': Token.IDENTIFIER,
'f': Token.IDENTIFIER,
'g': Token.IDENTIFIER,
'h': Token.IDENTIFIER,
'i': Token.IDENTIFIER,
'j': Token.IDENTIFIER,
'k': Token.IDENTIFIER,
'l': Token.IDENTIFIER,
'm': Token.IDENTIFIER,
'n': Token.IDENTIFIER,
'o': Token.IDENTIFIER,
'p': Token.IDENTIFIER,
'q': Token.IDENTIFIER,
'r': Token.IDENTIFIER,
's': Token.IDENTIFIER,
't': Token.IDENTIFIER,
'u': Token.IDENTIFIER,
'v': Token.IDENTIFIER,
'w': Token.IDENTIFIER,
'x': Token.IDENTIFIER,
'y': Token.IDENTIFIER,
'z': Token.IDENTIFIER,
'A': Token.IDENTIFIER,
'B': Token.IDENTIFIER,
'C': Token.IDENTIFIER,
'D': Token.IDENTIFIER,
'E': Token.IDENTIFIER,
'F': Token.IDENTIFIER,
'G': Token.IDENTIFIER,
'H': Token.IDENTIFIER,
'I': Token.IDENTIFIER,
'J': Token.IDENTIFIER,
'K': Token.IDENTIFIER,
'L': Token.IDENTIFIER,
'M': Token.IDENTIFIER,
'N': Token.IDENTIFIER,
'O': Token.IDENTIFIER,
'P': Token.IDENTIFIER,
'Q': Token.IDENTIFIER,
'R': Token.IDENTIFIER,
'S': Token.IDENTIFIER,
'T': Token.IDENTIFIER,
'U': Token.IDENTIFIER,
'V': Token.IDENTIFIER,
'W': Token.IDENTIFIER,
'X': Token.IDENTIFIER,
'Y': Token.IDENTIFIER,
'Z': Token.IDENTIFIER,
'$': Token.IDENTIFIER,
'_': Token.IDENTIFIER,
'\\': Token.IDENTIFIER,
'.': Token.PERIOD,
',': Token.COMMA,
':': Token.COLON,
';': Token.SEMICOLON,
'%': Token.MOD,
'~': Token.BIT_NOT,
'!': Token.NOT,
'+': Token.ADD,
'-': Token.SUB,
'*': Token.MUL,
'<': Token.LT,
'>': Token.GT,
'=': Token.ASSIGN,
'?': Token.CONDITIONAL,
'[': Token.LBRACK,
']': Token.RBRACK,
'(': Token.LPAREN,
')': Token.RPAREN,
'/': Token.DIV,
'^': Token.BIT_XOR,
'`': Token.TEMPLATE,
'{': Token.LBRACE,
'}': Token.RBRACE,
'&': Token.BIT_AND,
'|': Token.BIT_OR,
'"': Token.STRING,
'\'': Token.STRING,
};
export class Lexer {
constructor() {
this.currentToken = undefined;
this.peekToken = undefined;
this.peekAheadToken = undefined;
this.position = 0;
this.line = 1;
this.columnOffset = 0;
this.scannedValue = undefined;
this.lineTerminatorBeforeNextToken = false;
this.positionForNextToken = 0;
this.lineForNextToken = 0;
this.columnForNextToken = 0;
}
advance() {
this.lineTerminatorBeforeNextToken = false;
const type = this.nextToken();
return {
type,
startIndex: this.positionForNextToken,
endIndex: this.position,
line: this.lineForNextToken,
column: this.columnForNextToken,
hadLineTerminatorBefore: this.lineTerminatorBeforeNextToken,
name: TokenNames[type],
value: (
type === Token.IDENTIFIER
|| type === Token.NUMBER
|| type === Token.BIGINT
|| type === Token.STRING
|| type === Token.ESCAPED_KEYWORD
) ? this.scannedValue : RawTokens[type][1],
};
}
next() {
this.currentToken = this.peekToken;
if (this.peekAheadToken !== undefined) {
this.peekToken = this.peekAheadToken;
this.peekAheadToken = undefined;
} else {
this.peekToken = this.advance();
}
return this.currentToken;
}
peek() {
if (this.peekToken === undefined) {
this.next();
}
return this.peekToken;
}
peekAhead() {
if (this.peekAheadToken === undefined) {
this.peek();
this.peekAheadToken = this.advance();
}
return this.peekAheadToken;
}
matches(token, peek) {
if (typeof token === 'string') {
if (peek.type === Token.IDENTIFIER && peek.value === token) {
const escapeIndex = this.source.slice(peek.startIndex, peek.endIndex).indexOf('\\');
if (escapeIndex !== -1) {
return false;
}
return true;
} else {
return false;
}
}
return peek.type === token;
}
test(token) {
return this.matches(token, this.peek());
}
testAhead(token) {
return this.matches(token, this.peekAhead());
}
eat(token) {
if (this.test(token)) {
this.next();
return true;
}
return false;
}
expect(token) {
if (this.test(token)) {
return this.next();
}
return this.unexpected();
}
skipSpace() {
loop: // eslint-disable-line no-labels
while (this.position < this.source.length) {
const c = this.source[this.position];
switch (c) {
case ' ':
case '\t':
this.position += 1;
break;
case '/':
switch (this.source[this.position + 1]) {
case '/':
this.skipLineComment();
break;
case '*':
this.skipBlockComment();
break;
default:
break loop; // eslint-disable-line no-labels
}
break;
default:
if (isWhitespace(c)) {
this.position += 1;
} else if (isLineTerminator(c)) {
this.position += 1;
if (c === '\r' && this.source[this.position] === '\n') {
this.position += 1;
}
this.line += 1;
this.columnOffset = this.position;
this.lineTerminatorBeforeNextToken = true;
break;
} else {
break loop; // eslint-disable-line no-labels
}
break;
}
}
}
skipHashbangComment() {
if (this.position === 0
&& this.source[0] === '#'
&& this.source[1] === '!') {
this.skipLineComment();
}
}
skipLineComment() {
while (this.position < this.source.length) {
const c = this.source[this.position];
this.position += 1;
if (isLineTerminator(c)) {
if (c === '\r' && this.source[this.position] === '\n') {
this.position += 1;
}
this.line += 1;
this.columnOffset = this.position;
this.lineTerminatorBeforeNextToken = true;
break;
}
}
}
skipBlockComment() {
const end = this.source.indexOf('*/', this.position + 2);
if (end === -1) {
this.raise('UnterminatedComment', this.position);
}
this.position += 2;
for (const match of this.source.slice(this.position, end).matchAll(/\r\n?|[\n\u2028\u2029]/ug)) {
this.position = match.index;
this.line += 1;
this.columnOffset = this.position;
this.lineTerminatorBeforeNextToken = true;
}
this.position = end + 2;
}
nextToken() {
this.skipSpace();
// set token location info after skipping space
this.positionForNextToken = this.position;
this.lineForNextToken = this.line;
this.columnForNextToken = this.position - this.columnOffset + 1;
if (this.position >= this.source.length) {
return Token.EOS;
}
const c = this.source[this.position];
this.position += 1;
const c1 = this.source[this.position];
if (c.charCodeAt(0) <= 127) {
const single = SingleCharTokens[c];
switch (single) {
case Token.LPAREN:
case Token.RPAREN:
case Token.LBRACE:
case Token.RBRACE:
case Token.LBRACK:
case Token.RBRACK:
case Token.COLON:
case Token.SEMICOLON:
case Token.COMMA:
case Token.BIT_NOT:
case Token.TEMPLATE:
return single;
case Token.CONDITIONAL:
// ? ?. ?? ??=
if (c1 === '.' && !isDecimalDigit(this.source[this.position + 1])) {
this.position += 1;
return Token.OPTIONAL;
}
if (c1 === '?') {
this.position += 1;
if (this.source[this.position] === '=') {
this.position += 1;
return Token.ASSIGN_NULLISH;
}
return Token.NULLISH;
}
return Token.CONDITIONAL;
case Token.LT:
// < <= << <<=
if (c1 === '=') {
this.position += 1;
return Token.LTE;
}
if (c1 === '<') {
this.position += 1;
if (this.source[this.position] === '=') {
this.position += 1;
return Token.ASSIGN_SHL;
}
return Token.SHL;
}
return Token.LT;
case Token.GT:
// > >= >> >>= >>> >>>=
if (c1 === '=') {
this.position += 1;
return Token.GTE;
}
if (c1 === '>') {
this.position += 1;
if (this.source[this.position] === '>') {
this.position += 1;
if (this.source[this.position] === '=') {
this.position += 1;
return Token.ASSIGN_SHR;
}
return Token.SHR;
}
if (this.source[this.position] === '=') {
this.position += 1;
return Token.ASSIGN_SAR;
}
return Token.SAR;
}
return Token.GT;
case Token.ASSIGN:
// = == === =>
if (c1 === '=') {
this.position += 1;
if (this.source[this.position] === '=') {
this.position += 1;
return Token.EQ_STRICT;
}
return Token.EQ;
}
if (c1 === '>') {
this.position += 1;
return Token.ARROW;
}
return Token.ASSIGN;
case Token.NOT:
// ! != !==
if (c1 === '=') {
this.position += 1;
if (this.source[this.position] === '=') {
this.position += 1;
return Token.NE_STRICT;
}
return Token.NE;
}
return Token.NOT;
case Token.ADD:
// + ++ +=
if (c1 === '+') {
this.position += 1;
return Token.INC;
}
if (c1 === '=') {
this.position += 1;
return Token.ASSIGN_ADD;
}
return Token.ADD;
case Token.SUB:
// - -- -=
if (c1 === '-') {
this.position += 1;
return Token.DEC;
}
if (c1 === '=') {
this.position += 1;
return Token.ASSIGN_SUB;
}
return Token.SUB;
case Token.MUL:
// * *= ** **=
if (c1 === '=') {
this.position += 1;
return Token.ASSIGN_MUL;
}
if (c1 === '*') {
this.position += 1;
if (this.source[this.position] === '=') {
this.position += 1;
return Token.ASSIGN_EXP;
}
return Token.EXP;
}
return Token.MUL;
case Token.MOD:
// % %=
if (c1 === '=') {
this.position += 1;
return Token.ASSIGN_MOD;
}
return Token.MOD;
case Token.DIV:
// / /=
if (c1 === '=') {
this.position += 1;
return Token.ASSIGN_DIV;
}
return Token.DIV;
case Token.BIT_AND:
// & && &= &&=
if (c1 === '&') {
this.position += 1;
if (this.source[this.position] === '=') {
this.position += 1;
return Token.ASSIGN_AND;
}
return Token.AND;
}
if (c1 === '=') {
this.position += 1;
return Token.ASSIGN_BIT_AND;
}
return Token.BIT_AND;
case Token.BIT_OR:
// | || |=
if (c1 === '|') {
this.position += 1;
if (this.source[this.position] === '=') {
this.position += 1;
return Token.ASSIGN_OR;
}
return Token.OR;
}
if (c1 === '=') {
this.position += 1;
return Token.ASSIGN_BIT_OR;
}
return Token.BIT_OR;
case Token.BIT_XOR:
// ^ ^=
if (c1 === '=') {
this.position += 1;
return Token.ASSIGN_BIT_XOR;
}
return Token.BIT_XOR;
case Token.PERIOD:
// . ... NUMBER
if (isDecimalDigit(c1)) {
this.position -= 1;
return this.scanNumber();
}
if (c1 === '.') {
if (this.source[this.position + 1] === '.') {
this.position += 2;
return Token.ELLIPSIS;
}
}
return Token.PERIOD;
case Token.STRING:
return this.scanString(c);
case Token.NUMBER:
this.position -= 1;
return this.scanNumber();
case Token.IDENTIFIER:
this.position -= 1;
return this.scanIdentifierOrKeyword();
default:
this.unexpected(single);
break;
}
}
this.position -= 1;
if (isLeadingSurrogate(c.charCodeAt(0)) || isIdentifierStart(c)) {
return this.scanIdentifierOrKeyword();
}
return this.unexpected(this.position);
}
scanNumber() {
const separators = surroundingAgent.feature('numeric-separators');
const start = this.position;
let base = 10;
let check = isDecimalDigit;
if (this.source[this.position] === '0') {
this.scannedValue = 0;
this.position += 1;
switch (this.source[this.position]) {
case 'x':
case 'X':
base = 16;
break;
case 'o':
case 'O':
base = 8;
break;
case 'b':
case 'B':
base = 2;
break;
case '.':
case 'e':
case 'E':
break;
case 'n':
this.position += 1;
this.scannedValue = 0n;
return Token.BIGINT;
default:
return Token.NUMBER;
}
check = {
16: isHexDigit,
10: isDecimalDigit,
8: isOctalDigit,
2: isBinaryDigit,
}[base];
if (base !== 10) {
if (!check(this.source[this.position + 1])) {
return Token.NUMBER;
}
this.position += 1;
}
}
while (this.position < this.source.length) {
const c = this.source[this.position];
if (check(c) || (separators && c === '_')) {
this.position += 1;
if (separators && c === '_') {
if (!check(this.source[this.position])) {
this.unexpected(this.position);
}
}
} else {
break;
}
}
if (this.source[this.position] === 'n') {
const buffer = this.source.slice(start, this.position).replace(/_/g, '');
this.position += 1;
this.scannedValue = BigInt(buffer);
return Token.BIGINT;
}
if (base === 10 && this.source[this.position] === '.') {
this.position += 1;
if (separators && this.source[this.position] === '_') {
this.unexpected(this.position);
}
while (this.position < this.source.length) {
const c = this.source[this.position];
if (isDecimalDigit(c) || (separators && c === '_')) {
this.position += 1;
if (separators && c === '_') {
if (!isDecimalDigit(this.source[this.position])) {
this.unexpected(this.position);
}
}
} else {
break;
}
}
}
if (base === 10 && (this.source[this.position] === 'E' || this.source[this.position] === 'e')) {
this.position += 1;
if (this.source[this.position] === '-' || this.source[this.position] === '+') {
this.position += 1;
}
while (this.position < this.source.length) {
const c = this.source[this.position];
if (isDecimalDigit(c) || (separators && c === '_')) {
this.position += 1;
if (separators && c === '_') {
if (!isDecimalDigit(this.source[this.position])) {
this.unexpected(this.position);
}
}
} else {
break;
}
}
}
if (isIdentifierStart(this.source[this.position])) {
this.unexpected(this.position);
}
const buffer = this.source
.slice(base === 10 ? start : start + 2, this.position)
.replace(/_/g, '');
this.scannedValue = base === 10
? Number.parseFloat(buffer, base)
: Number.parseInt(buffer, base);
return Token.NUMBER;
}
scanString(char) {
let buffer = '';
while (true) {
if (this.position >= this.source.length) {
this.raise('UnterminatedString', this.position);
}
const c = this.source[this.position];
if (c === char) {
this.position += 1;
break;
}
if (c === '\r' || c === '\n') {
this.raise('UnterminatedString', this.position);
}
this.position += 1;
if (c === '\\') {
const l = this.source[this.position];
if (isLineTerminator(l)) {
this.position += 1;
if (l === '\r' && this.source[this.position] === '\n') {
this.position += 1;
}
this.line += 1;
this.columnOffset = this.position;
this.lineTerminatorBeforeNextToken = true;
} else {
buffer += this.scanEscapeSequence();
}
} else {
buffer += c;
}
}
this.scannedValue = buffer;
return Token.STRING;
}
scanEscapeSequence() {
const c = this.source[this.position];
switch (c) {
case 'b':
this.position += 1;
return '\b';
case 't':
this.position += 1;
return '\t';
case 'n':
this.position += 1;
return '\n';
case 'v':
this.position += 1;
return '\v';
case 'f':
this.position += 1;
return '\f';
case 'r':
this.position += 1;
return '\r';
case 'x':
this.position += 1;
return String.fromCodePoint(this.scanHex(2));
case 'u':
this.position += 1;
return String.fromCodePoint(this.scanCodePoint());
default:
if (c === '0' && !isDecimalDigit(this.source[this.position + 1])) {
this.position += 1;
return '\u{0000}';
} else if (this.isStrictMode() && isDecimalDigit(c)) {
this.raise('IllegalOctalEscape', this.position);
}
this.position += 1;
return c;
}
}
scanCodePoint() {
if (this.source[this.position] === '{') {
const end = this.source.indexOf('}', this.position);
this.position += 1;
const code = this.scanHex(end - this.position);
this.position += 1;
if (code > 0x10FFFF) {
this.raise('InvalidCodePoint', this.position);
}
return code;
}
return this.scanHex(4);
}
scanHex(length) {
if (length === 0) {
this.raise('InvalidCodePoint', this.position);
}
let n = 0;
for (let i = 0; i < length; i += 1) {
const c = this.source[this.position];
if (isHexDigit(c)) {
this.position += 1;
n = (n << 4) | Number.parseInt(c, 16);
} else {
this.unexpected(this.position);
}
}
return n;
}
scanIdentifierOrKeyword() {
let buffer = '';
let escapeIndex = -1;
let check = isIdentifierStart;
while (this.position < this.source.length) {
const c = this.source[this.position];
const code = c.charCodeAt(0);
if (c === '\\') {
if (escapeIndex === -1) {
escapeIndex = this.position;
}
this.position += 1;
if (this.source[this.position] !== 'u') {
this.raise('InvalidUnicodeEscape', this.position);
}
this.position += 1;
const raw = String.fromCodePoint(this.scanCodePoint());
if (!check(raw)) {
this.raise('InvalidUnicodeEscape', this.position);
}
buffer += raw;
} else if (isLeadingSurrogate(code)) {
const lowSurrogate = this.source.charCodeAt(this.position + 1);
if (!isTrailingSurrogate(lowSurrogate)) {
this.raise('InvalidUnicodeEscape', this.position);
}
const codePoint = UTF16SurrogatePairToCodePoint(code, lowSurrogate);
const raw = String.fromCodePoint(codePoint);
if (!check(raw)) {
this.raise('InvalidUnicodeEscape', this.position);
}
this.position += 2;
buffer += raw;
} else if (check(c)) {
buffer += c;
this.position += 1;
} else {
break;
}
check = isIdentifierPart;
}
if (isKeywordRaw(buffer)) {
if (escapeIndex !== -1) {
this.scannedValue = buffer;
return Token.ESCAPED_KEYWORD;
}
return KeywordLookup[buffer];
} else {
this.scannedValue = buffer;
return Token.IDENTIFIER;
}
}
scanRegularExpressionBody() {
let inClass = false;
let buffer = this.peek().type === Token.ASSIGN_DIV ? '=' : '';
while (true) {
if (this.position >= this.source.length) {
this.raise('UnterminatedRegExp', this.position);
}
const c = this.source[this.position];
switch (c) {
case '[':
inClass = true;
this.position += 1;
buffer += c;
break;
case ']':
if (inClass) {
inClass = false;
}
buffer += c;
this.position += 1;
break;
case '/':
this.position += 1;
if (!inClass) {
this.scannedValue = buffer;
return;
}
buffer += c;
break;
case '\\':
buffer += c;
this.position += 1;
if (isLineTerminator(this.source[this.position])) {
this.raise('UnterminatedRegExp', this.position);
}
buffer += this.source[this.position];
this.position += 1;
break;
default:
if (isLineTerminator(c)) {
this.raise('UnterminatedRegExp', this.position);
}
this.position += 1;
buffer += c;
break;
}
}
}
scanRegularExpressionFlags() {
let buffer = '';
while (true) {
if (this.position >= this.source.length) {
this.scannedValue = buffer;
return;
}
const c = this.source[this.position];
if (isRegularExpressionFlagPart(c)
&& 'gimsuy'.includes(c)
&& !buffer.includes(c)) {
this.position += 1;
buffer += c;
} else {
this.scannedValue = buffer;
return;
}
}
}
}
+153
View File
@@ -0,0 +1,153 @@
import { surroundingAgent } from '../engine.mjs';
import * as messages from '../messages.mjs';
import { LanguageParser } from './LanguageParser.mjs';
import { Token } from './tokens.mjs';
import { Scope } from './Scope.mjs';
import { isLineTerminator } from './Lexer.mjs';
export class Parser extends LanguageParser {
constructor({ source, specifier }) {
super();
this.source = source;
this.specifier = specifier;
this.earlyErrors = new Set();
this.state = {
hasTopLevelAwait: false,
strict: false,
};
this.scope = new Scope(this);
}
isStrictMode() {
return this.state.strict;
}
feature(name) {
// eslint-disable-next-line engine262/valid-feature
return surroundingAgent.feature(name);
}
startNode(inheritStart = undefined) {
this.peek();
const node = {
type: undefined,
location: {
startIndex: inheritStart ? inheritStart.location.startIndex : this.peekToken.startIndex,
endIndex: -1,
start: inheritStart ? { ...inheritStart.location.start } : {
line: this.peekToken.line,
column: this.peekToken.column,
},
end: {
line: -1,
column: -1,
},
},
strict: this.state.strict,
sourceText: () => this.source.slice(node.location.startIndex, node.location.endIndex),
};
return node;
}
finishNode(node, type) {
node.type = type;
node.location.endIndex = this.currentToken.endIndex;
node.location.end.line = this.currentToken.line;
node.location.end.column = this.currentToken.column;
return node;
}
createSyntaxError(context = this.peek(), template, templateArgs) {
if (template === 'UnexpectedToken' && context.type === Token.EOS) {
template = 'UnexpectedEOS';
}
let startIndex;
let endIndex;
let line;
let column;
if (typeof context === 'number') {
line = this.line;
if (context === this.source.length) {
while (isLineTerminator(this.source[context - 1])) {
line -= 1;
context -= 1;
}
}
startIndex = context;
endIndex = context + 1;
} else if (context.type === Token.EOS) {
line = this.line;
startIndex = context.startIndex;
while (isLineTerminator(this.source[startIndex - 1])) {
line -= 1;
startIndex -= 1;
}
endIndex = startIndex + 1;
} else {
if (context.location) {
context = context.location;
}
({
startIndex,
endIndex,
start: {
line,
column,
} = context,
} = context);
}
/*
* Source looks like:
*
* const a = 1;
* const b 'string string string'; // a string
* const c = 3; | |
* | | | |
* | | startIndex | endIndex |
* | lineStart | lineEnd
*
* Exception looks like:
*
* const b 'string string string'; // a string
* ^^^^^^^^^^^^^^^^^^^^^^
* SyntaxError: unexpected token
*/
let lineStart = startIndex;
while (!isLineTerminator(this.source[lineStart - 1]) && this.source[lineStart - 1] !== undefined) {
lineStart -= 1;
}
let lineEnd = startIndex;
while (!isLineTerminator(this.source[lineEnd]) && this.source[lineEnd] !== undefined) {
lineEnd += 1;
}
if (column === undefined) {
column = startIndex - lineStart + 1;
}
const e = new SyntaxError(messages[template](...templateArgs));
e.decoration = `\
${this.specifier ? `${this.specifier}:${line}:${column}\n` : ''}${this.source.slice(lineStart, lineEnd)}
${' '.repeat(startIndex - lineStart)}${'^'.repeat(Math.max(endIndex - startIndex, 1))}`;
return e;
}
raiseEarly(template, context, ...templateArgs) {
const e = this.createSyntaxError(context, template, templateArgs);
this.earlyErrors.add(e);
return e;
}
raise(template, context, ...templateArgs) {
const e = this.createSyntaxError(context, template, templateArgs);
throw e;
}
unexpected(...args) {
return this.raise('UnexpectedToken', ...args);
}
}
+911
View File
@@ -0,0 +1,911 @@
import {
BinaryUnicodeProperties,
NonbinaryUnicodeProperties,
UnicodeGeneralCategoryValues,
UnicodeScriptValues,
} from '../runtime-semantics/all.mjs';
import {
CharacterValue,
UTF16SurrogatePairToCodePoint,
} from '../static-semantics/all.mjs';
import {
isIdentifierStart,
isIdentifierPart,
isLeadingSurrogate,
isTrailingSurrogate,
isHexDigit,
} from './Lexer.mjs';
const isSyntaxCharacter = (c) => '^$\\.*+?()[]{}|'.includes(c);
const isClosingSyntaxCharacter = (c) => ')]}|'.includes(c);
const isDecimalDigit = (c) => /[0123456789]/u.test(c);
const isControlLetter = (c) => /[a-zA-Z]/u.test(c);
const PLUS_U = 1 << 0;
const PLUS_N = 1 << 1;
export class RegExpParser {
constructor(source) {
this.source = source;
this.position = 0;
this.capturingGroups = [];
this.groupSpecifiers = new Map();
this.decimalEscapes = [];
this.groupNameRefs = [];
this.state = 0;
}
scope(flags, f) {
const oldState = this.state;
if (flags.U === true) {
this.state |= PLUS_U;
} else if (flags.U === false) {
this.state &= ~PLUS_U;
}
if (flags.N === true) {
this.state |= PLUS_N;
} else if (flags.N === false) {
this.state &= ~PLUS_N;
}
const r = f();
this.state = oldState;
return r;
}
get plusU() {
return (this.state & PLUS_U) > 0;
}
get plusN() {
return (this.state & PLUS_N) > 0;
}
raise(message, position = this.position) {
const e = new SyntaxError(message);
e.position = position;
throw e;
}
peek() {
return this.source[this.position];
}
test(c) {
return this.source[this.position] === c;
}
eat(c) {
if (this.test(c)) {
this.next();
return true;
}
return false;
}
next() {
const c = this.source[this.position];
this.position += 1;
return c;
}
expect(c) {
if (!this.eat(c)) {
this.raise(`Expected ${c} but got ${this.peek()}`);
}
}
// Pattern ::
// Disjunction
parsePattern() {
const node = {
type: 'Pattern',
groupSpecifiers: this.groupSpecifiers,
capturingGroups: this.capturingGroups,
Disjunction: undefined,
};
node.Disjunction = this.parseDisjunction();
if (this.position < this.source.length) {
this.raise('Unexpected token');
}
this.decimalEscapes.forEach((d) => {
if (d.value > node.capturingGroups.length) {
this.raise('Invalid decimal escape', d.position);
}
});
this.groupNameRefs.forEach((g) => {
if (!node.groupSpecifiers.has(g.GroupName)) {
this.raise('Invalid group name', g.position);
}
});
return node;
}
// Disjunction ::
// Alternative
// Alternative `|` Disjunction
parseDisjunction() {
const node = {
type: 'Disjunction',
Alternative: undefined,
Disjunction: undefined,
};
node.Alternative = this.parseAlternative();
if (this.eat('|')) {
node.Disjunction = this.parseDisjunction();
}
return node;
}
// Alternative ::
// [empty]
// Term Alternative
parseAlternative() {
let node = {
type: 'Alternative',
Term: undefined,
Alternative: undefined,
};
while (this.position < this.source.length
&& !isClosingSyntaxCharacter(this.peek())) {
node = {
type: 'Alternative',
Term: this.parseTerm(),
Alternative: node,
};
}
return node;
}
// Term ::
// Assertion
// Atom
// Atom Quantifier
parseTerm() {
const assertion = this.maybeParseAssertion();
if (assertion) {
return assertion;
}
return {
type: 'Term',
capturingParenthesesBefore: this.capturingGroups.length,
Atom: this.parseAtom(),
Quantifier: this.maybeParseQuantifier(),
};
}
// Assertion ::
// `^`
// `$`
// `\` `b`
// `\` `B`
// `(` `?` `=` Disjunction `)`
// `(` `?` `!` Disjunction `)`
// `(` `?` `<=` Disjunction `)`
// `(` `?` `<!` Disjunction `)`
maybeParseAssertion() {
if (this.eat('^')) {
return { type: 'Assertion', subtype: '^' };
}
if (this.eat('$')) {
return { type: 'Assertion', subtype: '$' };
}
const look2 = this.source.slice(this.position, this.position + 2);
if (look2 === '\\b') {
this.position += 2;
return { type: 'Assertion', subtype: 'b' };
}
if (look2 === '\\B') {
this.position += 2;
return { type: 'Assertion', subtype: 'B' };
}
const look3 = this.source.slice(this.position, this.position + 3);
if (look3 === '(?=') {
this.position += 3;
const d = this.parseDisjunction();
this.expect(')');
return {
type: 'Assertion',
subtype: '?=',
Disjunction: d,
};
}
if (look3 === '(?!') {
this.position += 3;
const d = this.parseDisjunction();
this.expect(')');
return {
type: 'Assertion',
subtype: '?!',
Disjunction: d,
};
}
const look4 = this.source.slice(this.position, this.position + 4);
if (look4 === '(?<=') {
this.position += 4;
const d = this.parseDisjunction();
this.expect(')');
return {
type: 'Assertion',
subtype: '?<=',
Disjunction: d,
};
}
if (look4 === '(?<!') {
this.position += 4;
const d = this.parseDisjunction();
this.expect(')');
return {
type: 'Assertion',
subtype: '?<!',
Disjunction: d,
};
}
return undefined;
}
// Quantifier ::
// QuantifierPrefix
// QuantifierPrefix `?`
// QuantifierPrefix ::
// `*`
// `+`
// `?`
// `{` DecimalDigits `}`
// `{` DecimalDigits `,` `}`
// `{` DecimalDigits `,` DecimalDigits `}`
maybeParseQuantifier() {
let QuantifierPrefix;
if (this.eat('*')) {
QuantifierPrefix = '*';
} else if (this.eat('+')) {
QuantifierPrefix = '+';
} else if (this.eat('?')) {
QuantifierPrefix = '?';
} else if (this.eat('{')) {
QuantifierPrefix = {
DecimalDigits_a: undefined,
DecimalDigits_b: undefined,
};
QuantifierPrefix.DecimalDigits_a = Number.parseInt(this.parseDecimalDigits(), 10);
if (this.eat(',')) {
if (this.test('}')) {
QuantifierPrefix.DecimalDigits_b = Infinity;
} else {
QuantifierPrefix.DecimalDigits_b = Number.parseInt(this.parseDecimalDigits(), 10);
}
if (QuantifierPrefix.DecimalDigits_a > QuantifierPrefix.DecimalDigits_b) {
this.raise('Numbers out of order in quantifier');
}
}
this.expect('}');
}
if (QuantifierPrefix) {
return {
type: 'Quantifier',
QuantifierPrefix,
greedy: !this.eat('?'),
};
}
return undefined;
}
// Atom ::
// PatternCharacter
// `.`
// `\` AtomEscape
// CharacterClass
// `(` GroupSpecifier Disjunction `)`
// `(` `?` `:` Disjunction `)`
parseAtom() {
if (this.eat('.')) {
return { type: 'Atom', subtype: '.', enclosedCapturingParentheses: 0 };
}
if (this.eat('\\')) {
return this.parseAtomEscape();
}
if (this.eat('(')) {
const node = {
type: 'Atom',
capturingParenthesesBefore: this.capturingGroups.length,
enclosedCapturingParentheses: 0,
capturing: true,
GroupSpecifier: undefined,
Disjunction: undefined,
};
if (this.eat('?')) {
if (this.eat(':')) {
node.capturing = false;
} else {
node.GroupSpecifier = this.parseGroupName();
}
}
if (node.capturing) {
this.capturingGroups.push(node);
}
if (node.GroupSpecifier) {
if (this.groupSpecifiers.has(node.GroupSpecifier)) {
this.raise(`Duplicate group specifier '${node.GroupSpecifier}'`);
}
this.groupSpecifiers.set(node.GroupSpecifier, node.capturingParenthesesBefore);
}
node.Disjunction = this.parseDisjunction();
this.expect(')');
node.enclosedCapturingParentheses = this.capturingGroups.length - node.capturingParenthesesBefore - 1;
return node;
}
if (this.test('[')) {
return {
type: 'Atom',
CharacterClass: this.parseCharacterClass(),
};
}
if (isSyntaxCharacter(this.peek())) {
this.raise(`Expected a PatternCharacter but got ${this.peek()}`);
}
return {
type: 'Atom',
PatternCharacter: this.parseSourceCharacter(),
};
}
// AtomEscape ::
// DecimalEscape
// CharacterClassEscape
// CharacterEscape
// [+N] `k` GroupName
parseAtomEscape() {
if (this.plusN && this.eat('k')) {
const node = {
type: 'AtomEscape',
position: this.position,
GroupName: this.parseGroupName(),
};
this.groupNameRefs.push(node);
return node;
}
const CharacterClassEscape = this.maybeParseCharacterClassEscape();
if (CharacterClassEscape) {
return {
type: 'AtomEscape',
CharacterClassEscape,
};
}
const DecimalEscape = this.maybeParseDecimalEscape();
if (DecimalEscape) {
return {
type: 'AtomEscape',
DecimalEscape,
};
}
return {
type: 'AtomEscape',
CharacterEscape: this.parseCharacterEscape(),
};
}
// CharacterEscape ::
// ControlEscape
// `c` ControlLetter
// `0` [lookahead ∉ DecimalDigit]
// HexEscapeSequence
// RegExpUnicodeEscapeSequence
// IdentityEscape
//
// IdentityEscape ::
// [+U] SyntaxCharacter
// [+U] `/`
// [~U] SourceCharacter but not UnicodeIDContinue
parseCharacterEscape() {
switch (this.peek()) {
case 'f':
case 'n':
case 'r':
case 't':
case 'v':
return {
type: 'CharacterEscape',
ControlEscape: this.next(),
};
case 'c': {
this.next();
const c = this.next();
if (c === undefined) {
if (this.plusU) {
this.raise('Invalid identity escape');
}
return {
type: 'CharacterEscape',
IdentityEscape: 'c',
};
}
const p = c.codePointAt(0);
if ((p >= 65 && p <= 90) || (p >= 97 && p <= 122)) {
return {
type: 'CharacterEscape',
ControlLetter: c,
};
}
if (this.plusU) {
this.raise('Invalid identity escape');
}
return {
type: 'CharacterEscape',
IdentityEscape: c,
};
}
case 'x':
if (isHexDigit(this.source[this.position + 1]) && isHexDigit(this.source[this.position + 2])) {
return {
type: 'CharacterEscape',
HexEscapeSequence: this.parseHexEscapeSequence(),
};
}
if (this.plusU) {
this.raise('Invalid identity escape');
}
this.next();
return {
type: 'CharacterEscape',
IdentityEscape: 'x',
};
case 'u': {
const RegExpUnicodeEscapeSequence = this.maybeParseRegExpUnicodeEscapeSequence();
if (RegExpUnicodeEscapeSequence) {
return {
type: 'CharacterEscape',
RegExpUnicodeEscapeSequence,
};
}
if (this.plusU) {
this.raise('Invalid identity escape');
}
this.next();
return {
type: 'CharacterEscape',
IdentityEscape: 'u',
};
}
default: {
const c = this.next();
if (c === undefined) {
this.raise('Unexpected escape');
}
if (c === '0' && !isDecimalDigit(this.peek())) {
return {
type: 'CharacterEscape',
subtype: '0',
};
}
if (this.plusU && !isSyntaxCharacter(c) && c !== '/') {
this.raise('Invalid identity escape');
}
return {
type: 'CharacterEscape',
IdentityEscape: c,
};
}
}
}
// DecimalEscape ::
// NonZeroDigit DecimalDigits? [lookahead != DecimalDigit]
maybeParseDecimalEscape() {
if (isDecimalDigit(this.source[this.position]) && this.source[this.position] !== '0') {
const start = this.position;
let buffer = this.source[this.position];
this.position += 1;
while (isDecimalDigit(this.source[this.position])) {
buffer += this.source[this.position];
this.position += 1;
}
const node = {
type: 'DecimalEscape',
position: start,
value: Number.parseInt(buffer, 10),
};
this.decimalEscapes.push(node);
return node;
}
return undefined;
}
// CharacterClassEscape ::
// `d`
// `D`
// `s`
// `S`
// `w`
// `W`
// [+U] `p{` UnicodePropertyValueExpression `}`
// [+U] `P{` UnicodePropertyValueExpression `}`
maybeParseCharacterClassEscape() {
switch (this.peek()) {
case 'd':
case 'D':
case 's':
case 'S':
case 'w':
case 'W':
return {
type: 'CharacterClassEscape',
value: this.next(),
};
case 'p':
case 'P': {
if (!this.plusU) {
return undefined;
}
const value = this.next();
this.expect('{');
let sawDigit;
let LoneUnicodePropertyNameOrValue = '';
while (true) {
if (this.position >= this.source.length) {
this.raise('Invalid unicode property name or value');
}
const c = this.source[this.position];
if (isDecimalDigit(c)) {
sawDigit = true;
this.position += 1;
LoneUnicodePropertyNameOrValue += c;
continue;
}
if (c === '_') {
this.position += 1;
LoneUnicodePropertyNameOrValue += c;
continue;
}
if (!isControlLetter(c)) {
break;
}
this.position += 1;
LoneUnicodePropertyNameOrValue += c;
}
if (LoneUnicodePropertyNameOrValue.length === 0) {
this.raise('Invalid unicode property name or value');
}
if (sawDigit && this.eat('}')) {
if (!(LoneUnicodePropertyNameOrValue in UnicodeGeneralCategoryValues
|| LoneUnicodePropertyNameOrValue in BinaryUnicodeProperties)) {
this.raise('Invalid unicode property name or value');
}
return {
type: 'CharacterClassEscape',
value,
UnicodePropertyValueExpression: {
type: 'UnicodePropertyValueExpression',
LoneUnicodePropertyNameOrValue,
},
};
}
let UnicodePropertyValue;
if (this.source[this.position] === '=') {
this.position += 1;
UnicodePropertyValue = '';
while (true) {
if (this.position >= this.source.length) {
this.raise('Invalid unicode property value');
}
const c = this.source[this.position];
if (!isControlLetter(c) && !isDecimalDigit(c) && c !== '_') {
break;
}
this.position += 1;
UnicodePropertyValue += c;
}
if (UnicodePropertyValue.length === 0) {
this.raise('Invalid unicode property value');
}
}
this.expect('}');
if (UnicodePropertyValue) {
if (!(LoneUnicodePropertyNameOrValue in NonbinaryUnicodeProperties)) {
this.raise('Invalid unicode property name');
}
if (!(UnicodePropertyValue in UnicodeGeneralCategoryValues || UnicodePropertyValue in UnicodeScriptValues)) {
this.raise('Invalid unicode property value');
}
return {
type: 'CharacterClassEscape',
value,
UnicodePropertyValueExpression: {
type: 'UnicodePropertyValueExpression',
UnicodePropertyName: LoneUnicodePropertyNameOrValue,
UnicodePropertyValue,
},
};
}
if (!(LoneUnicodePropertyNameOrValue in UnicodeGeneralCategoryValues
|| LoneUnicodePropertyNameOrValue in BinaryUnicodeProperties)) {
this.raise('Invalid unicode property name or value');
}
return {
type: 'CharacterClassEscape',
value,
UnicodePropertyValueExpression: {
type: 'UnicodePropertyValueExpression',
LoneUnicodePropertyNameOrValue,
},
};
}
default:
return undefined;
}
}
// CharacterClass ::
// `[` ClassRanges `]`
// `[` `^` ClassRanges `]`
parseCharacterClass() {
this.expect('[');
const node = {
type: 'CharacterClass',
invert: false,
ClassRanges: undefined,
};
node.invert = this.eat('^');
node.ClassRanges = this.parseClassRanges();
this.expect(']');
return node;
}
// ClassRanges ::
// [empty]
// NonemptyClassRanges
parseClassRanges() {
const ranges = [];
while (!this.test(']')) {
if (this.position >= this.source.length) {
this.raise('Unexpected end of CharacterClass');
}
const atom = this.parseClassAtom();
if (this.eat('-')) {
if (atom.type === 'CharacterClassEscape') {
this.raise('Invalid class range');
}
if (this.test(']')) {
ranges.push(atom);
ranges.push({ type: 'ClassAtom', value: '-' });
} else {
const atom2 = this.parseClassAtom();
if (atom2.type === 'CharacterClassEscape') {
this.raise('Invalid class range');
}
if (CharacterValue(atom) > CharacterValue(atom2)) {
this.raise('Invalid class range');
}
ranges.push([atom, atom2]);
}
} else {
ranges.push(atom);
}
}
return ranges;
}
// ClassAtom ::
// `-`
// ClassAtomNoDash
// ClassAtomNoDash ::
// SourceCharacter but not one of `\` or `]` or `-`
// `\` ClassEscape
// ClassEscape :
// `b`
// [+U] `-`
// CharacterClassEscape
// CharacterEscape
parseClassAtom() {
if (this.eat('\\')) {
if (this.eat('b')) {
return {
type: 'ClassEscape',
value: 'b',
};
}
if (this.plusU && this.eat('-')) {
return {
type: 'ClassEscape',
value: '-',
};
}
const CharacterClassEscape = this.maybeParseCharacterClassEscape();
if (CharacterClassEscape) {
return CharacterClassEscape;
}
return {
type: 'ClassEscape',
CharacterEscape: this.parseCharacterEscape(),
};
}
return {
type: 'ClassAtom',
SourceCharacter: this.parseSourceCharacter(),
};
}
parseSourceCharacter() {
const lead = this.source.charCodeAt(this.position);
const trail = this.source.charCodeAt(this.position + 1);
if (trail && isLeadingSurrogate(lead) && isTrailingSurrogate(trail)) {
return this.next() + this.next();
}
return this.next();
}
parseGroupName() {
this.expect('<');
const RegExpIdentifierName = this.parseRegExpIdentifierName();
this.expect('>');
return RegExpIdentifierName;
}
// RegExpIdentifierName ::
// RegExpIdentifierStart
// RegExpIdentifierName RegExpIdentifierPart
parseRegExpIdentifierName() {
let buffer = '';
let check = isIdentifierStart;
while (this.position < this.source.length) {
const c = this.source[this.position];
const code = c.charCodeAt(0);
if (c === '\\') {
this.position += 1;
const RegExpUnicodeEscapeSequence = this.scope({ U: true }, () => this.maybeParseRegExpUnicodeEscapeSequence());
if (!RegExpUnicodeEscapeSequence) {
this.raise('Invalid unicode escape');
}
const raw = String.fromCodePoint(CharacterValue(RegExpUnicodeEscapeSequence));
if (!check(raw)) {
this.raise('Invalid identifier escape');
}
buffer += raw;
} else if (isLeadingSurrogate(code)) {
const lowSurrogate = this.source.charCodeAt(this.position + 1);
if (!isTrailingSurrogate(lowSurrogate)) {
this.raise('Invalid trailing surrogate');
}
const codePoint = UTF16SurrogatePairToCodePoint(code, lowSurrogate);
const raw = String.fromCodePoint(codePoint);
if (!check(raw)) {
this.raise('Invalid surrogate pair');
}
this.position += 2;
buffer += raw;
} else if (check(c)) {
buffer += c;
this.position += 1;
} else {
break;
}
check = isIdentifierPart;
}
if (buffer.length === 0) {
this.raise('Invalid empty identifier');
}
return buffer;
}
// DecimalDigits ::
// DecimalDigit
// DecimalDigits DecimalDigit
parseDecimalDigits() {
let n = '';
if (!isDecimalDigit(this.peek())) {
this.raise('Invalid decimal digits');
}
while (isDecimalDigit(this.peek())) {
n += this.next();
}
return n;
}
// HexEscapeSequence ::
// `x` HexDigit HexDigit
parseHexEscapeSequence() {
this.expect('x');
const HexDigit_a = this.next();
if (!isHexDigit(HexDigit_a)) {
this.raise('Not a hex digit');
}
const HexDigit_b = this.next();
if (!isHexDigit(HexDigit_b)) {
this.raise('Not a hex digit');
}
return {
type: 'HexEscapeSequence',
HexDigit_a,
HexDigit_b,
};
}
scanHex(length) {
if (length === 0) {
this.raise('Invalid code point');
}
let n = 0;
for (let i = 0; i < length; i += 1) {
const c = this.source[this.position];
if (isHexDigit(c)) {
this.position += 1;
n = (n << 4) | Number.parseInt(c, 16);
} else {
this.raise('Invalid hex digit');
}
}
return n;
}
// RegExpUnicodeEscapeSequence ::
// [+U] `u` HexLeadSurrogate `\u` HexTrailSurrogate
// [+U] `u` HexLeadSurrogate
// [+U] `u` HexTrailSurrogate
// [+U] `u` HexNonSurrogate
// [~U] `u` Hex4Digits
// [+U] `u{` CodePoint `}`
maybeParseRegExpUnicodeEscapeSequence() {
const start = this.position;
if (!this.eat('u')) {
this.position = start;
return undefined;
}
if (this.plusU && this.eat('{')) {
const end = this.source.indexOf('}', this.position);
if (end === -1) {
this.raise('Invalid code point');
}
const code = this.scanHex(end - this.position);
if (code > 0x10FFFF) {
this.raise('Invalid code point');
}
this.position += 1;
return {
type: 'RegExpUnicodeEscapeSequence',
CodePoint: code,
};
}
let lead;
try {
lead = this.scanHex(4);
} catch {
this.position = start;
return undefined;
}
if (this.plusU && isLeadingSurrogate(lead)) {
const back = this.position;
if (this.eat('\\') && this.eat('u')) {
let trail;
try {
trail = this.scanHex(4);
} catch {
this.position = back;
}
return {
type: 'RegExpUnicodeEscapeSequence',
HexLeadSurrogate: lead,
HexTrailSurrogate: trail,
};
}
return {
type: 'RegExpUnicodeEscapeSequence',
HexLeadSurrogate: lead,
};
}
return {
type: 'RegExpUnicodeEscapeSequence',
Hex4Digits: lead,
};
}
}
+371
View File
@@ -0,0 +1,371 @@
import { OutOfRange } from '../helpers.mjs';
export const Flag = {
__proto__: null,
};
[
'return',
'await',
'yield',
'parameters',
'newTarget',
'importMeta',
'superCall',
'superProperty',
'in',
'default',
'module',
].forEach((name, i) => {
/* istanbul ignore next */
if (i > 31) {
throw new RangeError(name);
}
Flag[name] = 1 << i;
});
export function getDeclarations(node) {
if (Array.isArray(node)) {
return node.flatMap((n) => getDeclarations(n));
}
switch (node.type) {
case 'LexicalBinding':
case 'VariableDeclaration':
case 'BindingRestElement':
case 'BindingRestProperty':
case 'ForBinding':
if (node.BindingIdentifier) {
return getDeclarations(node.BindingIdentifier);
}
if (node.BindingPattern) {
return getDeclarations(node.BindingPattern);
}
return [];
case 'SingleNameBinding':
return getDeclarations(node.BindingIdentifier);
case 'ImportClause': {
const d = [];
if (node.ImportedDefaultBinding) {
d.push(...getDeclarations(node.ImportedDefaultBinding));
}
if (node.NameSpaceImport) {
d.push(...getDeclarations(node.NameSpaceImport));
}
if (node.NamedImports) {
d.push(...getDeclarations(node.NamedImports));
}
return d;
}
case 'ImportSpecifier':
return getDeclarations(node.ImportedBinding);
case 'ImportedDefaultBinding':
case 'NameSpaceImport':
return getDeclarations(node.ImportedBinding);
case 'NamedImports':
return getDeclarations(node.ImportsList);
case 'ObjectBindingPattern': {
const declarations = getDeclarations(node.BindingPropertyList);
if (node.BindingRestProperty) {
declarations.push(...getDeclarations(node.BindingRestProperty));
}
return declarations;
}
case 'ArrayBindingPattern': {
const declarations = getDeclarations(node.BindingElementList);
if (node.BindingRestElement) {
declarations.push(...getDeclarations(node.BindingRestElement));
}
return declarations;
}
case 'BindingElement':
return getDeclarations(node.BindingPattern);
case 'BindingProperty':
return getDeclarations(node.BindingElement);
case 'BindingIdentifier':
case 'IdentifierName':
case 'LabelIdentifier':
return [{ name: node.name, node }];
case 'StringLiteral':
return [{ name: node.value, node }];
case 'Elision':
return [];
case 'ForDeclaration':
return getDeclarations(node.ForBinding);
case 'ExportSpecifier':
if (node.IdentifierName) {
return getDeclarations(node.IdentifierName);
}
return getDeclarations(node.IdentifierName_b);
case 'FunctionDeclaration':
case 'GeneratorDeclaration':
case 'AsyncFunctionDeclaration':
case 'AsyncGeneratorDeclaration':
return getDeclarations(node.BindingIdentifier);
case 'LexicalDeclaration':
return getDeclarations(node.BindingList);
case 'VariableStatement':
return getDeclarations(node.VariableDeclarationList);
case 'ClassDeclaration':
return getDeclarations(node.BindingIdentifier);
default:
throw new OutOfRange('getDeclarations', node);
}
}
export class Scope {
constructor(parser) {
this.parser = parser;
this.scopeStack = [];
this.labels = [];
this.arrowInfoStack = [];
this.assignmentInfoStack = [];
this.exports = new Set();
this.undefinedExports = new Map();
this.flags = 0;
}
hasReturn() {
return (this.flags & Flag.return) !== 0;
}
hasAwait() {
return (this.flags & Flag.await) !== 0;
}
hasYield() {
return (this.flags & Flag.yield) !== 0;
}
hasNewTarget() {
return (this.flags & Flag.newTarget) !== 0;
}
hasSuperCall() {
return (this.flags & Flag.superCall) !== 0;
}
hasSuperProperty() {
return (this.flags & Flag.superProperty) !== 0;
}
hasImportMeta() {
return (this.flags & Flag.importMeta) !== 0;
}
hasIn() {
return (this.flags & Flag.in) !== 0;
}
inParameters() {
return (this.flags & Flag.parameters) !== 0;
}
isDefault() {
return (this.flags & Flag.default) !== 0;
}
isModule() {
return (this.flags & Flag.module) !== 0;
}
with(flags, f) {
const oldFlags = this.flags;
Object.entries(flags)
.forEach(([k, v]) => {
if (k in Flag) {
if (v === true) {
this.flags |= Flag[k];
} else if (v === false) {
this.flags &= ~Flag[k];
}
}
});
if (flags.lexical || flags.variable) {
this.scopeStack.push({
flags,
lexicals: new Set(),
variables: new Set(),
functions: new Set(),
parameters: new Set(),
});
}
const oldLabels = this.labels;
if (flags.label === 'boundary') {
this.labels = [];
} else if (flags.label) {
this.labels.push({ type: flags.label });
}
const oldStrict = this.parser.state.strict;
if (flags.strict === true) {
this.parser.state.strict = true;
} else if (flags.strict === false) {
this.parser.state.strict = false;
}
const r = f();
if (flags.label === 'boundary') {
this.labels = oldLabels;
} else if (flags.label) {
this.labels.pop();
}
if (flags.lexical || flags.variable) {
this.scopeStack.pop();
}
this.parser.state.strict = oldStrict;
this.flags = oldFlags;
return r;
}
pushArrowInfo(isAsync = false) {
this.arrowInfoStack.push({
isAsync,
hasTrailingComma: false,
yieldExpressions: [],
awaitExpressions: [],
awaitIdentifiers: [],
});
}
popArrowInfo() {
return this.arrowInfoStack.pop();
}
pushAssignmentInfo(type) {
const parser = this.parser;
this.assignmentInfoStack.push({
type,
coverInitializedNameErrors: [],
clear() {
this.coverInitializedNameErrors.forEach((e) => {
parser.earlyErrors.delete(e);
});
},
});
}
popAssignmentInfo() {
return this.assignmentInfoStack.pop();
}
registerCoverInitializedName(CoverInitializedName) {
const error = this.parser.raiseEarly('UnexpectedToken', CoverInitializedName);
for (let i = this.assignmentInfoStack.length - 1; i >= 0; i -= 1) {
const info = this.assignmentInfoStack[i];
info.coverInitializedNameErrors.push(error);
if (info.type !== 'assign') {
break;
}
}
}
lexicalScope() {
for (let i = this.scopeStack.length - 1; i >= 0; i -= 1) {
const scope = this.scopeStack[i];
if (scope.flags.lexical) {
return scope;
}
}
/* istanbul ignore next */
throw new RangeError();
}
variableScope() {
for (let i = this.scopeStack.length - 1; i >= 0; i -= 1) {
const scope = this.scopeStack[i];
if (scope.flags.variable) {
return scope;
}
}
/* istanbul ignore next */
throw new RangeError();
}
declare(node, type) {
const declarations = getDeclarations(node);
declarations.forEach((d) => {
switch (type) {
case 'lexical':
case 'import': {
if (type === 'lexical' && d.name === 'let') {
this.parser.raiseEarly('LetInLexicalBinding', d.node);
}
const scope = this.lexicalScope();
if (scope.lexicals.has(d.name)
|| scope.variables.has(d.name)
|| scope.functions.has(d.name)
|| scope.parameters.has(d.name)) {
this.parser.raiseEarly('AlreadyDeclared', d.node, d.name);
}
scope.lexicals.add(d.name);
if (scope === this.scopeStack[0] && this.undefinedExports.has(d.name)) {
this.undefinedExports.delete(d.name);
}
break;
}
case 'function': {
const scope = this.lexicalScope();
if (scope.lexicals.has(d.name)) {
this.parser.raiseEarly('AlreadyDeclared', d.node, d.name);
}
if (scope.flags.variableFunctions) {
scope.functions.add(d.name);
} else {
if (scope.variables.has(d.name)) {
this.parser.raiseEarly('AlreadyDeclared', d.node, d.name);
}
scope.lexicals.add(d.name);
}
if (scope === this.scopeStack[0] && this.undefinedExports.has(d.name)) {
this.undefinedExports.delete(d.name);
}
break;
}
case 'parameter':
this.variableScope().parameters.add(d.name);
break;
case 'variable':
for (let i = this.scopeStack.length - 1; i >= 0; i -= 1) {
const scope = this.scopeStack[i];
scope.variables.add(d.name);
if (scope.lexicals.has(d.name) || (!scope.flags.variableFunctions && scope.functions.has(d.name))) {
this.parser.raiseEarly('AlreadyDeclared', d.node, d.name);
}
if (i === 0 && this.undefinedExports.has(d.name)) {
this.undefinedExports.delete(d.name);
}
if (scope.flags.variable) {
break;
}
}
break;
case 'export':
if (this.exports.has(d.name)) {
this.parser.raiseEarly('AlreadyDeclared', d.node, d.name);
} else {
this.exports.add(d.name);
}
break;
default:
/* istanbul ignore next */
throw new RangeError(type);
}
});
}
checkUndefinedExports(NamedExports) {
const scope = this.variableScope();
NamedExports.ExportsList.forEach((n) => {
const targetNode = n.IdentifierName || n.IdentifierName_a;
if (!scope.lexicals.has(targetNode.name) && !scope.variables.has(targetNode.name)) {
this.undefinedExports.set(targetNode.name, targetNode);
}
});
}
}
File diff suppressed because it is too large Load Diff
+175
View File
@@ -0,0 +1,175 @@
const MaybeAssignTokens = [
// Logical
['NULLISH', '??', 3],
['OR', '||', 4],
['AND', '&&', 5],
// Binop
['BIT_OR', '|', 6],
['BIT_XOR', '^', 7],
['BIT_AND', '&', 8],
['SHL', '<<', 11],
['SAR', '>>', 11],
['SHR', '>>>', 11],
['MUL', '*', 13],
['DIV', '/', 13],
['MOD', '%', 13],
['EXP', '**', 14],
// Unop
['ADD', '+', 12],
['SUB', '-', 12],
];
export const RawTokens = [
// BEGIN PropertyOrCall
// BEGIN Member
// BEGIN Template
['TEMPLATE', '`'],
// END Template
// BEGIN Property
['PERIOD', '.'],
['LBRACK', '['],
// END Property
// END Member
['OPTIONAL', '?.'],
['LPAREN', '('],
// END PropertyOrCall
['RPAREN', ')'],
['RBRACK', ']'],
['LBRACE', '{'],
['COLON', ':'],
['ELLIPSIS', '...'],
['CONDITIONAL', '?'],
// BEGIN AutoSemicolon
['SEMICOLON', ';'],
['RBRACE', '}'],
['EOS', 'EOS'],
// END AutoSemicolon
// BEGIN ArrowOrAssign
['ARROW', '=>'],
// BEGIN Assign
['ASSIGN', '=', 2],
...MaybeAssignTokens.map((t) => [`ASSIGN_${t[0]}`, `${t[1]}=`, 2]),
// END Assign
// END ArrowOrAssign
// Binary operators by precidence
['COMMA', ',', 1],
...MaybeAssignTokens,
['NOT', '!'],
['BIT_NOT', '~'],
['DELETE', 'delete'],
['TYPEOF', 'typeof'],
['VOID', 'void'],
// BEGIN IsCountOp
['INC', '++'],
['DEC', '--'],
// END IsCountOp
// END IsUnaryOrCountOp
['EQ', '==', 9],
['EQ_STRICT', '===', 9],
['NE', '!=', 9],
['NE_STRICT', '!==', 9],
['LT', '<', 10],
['GT', '>', 10],
['LTE', '<=', 10],
['GTE', '>=', 10],
['INSTANCEOF', 'instanceof', 10],
['IN', 'in', 10],
['BREAK', 'break'],
['CASE', 'case'],
['CATCH', 'catch'],
['CONTINUE', 'continue'],
['DEBUGGER', 'debugger'],
['DEFAULT', 'default'],
// DELETE
['DO', 'do'],
['ELSE', 'else'],
['FINALLY', 'finally'],
['FOR', 'for'],
['FUNCTION', 'function'],
['IF', 'if'],
// IN
// INSTANCEOF
['NEW', 'new'],
['RETURN', 'return'],
['SWITCH', 'switch'],
['THROW', 'throw'],
['TRY', 'try'],
// TYPEOF
['VAR', 'var'],
// VOID
['WHILE', 'while'],
['WITH', 'with'],
['THIS', 'this'],
['NULL', 'null'],
['TRUE', 'true'],
['FALSE', 'false'],
['NUMBER', null],
['STRING', null],
// BEGIN Callable
['SUPER', 'super'],
// BEGIN AnyIdentifier
['IDENTIFIER', null],
['AWAIT', 'await'],
['YIELD', 'yield'],
// END AnyIdentifier
// END Callable
['CLASS', 'class'],
['CONST', 'const'],
['EXPORT', 'export'],
['EXTENDS', 'extends'],
['IMPORT', 'import'],
['ENUM', 'enum'],
['ESCAPED_KEYWORD', null],
];
export const Token = RawTokens
.reduce((obj, [name], i) => {
obj[name] = i;
return obj;
}, {});
export const TokenNames = RawTokens.map((r) => r[0]);
export const TokenPrecedence = RawTokens.map((r) => (r[2] || 0));
const Keywords = RawTokens
.filter(([name, raw]) => name.toLowerCase() === raw)
.map(([, raw]) => raw);
export const KeywordLookup = Keywords
.reduce((obj, kw) => {
obj[kw] = Token[kw.toUpperCase()];
return obj;
}, Object.create(null));
const KeywordTokens = new Set(Object.values(KeywordLookup));
const isInRange = (t, l, h) => t >= l && t <= h;
export const isAutomaticSemicolon = (t) => isInRange(t, Token.SEMICOLON, Token.EOS);
export const isMember = (t) => isInRange(t, Token.TEMPLATE, Token.LBRACK);
export const isPropertyOrCall = (t) => isInRange(t, Token.TEMPLATE, Token.LPAREN);
export const isKeyword = (t) => KeywordTokens.has(t);
export const isKeywordRaw = (s) => Keywords.includes(s);
const ReservedWordsStrict = [
'implements', 'interface', 'let',
'package', 'private', 'protected',
'public', 'static', 'yield',
];
export const isReservedWordStrict = (s) => ReservedWordsStrict.includes(s);