Skip to content

Commit

Permalink
fix(parser): tweaked the label tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
KFlash committed May 25, 2019
1 parent 90d2d78 commit 77702c8
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 52 deletions.
76 changes: 54 additions & 22 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export function consume(parser: ParserState, context: Context, t: Token): void {
* Transforms a `LeftHandSideExpression` into a `AssignmentPattern` if possible,
* otherwise it returns the original tree.
*
* @param {ParserState} parser
* @param parser Parser state
* @param {*} node
*/
export function reinterpretToPattern(state: ParserState, node: any): void {
Expand Down Expand Up @@ -229,53 +229,70 @@ export function reinterpretToPattern(state: ParserState, node: any): void {
}
}

/**
* Validates binding identifier
*
* @param parser Parser state
* @param context Context masks
* @param type Binding type
* @param token Token
*/

export function validateBindingIdentifier(
parser: ParserState,
context: Context,
type: BindingType,
token: Token
t: Token
): void {
if ((token & Token.Keyword) !== Token.Keyword) return;
if ((t & Token.Keyword) !== Token.Keyword) return;

if (context & Context.Strict) {
if (token === Token.StaticKeyword) {
if (t === Token.StaticKeyword) {
report(parser, Errors.InvalidStrictStatic);
}

if ((token & Token.FutureReserved) === Token.FutureReserved) {
if ((t & Token.FutureReserved) === Token.FutureReserved) {
report(parser, Errors.FutureReservedWordInStrictModeNotId);
}

if ((token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) {
if ((t & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) {
report(parser, Errors.StrictEvalArguments);
}

if (token === Token.EscapedFutureReserved) {
if (t === Token.EscapedFutureReserved) {
report(parser, Errors.InvalidEscapedKeyword);
}
}

if ((token & Token.Reserved) === Token.Reserved) {
if ((t & Token.Reserved) === Token.Reserved) {
report(parser, Errors.KeywordNotId);
}

if (type & (BindingType.Let | BindingType.Const) && token === Token.LetKeyword) {
if (type & (BindingType.Let | BindingType.Const) && t === Token.LetKeyword) {
report(parser, Errors.InvalidLetConstBinding);
}

if (context & (Context.InAwaitContext | Context.Module) && token === Token.AwaitKeyword) {
if (context & (Context.InAwaitContext | Context.Module) && t === Token.AwaitKeyword) {
report(parser, Errors.AwaitOutsideAsync);
}

if (context & (Context.InYieldContext | Context.Strict) && token === Token.YieldKeyword) {
if (context & (Context.InYieldContext | Context.Strict) && t === Token.YieldKeyword) {
report(parser, Errors.DisallowedInContext, 'yield');
}

if (token === Token.EscapedReserved) {
if (t === Token.EscapedReserved) {
report(parser, Errors.InvalidEscapedKeyword);
}
}

/**
* Validates binding identifier
*
* @param parser Parser state
* @param context Context masks
* @param t Token
*/

export function isStrictReservedWord(parser: ParserState, context: Context, t: Token): boolean {
if (t === Token.AwaitKeyword) {
if (context & (Context.InAwaitContext | Context.Module)) report(parser, Errors.AwaitOutsideAsync);
Expand All @@ -301,27 +318,42 @@ export function isPropertyWithPrivateFieldKey(expr: any): boolean {
return !expr.property ? false : expr.property.type === 'PrivateName';
}

export function isValidLabel(parser: ParserState, labels: any, label: string, requireIterationStatement: 0 | 1): boolean {

let isInvalid = requireIterationStatement;
/**
* Checks if a label in `LabelledStatement` are valid or not
*
* @param parser Parser state
* @param labels Object holding the labels
* @param name Current label
* @param isIterationStatement
*/
export function isValidLabel(parser: ParserState, labels: any, name: string, isIterationStatement: 0 | 1): 0 | 1 {

do {
if (labels['€' + label]) {
if (isInvalid) report(parser, Errors.InvalidNestedStatement);
return true;
if (labels['€' + name]) {
if (isIterationStatement) report(parser, Errors.InvalidNestedStatement);
return 1;
}
if (isInvalid && labels.loop) isInvalid = 0;
if (isIterationStatement && labels.loop) isIterationStatement = 0;
labels = labels['€'];
} while (labels);

return false;
return 0;
}
export function validateAndTrackLabel(parser: ParserState, labels: any, name: string) {

/**
* Checks if current label already have been declrared, and if not
* declare it
*
* @param parser Parser state
* @param labels Object holding the labels
* @param name Current label
*/
export function validateAndDeclareLabel(parser: ParserState, labels: any, name: string): void {
let set = labels;
do {
if (set['€' + name]) report(parser, Errors.LabelRedeclaration, name);
set = set['€'];
} while (set);

labels['€' + name] = true;
labels['€' + name] = 1;
}
72 changes: 42 additions & 30 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
consumeSemicolon,
isPropertyWithPrivateFieldKey,
isValidLabel,
validateAndTrackLabel
validateAndDeclareLabel
} from './common';

/**
Expand Down Expand Up @@ -177,11 +177,11 @@ export function parseStatementList(parser: ParserState, context: Context): ESTre
while (parser.token === Token.StringLiteral) {
// "use strict" must be the exact literal without escape sequences or line continuation.
if (parser.index - parser.startIndex < 13 && parser.tokenValue === 'use strict') context |= Context.Strict;
statements.push(parseDirective(parser, context, /* labels */ {}));
statements.push(parseDirective(parser, context));
}

while (parser.token !== Token.EOF) {
statements.push(parseStatementListItem(parser, context, { '€': {} }) as ESTree.Statement);
statements.push(parseStatementListItem(parser, context, /* labels */ {}) as ESTree.Statement);
}
return statements;
}
Expand Down Expand Up @@ -219,12 +219,12 @@ export function parseModuleItem(
while (parser.token === Token.StringLiteral) {
// "use strict" must be the exact literal without escape sequences or line continuation.
if (parser.index - parser.startIndex < 13 && parser.tokenValue === 'use strict') context |= Context.Strict;
statements.push(parseDirective(parser, context, /* labels */ {}));
statements.push(parseDirective(parser, context));
}
}

while (parser.token !== Token.EOF) {
statements.push(parseparseModuleItemList(parser, context, { '€': {} }) as ESTree.Statement);
statements.push(parseparseModuleItemList(parser, context) as ESTree.Statement);
}
return statements;
}
Expand All @@ -237,7 +237,7 @@ export function parseModuleItem(
* @param parser Parser object
* @param context Context masks
*/
export function parseparseModuleItemList(parser: ParserState, context: Context, labels: any): any {
export function parseparseModuleItemList(parser: ParserState, context: Context): any {
// ecma262/#prod-ModuleItem
// ModuleItem :
// ImportDeclaration
Expand All @@ -253,7 +253,7 @@ export function parseparseModuleItemList(parser: ParserState, context: Context,
// async [no LineTerminator here] AsyncArrowBindingIdentifier ...
// async [no LineTerminator here] ArrowFormalParameters ...
default:
return parseStatementListItem(parser, context, labels);
return parseStatementListItem(parser, context, /* labels */ {});
}
}

Expand Down Expand Up @@ -308,7 +308,7 @@ export function parseStatementListItem(
case Token.ConstKeyword:
return parseVariableStatement(parser, context, BindingType.Const, BindingOrigin.Statement);
case Token.LetKeyword:
return parseLetIdentOrVarDeclarationStatement(parser, context, labels);
return parseLetIdentOrVarDeclarationStatement(parser, context);
// ExportDeclaration (only inside modules)
case Token.ExportKeyword:
report(parser, Errors.InvalidImportExportSloppy, 'export');
Expand Down Expand Up @@ -414,12 +414,20 @@ export function parseStatement(
report(parser, Errors.ClassForbiddenAsStatement);

case Token.YieldKeyword:
const { token } = parser;
const { token, tokenValue } = parser;
let expr = parseYieldExpressionOrIdentifier(parser, context);
if (parser.token === Token.Comma) expr = parseSequenceExpression(parser, context, expr);
if (context & Context.InYieldContext) return parseExpressionStatement(parser, context, expr);
if (parser.token === Token.Colon) {
return parseLabelledStatement(parser, context, labels, expr as ESTree.Identifier, token, allowFuncDecl);
return parseLabelledStatement(
parser,
context,
labels,
tokenValue,
expr as ESTree.Identifier,
token,
allowFuncDecl
);
}
expr = parseMemberOrUpdateExpression(parser, context, expr as ESTree.Expression, /* inNewExpression */ 0);

Expand Down Expand Up @@ -451,7 +459,7 @@ export function parseExpressionOrLabelledStatement(
// ExpressionStatement[Yield] :
// [lookahead notin {{, function, class, let [}] Expression[In, ?Yield] ;

const { token } = parser;
const { tokenValue, token } = parser;

let expr: ESTree.Expression;

Expand All @@ -460,7 +468,7 @@ export function parseExpressionOrLabelledStatement(
expr = parseIdentifier(parser, context);
if (context & Context.Strict) report(parser, Errors.UnexpectedLetStrictReserved);
if (parser.token === Token.Colon)
return parseLabelledStatement(parser, context, labels, expr, token, allowFuncDecl);
return parseLabelledStatement(parser, context, labels, tokenValue, expr, token, allowFuncDecl);
// "let" followed by either "[", "{" or an identifier means a lexical
// declaration, which should not appear here.
// However, ASI may insert a line break before an identifier or a brace.
Expand All @@ -482,7 +490,7 @@ export function parseExpressionOrLabelledStatement(
* [lookahead notin {{, function, class, let [}] Expression[In, ?Yield] ;
*/
if (token & Token.IsIdentifier && parser.token === Token.Colon) {
return parseLabelledStatement(parser, context, labels, expr as ESTree.Identifier, token, allowFuncDecl);
return parseLabelledStatement(parser, context, labels, tokenValue, expr as ESTree.Identifier, token, allowFuncDecl);
}
/** MemberExpression :
* 1. PrimaryExpression
Expand Down Expand Up @@ -627,7 +635,8 @@ export function parseLabelledStatement(
parser: ParserState,
context: Context,
labels: any,
label: ESTree.Identifier,
label: string,
expr: ESTree.Identifier,
token: Token,
allowFuncDecl: 0 | 1
): ESTree.LabeledStatement {
Expand All @@ -637,13 +646,13 @@ export function parseLabelledStatement(

if ((token & Token.Reserved) === Token.Reserved) report(parser, Errors.UnexpectedStrictReserved);

nextToken(parser, context | Context.AllowRegExp);
validateAndDeclareLabel(parser, labels, label);

validateAndTrackLabel(parser, labels, label.name);
nextToken(parser, context | Context.AllowRegExp);

return {
type: 'LabeledStatement',
label,
label: expr,
// In sloppy mode, Annex B.3.2 allows labelled function declarations.
// Otherwise it's a parse error.
body:
Expand Down Expand Up @@ -688,12 +697,12 @@ export function parseAsyncArrowOrAsyncFunctionDeclaration(
// AsyncFunctionBody:
// FunctionBody[~Yield, +Await]

const { token } = parser;
const { token, tokenValue } = parser;

let expr: ESTree.Expression = parseIdentifier(parser, context);

if (parser.token === Token.Colon)
return parseLabelledStatement(parser, context, labels, expr, token, /* allowFuncDecl */ 1);
return parseLabelledStatement(parser, context, labels, tokenValue, expr, token, /* allowFuncDecl */ 1);

const asyncNewLine = parser.flags & Flags.NewLine;

Expand Down Expand Up @@ -792,13 +801,9 @@ export function parseAsyncArrowOrAsyncFunctionDeclaration(
* @param parser Parser object
* @param context Context masks
*/
export function parseDirective(
parser: ParserState,
context: Context,
labels: any
): ESTree.Statement | ESTree.ExpressionStatement {
export function parseDirective(parser: ParserState, context: Context): ESTree.Statement | ESTree.ExpressionStatement {
if ((context & Context.OptionsDirectives) < 1)
return parseStatementListItem(parser, context, labels) as ESTree.Statement;
return parseStatementListItem(parser, context, /* labels */ {}) as ESTree.Statement;
const { tokenRaw } = parser;
const expression = parseAssignmentExpression(parser, context, parseLiteral(parser, context));
consumeSemicolon(parser, context | Context.AllowRegExp);
Expand Down Expand Up @@ -1192,10 +1197,9 @@ export function parseDoWhileStatement(parser: ParserState, context: Context, lab
*/
export function parseLetIdentOrVarDeclarationStatement(
parser: ParserState,
context: Context,
labels: any
context: Context
): ESTree.VariableDeclaration | ESTree.LabeledStatement | ESTree.ExpressionStatement {
const { token } = parser;
const { token, tokenValue } = parser;
let expr: ESTree.Identifier | ESTree.Expression = parseIdentifier(parser, context);
// If the next token is an identifier, `[`, or `{`, this is not
// a `let` declaration, and we parse it as an identifier.
Expand All @@ -1213,7 +1217,15 @@ export function parseLetIdentOrVarDeclarationStatement(
*/

if (parser.token === Token.Colon) {
return parseLabelledStatement(parser, context, labels, expr, token, FunctionStatement.Disallow);
return parseLabelledStatement(
parser,
context,
/* labels */ {},
tokenValue,
expr,
token,
FunctionStatement.Disallow
);
}

/**
Expand Down Expand Up @@ -2329,7 +2341,7 @@ export function parseFunctionBody(
reportAt(parser, parser.index, parser.line, parser.startIndex, Errors.IllegalUseStrict);
}
}
body.push(parseDirective(parser, context, /* labels */ {}));
body.push(parseDirective(parser, context));
}
if (
context & Context.Strict &&
Expand Down

0 comments on commit 77702c8

Please sign in to comment.