Skip to content

Commit

Permalink
Implement pipeline operator plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
gilbert committed Dec 11, 2015
1 parent 740604f commit fc1a982
Show file tree
Hide file tree
Showing 16 changed files with 233 additions and 3 deletions.
35 changes: 35 additions & 0 deletions packages/babel-plugin-syntax-pipeline-operator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# babel-plugin-syntax-pipeline-operator

Allow parsing of do expressions.

This comment has been minimized.

Copy link
@hzoo

hzoo Dec 11, 2015

Did you mean to put do expressions here?

This comment has been minimized.

Copy link
@gilbert

gilbert Dec 11, 2015

Author Owner

Thanks, will fix this and the other one.


## Installation

```sh
$ npm install babel-plugin-syntax-pipeline-operator
```

## Usage

### Via `.babelrc` (Recommended)

**.babelrc**

```json
{
"plugins": ["syntax-pipeline-operator"]
}
```

### Via CLI

```sh
$ babel --plugins syntax-pipeline-operator script.js
```

### Via Node API

```javascript
require("babel-core").transform("code", {
plugins: ["syntax-pipeline-operator"]
});
```
17 changes: 17 additions & 0 deletions packages/babel-plugin-syntax-pipeline-operator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "babel-plugin-syntax-pipeline-operator",
"version": "6.3.13",
"description": "Allow parsing of the pipeline operator",
"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-pipeline-operator",
"license": "MIT",
"main": "lib/index.js",
"keywords": [
"babel-plugin"
],
"dependencies": {
"babel-runtime": "^6.0.0"
},
"devDependencies": {
"babel-helper-plugin-test-runner": "^6.3.13"
}
}
7 changes: 7 additions & 0 deletions packages/babel-plugin-syntax-pipeline-operator/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function () {
return {
manipulateOptions(opts, parserOpts) {
parserOpts.plugins.push("pipelineOperator");
}
};
}
35 changes: 35 additions & 0 deletions packages/babel-plugin-transform-pipeline-operator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# babel-plugin-transform-pipeline-operator

