Skip to content

Commit efdac2d

Browse files
committed
parser: Support percentage topic with v8intrinsics
1 parent 6fda36e commit efdac2d

32 files changed

Lines changed: 510 additions & 33 deletions

File tree

packages/babel-parser/src/parser/expression.js

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,14 +1268,26 @@ export default class ExpressionParser extends LValParser {
12681268
}
12691269
}
12701270

1271-
// https://github.com/js-choi/proposal-hack-pipes
1271+
// This helper method attempts to parse a topic reference
1272+
// into a new node.
1273+
// See <https://github.com/js-choi/proposal-hack-pipes>.
1274+
//
1275+
// If the `pipelineOperator` plugin is active,
1276+
// and if the given `tokenType` matches the plugin’s configuration,
1277+
// then this method will return a new node.
1278+
//
1279+
// If the `pipelineOperator` plugin is active,
1280+
// but if the given `tokenType` does not match the plugin’s configuration,
1281+
// then this method will throw a `PipeTopicUnconfiguredToken` error.
1282+
//
1283+
// When an appropriate `pipelineOperator` proposal is not active,
1284+
// this method returns `undefined`.
12721285
maybeParseTopicReference(): ?N.Expression {
1273-
const pipeProposal = this.getPluginOption("pipelineOperator", "proposal");
1274-
12751286
// `pipeProposal` is falsy when an input program
12761287
// contains a topic reference on its own,
12771288
// outside of a pipe expression,
12781289
// and without having turned on the pipelineOperator plugin.
1290+
const pipeProposal = this.getPluginOption("pipelineOperator", "proposal");
12791291
if (pipeProposal) {
12801292
// A pipe-operator proposal is active.
12811293

@@ -1287,48 +1299,62 @@ export default class ExpressionParser extends LValParser {
12871299

12881300
const node = this.startNode();
12891301

1290-
// Determine the node type for the topic reference
1291-
// that is appropriate for the active pipe-operator proposal.
1292-
let nodeType;
1293-
if (pipeProposal === "smart") {
1294-
nodeType = "PipelinePrimaryTopicReference";
1295-
} else {
1296-
// The proposal must otherwise be "hack",
1297-
// as enforced by testTopicReferenceConfiguration.
1298-
nodeType = "TopicReference";
1299-
}
1300-
13011302
// Consume the token.
13021303
this.next();
13031304

1304-
// Register the topic reference so that its pipe body knows
1305-
// that its topic was used at least once.
1306-
this.registerTopicReference();
1307-
1308-
if (!this.topicReferenceIsAllowedInCurrentContext()) {
1309-
// The topic reference is not allowed in the current context:
1310-
// it is outside of a pipe body.
1311-
// Raise recoverable errors.
1312-
if (pipeProposal === "smart") {
1313-
this.raise(this.state.start, Errors.PrimaryTopicNotAllowed);
1314-
} else {
1315-
// In this case, `pipeProposal === "hack"` is true.
1316-
this.raise(this.state.start, Errors.PipeTopicUnbound);
1317-
}
1318-
}
1305+
const start = this.state.start;
13191306

1320-
return this.finishNode(node, nodeType);
1307+
return this.finishTopicReferenceNode(node, start, pipeProposal);
13211308
} else {
13221309
// The token does not match the plugin’s configuration.
1310+
const start = this.state.start;
13231311
throw this.raise(
1324-
this.state.start,
1312+
start,
13251313
Errors.PipeTopicUnconfiguredToken,
13261314
tokenType.label,
13271315
);
13281316
}
13291317
}
13301318
}
13311319

1320+
// This helper method attempts to finish the given `node`
1321+
// into a topic-reference node for the given `pipeProposal`.
1322+
// The method assumes that any topic token was consumed before it was called.
1323+
finishTopicReferenceNode(
1324+
node: N.Node,
1325+
start: number,
1326+
pipeProposal: string,
1327+
): ?N.Expression {
1328+
// Determine the node type for the topic reference
1329+
// that is appropriate for the active pipe-operator proposal.
1330+
let nodeType;
1331+
if (pipeProposal === "smart") {
1332+
nodeType = "PipelinePrimaryTopicReference";
1333+
} else {
1334+
// The proposal must otherwise be "hack",
1335+
// as enforced by testTopicReferenceConfiguration.
1336+
nodeType = "TopicReference";
1337+
}
1338+
1339+
if (!this.topicReferenceIsAllowedInCurrentContext()) {
1340+
// The topic reference is not allowed in the current context:
1341+
// it is outside of a pipe body.
1342+
// Raise recoverable errors.
1343+
if (pipeProposal === "smart") {
1344+
this.raise(start, Errors.PrimaryTopicNotAllowed);
1345+
} else {
1346+
// In this case, `pipeProposal === "hack"` is true.
1347+
this.raise(start, Errors.PipeTopicUnbound);
1348+
}
1349+
}
1350+
1351+
// Register the topic reference so that its pipe body knows
1352+
// that its topic was used at least once.
1353+
this.registerTopicReference();
1354+
1355+
return this.finishNode(node, nodeType);
1356+
}
1357+
13321358
// This helper method tests whether the given token type
13331359
// matches the pipelineOperator parser plugin’s configuration.
13341360
// If the active pipe proposal is Hack style,

