diff --git a/Gruntfile.js b/Gruntfile.js index 4c99f4d..6612e21 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -20,6 +20,25 @@ module.exports = function(grunt) { mochaTest: { files: ['dist/tests/*.js'] }, + browserify: { + dist: { + files: { + 'equation.js': 'dist/index.js' + }, + options: { + browserifyOptions: { + standalone: 'Equation' + } + } + } + }, + uglify: { + dist: { + files: { + 'equation.min.js': 'equation.js' + } + } + }, watch: { scripts: { files: 'lib/*.js', @@ -32,7 +51,10 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-babel'); grunt.loadNpmTasks('grunt-mocha-test'); + grunt.loadNpmTasks('grunt-browserify'); + grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.registerTask('default', [ 'babel']); + grunt.registerTask('default', ['babel']); + grunt.registerTask('build', ['babel', 'browserify', 'uglify']); grunt.registerTask('test', ['babel', 'mochaTest']); }; diff --git a/dist/index.js b/dist/index.js index b8ad384..cb91b24 100644 --- a/dist/index.js +++ b/dist/index.js @@ -26,10 +26,6 @@ var _import = require('./helpers'); var _ = _interopRequireWildcard(_import); -var _polyfills = require('babel/polyfill'); - -var _polyfills2 = _interopRequireWildcard(_polyfills); - var Equation = { /** * Solves the given math expression, following these steps: @@ -189,7 +185,7 @@ var parseGroups = function parseGroups(stack) { // Parantheses become inner arrays which will then be processed first var sub = 0; return stack.reduce(function (a, b) { - if (b.includes('(')) { + if (b.indexOf('(') > -1) { if (b.length > 1) { _.dive(a, sub).push(b.replace('(', ''), []); } else { diff --git a/equation.js b/equation.js new file mode 100644 index 0000000..fe254bd --- /dev/null +++ b/equation.js @@ -0,0 +1,751 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Equation = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o solve('2+5+6') + * + * @param {String} expression + * The expression to create an equation for (containing variables) + * @return {Function} + * The function which replaces variables with values in order + * and solves the expression + */ + equation: function equation(expression) { + var stack = parseExpression(expression); + var variables = []; + + stack.forEach(function (a) { + if (typeof a === 'string' && !_.isNumber(a) && !_operators2['default'][a] && a === a.toLowerCase()) { + // grouped variables like (y) need to have their parantheses removed + variables.push(_.removeSymbols(a)); + } + }); + + return function () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + expression = expression.replace(/[a-z]*/g, function (a) { + var index = variables.indexOf(a); + if (index > -1) { + return args[index] || 0; + } + return a; + }); + + return Equation.solve(expression); + }; + } +}; + +var solveStack = (function (_solveStack) { + function solveStack(_x) { + return _solveStack.apply(this, arguments); + } + + solveStack.toString = function () { + return _solveStack.toString(); + }; + + return solveStack; +})(function (stack) { + // $0(stack); + if (stack.some(Array.isArray)) { + stack = stack.map(function (group) { + if (!Array.isArray(group)) { + return group; + } + return solveStack(group); + }); + + return solveStack(stack); + } else { + return evaluate(stack); + } +}); + +var PRECEDENCES = Object.keys(_operators2['default']).map(function (key) { + return _operators2['default'][key].precedence; +}); + +var MAX_PRECEDENCE = Math.max.apply(Math, _toConsumableArray(PRECEDENCES)); +var MIN_PRECEDENCE = Math.min.apply(Math, _toConsumableArray(PRECEDENCES)); + +/** + * Parses the given expression into an array of separated + * numbers and operators/functions. + * The result is passed to parseGroups + * + * @param {String} expression + * The expression to parse + * @return {Array} + * The parsed array + */ +var parseExpression = function parseExpression(expression) { + var stream = new _ReadStream2['default'](expression), + stack = [], + record = ''; + + // Create an array of separated numbers & operators + while (stream.next()) { + var cur = stream.current(); + if (cur === ' ') { + continue; + } + // it's probably a function with a length more than one + if (!_.isNumber(cur) && !_operators2['default'][cur] && cur !== '.') { + record += cur; + } else if (record.length) { + stack.push(record, cur); + record = ''; + } else if (_.isNumber(stack[stack.length - 1]) && (_.isNumber(cur) || cur === '.')) { + + stack[stack.length - 1] += cur; + } else { + stack.push(cur); + } + } + if (record.length) { + stack.push(record); + } + + return parseGroups(stack); +}; + +/** + * Takes the parsed array from parseExpression and + * groups up expressions in parantheses in deep arrays + * + * Example: 2+(5+4) becomes [2, [5, '+', 4]] + * + * @param {Array} stack + * The parsed expression + * @return {Array} + * Grouped up expression + */ +var parseGroups = function parseGroups(stack) { + // Parantheses become inner arrays which will then be processed first + var sub = 0; + return stack.reduce(function (a, b) { + if (b.indexOf('(') > -1) { + if (b.length > 1) { + _.dive(a, sub).push(b.replace('(', ''), []); + } else { + _.dive(a, sub).push([]); + } + sub++; + } else if (b === ')') { + sub--; + } else { + _.dive(a, sub).push(b); + } + return a; + }, []); +}; + +/** + * Gives information about an operator's format + * including number of left and right arguments + * + * @param {String/Object} operator + * The operator object or operator name (e.g. +, -) + * @return {Object} + * An object including the count of left and right arguments + */ +var formatInfo = function formatInfo(operator) { + var op = typeof operator === 'string' ? _operators2['default'][operator] : operator; + + if (!op) { + return null; + } + + var format = op.format.split('1'), + left = format[0].length, + right = format[1].length; + + return { left: left, right: right }; +}; + +/** + * Groups up operators and their arguments based on their precedence + * in deep arrays, the higher the priority, the deeper the group. + * This simplifies the evaluating process, the only thing to do is to + * evaluate from bottom up, evaluating deep groups first + * + * @param {Array} stack + * The parsed and grouped expression + * @return {Array} + * Grouped expression based on precedences + */ +var sortStack = (function (_sortStack) { + function sortStack(_x2) { + return _sortStack.apply(this, arguments); + } + + sortStack.toString = function () { + return _sortStack.toString(); + }; + + return sortStack; +})(function (stack) { + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = stack.entries()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var _step$value = _slicedToArray(_step.value, 2); + + var index = _step$value[0]; + var item = _step$value[1]; + + if (Array.isArray(item)) { + stack.splice(index, 1, sortStack(item)); + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator['return']) { + _iterator['return'](); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + for (var i = MIN_PRECEDENCE; i <= MAX_PRECEDENCE; i++) { + for (var index = 0; index < stack.length; ++index) { + var item = stack[index]; + var op = _operators2['default'][item]; + + if (!op || op.precedence !== i) { + continue; + } + + var _formatInfo = formatInfo(op); + + var left = _formatInfo.left; + var right = _formatInfo.right; + + var group = stack.splice(index - left, left + right + 1, []); + stack[index - left] = group; + + for (var y = 0; y < i; y++) { + group = [group]; + } + + index -= right; + } + } + + return stack; +}); + +/** + * Evaluates the given math expression. + * The expression is an array with an operator and arguments + * + * Example: evaluate([2, '+', 4]) == 6 + * + * @param {Array} stack + * A single math expression + * @return {Number} + * Result of the expression + */ +var evaluate = function evaluate(stack) { + var _operators$op; + + var op = findOperator(stack); + if (!op) { + return stack[0]; + } + + var _formatInfo2 = formatInfo(op); + + var left = _formatInfo2.left; + + var leftArguments = stack.slice(0, left), + rightArguments = stack.slice(left + 1); + + return (_operators$op = _operators2['default'][op]).fn.apply(_operators$op, _toConsumableArray(leftArguments).concat(_toConsumableArray(rightArguments))); +}; + +/** + * Finds the first operator in an array and returns it + * + * @param {Array} arr + * The array to look for an operator in + * @return {Object} + * The operator object or null if no operator is found + */ +var findOperator = function findOperator(arr) { + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = arr[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var o = _step2.value; + + if (typeof o === 'string') { + return o; + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2['return']) { + _iterator2['return'](); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + return null; +}; + +/** + * Replaces constants in a string with their values + * + * @param {String} expression + * The expression to replace constants in + * @return {String} + * The expression with constants replaced + */ +var replaceConstants = function replaceConstants(expression) { + return expression.replace(/[A-Z]*/g, function (a) { + var c = _constants2['default'][a]; + if (!c) { + return a; + } + return typeof c === 'function' ? c() : c; + }); +}; + +exports['default'] = Equation; +module.exports = exports['default']; +},{"./constats":1,"./helpers":2,"./operators":4,"./readstream":5}],4:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +/* + * Operators and Functions + * fn: function used to evaluate value + * format: the format using which arguments are parsed: + * 0 indicates an argument and 1 indicates the operator + * e.g: factorial is 01, add is 010, like 2!, 2+2 + * precedence: determines which operators should be evaluated first + * the lower the value, the higher the precedence + */ +exports['default'] = { + '^': { + fn: function fn(a, b) { + return Math.pow(a, b); + }, + format: '010', + precedence: 0 + }, + '*': { + fn: function fn(a, b) { + return a * b; + }, + format: '010', + precedence: 1 + }, + '/': { + fn: function fn(a, b) { + return a / b; + }, + format: '010', + precedence: 1 + }, + '%': { + fn: function fn(a, b) { + return a % b; + }, + format: '010', + precedence: 1 + }, + '\\': { + fn: function fn(a, b) { + return Math.floor(a / b); + }, + format: '010', + precedence: 1 + }, + '+': { + fn: function fn(a, b) { + return a + b; + }, + format: '010', + precedence: 2 + }, + '-': { + fn: function fn(a, b) { + return a - b; + }, + format: '010', + precedence: 2 + }, + '!': { + fn: function fn(a) { + var sum = 1; + for (var i = 0; i < a; ++i) { + sum *= i; + } + return sum; + }, + format: '01', + precedence: 2 + }, + log: { + fn: Math.log, + format: '10', + precedence: -1 + }, + ln: { + fn: Math.log, + format: '10', + precedence: -1 + }, + lg: { + fn: function fn(a) { + return Math.log(a) / Math.log(2); + }, + format: '10', + precedence: -1 + }, + sin: { + fn: Math.sin, + format: '10', + precedence: -1 + }, + cos: { + fn: Math.cos, + format: '10', + precedence: -1 + }, + tan: { + fn: Math.tan, + format: '10', + precedence: -1 + }, + cot: { + fn: Math.cot, + format: '10', + precedence: -1 + } +}; +module.exports = exports['default']; +},{}],5:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +exports['default'] = function (string) { + var i = 0, + buffer = []; + return { + next: function next() { + buffer.push(string[i]); + + if (i >= string.length) { + return null; + } + return string[i++]; + }, + current: function current() { + return string[i - 1]; + }, + index: function index() { + return i - 1; + }, + to: function to(n) { + var temp = ''; + var dest = i + n; + for (i = i; i < dest; ++i) { + temp += [string[i]]; + } + return temp; + }, + drain: function drain() { + return buffer.splice(0, buffer.length); + }, + replace: (function (_replace) { + function replace(_x, _x2, _x3) { + return _replace.apply(this, arguments); + } + + replace.toString = function () { + return _replace.toString(); + }; + + return replace; + })(function (start, end, replace) { + var temp = string.split(''); + temp.splice(start, end, replace); + string = temp.join(''); + + i = i - (end - start); + }), + go: function go(n) { + i += n; + }, + all: function all() { + return string; + } + }; +}; + +module.exports = exports['default']; +},{}]},{},[3])(3) +}); \ No newline at end of file diff --git a/equation.min.js b/equation.min.js new file mode 100644 index 0000000..c8108ca --- /dev/null +++ b/equation.min.js @@ -0,0 +1 @@ +!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.Equation=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;gd;++d)c=c[c.length-1];return c};c.dive=g;var h=function(a){function b(b,c){return a.apply(this,arguments)}return b.toString=function(){return a.toString()},b}(function(a,b){var c=void 0===arguments[2]?0:arguments[2];if(2>b)return{arr:a,index:c};var d=a.reduce(function(a,d,e){if(Array.isArray(d)){var f=h(d,b-1,e),g=f.arr,i=f.index,j=a.concat(g);return c=i,j}return a},[]);return{arr:d,index:c}});c.deep=h;var i=function(a){function b(b,c,d){return a.apply(this,arguments)}return b.toString=function(){return a.toString()},b}(function(a,b,c){var d=[];if(!b.some(Array.isArray))return a[b[0]]=c,c;var e=!0,f=!1,g=void 0;try{for(var h,j=b[Symbol.iterator]();!(e=(h=j.next()).done);e=!0){var k=h.value;d.push(i(a,k,c))}}catch(l){f=!0,g=l}finally{try{!e&&j["return"]&&j["return"]()}finally{if(f)throw g}}return d});c.diveTo=i;var j=function(a){function b(b){return a.apply(this,arguments)}return b.toString=function(){return a.toString()},b}(function(a){return Array.isArray(a)&&a.some(Array.isArray)?a.reduce(function(a,b){return a.concat(j(b))},[]):a});c.flatten=j;var k=function(a){return a.replace(/\W/g,"")};c.removeSymbols=k},{}],3:[function(a,b,c){"use strict";var d=function(a){return a&&a.__esModule?a:{"default":a}},e=function(a,b){if(Array.isArray(a))return a;if(Symbol.iterator in Object(a)){var c=[],d=!0,e=!1,f=void 0;try{for(var g,h=a[Symbol.iterator]();!(d=(g=h.next()).done)&&(c.push(g.value),!b||c.length!==b);d=!0);}catch(i){e=!0,f=i}finally{try{!d&&h["return"]&&h["return"]()}finally{if(e)throw f}}return c}throw new TypeError("Invalid attempt to destructure non-iterable instance")},f=function(a){if(Array.isArray(a)){for(var b=0,c=Array(a.length);be;e++)d[e]=arguments[e];return a=a.replace(/[a-z]*/g,function(a){var b=c.indexOf(a);return b>-1?d[b]||0:a}),o.solve(a)}}},p=function(a){function b(b){return a.apply(this,arguments)}return b.toString=function(){return a.toString()},b}(function(a){return a.some(Array.isArray)?(a=a.map(function(a){return Array.isArray(a)?p(a):a}),p(a)):x(a)}),q=Object.keys(j["default"]).map(function(a){return j["default"][a].precedence}),r=Math.max.apply(Math,f(q)),s=Math.min.apply(Math,f(q)),t=function(a){for(var b=new h["default"](a),c=[],d="";b.next();){var e=b.current();" "!==e&&(n.isNumber(e)||j["default"][e]||"."===e?d.length?(c.push(d,e),d=""):n.isNumber(c[c.length-1])&&(n.isNumber(e)||"."===e)?c[c.length-1]+=e:c.push(e):d+=e)}return d.length&&c.push(d),u(c)},u=function(a){var b=0;return a.reduce(function(a,c){return c.indexOf("(")>-1?(c.length>1?n.dive(a,b).push(c.replace("(",""),[]):n.dive(a,b).push([]),b++):")"===c?b--:n.dive(a,b).push(c),a},[])},v=function(a){var b="string"==typeof a?j["default"][a]:a;if(!b)return null;var c=b.format.split("1"),d=c[0].length,e=c[1].length;return{left:d,right:e}},w=function(a){function b(b){return a.apply(this,arguments)}return b.toString=function(){return a.toString()},b}(function(a){var b=!0,c=!1,d=void 0;try{for(var f,g=a.entries()[Symbol.iterator]();!(b=(f=g.next()).done);b=!0){var h=e(f.value,2),i=h[0],k=h[1];Array.isArray(k)&&a.splice(i,1,w(k))}}catch(l){c=!0,d=l}finally{try{!b&&g["return"]&&g["return"]()}finally{if(c)throw d}}for(var m=s;r>=m;m++)for(var i=0;iu;u++)t=[t];i-=q}}return a}),x=function(a){var b,c=y(a);if(!c)return a[0];var d=v(c),e=d.left,g=a.slice(0,e),h=a.slice(e+1);return(b=j["default"][c]).fn.apply(b,f(g).concat(f(h)))},y=function(a){var b=!0,c=!1,d=void 0;try{for(var e,f=a[Symbol.iterator]();!(b=(e=f.next()).done);b=!0){var g=e.value;if("string"==typeof g)return g}}catch(h){c=!0,d=h}finally{try{!b&&f["return"]&&f["return"]()}finally{if(c)throw d}}return null},z=function(a){return a.replace(/[A-Z]*/g,function(a){var b=l["default"][a];return b?"function"==typeof b?b():b:a})};c["default"]=o,b.exports=c["default"]},{"./constats":1,"./helpers":2,"./operators":4,"./readstream":5}],4:[function(a,b,c){"use strict";Object.defineProperty(c,"__esModule",{value:!0}),c["default"]={"^":{fn:function(a,b){return Math.pow(a,b)},format:"010",precedence:0},"*":{fn:function(a,b){return a*b},format:"010",precedence:1},"/":{fn:function(a,b){return a/b},format:"010",precedence:1},"%":{fn:function(a,b){return a%b},format:"010",precedence:1},"\\":{fn:function(a,b){return Math.floor(a/b)},format:"010",precedence:1},"+":{fn:function(a,b){return a+b},format:"010",precedence:2},"-":{fn:function(a,b){return a-b},format:"010",precedence:2},"!":{fn:function(a){for(var b=1,c=0;a>c;++c)b*=c;return b},format:"01",precedence:2},log:{fn:Math.log,format:"10",precedence:-1},ln:{fn:Math.log,format:"10",precedence:-1},lg:{fn:function(a){return Math.log(a)/Math.log(2)},format:"10",precedence:-1},sin:{fn:Math.sin,format:"10",precedence:-1},cos:{fn:Math.cos,format:"10",precedence:-1},tan:{fn:Math.tan,format:"10",precedence:-1},cot:{fn:Math.cot,format:"10",precedence:-1}},b.exports=c["default"]},{}],5:[function(a,b,c){"use strict";Object.defineProperty(c,"__esModule",{value:!0}),c["default"]=function(a){var b=0,c=[];return{next:function(){return c.push(a[b]),b>=a.length?null:a[b++]},current:function(){return a[b-1]},index:function(){return b-1},to:function(c){var d="",e=b+c;for(b=b;e>b;++b)d+=[a[b]];return d},drain:function(){return c.splice(0,c.length)},replace:function(a){function b(b,c,d){return a.apply(this,arguments)}return b.toString=function(){return a.toString()},b}(function(c,d,e){var f=a.split("");f.splice(c,d,e),a=f.join(""),b-=d-c}),go:function(a){b+=a},all:function(){return a}}},b.exports=c["default"]},{}]},{},[3])(3)}); \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index a47413e..da83265 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,7 +2,6 @@ import ReadStream from './readstream'; import operators from './operators'; import constants from './constats'; import * as _ from './helpers'; -import polyfills from 'babel/polyfill'; let Equation = { /** @@ -151,7 +150,7 @@ const parseGroups = stack => { // Parantheses become inner arrays which will then be processed first let sub = 0; return stack.reduce((a, b) => { - if (b.includes('(')) { + if (b.indexOf('(') > -1) { if (b.length > 1) { _.dive(a, sub).push(b.replace('(', ''), []); } else { diff --git a/package.json b/package.json index 4b0333e..2af8c7c 100644 --- a/package.json +++ b/package.json @@ -19,12 +19,16 @@ "author": "Mahdi Dibaiee", "license": "MIT", "devDependencies": { + "browserify": "^9.0.8", "grunt": "^0.4.5", "grunt-babel": "^5.0.0", + "grunt-browserify": "^3.7.0", + "grunt-contrib-uglify": "^0.9.1", "grunt-contrib-watch": "^0.6.1", "grunt-eslint": "^11.0.0", "grunt-mocha-test": "^0.12.7", - "mocha": "^2.2.4" + "mocha": "^2.2.4", + "remapify": "^2.0.1" }, "dependencies": { "babel": "^5.1.11"