Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse async do expressions #13043

6 changes: 6 additions & 0 deletions packages/babel-core/src/parser/util/missing-plugin-helper.ts
@@ -1,4 +1,10 @@
const pluginNameMap = {
asyncDoExpressions: {
syntax: {
name: "@babel/plugin-syntax-async-do-expressions",
url: "https://git.io/JYer8",
},
},
classProperties: {
syntax: {
name: "@babel/plugin-syntax-class-properties",
Expand Down
4 changes: 4 additions & 0 deletions packages/babel-generator/src/generators/expressions.ts
Expand Up @@ -20,6 +20,10 @@ export function UnaryExpression(this: Printer, node: t.UnaryExpression) {
}

export function DoExpression(this: Printer, node: t.DoExpression) {
if (node.async) {
this.word("async");
this.space();
}
this.word("do");
this.space();
this.print(node.body, node);
Expand Down
3 changes: 2 additions & 1 deletion packages/babel-generator/src/node/parentheses.ts
Expand Up @@ -82,7 +82,8 @@ export function DoExpression(
parent: any,
printStack: Array<any>,
): boolean {
return isFirstInStatement(printStack);
// `async do` can start an expression statement
return !node.async && isFirstInStatement(printStack);
}

export function Binary(node: any, parent: any): boolean {
Expand Down
@@ -0,0 +1,5 @@
async do {
1;
};

(async do {});
@@ -0,0 +1,4 @@
async do {
1;
};
async do {};
@@ -0,0 +1,3 @@
{
"plugins": ["asyncDoExpressions", "doExpressions"]
}
@@ -0,0 +1,3 @@
/* leading comments
*/async do
{ 1 } + 0;
@@ -0,0 +1,3 @@
{
"retainLines": true
}
@@ -0,0 +1,3 @@
/* leading comments
*/async do
{1;} + 0;
1 change: 1 addition & 0 deletions packages/babel-parser/ast/spec.md
Expand Up @@ -1084,6 +1084,7 @@ An expression wrapped by parentheses. By default `@babel/parser` does not create
interface DoExpression <: Expression {
type: "DoExpression";
body: BlockStatement;
async: boolean;
}
```

Expand Down
22 changes: 19 additions & 3 deletions packages/babel-parser/src/parser/expression.js
Expand Up @@ -1033,6 +1033,8 @@ export default class ExpressionParser extends LValParser {
);
} else if (this.match(tt.name)) {
return this.parseAsyncArrowUnaryFunction(id);
} else if (this.match(tt._do)) {
return this.parseDo(true);
}
}

Expand All @@ -1049,7 +1051,7 @@ export default class ExpressionParser extends LValParser {
}

case tt._do: {
return this.parseDo();
return this.parseDo(false);
}

case tt.regexp: {
Expand Down Expand Up @@ -1231,13 +1233,27 @@ export default class ExpressionParser extends LValParser {
}

// https://github.com/tc39/proposal-do-expressions
parseDo(): N.DoExpression {
// https://github.com/tc39/proposal-async-do-expressions
parseDo(isAsync: boolean): N.DoExpression {
this.expectPlugin("doExpressions");
if (isAsync) {
this.expectPlugin("asyncDoExpressions");
}
JLHwung marked this conversation as resolved.
Show resolved Hide resolved
const node = this.startNode();
node.async = isAsync;
this.next(); // eat `do`
const oldLabels = this.state.labels;
this.state.labels = [];
node.body = this.parseBlock();
if (isAsync) {
// AsyncDoExpression :
// async [no LineTerminator here] do Block[~Yield, +Await, ~Return]
this.prodParam.enter(PARAM_AWAIT);
node.body = this.parseBlock();
this.prodParam.exit();
} else {
node.body = this.parseBlock();
}

this.state.labels = oldLabels;
return this.finishNode(node, "DoExpression");
}
Expand Down
12 changes: 12 additions & 0 deletions packages/babel-parser/src/plugin-utils.js
Expand Up @@ -117,6 +117,18 @@ export function validatePlugins(plugins: PluginList) {
RECORD_AND_TUPLE_SYNTAX_TYPES.map(p => `'${p}'`).join(", "),
);
}

if (
hasPlugin(plugins, "asyncDoExpressions") &&
!hasPlugin(plugins, "doExpressions")
) {
const error = new Error(
"'asyncDoExpressions' requires 'doExpressions', please add 'doExpressions' to parser plugins.",
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we add .missingPlugin = ["doExpressions"] to the error (similarly to what expectPlugin does), @babel/core will suggest adding plugin-proposal-do-expressions to the config.

// $FlowIgnore
error.missingPlugins = "doExpressions"; // so @babel/core can provide better error message
throw error;
}
}

// These plugins are defined using a mixin which extends the parser class.
Expand Down
1 change: 1 addition & 0 deletions packages/babel-parser/src/types.js
Expand Up @@ -402,6 +402,7 @@ export type ArrayExpression = NodeBase & {
export type DoExpression = NodeBase & {
type: "DoExpression",
body: ?BlockStatement,
async: boolean,
};

export type TupleExpression = NodeBase & {
Expand Down
@@ -0,0 +1 @@
(async do {x})
@@ -0,0 +1,6 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'asyncDoExpressions' (1:7)",
"plugins": [
"doExpressions"
]
}
@@ -0,0 +1 @@
(async do {x})
@@ -0,0 +1,4 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'doExpressions' (1:7)",
"plugins": []
}
@@ -0,0 +1,5 @@
async
do {
42
}
while (false);
@@ -0,0 +1,51 @@
{
"type": "File",
"start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":14}},
"program": {
"type": "Program",
"start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":14}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}},
"expression": {
"type": "Identifier",
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5},"identifierName":"async"},
"name": "async"
}
},
{
"type": "DoWhileStatement",
"start":6,"end":32,"loc":{"start":{"line":2,"column":0},"end":{"line":5,"column":14}},
"body": {
"type": "BlockStatement",
"start":9,"end":17,"loc":{"start":{"line":2,"column":3},"end":{"line":4,"column":1}},
"body": [
{
"type": "ExpressionStatement",
"start":13,"end":15,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":4}},
"expression": {
"type": "NumericLiteral",
"start":13,"end":15,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":4}},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}
],
"directives": []
},
"test": {
"type": "BooleanLiteral",
"start":25,"end":30,"loc":{"start":{"line":5,"column":7},"end":{"line":5,"column":12}},
"value": false
}
}
],
"directives": []
}
}
@@ -0,0 +1,4 @@
async do {
42
}
while (false);
@@ -0,0 +1,55 @@
{
"type": "File",
"start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":14}},
"program": {
"type": "Program",
"start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":14}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":17,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"expression": {
"type": "DoExpression",
"start":6,"end":17,"loc":{"start":{"line":1,"column":6},"end":{"line":3,"column":1}},
"async": true,
"body": {
"type": "BlockStatement",
"start":9,"end":17,"loc":{"start":{"line":1,"column":9},"end":{"line":3,"column":1}},
"body": [
{
"type": "ExpressionStatement",
"start":13,"end":15,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":4}},
"expression": {
"type": "NumericLiteral",
"start":13,"end":15,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":4}},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}
],
"directives": []
}
}
},
{
"type": "WhileStatement",
"start":18,"end":32,"loc":{"start":{"line":4,"column":0},"end":{"line":4,"column":14}},
"test": {
"type": "BooleanLiteral",
"start":25,"end":30,"loc":{"start":{"line":4,"column":7},"end":{"line":4,"column":12}},
"value": false
},
"body": {
"type": "EmptyStatement",
"start":31,"end":32,"loc":{"start":{"line":4,"column":13},"end":{"line":4,"column":14}}
}
}
],
"directives": []
}
}
@@ -0,0 +1,4 @@
async
do {
42
}
@@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \"while\" (4:1)"
}
@@ -0,0 +1,5 @@
let x = async do {
if (foo()) { f() }
else if (bar()) { g() }
else { h() }
};