Skip to content

Commit

Permalink
fix(parser): fixed AST output for optional chaining
Browse files Browse the repository at this point in the history
This changes are more in line with the ECMA proposal specs (draft)

https://tc39.es/proposal-optional-chaining/
  • Loading branch information
KFlash committed Aug 2, 2019
1 parent bdb1b79 commit 18d6735
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 372 deletions.
4 changes: 2 additions & 2 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export const enum Errors {
UnCorrespondingFragmentTag,
InvalidCoalescing,
InvalidChaining,
InvalidTaggedTemplateChain
OptionalChainingNoTemplate
}

/*@internal*/
Expand Down Expand Up @@ -356,7 +356,7 @@ export const errorMessages: {
[Errors.InvalidCoalescing]:
'Coalescing and logical operators used together in the same expression must be disambiguated with parentheses',
[Errors.InvalidChaining]: 'Constructors in/after an Optional Chain are not allowed',
[Errors.InvalidTaggedTemplateChain]: 'Tagged Template Literals are not allowed in optionalChain'
[Errors.OptionalChainingNoTemplate]: 'Tagged Template Literals are not allowed in optionalChain'
};

export class ParseError extends SyntaxError {
Expand Down
35 changes: 8 additions & 27 deletions src/estree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export type Node =
| BlockStatement
| BreakStatement
| CallExpression
| OptionalCallExpression
| OptionalChain
| ImportExpression
| CatchClause
| ClassBody
Expand Down Expand Up @@ -106,7 +106,6 @@ export type Node =
| LogicalExpression
| CoalesceExpression
| MemberExpression
| OptionalMemberExpression
| MetaProperty
| MethodDefinition
| NewExpression
Expand Down Expand Up @@ -185,14 +184,13 @@ export type JSXExpression = JSXEmptyExpression | JSXSpreadChild | JSXExpressionC
export type JSXTagNameExpression = JSXIdentifier | JSXMemberExpression | JSXNamespacedName;
export type LeftHandSideExpression =
| CallExpression
| OptionalCallExpression
| OptionalChain
| ImportExpression
| ClassExpression
| ClassDeclaration
| FunctionExpression
| LiteralExpression
| MemberExpression
| OptionalMemberExpression
| PrimaryExpression
| TaggedTemplateExpression;
export type LiteralExpression = BigIntLiteral | Literal | TemplateLiteral;
Expand Down Expand Up @@ -358,17 +356,15 @@ export interface ImportExpression extends _Node {
source: Expression;
}

export interface CallExpression extends _Node {
type: 'CallExpression';
callee: any; //Expression | Super;
arguments: (Expression | SpreadElement)[];
export interface OptionalChain extends _Node {
type: 'OptionalChain';
expression: MemberExpression | CallExpression | Identifier | OptionalChain;
}

export interface OptionalCallExpression extends _Node {
type: 'OptionalCallExpression';
export interface CallExpression extends _Node {
type: 'CallExpression';
callee: any; //Expression | Super;
arguments: (Expression | SpreadElement)[];
optional: boolean;
}

export interface CatchClause extends _Node {
Expand Down Expand Up @@ -661,22 +657,7 @@ export interface MemberExpression extends _Node {
computed?: boolean;
}

export interface OptionalMemberExpression extends _Node {
type: 'OptionalMemberExpression';
object: Expression | Super;
property: Expression | PrivateName;
optional: boolean;
computed?: boolean;
}

export type Pattern =
| Identifier
| ObjectPattern
| ArrayPattern
| RestElement
| AssignmentPattern
| OptionalMemberExpression
| MemberExpression;
export type Pattern = Identifier | ObjectPattern | ArrayPattern | RestElement | AssignmentPattern | MemberExpression;

export interface MetaProperty extends _Node {
type: 'MetaProperty';
Expand Down
78 changes: 48 additions & 30 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3622,7 +3622,8 @@ export function parseMemberOrUpdateExpression(
start: number,
line: number,
column: number,
chained: 0 | 1 = 0
isOptional?: 0 | 1,
optionalChaining?: 0 | 1
): any {
// Update + Member expression
if ((parser.token & Token.IsUpdateOp) === Token.IsUpdateOp && (parser.flags & Flags.NewLine) < 1) {
Expand All @@ -3648,6 +3649,8 @@ export function parseMemberOrUpdateExpression(
switch (parser.token) {
/* Property */
case Token.Period: {
if (isOptional) report(parser, Errors.Unexpected);

nextToken(parser, context);

parser.assignable = AssignmentKind.Assignable;
Expand All @@ -3670,23 +3673,20 @@ export function parseMemberOrUpdateExpression(
const { tokenPos, linePos, colPos } = parser;

const property = parseExpressions(parser, context, inGroup, 1, tokenPos, linePos, colPos);

consume(parser, context, Token.RightBracket);

parser.assignable = AssignmentKind.Assignable;

expr = finishNode(
parser,
context,
start,
line,
column,
chained
context & Context.OptionsNext
? {
type: 'OptionalMemberExpression',
type: 'MemberExpression',
object: expr,
computed: true,
optional: chained === 1,
optional: isOptional === 1,
property
}
: {
Expand All @@ -3696,26 +3696,25 @@ export function parseMemberOrUpdateExpression(
property
}
);
isOptional = 0;
break;
}

/* Call */
case Token.LeftParen: {
const args = parseArguments(parser, context, inGroup);

parser.assignable = AssignmentKind.CannotAssign;

expr = finishNode(
parser,
context,
start,
line,
column,
chained
context & Context.OptionsNext
? {
type: 'OptionalCallExpression',
type: 'CallExpression',
callee: expr,
optional: chained === 1,
optional: isOptional === 1,
arguments: args
}
: {
Expand All @@ -3724,35 +3723,26 @@ export function parseMemberOrUpdateExpression(
arguments: args
}
);
isOptional = 0;
break;
}

/* Optional chaining */
case Token.QuestionMarkPeriod: {
if (isOptional) report(parser, Errors.Unexpected);
nextToken(parser, context); // skips: '?.'

if ((parser.token & Token.IsMemberOrCallExpression) === Token.IsMemberOrCallExpression) {
expr = parseMemberOrUpdateExpression(parser, context, expr, 0, start, line, column, 1);
} else {
const property = parseIdentifier(parser, context, 0);
if (parser.token === Token.TemplateSpan) report(parser, Errors.InvalidTaggedTemplateChain);
expr = finishNode(parser, context, start, line, column, {
type: 'OptionalMemberExpression',
object: expr,
computed: false,
optional: true,
property
} as any);
}

isOptional = 1;
optionalChaining = 1;
parser.assignable = AssignmentKind.CannotAssign;

break;
}

/* Template */
default: {
if (optionalChaining) report(parser, Errors.OptionalChainingNoTemplate);

parser.assignable = AssignmentKind.CannotAssign;

expr = finishNode(parser, context, parser.tokenPos, parser.linePos, parser.colPos, {
type: 'TaggedTemplateExpression',
tag: expr,
Expand All @@ -3764,9 +3754,37 @@ export function parseMemberOrUpdateExpression(
}
}

return parseMemberOrUpdateExpression(parser, context, expr, 0, start, line, column);
if (context & Context.OptionsNext && isOptional && parser.token & Token.IsIdentifier) {
parser.assignable = AssignmentKind.CannotAssign;
const property = parsePropertyOrPrivatePropertyName(parser, context);
expr = finishNode(parser, context, start, line, column, {
type: 'MemberExpression',
optional: true,
object: expr,
computed: false,
property
});
isOptional = 0;
}

return parseMemberOrUpdateExpression(parser, context, expr, 0, start, line, column, isOptional, optionalChaining);
}
return expr;

return optionalChaining ? parserOptionalChain(parser, context, expr, start, line, column) : expr;
}

function parserOptionalChain(
parser: ParserState,
context: Context,
expr: ESTree.Expression,
start: number,
line: number,
column: number
): ESTree.OptionalChain {
return finishNode(parser, context, start, line, column, {
type: 'OptionalChain',
expression: expr
} as any);
}

/**
Expand Down
Loading

0 comments on commit 18d6735

Please sign in to comment.