Skip to content

Commit

Permalink
Simplify pipeline parsing with using declarations
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Jan 15, 2024
1 parent 218faee commit b092ddd
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 176 deletions.
2 changes: 2 additions & 0 deletions babel.config.js
Expand Up @@ -182,6 +182,8 @@ module.exports = function (api) {
plugins: [
["@babel/transform-object-rest-spread", { useBuiltIns: true }],

"@babel/plugin-proposal-explicit-resource-management",

convertESM ? "@babel/transform-export-namespace-from" : null,

pluginPackageJsonMacro,
Expand Down
5 changes: 4 additions & 1 deletion eslint.config.js
Expand Up @@ -97,7 +97,10 @@ module.exports = [
rules: {
...config.rules,
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{ varsIgnorePattern: "^_" },
],
"no-dupe-class-members": "off",
"@typescript-eslint/no-dupe-class-members": "error",
"no-undef": "off",
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -27,6 +27,7 @@
"@babel/eslint-parser": "workspace:^",
"@babel/eslint-plugin-development": "workspace:^",
"@babel/eslint-plugin-development-internal": "workspace:^",
"@babel/plugin-proposal-explicit-resource-management": "8.0.0-alpha.5",
"@babel/plugin-transform-dynamic-import": "8.0.0-alpha.5",
"@babel/plugin-transform-export-namespace-from": "8.0.0-alpha.5",
"@babel/plugin-transform-modules-commonjs": "8.0.0-alpha.5",
Expand Down
129 changes: 29 additions & 100 deletions packages/babel-parser/src/parser/expression.ts
Expand Up @@ -514,26 +514,27 @@ export default abstract class ExpressionParser extends LValParser {
switch (op) {
case tt.pipeline:
switch (this.getPluginOption("pipelineOperator", "proposal")) {
case "hack":
return this.withTopicBindingContext(() => {
return this.parseHackPipeBody();
});

case "smart":
return this.withTopicBindingContext(() => {
if (this.prodParam.hasYield && this.isContextual(tt._yield)) {
throw this.raise(Errors.PipeBodyIsTighter, this.state.startLoc);
}
return this.parseSmartPipelineBodyInStyle(
this.parseExprOpBaseRightExpr(op, prec),
startLoc,
);
});
case "hack": {
using _ = this.withState("topicReferenceUsage", { used: false });

Check failure on line 518 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Publish to local Verdaccio registry

The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.

Check failure on line 518 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Publish to local Verdaccio registry

The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.

Check failure on line 518 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Build Babel 8 Artifacts

The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.

Check failure on line 518 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Lint

The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.
return this.parseHackPipeBody();
}

case "smart": {
if (this.prodParam.hasYield && this.isContextual(tt._yield)) {
throw this.raise(Errors.PipeBodyIsTighter, this.state.startLoc);
}

case "fsharp":
return this.withSoloAwaitPermittingContext(() => {
return this.parseFSharpPipelineBody(prec);
});
using _ = this.withState("topicReferenceUsage", { used: false });

Check failure on line 527 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Publish to local Verdaccio registry

The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.

Check failure on line 527 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Publish to local Verdaccio registry

The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.

Check failure on line 527 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Build Babel 8 Artifacts

The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.

Check failure on line 527 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Lint

The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.
return this.parseSmartPipelineBodyInStyle(
this.parseExprOpBaseRightExpr(op, prec),
startLoc,
);
}

case "fsharp": {
using _ = this.withState("soloAwait", true);

Check failure on line 535 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Publish to local Verdaccio registry

The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.

Check failure on line 535 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Publish to local Verdaccio registry

The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.

Check failure on line 535 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Build Babel 8 Artifacts

The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.

Check failure on line 535 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Lint

The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.
return this.parseFSharpPipelineBody(prec);
}
}

// Falls through.
Expand Down Expand Up @@ -573,7 +574,7 @@ export default abstract class ExpressionParser extends LValParser {
type: body.type as UnparenthesizedPipeBodyTypes,
});
}
if (!this.topicReferenceWasUsedInCurrentContext()) {
if (!this.state.topicReferenceUsage.used) {
// A Hack pipe body must use the topic reference at least once.
this.raise(Errors.PipeTopicUnused, startLoc);
}
Expand Down Expand Up @@ -1435,7 +1436,7 @@ export default abstract class ExpressionParser extends LValParser {
// as enforced by testTopicReferenceConfiguration.
"TopicReference";

if (!this.topicReferenceIsAllowedInCurrentContext()) {
if (!this.state.topicReferenceUsage) {
this.raise(
// The topic reference is not allowed in the current context:
// it is outside of a pipe body.
Expand All @@ -1446,12 +1447,12 @@ export default abstract class ExpressionParser extends LValParser {
Errors.PipeTopicUnbound,
startLoc,
);
} else {
// Register the topic reference so that its pipe body knows
// that its topic was used at least once.
this.state.topicReferenceUsage.used = true;
}

// Register the topic reference so that its pipe body knows
// that its topic was used at least once.
this.registerTopicReference();

return this.finishNode(node, nodeType);
} else {
// The token does not match the plugin鈥檚 configuration.
Expand Down Expand Up @@ -3027,74 +3028,19 @@ export default abstract class ExpressionParser extends LValParser {
}

// A topic-style smart-mix pipe body must use the topic reference at least once.
if (!this.topicReferenceWasUsedInCurrentContext()) {
if (!this.state.topicReferenceUsage.used) {
this.raise(Errors.PipelineTopicUnused, startLoc);
}
}

// Enable topic references from outer contexts within Hack-style pipe bodies.
// The function modifies the parser's topic-context state to enable or disable
// the use of topic references.
// The function then calls a callback, then resets the parser
// to the old topic-context state that it had before the function was called.

withTopicBindingContext<T>(callback: () => T): T {
const outerContextTopicState = this.state.topicContext;
this.state.topicContext = {
// Enable the use of the primary topic reference.
maxNumOfResolvableTopics: 1,
// Hide the use of any topic references from outer contexts.
maxTopicIndex: null,
};

try {
return callback();
} finally {
this.state.topicContext = outerContextTopicState;
}
}

// This helper method is used only with the deprecated smart-mix pipe proposal.
// Disables topic references from outer contexts within syntax constructs
// such as the bodies of iteration statements.
// The function modifies the parser's topic-context state to enable or disable
// the use of topic references with the smartPipelines plugin. They then run a
// callback, then they reset the parser to the old topic-context state that it
// had before the function was called.

withSmartMixTopicForbiddingContext<T>(callback: () => T): T {
withSmartMixTopicForbiddingContext(): Disposable | undefined {
if (this.hasPlugin(["pipelineOperator", { proposal: "smart" }])) {
// Reset the parser鈥檚 topic context only if the smart-mix pipe proposal is active.
const outerContextTopicState = this.state.topicContext;
this.state.topicContext = {
// Disable the use of the primary topic reference.
maxNumOfResolvableTopics: 0,
// Hide the use of any topic references from outer contexts.
maxTopicIndex: null,
};

try {
return callback();
} finally {
this.state.topicContext = outerContextTopicState;
}
} else {
// If the pipe proposal is "minimal", "fsharp", or "hack",
// or if no pipe proposal is active,
// then the callback result is returned
// without touching any extra parser state.
return callback();
}
}

withSoloAwaitPermittingContext<T>(callback: () => T): T {
const outerContextSoloAwaitState = this.state.soloAwait;
this.state.soloAwait = true;

try {
return callback();
} finally {
this.state.soloAwait = outerContextSoloAwaitState;
return this.withState("topicReferenceUsage", null);

Check failure on line 3043 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Publish to local Verdaccio registry

Property '[Symbol.dispose]' is missing in type '{ [x: symbol]: () => void; }' but required in type 'Disposable'.

Check failure on line 3043 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Publish to local Verdaccio registry

Property '[Symbol.dispose]' is missing in type '{ [x: symbol]: () => void; }' but required in type 'Disposable'.

Check failure on line 3043 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Build Babel 8 Artifacts

Property '[Symbol.dispose]' is missing in type '{ [x: symbol]: () => void; }' but required in type 'Disposable'.

Check failure on line 3043 in packages/babel-parser/src/parser/expression.ts

View workflow job for this annotation

GitHub Actions / Lint

Property '[Symbol.dispose]' is missing in type '{ [x: symbol]: () => void; }' but required in type 'Disposable'.
}
}

Expand Down Expand Up @@ -3126,23 +3072,6 @@ export default abstract class ExpressionParser extends LValParser {
return callback();
}

// Register the use of a topic reference within the current
// topic-binding context.
registerTopicReference(): void {
this.state.topicContext.maxTopicIndex = 0;
}

topicReferenceIsAllowedInCurrentContext(): boolean {
return this.state.topicContext.maxNumOfResolvableTopics >= 1;
}

topicReferenceWasUsedInCurrentContext(): boolean {
return (
this.state.topicContext.maxTopicIndex != null &&
this.state.topicContext.maxTopicIndex >= 0
);
}

parseFSharpPipelineBody(this: Parser, prec: number): N.Expression {
const startLoc = this.state.startLoc;

Expand Down
105 changes: 48 additions & 57 deletions packages/babel-parser/src/parser/statement.ts
Expand Up @@ -874,15 +874,15 @@ export default abstract class StatementParser extends ExpressionParser {
this.next();
this.state.labels.push(loopLabel);

// Parse the loop body's body.
node.body =
{
// For the smartPipelines plugin: Disable topic references from outer
// contexts within the loop body. They are permitted in test expressions,
// outside of the loop body.
this.withSmartMixTopicForbiddingContext(() =>
// Parse the loop body's body.
this.parseStatement(),
);
using _ = this.withSmartMixTopicForbiddingContext();

// Parse the loop body's body.
node.body = this.parseStatement();
}

this.state.labels.pop();

Expand Down Expand Up @@ -1159,14 +1159,12 @@ export default abstract class StatementParser extends ExpressionParser {
this.scope.enter(ScopeFlag.OTHER);
}

// For the smartPipelines plugin: Disable topic references from outer
// contexts within the catch clause's body.
using _ = this.withSmartMixTopicForbiddingContext();

// Parse the catch clause's body.
clause.body =
// For the smartPipelines plugin: Disable topic references from outer
// contexts within the catch clause's body.
this.withSmartMixTopicForbiddingContext(() =>
// Parse the catch clause's body.
this.parseBlock(false, false),
);
clause.body = this.parseBlock(false, false);

this.scope.exit();
node.handler = this.finishNode(clause, "CatchClause");
Expand Down Expand Up @@ -1204,15 +1202,13 @@ export default abstract class StatementParser extends ExpressionParser {
node.test = this.parseHeaderExpression();
this.state.labels.push(loopLabel);

// For the smartPipelines plugin:
// Disable topic references from outer contexts within the loop body.
// They are permitted in test expressions, outside of the loop body.
using _ = this.withSmartMixTopicForbiddingContext();

// Parse the loop body.
node.body =
// For the smartPipelines plugin:
// Disable topic references from outer contexts within the loop body.
// They are permitted in test expressions, outside of the loop body.
this.withSmartMixTopicForbiddingContext(() =>
// Parse loop body.
this.parseStatement(),
);
node.body = this.parseStatement();

this.state.labels.pop();

Expand All @@ -1229,16 +1225,14 @@ export default abstract class StatementParser extends ExpressionParser {
this.next();
node.object = this.parseHeaderExpression();

// For the smartPipelines plugin:
// Disable topic references from outer contexts within the with statement's body.
// They are permitted in function default-parameter expressions, which are
// part of the outer context, outside of the with statement's body.
using _ = this.withSmartMixTopicForbiddingContext();

// Parse the statement body.
node.body =
// For the smartPipelines plugin:
// Disable topic references from outer contexts within the with statement's body.
// They are permitted in function default-parameter expressions, which are
// part of the outer context, outside of the with statement's body.
this.withSmartMixTopicForbiddingContext(() =>
// Parse the statement body.
this.parseStatement(),
);
node.body = this.parseStatement();

return this.finishNode(node, "WithStatement");
}
Expand Down Expand Up @@ -1432,15 +1426,13 @@ export default abstract class StatementParser extends ExpressionParser {
node.update = this.match(tt.parenR) ? null : this.parseExpression();
this.expect(tt.parenR);

// For the smartPipelines plugin: Disable topic references from outer
// contexts within the loop body. They are permitted in test expressions,
// outside of the loop body.
using _ = this.withSmartMixTopicForbiddingContext();

// Parse the loop body.
node.body =
// For the smartPipelines plugin: Disable topic references from outer
// contexts within the loop body. They are permitted in test expressions,
// outside of the loop body.
this.withSmartMixTopicForbiddingContext(() =>
// Parse the loop body.
this.parseStatement(),
);
node.body = this.parseStatement();

this.scope.exit();
this.state.labels.pop();
Expand Down Expand Up @@ -1492,15 +1484,13 @@ export default abstract class StatementParser extends ExpressionParser {
: this.parseMaybeAssignAllowIn();
this.expect(tt.parenR);

// For the smartPipelines plugin:
// Disable topic references from outer contexts within the loop body.
// They are permitted in test expressions, outside of the loop body.
using _ = this.withSmartMixTopicForbiddingContext();

// Parse the loop body.
node.body =
// For the smartPipelines plugin:
// Disable topic references from outer contexts within the loop body.
// They are permitted in test expressions, outside of the loop body.
this.withSmartMixTopicForbiddingContext(() =>
// Parse loop body.
this.parseStatement(),
);
node.body = this.parseStatement();

this.scope.exit();
this.state.labels.pop();
Expand Down Expand Up @@ -1624,13 +1614,12 @@ export default abstract class StatementParser extends ExpressionParser {
// For the smartPipelines plugin: Disable topic references from outer
// contexts within the function body. They are permitted in function
// default-parameter expressions, outside of the function body.
this.withSmartMixTopicForbiddingContext(() => {
// Parse the function body.
this.parseFunctionBodyAndFinish(
node,
isDeclaration ? "FunctionDeclaration" : "FunctionExpression",
);
});
using _ = this.withSmartMixTopicForbiddingContext();

this.parseFunctionBodyAndFinish(
node,
isDeclaration ? "FunctionDeclaration" : "FunctionExpression",
);

this.prodParam.exit();
this.scope.exit();
Expand Down Expand Up @@ -1748,9 +1737,11 @@ export default abstract class StatementParser extends ExpressionParser {

this.expect(tt.braceL);

// For the smartPipelines plugin: Disable topic references from outer
// contexts within the class body.
this.withSmartMixTopicForbiddingContext(() => {
{
// For the smartPipelines plugin: Disable topic references from outer
// contexts within the class body.
using _ = this.withSmartMixTopicForbiddingContext();

// Parse the contents within the braces.
while (!this.match(tt.braceR)) {
if (this.eat(tt.semi)) {
Expand Down Expand Up @@ -1791,7 +1782,7 @@ export default abstract class StatementParser extends ExpressionParser {
this.raise(Errors.DecoratorConstructor, member);
}
}
});
}

this.state.strict = oldStrict;

Expand Down

0 comments on commit b092ddd

Please sign in to comment.