Skip to content

Commit

Permalink
Revert removal of pipe-operator smart mix
Browse files Browse the repository at this point in the history
For backwards compatibility until next major change.
To maintain simplicity in the code before smart mix is removed,
smart mix was modified to match the new Hack-pipe proposal:
• No early errors for sequences in pipe heads or bodies.
• Function declarations and if/catch/for/with statements do not hide
  outer topics.
  • Loading branch information
js-choi committed Apr 14, 2021
1 parent f58fcf6 commit 11a7a49
Show file tree
Hide file tree
Showing 61 changed files with 1,066 additions and 59 deletions.
20 changes: 20 additions & 0 deletions packages/babel-generator/src/generators/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ export function DecimalLiteral(this: Printer, node: t.DecimalLiteral) {
this.word(node.value + "m");
}

// Hack pipe operator
export function TopicReference(this: Printer) {
const { topicToken } = this.format;
switch (topicToken) {
Expand All @@ -250,3 +251,22 @@ export function TopicReference(this: Printer) {
}
}
}

// Smart-mix pipe operator
export function PipelineTopicExpression(
this: Printer,
node: t.PipelineTopicExpression,
) {
this.print(node.expression, node);
}

export function PipelineBareFunction(
this: Printer,
node: t.PipelineBareFunction,
) {
this.print(node.callee, node);
}

