Skip to content

Commit

Permalink
parser: Support percentage topic with v8intrinsics
Browse files Browse the repository at this point in the history
  • Loading branch information
js-choi committed Jun 4, 2021
1 parent 6fda36e commit 4e53882
Show file tree
Hide file tree
Showing 32 changed files with 510 additions and 33 deletions.
88 changes: 57 additions & 31 deletions packages/babel-parser/src/parser/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -1268,14 +1268,26 @@ export default class ExpressionParser extends LValParser {
}
}

// https://github.com/js-choi/proposal-hack-pipes
// This helper method attempts to parse a topic reference
// into a new node.
// See <https://github.com/js-choi/proposal-hack-pipes>.
//
// If the `pipelineOperator` plugin is active,
// and if the given `tokenType` matches the plugin鈥檚 configuration,
// then this method will return a new node.
//
// If the `pipelineOperator` plugin is active,
// but if the given `tokenType` does not match the plugin鈥檚 configuration,
// then this method will throw a `PipeTopicUnconfiguredToken` error.
//
// When an appropriate `pipelineOperator` proposal is not active,
// this method returns `undefined`.
maybeParseTopicReference(): ?N.Expression {
const pipeProposal = this.getPluginOption("pipelineOperator", "proposal");

// `pipeProposal` is falsy when an input program
// contains a topic reference on its own,
// outside of a pipe expression,
// and without having turned on the pipelineOperator plugin.
const pipeProposal = this.getPluginOption("pipelineOperator", "proposal");
if (pipeProposal) {
// A pipe-operator proposal is active.

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

const node = this.startNode();

// Determine the node type for the topic reference
// that is appropriate for the active pipe-operator proposal.
let nodeType;
if (pipeProposal === "smart") {
nodeType = "PipelinePrimaryTopicReference";
} else {
// The proposal must otherwise be "hack",
// as enforced by testTopicReferenceConfiguration.
nodeType = "TopicReference";
}

// Consume the token.
this.next();

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

if (!this.topicReferenceIsAllowedInCurrentContext()) {
// The topic reference is not allowed in the current context:
// it is outside of a pipe body.
// Raise recoverable errors.
if (pipeProposal === "smart") {
this.raise(this.state.start, Errors.PrimaryTopicNotAllowed);
} else {
// In this case, `pipeProposal === "hack"` is true.
this.raise(this.state.start, Errors.PipeTopicUnbound);
}
}
const start = this.state.start;

return this.finishNode(node, nodeType);
return this.finishTopicReferenceNode(node, start, pipeProposal);
} else {
// The token does not match the plugin鈥檚 configuration.
const start = this.state.start;
throw this.raise(
this.state.start,
start,
Errors.PipeTopicUnconfiguredToken,
tokenType.label,
);
}
}
}

// This helper method attempts to finish the given `node`
// into a topic-reference node for the given `pipeProposal`.
// The method assumes that any topic token was consumed before it was called.
finishTopicReferenceNode(
node: N.Node,
start: int,
pipeProposal: string,
): ?N.Expression {
// Determine the node type for the topic reference
// that is appropriate for the active pipe-operator proposal.
let nodeType;
if (pipeProposal === "smart") {
nodeType = "PipelinePrimaryTopicReference";
} else {
// The proposal must otherwise be "hack",
// as enforced by testTopicReferenceConfiguration.
nodeType = "TopicReference";
}

if (!this.topicReferenceIsAllowedInCurrentContext()) {
// The topic reference is not allowed in the current context:
// it is outside of a pipe body.
// Raise recoverable errors.
if (pipeProposal === "smart") {
this.raise(start, Errors.PrimaryTopicNotAllowed);
} else {
// In this case, `pipeProposal === "hack"` is true.
this.raise(start, Errors.PipeTopicUnbound);
}
}

// 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);
}

// This helper method tests whether the given token type
// matches the pipelineOperator parser plugin鈥檚 configuration.
// If the active pipe proposal is Hack style,
Expand Down
37 changes: 35 additions & 2 deletions packages/babel-parser/src/plugins/v8intrinsic.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type Parser from "../parser";
import { types as tt } from "../tokenizer/types";
import * as N from "../types";
import { Errors } from "../parser/error";

