expression-calculator
Advanced tools
+185
-188
@@ -120,3 +120,3 @@ (function(root, factory) { | ||
| var RPNError = function(m, token){ | ||
| var RPNError = function(m, token, pos){ | ||
| this.name = 'RPNError'; | ||
@@ -126,2 +126,3 @@ this.message = m; | ||
| this.token = token; | ||
| this.pos = pos; | ||
| } | ||
@@ -267,210 +268,206 @@ var RPNErrorPrototype = RPNError.prototype; | ||
| var Calc = function(){ | ||
| var rpnTokens = undefined; | ||
| this.compile = function(expr, debug) { | ||
| var tokens = lexParser(expr); | ||
| processTokens(tokens); | ||
| rpnTokens = makeRPN(tokens, debug); | ||
| return this; | ||
| this.rpnTokens = undefined; | ||
| }; | ||
| Calc.SyntaxError = SyntaxError; | ||
| Calc.RPNError = RPNError; | ||
| Calc.TOKEN_NUM = NUM; | ||
| Calc.TOKEN_VAR = VAR; | ||
| Calc.TOKEN_OPER = OPER; | ||
| var cp = Calc.prototype; | ||
| cp.compile = function(expr, debug) { | ||
| var tokens = lexParser(expr); | ||
| processTokens(tokens); | ||
| this.rpnTokens = makeRPN(tokens, debug); | ||
| return this; | ||
| } | ||
| cp.getRPN = function(){ | ||
| return this.rpnTokens; | ||
| } | ||
| cp.setRPN = function(tokens){ | ||
| if (tokens.length == 0) { | ||
| throw new RPNError("Empty RPN"); | ||
| } | ||
| this.getRPN = function(){ | ||
| return rpnTokens; | ||
| } | ||
| this.setRPN = function(tokens){ | ||
| rpnTokens = new Array(); | ||
| var vstack = 0; | ||
| for (var i = 0; i < tokens.length; i++) { | ||
| var token = tokens[i]; | ||
| if (token.type == NUM) { | ||
| if (isNaN(token.value)) { | ||
| rpnTokens = undefined; | ||
| throw new RPNError("Invalid Number", token); | ||
| var vstack = 0, rpnTokens = []; | ||
| for (var i = 0; i < tokens.length; i++) { | ||
| var token = tokens[i]; | ||
| if (token.type == NUM) { | ||
| if (isNaN(token.value)) { | ||
| throw new RPNError("Invalid Number", token, i); | ||
| } | ||
| token.value = Number(token.value); | ||
| vstack++; | ||
| } else if (token.type == VAR) { | ||
| if ('string' != typeof(token.value)) { | ||
| throw new RPNError("Invalid Variable", token, i); | ||
| } | ||
| vstack++; | ||
| } else if (token.type == OPER) { | ||
| var operand_data = operand[token.value]; | ||
| if (undefined === operand_data) { | ||
| throw new RPNError("Invalid Operand", token, i); | ||
| } else if (operand_data.binocular) { | ||
| if (vstack <= 1) { | ||
| throw new RPNError("Unexpected Operand", token, i); | ||
| } | ||
| token.value = Number(token.value); | ||
| vstack++; | ||
| } else if (token.type == VAR) { | ||
| if ('string' != typeof(token.value)) { | ||
| rpnTokens = undefined; | ||
| throw new RPNError("Invalid Variable", token); | ||
| } | ||
| vstack++; | ||
| } else if (token.type == OPER) { | ||
| var operand_data = operand[token.value]; | ||
| if (undefined === operand_data) { | ||
| rpnTokens = undefined; | ||
| throw new RPNError("Invalid Operand", token); | ||
| } else if (operand_data.binocular) { | ||
| if (vstack <= 1) { | ||
| rpnTokens = undefined; | ||
| throw new RPNError("Unexpected Operand", token); | ||
| } | ||
| vstack--; | ||
| } else if (vstack == 0) { | ||
| rpnTokens = undefined; | ||
| throw new RPNError("Unexpected Operand", token); | ||
| } | ||
| } else { | ||
| rpnTokens = undefined; | ||
| throw new RPNError("Invalid Token", token); | ||
| vstack--; | ||
| } else if (vstack == 0) { | ||
| throw new RPNError("Unexpected Operand", token, i); | ||
| } | ||
| rpnTokens.push({type:token.type, value:token.value}); | ||
| } else { | ||
| throw new RPNError("Invalid Token", token, i); | ||
| } | ||
| if (vstack != 1) { | ||
| rpnTokens = undefined; | ||
| throw new RPNError("Invalid RPN", token); | ||
| } | ||
| if (rpnTokens.length == 0) { | ||
| rpnTokens = undefined; | ||
| } | ||
| return this; | ||
| rpnTokens.push({type:token.type, value:token.value}); | ||
| } | ||
| this.calc = function(vars) { | ||
| if (undefined === rpnTokens || rpnTokens.length == 0) { | ||
| return false; | ||
| } else if (rpnTokens.length == 1 && rpnTokens[0].type == NUM) { | ||
| return rpnTokens[0].value; | ||
| } | ||
| if (vstack != 1) { | ||
| throw new RPNError("Invalid RPN"); | ||
| } | ||
| this.rpnTokens = rpnTokens; | ||
| return this; | ||
| } | ||
| cp.calc = function(vars) { | ||
| var rpnTokens = this.rpnTokens; | ||
| if (undefined === rpnTokens || rpnTokens.length == 0) { | ||
| return false; | ||
| } else if (rpnTokens.length == 1 && rpnTokens[0].type == NUM) { | ||
| return rpnTokens[0].value; | ||
| } | ||
| var stack = new Array(); | ||
| for (var i = 0; i < rpnTokens.length; i++) { | ||
| var token = rpnTokens[i]; | ||
| if (token.type == NUM) { | ||
| stack.push(token.value); | ||
| } else if (token.type == VAR) { | ||
| if (undefined === vars || isNaN(vars[token.value])) { | ||
| stack.push(0); | ||
| } else { | ||
| stack.push(Number(vars[token.value])); | ||
| } | ||
| } else if (token.type == OPER){ | ||
| //Calculate by opers | ||
| var operand_data = operand[token.value]; | ||
| if (operand_data.binocular) { | ||
| var b = stack.pop(), a = stack.pop(); | ||
| stack.push(operand_data.processor(a,b)); | ||
| } else { | ||
| stack.push(operand_data.processor(stack.pop())); | ||
| } | ||
| var stack = new Array(); | ||
| for (var i = 0; i < rpnTokens.length; i++) { | ||
| var token = rpnTokens[i]; | ||
| if (token.type == NUM) { | ||
| stack.push(token.value); | ||
| } else if (token.type == VAR) { | ||
| if (undefined === vars || isNaN(vars[token.value])) { | ||
| stack.push(0); | ||
| } else { | ||
| stack.push(Number(vars[token.value])); | ||
| } | ||
| } else if (token.type == OPER){ | ||
| //Calculate by opers | ||
| var operand_data = operand[token.value]; | ||
| if (operand_data.binocular) { | ||
| var b = stack.pop(), a = stack.pop(); | ||
| stack.push(operand_data.processor(a,b)); | ||
| } else { | ||
| stack.push(operand_data.processor(stack.pop())); | ||
| } | ||
| } | ||
| return stack[0]; | ||
| } | ||
| this.getSimplifiedExpr = function(){ | ||
| var processNum = function(stack, n){ | ||
| stack.push({value:n, priority:100}); | ||
| } | ||
| var processOper = function(stack, oper){ | ||
| var revAddMinus = function(e){ | ||
| var arr = e.split(''); | ||
| var bracket = 0; | ||
| for (var i = 0; i < arr.length; i++) { | ||
| switch (e[i]) { | ||
| case '+': | ||
| if (0 == bracket) { | ||
| arr[i] = '-'; | ||
| } | ||
| break; | ||
| case '-': | ||
| if (0 == bracket) { | ||
| arr[i] = '+'; | ||
| } | ||
| break; | ||
| case '(': | ||
| bracket++; | ||
| break; | ||
| case ')': | ||
| bracket--; | ||
| break; | ||
| } | ||
| return stack[0]; | ||
| } | ||
| cp.getSimplifiedExpr = function(){ | ||
| var processNum = function(stack, n){ | ||
| stack.push({value:n, priority:100}); | ||
| } | ||
| var processOper = function(stack, oper){ | ||
| var revAddMinus = function(e){ | ||
| var arr = e.split(''); | ||
| var bracket = 0; | ||
| for (var i = 0; i < arr.length; i++) { | ||
| switch (e[i]) { | ||
| case '+': | ||
| if (0 == bracket) { | ||
| arr[i] = '-'; | ||
| } | ||
| break; | ||
| case '-': | ||
| if (0 == bracket) { | ||
| arr[i] = '+'; | ||
| } | ||
| break; | ||
| case '(': | ||
| bracket++; | ||
| break; | ||
| case ')': | ||
| bracket--; | ||
| break; | ||
| } | ||
| return arr.join(''); | ||
| } | ||
| var revMulDiv = function(e){ | ||
| var arr = e.split(''); | ||
| var bracket = 0; | ||
| for (var i = 0; i < arr.length; i++) { | ||
| switch (e[i]) { | ||
| case '*': | ||
| if (0 == bracket) { | ||
| arr[i] = '/'; | ||
| } | ||
| break; | ||
| case '/': | ||
| if (0 == bracket) { | ||
| arr[i] = '*'; | ||
| } | ||
| break; | ||
| case '(': | ||
| bracket++; | ||
| break; | ||
| case ')': | ||
| bracket--; | ||
| break; | ||
| } | ||
| return arr.join(''); | ||
| } | ||
| var revMulDiv = function(e){ | ||
| var arr = e.split(''); | ||
| var bracket = 0; | ||
| for (var i = 0; i < arr.length; i++) { | ||
| switch (e[i]) { | ||
| case '*': | ||
| if (0 == bracket) { | ||
| arr[i] = '/'; | ||
| } | ||
| break; | ||
| case '/': | ||
| if (0 == bracket) { | ||
| arr[i] = '*'; | ||
| } | ||
| break; | ||
| case '(': | ||
| bracket++; | ||
| break; | ||
| case ')': | ||
| bracket--; | ||
| break; | ||
| } | ||
| return arr.join(''); | ||
| } | ||
| var operData = operand[oper]; | ||
| if (operData.binocular) { | ||
| var b = stack.pop(), a = stack.pop(), temp; | ||
| if (operData.exchangeable && String(a.value) > String(b.value)) { | ||
| temp = a; a = b; b = temp; | ||
| } | ||
| if (a.priority < operData.priority) { | ||
| a.value = '('+a.value+')'; | ||
| } | ||
| var value = a.value + oper; | ||
| if (b.priority > operData.priority) { | ||
| value += b.value; | ||
| } else if (b.priority < operData.priority) { | ||
| b.value = '('+b.value+')'; | ||
| value += b.value; | ||
| } else { | ||
| switch(oper) { | ||
| case '-': | ||
| value += revAddMinus(b.value); | ||
| break; | ||
| case '/': | ||
| value += revMulDiv(b.value); | ||
| break; | ||
| default: | ||
| value += b.value; | ||
| break; | ||
| } | ||
| } | ||
| stack.push({ | ||
| value:value, | ||
| priority:operData.priority, | ||
| }); | ||
| return arr.join(''); | ||
| } | ||
| var operData = operand[oper]; | ||
| if (operData.binocular) { | ||
| var b = stack.pop(), a = stack.pop(), temp; | ||
| if (operData.exchangeable && String(a.value) > String(b.value)) { | ||
| temp = a; a = b; b = temp; | ||
| } | ||
| if (a.priority < operData.priority) { | ||
| a.value = '('+a.value+')'; | ||
| } | ||
| var value = a.value + oper; | ||
| if (b.priority > operData.priority) { | ||
| value += b.value; | ||
| } else if (b.priority < operData.priority) { | ||
| b.value = '('+b.value+')'; | ||
| value += b.value; | ||
| } else { | ||
| var node = stack[stack.length-1]; | ||
| if ('NEG' == oper) { | ||
| node.value = (node.priority == 100 ? '-'+node.value : '-('+node.value+')'); | ||
| switch(oper) { | ||
| case '-': | ||
| value += revAddMinus(b.value); | ||
| break; | ||
| case '/': | ||
| value += revMulDiv(b.value); | ||
| break; | ||
| default: | ||
| value += b.value; | ||
| break; | ||
| } | ||
| node.priority = 0; | ||
| } | ||
| }; | ||
| if (undefined === rpnTokens || rpnTokens.length == 0) { | ||
| return ''; | ||
| } | ||
| var stack = new Array(); | ||
| for (var i = 0; i < rpnTokens.length; i++) { | ||
| var token = rpnTokens[i]; | ||
| if (token.type == NUM || token.type == VAR) { | ||
| processNum(stack, token.value) | ||
| } else if (token.type == OPER){ | ||
| processOper(stack, token.value); | ||
| stack.push({ | ||
| value:value, | ||
| priority:operData.priority, | ||
| }); | ||
| } else { | ||
| var node = stack[stack.length-1]; | ||
| if ('NEG' == oper) { | ||
| node.value = (node.priority == 100 ? '-'+node.value : '-('+node.value+')'); | ||
| } | ||
| node.priority = 0; | ||
| } | ||
| return stack[0].value; | ||
| }; | ||
| if (undefined === this.rpnTokens || this.rpnTokens.length == 0) { | ||
| return ''; | ||
| } | ||
| }; | ||
| var cp = Calc; | ||
| cp.SyntaxError = SyntaxError; | ||
| cp.RPNError = RPNError; | ||
| cp.TOKEN_NUM = NUM; | ||
| cp.TOKEN_VAR = VAR; | ||
| cp.TOKEN_OPER = OPER; | ||
| var stack = new Array(); | ||
| for (var i = 0; i < this.rpnTokens.length; i++) { | ||
| var token = this.rpnTokens[i]; | ||
| if (token.type == NUM || token.type == VAR) { | ||
| processNum(stack, token.value) | ||
| } else if (token.type == OPER){ | ||
| processOper(stack, token.value); | ||
| } | ||
| } | ||
| return stack[0].value; | ||
| } | ||
| return Calc; | ||
| })); | ||
+1
-1
@@ -1,1 +0,1 @@ | ||
| !function(e,r){"use strict";"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?module.exports=r():e.Calc=r()}(this,function(){"use strict";var e=[function(e){return!!/^(\-\-)/g.exec(e)&&{type:0}},function(e){var r=/^0x[0-9A-Fa-f]+/g.exec(e);return r?{type:1,value:r[0]}:!!(r=/^[0-9]+(\.[0-9]+)?([Ee][+\-][0-9]+)?/g.exec(e))&&{type:1,value:r[0]}},function(e){var r=/^[A-Za-z_\u0080-\uffff][A-Za-z0-9_\u0080-\uffff]*/g.exec(e);return!!r&&{type:2,value:r[0]}},function(e){var r=/^[+\-\*\/\(\)]/g.exec(e);return!!r&&{type:3,value:r[0]}},function(e){var r=/^\s+/g.exec(e);return!!r&&{type:null,value:r[0]}}],r={"+":{binocular:!0,priority:10,exchangeable:!0,processor:function(e,r){return e+r}},"-":{binocular:!0,priority:10,exchangeable:!1,processor:function(e,r){return e-r}},"*":{binocular:!0,priority:20,exchangeable:!0,processor:function(e,r){return e*r}},"/":{binocular:!0,priority:20,exchangeable:!1,processor:function(e,r){return e/r}},NEG:{binocular:!1,priority:50,processor:function(e){return-e}}},t={START:["N","S","("],N:["B",")","END"],S:["N","("],B:["N","(","S"],"(":["S","N","("],")":[")","B","END"]},n=Error.prototype,a=function(e,r){this.name="SyntaxError",this.message=e,this.stack=(new Error).stack,this.token=r};Object.create(n).constructor=a;var i=function(e,r){this.name="RPNError",this.message=e,this.stack=(new Error).stack,this.token=r};Object.create(n).constructor=i;var u=function(r){for(var t,n=[],i=0;r.length;){for(var u=0;u<e.length;u++)if(!1!==(t=e[u](r))){if(0==t.type)throw new a("Unexpected Character",i);null!==t.type&&(t.pos=i,n.push(t)),i+=t.value.length,r=r.slice(t.value.length);break}if(!t)throw new a("Unexpected Character",i)}return n},o=function(e){return 1==e.type||2==e.type?"N":3==e.type?"("==e.value?"(":")"==e.value?")":r[e.value].binocular?"B":"S":null},l=function(e){for(var r,n="START",i=new Array,u=0;u<e.length;u++){var l=e[u];if(3==l.type&&"-"==l.value&&["N",")"].indexOf(n)<0&&(l.value=e[u].value="NEG"),r=o(l),t[n].indexOf(r)<0)throw new a("Unexpected Token",l);if("("==r)i.push(e[u]);else if(")"==r){if(0==i.length)throw new a("Unmatched Bracket",l);i.pop()}n=r}if(t[n].indexOf("END")<0)throw new a("Unexpected End Of Expression");if(i.length>0)throw new a("Unmatched Bracket",i[i.length-1])},p=function(e,t){for(var n,a=new Array,i=new Array,u=function(e,t){if(t)a.push(e);else{var n=r[e.value];if(n.binocular&&a.length>1&&1==a[a.length-1].type&&1==a[a.length-2].type){var i=a.pop().value,u=a.pop().value;a.push({type:1,value:n.processor(u,i)})}else!n.binocular&&a.length>0&&1==a[a.length-1].type?a.push({type:1,value:n.processor(a.pop().value)}):a.push(e)}},o=0;o<e.length;o++){var l=e[o];if(delete l.pos,1==l.type)l.value=Number(l.value),a.push(l);else if(2==l.type)a.push(l);else if(3==l.type)if(0==i.length||"("==l.value)i.push(l);else if(")"==l.value)for(;(n=i.pop())&&"("!=n.value;)u(n,t);else{for(;i.length>0&&"("!=i[i.length-1].value&&r[i[i.length-1].value].priority>=r[l.value].priority;)u(i.pop(),t);i.push(l)}}for(;n=i.pop();)u(n,t);return a},s=function(){var e=void 0;this.compile=function(r,t){var n=u(r);return l(n),e=p(n,t),this},this.getRPN=function(){return e},this.setRPN=function(t){e=new Array;for(var n=0,a=0;a<t.length;a++){var u=t[a];if(1==u.type){if(isNaN(u.value))throw e=void 0,new i("Invalid Number",u);u.value=Number(u.value),n++}else if(2==u.type){if("string"!=typeof u.value)throw e=void 0,new i("Invalid Variable",u);n++}else{if(3!=u.type)throw e=void 0,new i("Invalid Token",u);var o=r[u.value];if(void 0===o)throw e=void 0,new i("Invalid Operand",u);if(o.binocular){if(n<=1)throw e=void 0,new i("Unexpected Operand",u);n--}else if(0==n)throw e=void 0,new i("Unexpected Operand",u)}e.push({type:u.type,value:u.value})}if(1!=n)throw e=void 0,new i("Invalid RPN",u);return 0==e.length&&(e=void 0),this},this.calc=function(t){if(void 0===e||0==e.length)return!1;if(1==e.length&&1==e[0].type)return e[0].value;for(var n=new Array,a=0;a<e.length;a++){var i=e[a];if(1==i.type)n.push(i.value);else if(2==i.type)void 0===t||isNaN(t[i.value])?n.push(0):n.push(Number(t[i.value]));else if(3==i.type){var u=r[i.value];if(u.binocular){var o=n.pop(),l=n.pop();n.push(u.processor(l,o))}else n.push(u.processor(n.pop()))}}return n[0]},this.getSimplifiedExpr=function(){if(void 0===e||0==e.length)return"";for(var t=new Array,n=0;n<e.length;n++){var a=e[n];1==a.type||2==a.type?function(e,r){e.push({value:r,priority:100})}(t,a.value):3==a.type&&function(e,t){var n=r[t];if(n.binocular){var a,i=e.pop(),u=e.pop();n.exchangeable&&String(u.value)>String(i.value)&&(a=u,u=i,i=a),u.priority<n.priority&&(u.value="("+u.value+")");var o=u.value+t;if(i.priority>n.priority)o+=i.value;else if(i.priority<n.priority)i.value="("+i.value+")",o+=i.value;else switch(t){case"-":o+=function(e){for(var r=e.split(""),t=0,n=0;n<r.length;n++)switch(e[n]){case"+":0==t&&(r[n]="-");break;case"-":0==t&&(r[n]="+");break;case"(":t++;break;case")":t--}return r.join("")}(i.value);break;case"/":o+=function(e){for(var r=e.split(""),t=0,n=0;n<r.length;n++)switch(e[n]){case"*":0==t&&(r[n]="/");break;case"/":0==t&&(r[n]="*");break;case"(":t++;break;case")":t--}return r.join("")}(i.value);break;default:o+=i.value}e.push({value:o,priority:n.priority})}else{var l=e[e.length-1];"NEG"==t&&(l.value=100==l.priority?"-"+l.value:"-("+l.value+")"),l.priority=0}}(t,a.value)}return t[0].value}},v=s;return v.SyntaxError=a,v.RPNError=i,v.TOKEN_NUM=1,v.TOKEN_VAR=2,v.TOKEN_OPER=3,s}); | ||
| !function(e,r){"use strict";"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?module.exports=r():e.Calc=r()}(this,function(){"use strict";var e=[function(e){return!!/^(\-\-)/g.exec(e)&&{type:0}},function(e){var r=/^0x[0-9A-Fa-f]+/g.exec(e);return r?{type:1,value:r[0]}:!!(r=/^[0-9]+(\.[0-9]+)?([Ee][+\-][0-9]+)?/g.exec(e))&&{type:1,value:r[0]}},function(e){var r=/^[A-Za-z_\u0080-\uffff][A-Za-z0-9_\u0080-\uffff]*/g.exec(e);return!!r&&{type:2,value:r[0]}},function(e){var r=/^[+\-\*\/\(\)]/g.exec(e);return!!r&&{type:3,value:r[0]}},function(e){var r=/^\s+/g.exec(e);return!!r&&{type:null,value:r[0]}}],r={"+":{binocular:!0,priority:10,exchangeable:!0,processor:function(e,r){return e+r}},"-":{binocular:!0,priority:10,exchangeable:!1,processor:function(e,r){return e-r}},"*":{binocular:!0,priority:20,exchangeable:!0,processor:function(e,r){return e*r}},"/":{binocular:!0,priority:20,exchangeable:!1,processor:function(e,r){return e/r}},NEG:{binocular:!1,priority:50,processor:function(e){return-e}}},t={START:["N","S","("],N:["B",")","END"],S:["N","("],B:["N","(","S"],"(":["S","N","("],")":[")","B","END"]},n=Error.prototype,a=function(e,r){this.name="SyntaxError",this.message=e,this.stack=(new Error).stack,this.token=r},i=a.prototype;i=Object.create(n),i.constructor=a;var o=function(e,r,t){this.name="RPNError",this.message=e,this.stack=(new Error).stack,this.token=r,this.pos=t},u=o.prototype;u=Object.create(n),u.constructor=o;var p=function(r){for(var t,n=[],i=0;r.length;){for(var o=0;o<e.length;o++)if(!1!==(t=e[o](r))){if(0==t.type)throw new a("Unexpected Character",i);null!==t.type&&(t.pos=i,n.push(t)),i+=t.value.length,r=r.slice(t.value.length);break}if(!t)throw new a("Unexpected Character",i)}return n},l=function(e){return 1==e.type||2==e.type?"N":3==e.type?"("==e.value?"(":")"==e.value?")":r[e.value].binocular?"B":"S":null},s=function(e){for(var r,n="START",i=new Array,o=0;o<e.length;o++){var u=e[o];if(3==u.type&&"-"==u.value&&["N",")"].indexOf(n)<0&&(u.value=e[o].value="NEG"),r=l(u),t[n].indexOf(r)<0)throw new a("Unexpected Token",u);if("("==r)i.push(e[o]);else if(")"==r){if(0==i.length)throw new a("Unmatched Bracket",u);i.pop()}n=r}if(t[n].indexOf("END")<0)throw new a("Unexpected End Of Expression");if(i.length>0)throw new a("Unmatched Bracket",i[i.length-1])},c=function(e,t){for(var n,a=new Array,i=new Array,o=function(e,t){if(t)return void a.push(e);var n=r[e.value];if(n.binocular&&a.length>1&&1==a[a.length-1].type&&1==a[a.length-2].type){var i=a.pop().value,o=a.pop().value;a.push({type:1,value:n.processor(o,i)})}else!n.binocular&&a.length>0&&1==a[a.length-1].type?a.push({type:1,value:n.processor(a.pop().value)}):a.push(e)},u=0;u<e.length;u++){var p=e[u];if(delete p.pos,1==p.type)p.value=Number(p.value),a.push(p);else if(2==p.type)a.push(p);else if(3==p.type)if(0==i.length||"("==p.value)i.push(p);else if(")"==p.value)for(;(n=i.pop())&&"("!=n.value;)o(n,t);else{for(;i.length>0&&"("!=i[i.length-1].value&&r[i[i.length-1].value].priority>=r[p.value].priority;)o(i.pop(),t);i.push(p)}}for(;n=i.pop();)o(n,t);return a},v=function(){this.rpnTokens=void 0};v.SyntaxError=a,v.RPNError=o,v.TOKEN_NUM=1,v.TOKEN_VAR=2,v.TOKEN_OPER=3;var f=v.prototype;return f.compile=function(e,r){var t=p(e);return s(t),this.rpnTokens=c(t,r),this},f.getRPN=function(){return this.rpnTokens},f.setRPN=function(e){if(0==e.length)throw new o("Empty RPN");for(var t=0,n=[],a=0;a<e.length;a++){var i=e[a];if(1==i.type){if(isNaN(i.value))throw new o("Invalid Number",i,a);i.value=Number(i.value),t++}else if(2==i.type){if("string"!=typeof i.value)throw new o("Invalid Variable",i,a);t++}else{if(3!=i.type)throw new o("Invalid Token",i,a);var u=r[i.value];if(void 0===u)throw new o("Invalid Operand",i,a);if(u.binocular){if(t<=1)throw new o("Unexpected Operand",i,a);t--}else if(0==t)throw new o("Unexpected Operand",i,a)}n.push({type:i.type,value:i.value})}if(1!=t)throw new o("Invalid RPN");return this.rpnTokens=n,this},f.calc=function(e){var t=this.rpnTokens;if(void 0===t||0==t.length)return!1;if(1==t.length&&1==t[0].type)return t[0].value;for(var n=new Array,a=0;a<t.length;a++){var i=t[a];if(1==i.type)n.push(i.value);else if(2==i.type)void 0===e||isNaN(e[i.value])?n.push(0):n.push(Number(e[i.value]));else if(3==i.type){var o=r[i.value];if(o.binocular){var u=n.pop(),p=n.pop();n.push(o.processor(p,u))}else n.push(o.processor(n.pop()))}}return n[0]},f.getSimplifiedExpr=function(){if(void 0===this.rpnTokens||0==this.rpnTokens.length)return"";for(var e=new Array,t=0;t<this.rpnTokens.length;t++){var n=this.rpnTokens[t];1==n.type||2==n.type?function(e,r){e.push({value:r,priority:100})}(e,n.value):3==n.type&&function(e,t){var n=r[t];if(n.binocular){var a,i=e.pop(),o=e.pop();n.exchangeable&&String(o.value)>String(i.value)&&(a=o,o=i,i=a),o.priority<n.priority&&(o.value="("+o.value+")");var u=o.value+t;if(i.priority>n.priority)u+=i.value;else if(i.priority<n.priority)i.value="("+i.value+")",u+=i.value;else switch(t){case"-":u+=function(e){for(var r=e.split(""),t=0,n=0;n<r.length;n++)switch(e[n]){case"+":0==t&&(r[n]="-");break;case"-":0==t&&(r[n]="+");break;case"(":t++;break;case")":t--}return r.join("")}(i.value);break;case"/":u+=function(e){for(var r=e.split(""),t=0,n=0;n<r.length;n++)switch(e[n]){case"*":0==t&&(r[n]="/");break;case"/":0==t&&(r[n]="*");break;case"(":t++;break;case")":t--}return r.join("")}(i.value);break;default:u+=i.value}e.push({value:u,priority:n.priority})}else{var p=e[e.length-1];"NEG"==t&&(p.value=100==p.priority?"-"+p.value:"-("+p.value+")"),p.priority=0}}(e,n.value)}return e[0].value},v}); |
+2
-2
| { | ||
| "name": "expression-calculator", | ||
| "version": "0.0.4", | ||
| "version": "0.0.5", | ||
| "description": "Calculate expressions without using `eval()`, using LL(1) syntax analyzer.", | ||
| "main": "", | ||
| "main": "exprcalc.js", | ||
| "scripts": { | ||
@@ -7,0 +7,0 @@ "build": "uglifyjs -c -m -o exprcalc.min.js exprcalc.js", |
+179
-2
@@ -1,2 +0,3 @@ | ||
| var Calc = require('./exprcalc.min'); | ||
| var Calc = require('./exprcalc'); | ||
| var Calc2 = require('./exprcalc.min'); | ||
| var exprGen = require('./exprgen'); | ||
@@ -9,2 +10,7 @@ | ||
| assert.strictEqual((new Calc()).compile('3-((-4))+4').calc(), 11); | ||
| assert.strictEqual((new Calc2()).compile('1+1').calc(), 2); | ||
| assert.strictEqual((new Calc2()).compile('- 3+ - (-3.1-4)*5/-10').calc(), -6.55); | ||
| assert.strictEqual((new Calc2()).compile('3-((-4))').calc(), 7); | ||
| assert.strictEqual((new Calc2()).compile('3-((-4))+4').calc(), 11); | ||
| }); | ||
@@ -17,2 +23,8 @@ QUnit.test("Compile Multiple Exprs", function(assert) { | ||
| assert.strictEqual(calc.compile('3-((-4))+4').calc(), 11); | ||
| calc = new Calc2(); | ||
| assert.strictEqual(calc.compile('1+1').calc(), 2); | ||
| assert.strictEqual(calc.compile('- 3+ - (-3.1-4)*5/-10').calc(), -6.55); | ||
| assert.strictEqual(calc.compile('3-((-4))').calc(), 7); | ||
| assert.strictEqual(calc.compile('3-((-4))+4').calc(), 11); | ||
| }); | ||
@@ -24,2 +36,7 @@ QUnit.test("Zero Division", function(assert) { | ||
| assert.strictEqual(calc.compile('-3/(1-1)').calc(), -Infinity); | ||
| calc = new Calc2(); | ||
| assert.strictEqual(calc.compile('3/0').calc(), Infinity); | ||
| assert.strictEqual(calc.compile('3/(1-1)*2').calc(), Infinity); | ||
| assert.strictEqual(calc.compile('-3/(1-1)').calc(), -Infinity); | ||
| }); | ||
@@ -200,2 +217,156 @@ QUnit.test("RPN Output", function(assert) { | ||
| }); | ||
| QUnit.test("Malformed RPN", function(assert) { | ||
| var calc = new Calc(); | ||
| var rpnGood = [ | ||
| {type:Calc.TOKEN_NUM, value:1}, | ||
| {type:Calc.TOKEN_NUM, value:2}, | ||
| {type:Calc.TOKEN_OPER, value:'+'}, | ||
| ]; | ||
| calc.setRPN(rpnGood); | ||
| assert.throws( | ||
| function(){ | ||
| calc.setRPN([]); | ||
| }, | ||
| function(e){ | ||
| return ( | ||
| (e instanceof Calc.RPNError) | ||
| && e.message === 'Empty RPN' | ||
| && e.token === undefined | ||
| && e.pos === undefined | ||
| ); | ||
| } | ||
| ); | ||
| assert.throws( | ||
| function(){ | ||
| calc.setRPN([{ | ||
| aaa: 111, | ||
| }]); | ||
| }, | ||
| function(e){ | ||
| return ( | ||
| (e instanceof Calc.RPNError) | ||
| && e.message === 'Invalid Token' | ||
| && e.token.aaa === 111 | ||
| && e.pos === 0 | ||
| ); | ||
| } | ||
| ); | ||
| assert.throws( | ||
| function(){ | ||
| calc.setRPN([ | ||
| {type:Calc.TOKEN_NUM, value:5}, | ||
| {type:Calc.TOKEN_NUM, value:6}, | ||
| {type:Calc.TOKEN_OPER, value:'+'}, | ||
| {type:Calc.TOKEN_NUM, value:'aaa'}, | ||
| {type:Calc.TOKEN_OPER, value:'*'}, | ||
| ]); | ||
| }, | ||
| function(e){ | ||
| return ( | ||
| (e instanceof Calc.RPNError) | ||
| && e.message === 'Invalid Number' | ||
| && e.token.type === Calc.TOKEN_NUM | ||
| && e.token.value === 'aaa' | ||
| && e.pos === 3 | ||
| ); | ||
| } | ||
| ); | ||
| assert.throws( | ||
| function(){ | ||
| calc.setRPN([ | ||
| {type:Calc.TOKEN_NUM, value:5}, | ||
| {type:Calc.TOKEN_VAR, value:2333}, | ||
| {type:Calc.TOKEN_OPER, value:'+'}, | ||
| {type:Calc.TOKEN_VAR, value:'b'}, | ||
| {type:Calc.TOKEN_OPER, value:'*'}, | ||
| ]); | ||
| }, | ||
| function(e){ | ||
| return ( | ||
| (e instanceof Calc.RPNError) | ||
| && e.message === 'Invalid Variable' | ||
| && e.token.type === Calc.TOKEN_VAR | ||
| && e.token.value === 2333 | ||
| && e.pos === 1 | ||
| ); | ||
| } | ||
| ); | ||
| assert.throws( | ||
| function(){ | ||
| calc.setRPN([ | ||
| {type:Calc.TOKEN_NUM, value:5}, | ||
| {type:Calc.TOKEN_NUM, value:6}, | ||
| {type:Calc.TOKEN_OPER, value:'$'}, | ||
| {type:Calc.TOKEN_VAR, value:'a'}, | ||
| {type:Calc.TOKEN_OPER, value:'*'}, | ||
| ]); | ||
| }, | ||
| function(e){ | ||
| return ( | ||
| (e instanceof Calc.RPNError) | ||
| && e.message === 'Invalid Operand' | ||
| && e.token.type === Calc.TOKEN_OPER | ||
| && e.token.value === '$' | ||
| && e.pos === 2 | ||
| ); | ||
| } | ||
| ); | ||
| assert.throws( | ||
| function(){ | ||
| calc.setRPN([ | ||
| {type:Calc.TOKEN_NUM, value:5}, | ||
| {type:Calc.TOKEN_NUM, value:6}, | ||
| {type:Calc.TOKEN_OPER, value:'+'}, | ||
| {type:Calc.TOKEN_OPER, value:'*'}, | ||
| ]); | ||
| }, | ||
| function(e){ | ||
| return ( | ||
| (e instanceof Calc.RPNError) | ||
| && e.message === 'Unexpected Operand' | ||
| && e.token.type === Calc.TOKEN_OPER | ||
| && e.token.value === '*' | ||
| && e.pos === 3 | ||
| ); | ||
| } | ||
| ); | ||
| assert.throws( | ||
| function(){ | ||
| calc.setRPN([ | ||
| {type:Calc.TOKEN_OPER, value:'NEG'}, | ||
| ]); | ||
| }, | ||
| function(e){ | ||
| return ( | ||
| (e instanceof Calc.RPNError) | ||
| && e.message === 'Unexpected Operand' | ||
| && e.token.type === Calc.TOKEN_OPER | ||
| && e.token.value === 'NEG' | ||
| && e.pos === 0 | ||
| ); | ||
| } | ||
| ); | ||
| assert.throws( | ||
| function(){ | ||
| calc.setRPN([ | ||
| {type:Calc.TOKEN_NUM, value:5}, | ||
| {type:Calc.TOKEN_NUM, value:6}, | ||
| {type:Calc.TOKEN_OPER, value:'+'}, | ||
| {type:Calc.TOKEN_NUM, value:7}, | ||
| {type:Calc.TOKEN_OPER, value:'*'}, | ||
| {type:Calc.TOKEN_NUM, value:8}, | ||
| ]); | ||
| }, | ||
| function(e){ | ||
| return ( | ||
| (e instanceof Calc.RPNError) | ||
| && e.message === 'Invalid RPN' | ||
| && e.token === undefined | ||
| && e.pos === undefined | ||
| ); | ||
| } | ||
| ); | ||
| assert.deepEqual(calc.getRPN(), rpnGood); | ||
| }); | ||
| QUnit.test("Applying Variables", function(assert) { | ||
@@ -267,6 +438,12 @@ var rpn, calc = new Calc(); | ||
| QUnit.test("Random Expression", function(assert) { | ||
| var calc = new Calc(), count = 1000; | ||
| var calc = new Calc(), calc2 = new Calc2(), calc3 = new Calc(), calc4 = new Calc2(), count = 1000; | ||
| while (count--){ | ||
| var expr = exprGen(3+(Math.random()*40).toFixed(0)); | ||
| assert.strictEqual(calc.compile(expr).calc(), eval(expr), expr); | ||
| expr = exprGen(3+(Math.random()*40).toFixed(0)); | ||
| assert.strictEqual(calc2.compile(expr).calc(), eval(expr), expr); | ||
| expr = exprGen(3+(Math.random()*40).toFixed(0)); | ||
| assert.strictEqual(calc3.compile(expr).calc(), eval(expr), expr); | ||
| expr = exprGen(3+(Math.random()*40).toFixed(0)); | ||
| assert.strictEqual(calc4.compile(expr).calc(), eval(expr), expr); | ||
| } | ||
@@ -273,0 +450,0 @@ }); |
38593
12.66%1016
19.81%6
100%