Compile pipeline operator `|>` usage to ES5. See [the ES.next proposal](https://github.com/mindeavor/es-pipeline-operator) for details.

## Installation

```sh
$ npm install babel-plugin-transform-pipeline-operator
```

## Usage

### Via `.babelrc` (Recommended)

**.babelrc**

```json
{
"plugins": ["transform-pipeline-operator"]
}
```

### Via CLI

```sh
$ babel --plugins transform-pipeline-operator script.js
```

### Via Node API

```javascript
require("babel-core").transform("code", {
plugins: ["transform-pipeline-operator"]
});
```
18 changes: 18 additions & 0 deletions packages/babel-plugin-transform-pipeline-operator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "babel-plugin-transform-pipeline-operator",
"version": "6.3.13",
"description": "Compile do expressions to ES5",

This comment has been minimized.

Copy link
@hzoo

hzoo Dec 11, 2015

Same here

"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-pipeline-operator",
"license": "MIT",
"main": "lib/index.js",
"keywords": [
"babel-plugin"
],
"dependencies": {
"babel-plugin-syntax-pipeline-operator": "^6.3.13",
"babel-runtime": "^6.0.0"
},
"devDependencies": {
"babel-helper-plugin-test-runner": "^6.3.13"
}
}
71 changes: 71 additions & 0 deletions packages/babel-plugin-transform-pipeline-operator/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
export default function ({ types: t }) {
return {
inherits: require("babel-plugin-syntax-pipeline-operator"),

visitor: {

BinaryExpression(path) {
if (path.node.operator === "|>") {
let right = path.node.right;

if (
right.type === "ArrowFunctionExpression" &&
right.params.length === 1 &&
t.isIdentifier(right.params[0]) &&
t.isExpression(right.body)
) {
//
// Optimize away arrow function!
//
// This step converts:
// let result = arg |> x => x + x;
// To:
// let _x = arg;
// let result = arg |> x => x + x;
//
let paramName = right.params[0].name;
let placeholder = path.scope.generateUidIdentifier(paramName);
path.parentPath.insertBefore(t.variableDeclarator(placeholder, path.node.left))

//
// This step converts:
// let _x = arg;
// let result = arg |> x => x + x;
// To:
// let _x = arg;
// let result = arg |> _x => _x + _x;
//
let rightPath = path.node.right._paths[0]
rightPath.scope.rename(paramName, placeholder.name)

//
// This step converts:
// let _x = arg;
// let result = arg |> _x => _x + _x;
// To:
// let _x = arg;
// let result = _x + _x;
//
path.replaceWith(right.body);
}
else {
//
// Simple invocation.
//
// Converts:
// x |> obj.f;
// To:
// obj.f(x);
//
path.replaceWith({
type: "CallExpression",
callee: path.node.right,
arguments: [ path.node.left ]
});
}
}
}

}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var result = [5,10]
|> _ => _.map(x => x * 2)
|> _ => _.reduce( (a,b) => a + b )
|> sum => sum + 1

assert.equal(result, 31)


var inc = (x) => x + 1;
var double = (x) => x * 2;

var result2 = [4, 9].map( x => x |> inc |> double )

assert.deepEqual(result2, [10, 20])
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
var inc = (x) => x + 1

assert.equal(10 |> inc, 11);
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
var inc = (x) => x + 1;
var double = (x) => x * 2;

assert.equal(10 |> inc |> double, 22);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

var map = (fn) => (array) => array.map(fn);

var result = [10,20] |> map(x => x * 20);

assert.deepEqual(result, [200, 400])
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var array = [10,20,30];

var last = array |> a => a[a.length-1];

assert.equal(last, 30);
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["transform-pipeline-operator"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require("babel-helper-plugin-test-runner")(__dirname);
6 changes: 6 additions & 0 deletions packages/babylon/src/parser/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {

let startPos = this.state.start;
let startLoc = this.state.startLoc;

if (node.operator === "|>") {
// Support syntax such as 10 |> x => x + 1
this.state.potentialArrowAt = startPos;
}

node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, op.rightAssociative ? prec - 1 : prec, noIn);

this.finishNode(node, (op === tt.logicalOR || op === tt.logicalAND) ? "LogicalExpression" : "BinaryExpression");
Expand Down
8 changes: 6 additions & 2 deletions packages/babylon/src/tokenizer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,12 @@ export default class Tokenizer {
return this.finishOp(type, width);
}

readToken_pipe_amp(code) { // '|&'
readToken_pipe_amp(code) { // '|&', '|>'
let next = this.input.charCodeAt(this.state.pos + 1);

if (code === 124 && next === 62 && this.hasPlugin("pipelineOperator")) {
return this.finishOp(tt.pipeline, 2);
}
if (next === code) return this.finishOp(code === 124 ? tt.logicalOR : tt.logicalAND, 2);
if (next === 61) return this.finishOp(tt.assign, 2);
return this.finishOp(code === 124 ? tt.bitwiseOR : tt.bitwiseAND, 1);
Expand Down Expand Up @@ -444,7 +448,7 @@ export default class Tokenizer {
case 37: case 42: // '%*'
return this.readToken_mult_modulo(code);

case 124: case 38: // '|&'
case 124: case 38: // '|&', '|>'
return this.readToken_pipe_amp(code);

case 94: // '^'
Expand Down
3 changes: 2 additions & 1 deletion packages/babylon/src/tokenizer/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class TokenType {
this.isAssign = !!conf.isAssign;
this.prefix = !!conf.prefix;
this.postfix = !!conf.postfix;
this.binop = conf.binop || null;
this.binop = (conf.binop === 0) ? 0 : (conf.binop || null);
this.updateContext = null;
}
}
Expand Down Expand Up @@ -82,6 +82,7 @@ export const types = {
assign: new TokenType("_=", {beforeExpr: true, isAssign: true}),
incDec: new TokenType("++/--", {prefix: true, postfix: true, startsExpr: true}),
prefix: new TokenType("prefix", {beforeExpr: true, prefix: true, startsExpr: true}),
pipeline: binop("|>", 0),

This comment has been minimized.

Copy link
@hzoo

hzoo Dec 11, 2015

Is there a reason you wanted it to be 0? Would doing 12 work so you don't have to change the logic above either?

This comment has been minimized.

Copy link
@gilbert

gilbert Dec 11, 2015

Author Owner

Lowest precedence and highest precedence determine how the operator interacts with other operators. But that is a good question to ask: should the pipeline operator be of high, or low precedence?

I'd like to keep it at 0 for now, but I will create a GitHub issue to discuss the matter.

This comment has been minimized.

Copy link
@hzoo

hzoo Dec 11, 2015

Ah yeah actually I forgot about that too - good to think about.

logicalOR: binop("||", 1),
logicalAND: binop("&&", 2),
bitwiseOR: binop("|", 3),
Expand Down

0 comments on commit fc1a982

Please sign in to comment.