export function PipelinePrimaryTopicReference(this: Printer) {
this.token("#");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2 + 3 |> #.toString(16);
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": [["pipelineOperator", { "proposal": "smart" }]]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2 + 3 |> #.toString(16);
49 changes: 49 additions & 0 deletions packages/babel-parser/ast/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -1393,3 +1393,52 @@ interface ExportAllDeclaration <: ModuleDeclaration {
```

An export batch declaration, e.g., `export * from "mod";`.

### Smart-mix pipelines

They are used by the smart-mix pipe operator to determine
the type of a pipe expression's the body expression.
The Hack and F# pipe operators use simple `BinaryExpression`s.

#### PipelineBody

```js
interface PipelineBody <: NodeBase {
type: "PipelineBody";
}
```

#### PipelineBareFunctionBody

```js
interface PipelineBody <: NodeBase {
type: "PipelineBareFunctionBody";
callee: Expression;
}
```

#### PipelineBareConstructorBody

```js
interface PipelineBareConstructorBody <: NodeBase {
type: "PipelineBareConstructorBody";
callee: Expression;
}
```

#### PipelineBareAwaitedFunctionBody

```js
interface PipelineBareConstructorBody <: NodeBase {
type: "PipelineTopicBody";
expression: Expression;
}
```

#### PipelineTopicBody

```js
interface PipelineBareConstructorBody <: NodeBase {
type: "PipelineBareAwaitedFunctionBody";
callee: Expression;
}
2 changes: 1 addition & 1 deletion packages/babel-parser/src/parser/error-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export const ErrorMessages = Object.freeze({
PipeBodyCannotBeArrow:
'Unexpected arrow "=>" after pipeline body; arrow function acting as pipe body must be parenthesized due to operator precedence.',
PipeTopicRequiresHackPipes:
'Topic reference is used, but the pipelineOperator plugin was not passed a "proposal": "hack" option.',
'Topic reference is used, but the pipelineOperator plugin was not passed a "proposal": "hack" or "smart" option.',
PipeTopicUnbound:
"Topic reference is unbound; it must be inside a Hack-style pipe body.",
PipeTopicUnused:
Expand Down
54 changes: 50 additions & 4 deletions packages/babel-parser/src/parser/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,13 @@ export default class ExpressionParser extends LValParser {
this.checkHackPipeBodyEarlyErrors(startPos);
return bodyExpr;
});

case "smart":
return this.withTopicBindingContext(() => {
const childExpr = this.parseExprOpBaseRightExpr(op, prec);
return this.parseSmartPipelineBodyInStyle(childExpr);
});

case "fsharp":
return this.withSoloAwaitPermittingContext(() => {
return this.parseFSharpPipelineBody(prec);
Expand Down Expand Up @@ -1149,9 +1156,17 @@ export default class ExpressionParser extends LValParser {
if (this.state.inPipeline) {
node = this.startNode();

if (this.getPluginOption("pipelineOperator", "proposal") !== "hack") {
this.raise(node.start, Errors.PipeTopicRequiresHackPipes);
}
const proposal = this.getPluginOption("pipelineOperator", "proposal");
const proposalToNodeType = {
hack: "TopicReference",
smart: "PipelinePrimaryTopicReference",
};
const throwPipeTopicRequiresHackPipesError = () => {
throw this.raise(node.start, Errors.PipeTopicRequiresHackPipes);
};
const nodeType =
proposalToNodeType[proposal] ||
throwPipeTopicRequiresHackPipesError();

this.next();

Expand All @@ -1160,7 +1175,8 @@ export default class ExpressionParser extends LValParser {
}

this.registerTopicReference();
return this.finishNode(node, "TopicReference");

return this.finishNode(node, nodeType);
}

// https://tc39.es/proposal-private-fields-in-in
Expand Down Expand Up @@ -2474,12 +2490,42 @@ export default class ExpressionParser extends LValParser {
if (this.match(tt.arrow)) {
throw this.raise(this.state.start, Errors.PipeBodyCannotBeArrow);
}

// A Hack pipe body must use the topic reference at least once.
else if (!this.topicReferenceWasUsedInCurrentContext()) {
this.raise(startPos, Errors.PipeTopicUnused);
}
}

parseSmartPipelineBodyInStyle(
childExpr: N.Expression,
startPos: number,
startLoc: Position,
): N.PipelineBody {
const bodyNode = this.startNodeAt(startPos, startLoc);
if (this.isSimpleReference(childExpr)) {
bodyNode.callee = childExpr;
return this.finishNode(bodyNode, "PipelineBareFunction");
} else {
this.checkHackPipeBodyEarlyErrors(startPos);
bodyNode.expression = childExpr;
return this.finishNode(bodyNode, "PipelineTopicExpression");
}
}

isSimpleReference(expression: N.Expression): boolean {
switch (expression.type) {
case "MemberExpression":
return (
!expression.computed && this.isSimpleReference(expression.object)
);
case "Identifier":
return true;
default:
return false;
}
}

// 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.
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-parser/src/plugin-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function getPluginOption(
return null;
}

const PIPELINE_PROPOSALS = ["minimal", "fsharp", "hack"];
const PIPELINE_PROPOSALS = ["minimal", "fsharp", "hack", "smart"];
const TOPIC_TOKENS = ["#"];
const RECORD_AND_TUPLE_SYNTAX_TYPES = ["hash", "bar"];

Expand Down
20 changes: 20 additions & 0 deletions packages/babel-parser/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,26 @@ export type TopicReference = NodeBase & {
type: "TopicReference",
};
// Smart-mix pipe operator
export type PipelineBody = NodeBase & {
type: "PipelineBody",
};
export type PipelineBareFunctionBody = NodeBase & {
type: "PipelineBareFunctionBody",
callee: Expression,
};
export type PipelineTopicBody = NodeBase & {
type: "PipelineTopicBody",
expression: Expression,
};
export type PipelinePrimaryTopicReference = NodeBase & {
type: "PipelinePrimaryTopicReference",
};
// Template Literals
export type TemplateLiteral = NodeBase & {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"plugins": [["pipelineOperator", { "proposal": "fsharp" }]]
"plugins": [["pipelineOperator", { "proposal": "fsharp" }]],
"throws": "Topic reference is used, but the pipelineOperator plugin was not passed a \"proposal\": \"hack\" or \"smart\" option. (1:5)"
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
],
"estree"
],
"throws": "\"pipelineOperator\" requires \"proposal\" option whose value must be one of: \"minimal\", \"fsharp\", \"hack\"."
"throws": "\"pipelineOperator\" requires \"proposal\" option whose value must be one of: \"minimal\", \"fsharp\", \"hack\", \"smart\"."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x |> (() => # |> f)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": [["pipelineOperator", { "proposal": "smart" }]]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"type": "File",
"start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}},
"program": {
"type": "Program",
"start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}},
"expression": {
"type": "BinaryExpression",
"start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}},
"left": {
"type": "Identifier",
"start":0,"end":1,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":1},"identifierName":"x"},
"name": "x"
},
"operator": "|>",
"right": {
"type": "PipelineTopicExpression",
"end": 19,
"loc": {
"end": {
"line": 1,
"column": 19
}
},
"expression": {
"type": "ArrowFunctionExpression",
"start":6,"end":18,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":18}},
"extra": {
"parenthesized": true,
"parenStart": 5
},
"id": null,
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BinaryExpression",
"start":12,"end":18,"loc":{"start":{"line":1,"column":12},"end":{"line":1,"column":18}},
"left": {
"type": "PipelinePrimaryTopicReference",
"start":12,"end":13,"loc":{"start":{"line":1,"column":12},"end":{"line":1,"column":13}}
},
"operator": "|>",
"right": {
"type": "PipelineBareFunction",
"end": 18,
"loc": {
"end": {
"line": 1,
"column": 18
}
},
"callee": {
"type": "Identifier",
"start":17,"end":18,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":18},"identifierName":"f"},
"name": "f"
}
}
}
}
}
}
}
],
"directives": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x |> ($ => $ |> f)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": [["pipelineOperator", { "proposal": "smart" }]]
}

0 comments on commit 11a7a49

Please sign in to comment.