Operator overloading for JavaScript. Blog post resource files. Post: http://nixtu.blogspot.com/2011/05/using-jsshaper-to-provide-operator.html
var opOverTest = function() {
var a = new Point(4, 6);
var b = new Point(2, 9);
return {
add: a + b,
sub: a - b,
div: a / b,
mul: a * b,
combo: a + (b - a) * a / b
};
};
var opOverTest = function() {
var a = new Point(4, 6);
var b = new Point(2, 9);
return {
add: ops.add(a, b),
sub: ops.sub(a, b),
div: ops.div(a, b),
mul: ops.mul(a, b),
combo: ops.add(a, ops.div(ops.mul((ops.sub(b, a)), a), b))
};
};
var ops = function() {
var ret = {};
var customTypes = [Point];
var defaults = {
add: function(a, b) {return a + b;},
sub: function(a, b) {return a - b;},
div: function(a, b) {return a / b;},
mul: function(a, b) {return a * b;}
};
var addOp = function(op, func) {
ret[op] = function(a, b) {
for(var i = 0; i < customTypes.length; i++) {
var type = customTypes[i];
if(a instanceof type) {
return a[op](b);
}
}
return func(a, b);
};
};
for(var op in defaults) {
var func = defaults[op];
addOp(op, func);
}
return ret;
}();
"use strict"; "use restrict";
var Shaper = require("shaper.js");
Shaper("op-overloading", function(root) {
var prefix = 'ops.';
var ops = {
'+': 'add',
'-': 'sub',
'/': 'div',
'*': 'mul'
};
// cache expressions here
// Note that "call" may not be cached since it is mutable.
var exprs = function() {
var ret = [];
for(var op in ops) {
var func = ops[op];
ret.push({
from: Shaper.parseExpression('$ ' + op + ' $'),
func: func
});
}
return ret;
}();
return Shaper.traverseTree(root, {
pre: function(node, ref) {
for(var i = 0; i < exprs.length; i++) {
var expr = exprs[i];
if(Shaper.match(expr.from, node)) {
var callExpr = prefix + expr.func + '($, $)';
var call = Shaper.parseExpression(callExpr);
Shaper.replace(call, node.children[0], node.children[1]);
Shaper.cloneComments(call, node);
return ref.set(call);
}
}
}
});
});
var a = new Point(3, 5);
var b = new Point(2, 23);
var result = a.mul(a).sub(b.mul(b)); // a * a - b * b
var isNumber = function() {
// borrowed from RightJS
return typeof(val) === 'number';
};
function Point(x, y) {
this.init(x, y);
}
Point.prototype = {
init: function(x, y) {
this.x = x? x: 0;
this.y = y? y: 0;
},
add: function(other) {
return this._opTemplate(other, function(a, b) {return a + b});
},
sub: function(other) {
return this._opTemplate(other, function(a, b) {return a - b});
},
mul: function(other) {
return this._opTemplate(other, function(a, b) {return a * b});
},
div: function(other) {
return this._opTemplate(other, function(a, b) {return a / b});
},
floor: function() {
return this._opTemplate(null, function(a) {return Math.floor(a)});
},
round: function() {
return this._opTemplate(null, function(a) {return Math.round(a)});
},
_opTemplate: function(other, op) {
if(isNumber(other)) {
return new Point(op(this.x, other), op(this.y, other));
}
if(other == null) {
return new Point(op(this.x), op(this.y));
}
return new Point(op(this.x, other.x), op(this.y, other.y));
},
toDist: function() {
return Math.sqrt(this.toDistSquared());
},
toDistSquared: function() {
return this.x * this.x + this.y * this.y;
},
toAngle: function() {
// returns vec as angle (clockwise) in rad [0, 2*PI] starting from (1, 0)
var ret = Math.atan2(this.y, this.x);
ret += ret < 0? 2 * Math.PI: 0;
ret = Math.min(Math.PI * 2, ret);
return ret - Math.PI / 2;
},
normalize: function() {
return this.div(this.toDist());
},
invert: function() {
return new Point(-this.x, -this.y);
},
toString: function() {
return 'x: ' + this.x + ', y: ' + this.y;
}
};