Skip to content

Commit

Permalink
Revert "[hack pipes] Loosen right precedence of |>"
Browse files Browse the repository at this point in the history
This reverts commit 3f5779f.
  • Loading branch information
nicolo-ribaudo committed Aug 13, 2021
1 parent 3f5779f commit 467cac3
Show file tree
Hide file tree
Showing 42 changed files with 131 additions and 698 deletions.
2 changes: 0 additions & 2 deletions packages/babel-parser/src/parser/error-message.js
Expand Up @@ -144,8 +144,6 @@ export const ErrorMessages = makeErrorTemplates(
'Invalid topic token %0. In order to use %0 as a topic reference, the pipelineOperator plugin must be configured with { "proposal": "hack", "topicToken": "%0" }.',
PipeTopicUnused:
"Hack-style pipe body does not contain a topic reference; Hack-style pipes must use topic at least once.",
PipeUnparenthesizedYield:
"Hack-style pipe body cannot be an unparenthesized yield expression; please wrap it in parentheses: x |> (yield y) |> z.",

// Messages whose codes start with “Pipeline” or “PrimaryTopic”
// are retained for backwards compatibility
Expand Down
109 changes: 85 additions & 24 deletions packages/babel-parser/src/parser/expression.js
Expand Up @@ -285,6 +285,28 @@ export default class ExpressionParser extends LValParser {
const operator = this.state.value;
node.operator = operator;

const leftIsHackPipeExpression =
left.type === "BinaryExpression" &&
left.operator === "|>" &&
this.getPluginOption("pipelineOperator", "proposal") === "hack";

if (leftIsHackPipeExpression) {
// If the pipelinePlugin is configured to use Hack pipes,
// and if an assignment expression’s LHS invalidly contains `|>`,
// then the user likely meant to parenthesize the assignment expression.
// Throw a human-friendly error
// instead of something like 'Invalid left-hand side'.
// For example, `x = x |> y = #` (assuming `#` is the topic reference)
// groups into `x = (x |> y) = #`,
// and `(x |> y)` is an invalid assignment LHS.
// This is because Hack-style `|>` has tighter precedence than `=>`.
// (Unparenthesized `yield` expressions are handled
// in `parseHackPipeBody`,
// and unparenthesized `=>` expressions are handled
// in `checkHackPipeBodyEarlyErrors`.)
throw this.raise(this.state.start, Errors.PipeBodyIsTighter, operator);
}

if (this.match(tt.eq)) {
node.left = this.toAssignable(left, /* isLHS */ true);
refExpressionErrors.doubleProto = -1; // reset because double __proto__ is valid in assignment expression
Expand Down Expand Up @@ -447,20 +469,16 @@ export default class ExpressionParser extends LValParser {
switch (this.getPluginOption("pipelineOperator", "proposal")) {
case "hack":
return this.withTopicBindingContext(() => {
return this.parseHackPipeBody();
const bodyExpr = this.parseHackPipeBody(op, prec);
this.checkHackPipeBodyEarlyErrors(startPos);
return bodyExpr;
});

case "smart":
return this.withTopicBindingContext(() => {
if (this.prodParam.hasYield && this.isContextual("yield")) {
throw this.raise(
this.state.start,
Errors.PipeBodyIsTighter,
this.state.value,
);
}
const childExpr = this.parseHackPipeBody(op, prec);
return this.parseSmartPipelineBodyInStyle(
this.parseExprOpBaseRightExpr(op, prec),
childExpr,
startPos,
startLoc,
);
Expand All @@ -485,28 +503,54 @@ export default class ExpressionParser extends LValParser {
const startPos = this.state.start;
const startLoc = this.state.startLoc;

let { rightAssociative } = op;
if (
op === tt.pipeline &&
this.getPluginOption("pipelineOperator", "proposal") === "hack"
) {
// Hack-style pipeline operator is right associative.
rightAssociative = true;
}

return this.parseExprOp(
this.parseMaybeUnary(),
startPos,
startLoc,
op.rightAssociative ? prec - 1 : prec,
rightAssociative ? prec - 1 : prec,
);
}

parseHackPipeBody(): N.Expression {
const { start } = this.state;

const body = this.parseMaybeAssign();

if (body.type === "YieldExpression" && !body.extra?.parenthesized) {
this.raise(start, Errors.PipeUnparenthesizedYield);
}
if (!this.topicReferenceWasUsedInCurrentContext()) {
// A Hack pipe body must use the topic reference at least once.
this.raise(start, Errors.PipeTopicUnused);
// Helper function for `parseExprOpRightExpr` for the Hack-pipe operator
// (and the Hack-style smart-mix pipe operator).

parseHackPipeBody(op: TokenType, prec: number): N.Expression {
// If the following expression is invalidly a `yield` expression,
// then throw a human-friendly error.
// A `yield` expression in a generator context (i.e., a [Yield] production)
// starts a YieldExpression.
// Outside of a generator context, any `yield` as a pipe body
// is considered simply an identifier.
// This error is checked here, before actually parsing the body expression,
// because `yield`’s “not allowed as identifier in generator” error
// would otherwise have immediately
// occur before the pipe body is fully parsed.
// (Unparenthesized assignment expressions are handled
// in `parseMaybeAssign`,
// and unparenthesized `=>` expressions are handled
// in `checkHackPipeBodyEarlyErrors`.)
const bodyIsInGeneratorContext = this.prodParam.hasYield;
const bodyIsYieldExpression =
bodyIsInGeneratorContext && this.isContextual("yield");

if (bodyIsYieldExpression) {
throw this.raise(
this.state.start,
Errors.PipeBodyIsTighter,
this.state.value,
);
} else {
return this.parseExprOpBaseRightExpr(op, prec);
}

return body;
}

checkExponentialAfterUnary(node: N.AwaitExpression | N.UnaryExpression) {
Expand Down Expand Up @@ -2664,7 +2708,24 @@ export default class ExpressionParser extends LValParser {
// The `startPos` is the starting position of the pipe body.

checkHackPipeBodyEarlyErrors(startPos: number): void {
if (!this.topicReferenceWasUsedInCurrentContext()) {
// If the following token is invalidly `=>`,
// then throw a human-friendly error
// instead of something like 'Unexpected token, expected ";"'.
// For example, `x => x |> y => #` (assuming `#` is the topic reference)
// groups into `x => (x |> y) => #`,
// and `(x |> y) => #` is an invalid arrow function.
// This is because Hack-style `|>` has tighter precedence than `=>`.
// (Unparenthesized `yield` expressions are handled
// in `parseHackPipeBody`,
// and unparenthesized assignment expressions are handled
// in `parseMaybeAssign`.)
if (this.match(tt.arrow)) {
throw this.raise(
this.state.start,
Errors.PipeBodyIsTighter,
tt.arrow.label,
);
} else if (!this.topicReferenceWasUsedInCurrentContext()) {
// A Hack pipe body must use the topic reference at least once.
this.raise(startPos, Errors.PipeTopicUnused);
}
Expand Down
Expand Up @@ -7,5 +7,6 @@
"topicToken": "#"
}
]
]
],
"throws": "Unexpected => after pipeline body; any => expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence. (1:8)"
}

This file was deleted.

Expand Up @@ -7,5 +7,6 @@
"topicToken": "#"
}
]
]
}
],
"throws": "Unexpected &&= after pipeline body; any &&= expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence. (1:11)"
}

This file was deleted.

Expand Up @@ -7,5 +7,6 @@
"topicToken": "#"
}
]
]
}
],
"throws": "Unexpected = after pipeline body; any = expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence. (1:16)"
}

This file was deleted.

Expand Up @@ -7,5 +7,6 @@
"topicToken": "#"
}
]
]
}
],
"throws": "Unexpected += after pipeline body; any += expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence. (1:11)"
}

This file was deleted.

0 comments on commit 467cac3

Please sign in to comment.