A babel plugin for operator overloading
npm install @jetblack/operator-overloadingA Babel plugin for operator overloading.
There is a trivial template project here.
This was based on an idea
by Benjamin Fox
There are a number of great implementations of operator overloading including a
current proposal. This
proposal is far more sophisticated than this implementation, but I had issues
with my use case. This is a very simple version that worked for me and might
be a good work-around for you while we wait for the proposal to be accepted.
The following code adds two integers and then two points.
The directive at the start is required to enable the transformation.
``javascript
'operator-overloading enabled'
class Point {
constructor(x, y) {
this.x = x
this.y = y
}
Symbol.for('+') {
const x = this.x + other.x
const y = this.y + other.y
return new Point(x, y)
}
}
// Built in operators still work.
const x1 = 2
const x2 = 3
const x3 = x1 + x2
console.log(x3)
// Overridden operators work!
const p1 = new Point(5, 5)
const p2 = new Point(2, 3)
const p3 = p1 + p2
console.log(p3)
``
produces the following output:bash`
5
Point { x: 7, y: 8 }
This is the first babel plugin I have written, so your mileage may vary.
I would appreciate any help!
1. Make a new folder and create a package.json file.
`bash`
~$ mkdir my-app
~$ cd my-app
~/my-app$ npm init -y
2. Install babel and the basic preset (also @babel/cli for easier testing).
`bash`
~/my-app$ npm install --save-dev @babel/core @babel/preset-env @babel/cli"node": "current"
3. Install the operator overload plugin. Note that is specified in targets. The targets specified must support arrow functions.
`bash`
~/my-app$ npm install --save-dev @jetblack/operator-overloading.babelrc
4. Create a file:`json`
{
"presets": [
[
"@babel/preset-env",
{
"targets" : {
"node": "current"
}
}
]
],
"plugins": ["module:@jetblack/operator-overloading"]
}
5. Write some code:
`javascript
'operator-overloading enabled'
class Point {
constructor(x, y) {
this.x = x
this.y = y
}
Symbol.for('+') {
const x = this.x + other.x
const y = this.y + other.y
return new Point(x, y)
}
}
const p1 = new Point(5, 5)
const p2 = new Point(2, 3)
const p3 = p1 + p2
console.log(p3)
`
6. Run it with babel-node:
`bash`
~/my-app$ ./node_modules/.bin/babel-node.cmd index.js
Point { x: 7, y: 8 }
The plugin wraps expressions in arrow functions. The following is produced for x + y.
`javascript
(() => {
'operator-overloading disabled';
return x !== undefined && x !== null && x[Symbol.for("+")]
? xSymbol.for("+")
: x + y;
})()
`
For example the following code:
`javascript`
let x = 1, y = 2
let z = x + y`
gets re-written as:javascript`
let x = 1, y = 2
let z = (() => {
return x !== undefined && x !== null && x[Symbol.for("+")]
? xSymbol.for("+")
: x + y;
})();
This allows the creation of custom overrides such as:
`javascript
class Point {
constructor(x, y) {
this.x = x
this.y = y
}
Symbol.For('+') {
const x = this.x + other.x
const y = this.y + other.y
return new Point(x, y)
}
}
`
As the plugin requires arrow functions the @babel/preset-env preset targets must support them.
Operator overloading is disabled for all files by default.
This can be enabled globally in the .babelrc:`json`
{
"presets": [
[
"@babel/preset-env",
{
"targets" : {
"node": "current"
}
}
]
],
"plugins": [
[
"@jetblack/operator-overloading",
{
"enabled": true
}
]
]
}
It can be enabled or disabled locally by including the 'operator-overloading' directive at the start of a file or within a block.
`javascript
'operator-overloading disabled'
let a = 1
let b = 2
let c = a + b
'operator-overloading enabled'
let x = Point(2, 4)
let y = Point(3, 5)
let z = x + y
`
Using operator overloading transpilation will increase compilation and run time when enabled.
No transpilation will be applied to he following operators.
- typeof===
- !==
- &&
- ||
- instanceof
-
- + addition Symbol.for('+')-
- subtraction Symbol.for('-')
- multiplication Symbol.for('')/
- division Symbol.for('/')%
- remainder Symbol.for('%')
- +: plus [Symbol.for('plus')]()-
- : minus [Symbol.for('minus')]()
increment [Symbol.for('prefix-increment')]() and [Symbol.for('postfix-increment')]()
- -- decrement [Symbol.for('prefix-decrement')]() and [Symbol.for('postfix-decrement')]()$3
Arithmetic assignment reuses the overrides for arithmetic.
-
+= addition assignment Symbol.for('+')
- -= subtraction assignment Symbol.for('-')
- = multiplication assignment Symbol.for('')
- /= division assignment Symbol.for('/')
- %= remainder assignment Symbol.for('%')$3
-
& bitwise and Symbol.for('&')
- | bitwise or Symbol.for('|')
- ~ bitwise not [Symbol.for('~')]()
- ^ bitwise xor Symbol.for('^')
- << bitwise shift left Symbol.for('<<')
- >> bitwise sign propagating shift right Symbol.for('>>')
- >>> bitwise zero padding shift right Symbol.for('>>>')$3
Bitwise assignment reuses the overrides for bitwise.
-
&= bitwise and Symbol.for('&')
- |= bitwise or Symbol.for('|')
- ~= bitwise not [Symbol.for('~')]()
- ^= bitwise xor Symbol.for('^')
- <<= bitwise shift left Symbol.for('<<')
- >>= bitwise sign propagating shift right Symbol.for('>>')
- >>>= bitwise zero padding shift right Symbol.for('>>>')$3
-
delete delete property Symbol.for('delete')
- in has property Symbol.for('in')`