string-math
Advanced tools
Comparing version 1.0.0 to 1.2.1
{ | ||
"name": "string-math", | ||
"version": "1.0.0", | ||
"version": "1.2.1", | ||
"description": "Evaluates the result of the String arithmetical expression", | ||
@@ -5,0 +5,0 @@ "main": "string-math.js", |
@@ -5,3 +5,3 @@ # Description | ||
* It **does not use** `eval()` | ||
* It uses regular expressions to parse [String] equations | ||
* It uses regular expressions to parse [String] formulas `"2+2"` into [Number] formulas `2+2`. Then it is performed as the common JavaScript arithmetic operation. | ||
@@ -13,6 +13,7 @@ ```javascript | ||
"3.5+5*(-4-(3/(3+1)-12*3-.2*22)-16/4*12-5/(2)+3.5+2.5*(1.5-2*7))-16" //-225.5 | ||
".25e+2*10" //250 | ||
``` | ||
* Any bugs found? Give me to know on [GitHub](https://github.com/devrafalko/string-math) | ||
* Also check out [**`exact-math`**](https://www.npmjs.com/package/exact-math) to compute arithmetical formulas with precise floating point values; *(get rid of `0.30000000000000004` values)* | ||
* If you need to perform arithmetic formulas with the **floating point precision** *(avoid `0.30000000000000004` return values)* or to perform arithmetic formulas with **big numbers**, check out [**`exact-math`**](https://www.npmjs.com/package/exact-math) package. It implements `string-math` features as the `exactMath.formula` method. | ||
@@ -65,3 +66,3 @@ | ||
### `stringMath(expression)` | ||
### `stringMath(expression[,callback])` | ||
##### `expression` **[String]** | ||
@@ -73,2 +74,3 @@ * the arithmetical formula | ||
* `-5`, `-.4`, `-5.55` negative values | ||
* `2e-2`, `.25e+12`, `-3e-10` exponential notation values | ||
* `*` multiplication sign | ||
@@ -80,2 +82,16 @@ * `/` division sign | ||
##### `callback` **[Function]** *(optional)* *(synchronous)* | ||
* by default, if the `callback` argument **is omitted** and the `expression` is of incorrect type or is invalid, the `Error` object is **`thrown`**. If the `callback` is defined, the `Error` object is passed through the `callback` function, rather than being **`thrown`**. | ||
* if the [Function] `callback` argument is defined, it is called with the following arguments: | ||
* [0] `error` | ||
It equals `null`, if the `expression` is of correct type and is valid math formula. | ||
Otherwise it equals `Error` object. | ||
* [1] `result` | ||
It equals `null` if the `expression` is of incorrect type or if the math formula is invalid. | ||
Otherwise it equals [Number] result. | ||
# Return | ||
If the math formula is of correct type and is valid, it returns the [Number] result. Otherwise it returns `null`. | ||
# Tips | ||
@@ -100,5 +116,7 @@ * the arithmetic **order of operations** is respected: | ||
* ` -.1 - -5` | ||
* `2 + 3e-5` | ||
* `.25e+5 * -.25e-5` | ||
* the spaces are **not allowed** between: | ||
* negative sign and value: `-2 - - 2` | ||
* period and digit in decimal fraction: `5 + . 3` | ||
* uneven opening and closing brackets: `5 + (3 * (3 + 4)` | ||
* exponential notation formula: `.2 e-5`, `2e - 5`, `3e +10` |
@@ -1,20 +0,20 @@ | ||
function stringMath(eq){ | ||
if(typeof eq !== 'string') throw TypeError('The [String] argument is expected.'); | ||
const mulDiv = /(-?\d*\.?\d+)\s*([*/])\s*([+-]?\d*\.?\d+)/; | ||
const plusMin = /([+-]?\d*\.?\d+)\s*([+-])\s*([+-]?\d*\.?\d+)/; | ||
function stringMath(eq, callback) { | ||
if (typeof eq !== 'string') return handleCallback(new TypeError('The [String] argument is expected.'), null); | ||
const mulDiv = /([+-]?\d*\.?\d+(?:e[+-]\d+)?)\s*([*/])\s*([+-]?\d*\.?\d+(?:e[+-]\d+)?)/; | ||
const plusMin = /([+-]?\d*\.?\d+(?:e[+-]\d+)?)\s*([+-])\s*([+-]?\d*\.?\d+(?:e[+-]\d+)?)/; | ||
const parentheses = /(\d)?\s*\(([^()]*)\)\s*/; | ||
var current; | ||
while(eq.search(/^\s*([+-]?\d*\.?\d+)\s*$/)===-1){ | ||
while (eq.search(/^\s*([+-]?\d*\.?\d+(?:e[+-]\d+)?)\s*$/) === -1) { | ||
eq = fParentheses(eq); | ||
if(eq===current) throw new SyntaxError('The equation is invalid.'); | ||
if (eq === current) return handleCallback(new SyntaxError('The equation is invalid.'), null); | ||
current = eq; | ||
} | ||
return +eq; | ||
return handleCallback(null, +eq); | ||
function fParentheses(eq){ | ||
while(eq.search(parentheses)!==-1){ | ||
eq = eq.replace(parentheses,function(a,b,c){ | ||
function fParentheses(eq) { | ||
while (eq.search(parentheses) !== -1) { | ||
eq = eq.replace(parentheses, function (a, b, c) { | ||
c = fMulDiv(c); | ||
c = fPlusMin(c); | ||
return typeof b==='string' ? `${b}*${c}`:c; | ||
return typeof b === 'string' ? b + '*' + c : c; | ||
}); | ||
@@ -27,7 +27,8 @@ } | ||
function fMulDiv(eq){ | ||
while(eq.search(mulDiv)!==-1){ | ||
eq = eq.replace(mulDiv,function(a){ | ||
function fMulDiv(eq) { | ||
while (eq.search(mulDiv) !== -1) { | ||
eq = eq.replace(mulDiv, function (a) { | ||
const sides = mulDiv.exec(a); | ||
return sides[2]==='*' ? sides[1] * sides[3]:sides[1]/sides[3]; | ||
const result = sides[2] === '*' ? sides[1] * sides[3] : sides[1] / sides[3]; | ||
return result >= 0 ? '+' + result : result; | ||
}); | ||
@@ -38,8 +39,8 @@ } | ||
function fPlusMin(eq){ | ||
eq = eq.replace(/([+-])([+-])(\d|\.)/g,function(a,b,c,d){return `${b===c ? '+':'-'}${d}`;}); | ||
while(eq.search(plusMin)!==-1){ | ||
eq = eq.replace(plusMin,function(a){ | ||
function fPlusMin(eq) { | ||
eq = eq.replace(/([+-])([+-])(\d|\.)/g, function (a, b, c, d) { return (b === c ? '+' : '-') + d; }); | ||
while (eq.search(plusMin) !== -1) { | ||
eq = eq.replace(plusMin, function (a) { | ||
const sides = plusMin.exec(a); | ||
return sides[2]==='+' ? +sides[1] + +sides[3]:sides[1]-sides[3]; | ||
return sides[2] === '+' ? +sides[1] + +sides[3] : sides[1] - sides[3]; | ||
}); | ||
@@ -50,2 +51,12 @@ } | ||
function handleCallback(errObject, result) { | ||
if (typeof callback !== 'function') { | ||
if (errObject !== null) throw errObject; | ||
} else { | ||
callback(errObject, result); | ||
} | ||
return result; | ||
} | ||
} | ||
@@ -52,0 +63,0 @@ |
@@ -38,2 +38,38 @@ /* global expect */ | ||
describe("When the module is executed with incorrect [non-String] first argument and [Function] second argument",function(){ | ||
beforeEach(function(){ | ||
this.callback = jasmine.createSpy('callback'); | ||
this.types = [ | ||
1, | ||
true, | ||
null, | ||
{}, | ||
[], | ||
/hello/, | ||
()=>{} | ||
]; | ||
this.stringMath = stringMath; | ||
}); | ||
it('it should not throw an error',function(){ | ||
for(var type of this.types){ | ||
expect(this.stringMath.bind(this,type,function(){})).not.toThrowError(); | ||
} | ||
}); | ||
it('it should call this function with error object as the first argument and result value equal to null as the second argument',function(){ | ||
for(var type of this.types){ | ||
expect(this.stringMath.bind(this,type,this.callback)).not.toThrowError(); | ||
expect(this.callback).toHaveBeenCalled(); | ||
expect(this.callback).toHaveBeenCalledWith(jasmine.any(Error), null); | ||
} | ||
}); | ||
it('it should return null',function(){ | ||
for(var type of this.types){ | ||
expect(this.stringMath(type,function(){})).toEqual(null); | ||
} | ||
}); | ||
}); | ||
describe("When the module is executed with correct [String] argument",function(){ | ||
@@ -49,2 +85,24 @@ beforeEach(function(){ | ||
describe("When the module is executed with correct [String] first argument and [Function] second argument",function(){ | ||
beforeEach(function(){ | ||
this.callback = jasmine.createSpy('callback'); | ||
this.stringMath = stringMath; | ||
}); | ||
it('it should not throw an error',function(){ | ||
expect(this.stringMath.bind(this,'2+2',function(){})).not.toThrowError(); | ||
}); | ||
it('it should call this function with null as the first argument and [Number] result value as the second argument',function(){ | ||
expect(this.stringMath.bind(this,'2+2',this.callback)).not.toThrowError(); | ||
expect(this.callback).toHaveBeenCalled(); | ||
expect(this.callback).toHaveBeenCalledWith(null,jasmine.any(Number)); | ||
}); | ||
it('it should return [Number] value',function(){ | ||
expect(this.stringMath('2+2',function(){})).toEqual(jasmine.any(Number)); | ||
}); | ||
}); | ||
describe("When the module is executed with correct [String] argument but incorrect arithmetical formula",function(){ | ||
@@ -61,2 +119,32 @@ beforeEach(function(){ | ||
}); | ||
}); | ||
describe("When the module is executed with correct [String] first argument but of incorrect arithmetical formula and [Function] second argument",function(){ | ||
beforeEach(function(){ | ||
this.callback = jasmine.createSpy('callback'); | ||
this.incorrectList = incorrects; | ||
this.stringMath = stringMath; | ||
}); | ||
it('it should not throw an error',function(){ | ||
for(var incorrect of this.incorrectList){ | ||
expect(this.stringMath.bind(this,incorrect,function(){})).not.toThrowError(); | ||
} | ||
}); | ||
it('it should call this function with error object as the first argument and result value equal to null as the second argument',function(){ | ||
for(var incorrect of this.incorrectList){ | ||
expect(this.stringMath.bind(this,incorrect,this.callback)).not.toThrowError(); | ||
expect(this.callback).toHaveBeenCalled(); | ||
expect(this.callback).toHaveBeenCalledWith(jasmine.any(Error), null); | ||
} | ||
}); | ||
it('it should return null',function(){ | ||
for(var incorrect of this.incorrectList){ | ||
expect(this.stringMath(incorrect,function(){})).toEqual(null); | ||
} | ||
}); | ||
}); |
@@ -22,3 +22,13 @@ module.exports = [ | ||
'3(/5)', | ||
'3-(*6)' | ||
'3-(*6)', | ||
'e5', | ||
'e+5', | ||
'e-5', | ||
'3e+', | ||
'3e-', | ||
'.4e+3.2', | ||
'1.5e-1.25', | ||
'2 e+3', | ||
'2e +3', | ||
'2e + 3' | ||
]; |
@@ -60,2 +60,7 @@ module.exports = [ | ||
{expression:"-2*-2",result:4}, | ||
{expression:"5-1*-1",result:6}, | ||
{expression:"5+-1*-1",result:6}, | ||
{expression:"5 + -1 * -1",result:6}, | ||
{expression:"5--1*-1",result:4}, | ||
{expression:"5 - -1 * -1",result:4}, | ||
{expression:"(-2)*(-2)",result:4}, | ||
@@ -126,3 +131,24 @@ {expression:"-2*2",result:-4}, | ||
{expression:"3.5+5(-4-(3/(3+1)-12*3-.2*22)-16/4*12-5/(2)+3.5+2.5(1.5-2*7))-16",result:-225.5}, | ||
{expression:"12.436 - (2.15-1.6/.1) * (-8.33+(3 -1 *1.11)/ (3/(2.19+ .7*1)/1)+1)-11.5",result:-75.367805} | ||
{expression:"12.436 - (2.15-1.6/.1) * (-8.33+(3 -1 *1.11)/ (3/(2.19+ .7*1)/1)+1)-11.5",result:-75.367805}, | ||
{expression:"1e-2",result:0.01}, | ||
{expression:".1e-2",result:0.001}, | ||
{expression:"1.234e+5",result:123400}, | ||
{expression:"1e-2+1e+2",result:100.01}, | ||
{expression:"1e-2 + 1e+2",result:100.01}, | ||
{expression:".2e-3 / 2e-5",result:10}, | ||
{expression:".1*1e+5*(.25e-5/.5)",result:0.05}, | ||
{expression:"1234567890000*1234567890000",result:1.524157875019052e+24}, | ||
{expression:"1/1000000",result:0.000001}, | ||
{expression:"1/10000000",result:1e-7}, | ||
{expression:"1/100000000",result:1e-8}, | ||
{expression:"1/1000000000",result:1e-9}, | ||
{expression:".25/123456789",result:2.0250000184275003e-9}, | ||
{expression:"123/-.25e-50",result:-4.92e+52}, | ||
{expression:"1.45*0e+10",result:0}, | ||
{expression:"0.055/(-5e-5)",result:-1100}, | ||
{expression:"5e+5*2e-3+5e+2/5e-4",result:1001000}, | ||
{expression:".5e+123 * .25e-123",result:0.125}, | ||
{expression:"(2e+2)",result:200}, | ||
{expression:"123/1000/2000/3000",result:2.05e-8}, | ||
{expression:"(2.5e-2*(.25e+2*(.05e+5*5e-2)))",result:156.25} | ||
]; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
20302
399
116