Skip to content

Commit

Permalink
Parse async do expressions (#13043)
Browse files Browse the repository at this point in the history
* parse async do expressions

* add test cases

* update test fixtures

* chore: add syntax-async-do-expressions

* generater support

* fix: do not transform async do expressions

* chore: add asyncDoExpressions to missing plugin helpers

* update ast types

* add more test cases

* throw when asyncDoExpressions is enabled but not doExpressions

* avoid add parentheses for async do expressions

* address review comments

* chore: update parser typings
  • Loading branch information
JLHwung authored and nicolo-ribaudo committed Apr 20, 2021
1 parent b971e00 commit 1989353
Show file tree
Hide file tree
Showing 76 changed files with 901 additions and 27 deletions.
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");
}
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.",
);
// $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() }
};

0 comments on commit 1989353

Please sign in to comment.