packages/babel-parser/src/plugins/v8intrinsic.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import type Parser from "../parser";
22
import { types as tt } from "../tokenizer/types";
33
import * as N from "../types";
4+
import { Errors } from "../parser/error";
45

56
export default (superClass: Class<Parser>): Class<Parser> =>
67
class extends superClass {
78
parseV8Intrinsic(): N.Expression {
89
if (this.match(tt.modulo)) {
9-
const v8IntrinsicStart = this.state.start;
10+
const start = this.state.start;
1011
// let the `loc` of Identifier starts from `%`
1112
const node = this.startNode();
1213
this.eat(tt.modulo);
@@ -18,7 +19,39 @@ export default (superClass: Class<Parser>): Class<Parser> =>
1819
return identifier;
1920
}
2021
}
21-
this.unexpected(v8IntrinsicStart);
22+
23+
// In this case, the `%` currently being parsed is not followed by an identifier,
24+
// so it therefore cannot be a V8 intrinsic.
25+
// If the `pipelineOperator` plugin is active,
26+
// then the `%` currently being parsed may instead be a topic reference.
27+
const pipeProposal = this.getPluginOption(
28+
"pipelineOperator",
29+
"proposal",
30+
);
31+
32+
if (pipeProposal) {
33+
// A pipe-operator proposal is active.
34+
const moduloTopicTokenIsActive = this.testTopicReferenceConfiguration(
35+
pipeProposal,
36+
tt.modulo,
37+
);
38+
39+
if (moduloTopicTokenIsActive) {
40+
// The token matches the plugin’s configuration.
41+
// The token is therefore a topic reference.
42+
return this.finishTopicReferenceNode(node, start, pipeProposal);
43+
} else {
44+
// The token does not match the plugin’s configuration.
45+
throw this.raise(
46+
start,
47+
Errors.PipeTopicUnconfiguredToken,
48+
tt.modulo.label,
49+
);
50+
}
51+
} else {
52+
// A pipe-operator proposal is not active.
53+
this.unexpected(start);
54+
}
2255
}
2356
}
2457

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
input |> %GetOptimizationStatus(#);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"plugins": [
3+
["pipelineOperator", { "proposal": "hack", "topicToken": "#" }],
4+
"v8intrinsic"
5+
]
6+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"type": "File",
3+
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}},
4+
"program": {
5+
"type": "Program",
6+
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}},
7+
"sourceType": "script",
8+
"interpreter": null,
9+
"body": [
10+
{
11+
"type": "ExpressionStatement",
12+
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}},
13+
"expression": {
14+
"type": "BinaryExpression",
15+
"start":0,"end":34,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":34}},
16+
"left": {
17+
"type": "Identifier",
18+
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5},"identifierName":"input"},
19+
"name": "input"
20+
},
21+
"operator": "|>",
22+
"right": {
23+
"type": "CallExpression",
24+
"start":9,"end":34,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":34}},
25+
"callee": {
26+
"type": "V8IntrinsicIdentifier",
27+
"start":9,"end":31,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":31},"identifierName":"GetOptimizationStatus"},
28+
"name": "GetOptimizationStatus"
29+
},
30+
"arguments": [
31+
{
32+
"type": "TopicReference",
33+
"start":32,"end":33,"loc":{"start":{"line":1,"column":32},"end":{"line":1,"column":33}}
34+
}
35+
]
36+
}
37+
}
38+
}
39+
],
40+
"directives": []
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
input |> %GetOptimizationStatus(%);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"plugins": [
3+
["pipelineOperator", { "proposal": "hack", "topicToken": "#" }],
4+
"v8intrinsic"
5+
],
6+
"throws": "Invalid topic token %. In order to use % as a topic reference, the pipelineOperator plugin must be configured with { \"proposal\": \"hack\", \"topicToken\": \"%\" }. (1:32)"
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
input |> %GetOptimizationStatus(f);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"plugins": [
3+
["pipelineOperator", { "proposal": "hack", "topicToken": "%" }],
4+
"v8intrinsic"
5+
]
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"type": "File",
3+
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}},
4+
"errors": [
5+
"SyntaxError: Hack-style pipe body does not contain a topic reference; Hack-style pipes must use topic at least once. (1:9)"
6+
],
7+
"program": {
8+
"type": "Program",
9+
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}},
10+
"sourceType": "script",
11+
"interpreter": null,
12+
"body": [
13+
{
14+
"type": "ExpressionStatement",
15+
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}},
16+
"expression": {
17+
"type": "BinaryExpression",
18+
"start":0,"end":34,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":34}},
19+
"left": {
20+
"type": "Identifier",
21+
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5},"identifierName":"input"},
22+
"name": "input"
23+
},
24+
"operator": "|>",
25+
"right": {
26+
"type": "CallExpression",
27+
"start":9,"end":34,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":34}},
28+
"callee": {
29+
"type": "V8IntrinsicIdentifier",
30+
"start":9,"end":31,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":31},"identifierName":"GetOptimizationStatus"},
31+
"name": "GetOptimizationStatus"
32+
},
33+
"arguments": [
34+
{
35+
"type": "Identifier",
36+
"start":32,"end":33,"loc":{"start":{"line":1,"column":32},"end":{"line":1,"column":33},"identifierName":"f"},
37+
"name": "f"
38+
}
39+
]
40+
}
41+
}
42+
}
43+
],
44+
"directives": []
45+
}
46+
}

0 commit comments

Comments
 (0)