export default (superClass: Class<Parser>): Class<Parser> =>
class extends superClass {
parseV8Intrinsic(): N.Expression {
if (this.match(tt.modulo)) {
const v8IntrinsicStart = this.state.start;
const start = this.state.start;
// let the `loc` of Identifier starts from `%`
const node = this.startNode();
this.eat(tt.modulo);
Expand All @@ -18,7 +19,39 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return identifier;
}
}
this.unexpected(v8IntrinsicStart);

// In this case, the `%` currently being parsed is not followed by an identifier,
// so it therefore cannot be a V8 intrinsic.
// If the `pipelineOperator` plugin is active,
// then the `%` currently being parsed may instead be a topic reference.
const pipeProposal = this.getPluginOption(
"pipelineOperator",
"proposal",
);

if (pipeProposal) {
// A pipe-operator proposal is active.
const moduloTopicTokenIsActive = this.testTopicReferenceConfiguration(
pipeProposal,
tt.modulo,
);

if (moduloTopicTokenIsActive) {
// The token matches the plugin鈥檚 configuration.
// The token is therefore a topic reference.
return this.finishTopicReferenceNode(node, start, pipeProposal);
} else {
// The token does not match the plugin鈥檚 configuration.
throw this.raise(
start,
Errors.PipeTopicUnconfiguredToken,
tt.modulo.label,
);
}
} else {
// A pipe-operator proposal is not active.
this.unexpected(start);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
input |> %GetOptimizationStatus(#);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"plugins": [
["pipelineOperator", { "proposal": "hack", "topicToken": "#" }],
"v8intrinsic"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"type": "File",
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}},
"program": {
"type": "Program",
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}},
"expression": {
"type": "BinaryExpression",
"start":0,"end":34,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":34}},
"left": {
"type": "Identifier",
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5},"identifierName":"input"},
"name": "input"
},
"operator": "|>",
"right": {
"type": "CallExpression",
"start":9,"end":34,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":34}},
"callee": {
"type": "V8IntrinsicIdentifier",
"start":9,"end":31,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":31},"identifierName":"GetOptimizationStatus"},
"name": "GetOptimizationStatus"
},
"arguments": [
{
"type": "TopicReference",
"start":32,"end":33,"loc":{"start":{"line":1,"column":32},"end":{"line":1,"column":33}}
}
]
}
}
}
],
"directives": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
input |> %GetOptimizationStatus(%);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"plugins": [
["pipelineOperator", { "proposal": "hack", "topicToken": "#" }],
"v8intrinsic"
],
"throws": "Invalid topic token %. In order to use % as a topic reference, the pipelineOperator plugin must be configured with { \"proposal\": \"hack\", \"topicToken\": \"%\" }. (1:32)"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
input |> %GetOptimizationStatus(f);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"plugins": [
["pipelineOperator", { "proposal": "hack", "topicToken": "%" }],
"v8intrinsic"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"type": "File",
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}},
"errors": [
"SyntaxError: Hack-style pipe body does not contain a topic reference; Hack-style pipes must use topic at least once. (1:9)"
],
"program": {
"type": "Program",
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}},
"expression": {
"type": "BinaryExpression",
"start":0,"end":34,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":34}},
"left": {
"type": "Identifier",
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5},"identifierName":"input"},
"name": "input"
},
"operator": "|>",
"right": {
"type": "CallExpression",
"start":9,"end":34,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":34}},
"callee": {
"type": "V8IntrinsicIdentifier",
"start":9,"end":31,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":31},"identifierName":"GetOptimizationStatus"},
"name": "GetOptimizationStatus"
},
"arguments": [
{
"type": "Identifier",
"start":32,"end":33,"loc":{"start":{"line":1,"column":32},"end":{"line":1,"column":33},"identifierName":"f"},
"name": "f"
}
]
}
}
}
],
"directives": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
%GetOptimizationStatus(f);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"plugins": [
["pipelineOperator", { "proposal": "hack", "topicToken": "%" }],
"v8intrinsic"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"type": "File",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}},
"program": {
"type": "Program",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}},
"expression": {
"type": "CallExpression",
"start":0,"end":25,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":25}},
"callee": {
"type": "V8IntrinsicIdentifier",
"start":0,"end":22,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":22},"identifierName":"GetOptimizationStatus"},
"name": "GetOptimizationStatus"
},
"arguments": [
{
"type": "Identifier",
"start":23,"end":24,"loc":{"start":{"line":1,"column":23},"end":{"line":1,"column":24},"identifierName":"f"},
"name": "f"
}
]
}
}
],
"directives": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
input |> %GetOptimizationStatus(#);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"plugins": [
["pipelineOperator", { "proposal": "hack", "topicToken": "%" }],
"v8intrinsic"
],
"throws": "Invalid topic token #. In order to use # as a topic reference, the pipelineOperator plugin must be configured with { \"proposal\": \"hack\", \"topicToken\": \"#\" }. (1:32)"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
input |> %GetOptimizationStatus(%);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"plugins": [
["pipelineOperator", { "proposal": "hack", "topicToken": "%" }],
"v8intrinsic"
]
}

0 comments on commit 4e53882

Please sign in to comment.