From 9d05bb1675c419c599373257c8ed895035cc1ddd Mon Sep 17 00:00:00 2001 From: Ankit Date: Sun, 22 Oct 2023 19:36:51 +0000 Subject: [PATCH] fix: fix when - comes just after function --- dist/browser/math-expression-evaluator.min.js | 2 +- src/lexer.ts | 816 +++++++++--------- test/index.js | 6 + 3 files changed, 430 insertions(+), 394 deletions(-) diff --git a/dist/browser/math-expression-evaluator.min.js b/dist/browser/math-expression-evaluator.min.js index a3408d8..7efea94 100644 --- a/dist/browser/math-expression-evaluator.min.js +++ b/dist/browser/math-expression-evaluator.min.js @@ -1 +1 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Mexp=t()}(this,(function(){"use strict";function e(){return e=Object.assign?Object.assign.bind():function(e){for(var t=1;ti.length-2?i.length-1:t.length-u;a>0;a--)if(void 0!==i[a])for(o=0;o0&&nh)u.push(t);else{for(;h>=r&&!l||l&&r1)throw new Error("Uncaught Syntax error");return parseFloat(h[0].value.toFixed(15))}var m=function(){function t(){var t;this.toPostfix=c,this.addToken=v,this.lex=y,this.postfixEval=w,this.math=(t=this,{isDegree:!0,acos:function(e){return t.math.isDegree?180/Math.PI*Math.acos(e):Math.acos(e)},add:function(e,t){return e+t},asin:function(e){return t.math.isDegree?180/Math.PI*Math.asin(e):Math.asin(e)},atan:function(e){return t.math.isDegree?180/Math.PI*Math.atan(e):Math.atan(e)},acosh:function(e){return Math.log(e+Math.sqrt(e*e-1))},asinh:function(e){return Math.log(e+Math.sqrt(e*e+1))},atanh:function(e){return Math.log((1+e)/(1-e))},C:function(e,n){var a=1,o=e-n,h=n;hi.length-2?i.length-1:t.length-u;a>0;a--)if(void 0!==i[a])for(o=0;o0&&Eh)u.push(t);else{for(;h>=r&&!l||l&&r1)throw new Error("Uncaught Syntax error");return parseFloat(h[0].value.toFixed(15))}var A=function(){function t(){var t;this.toPostfix=f,this.addToken=E,this.lex=v,this.postfixEval=y,this.math=(t=this,{isDegree:!0,acos:function(e){return t.math.isDegree?180/Math.PI*Math.acos(e):Math.acos(e)},add:function(e,t){return e+t},asin:function(e){return t.math.isDegree?180/Math.PI*Math.asin(e):Math.asin(e)},atan:function(e){return t.math.isDegree?180/Math.PI*Math.atan(e):Math.atan(e)},acosh:function(e){return Math.log(e+Math.sqrt(e*e-1))},asinh:function(e){return Math.log(e+Math.sqrt(e*e+1))},atanh:function(e){return Math.log((1+e)/(1-e))},C:function(e,n){var a=1,o=e-n,h=n;h newAr.length - 2 ? newAr.length - 1 : string.length - i; x > 0; x--) { - if (newAr[x] === undefined) continue - for (y = 0; y < newAr[x].length; y++) { - if (match(string, newAr[x][y], i, x)) { - key = newAr[x][y] - y = newAr[x].length - x = 0 - } - } - } - i += key.length - 1 - if (key === '') { - throw new Error("Can't understand after " + string.slice(i)) - } - nodes.push(mexp.tokens[indexOfToken(key, mexp.tokens)]) - } - return nodes + var nodes = []; + var length = string.length; + var key, x, y; + for (var i = 0; i < length; i++) { + if (i < length - 1 && string[i] === " " && string[i + 1] === " ") { + continue; + } + key = ""; + for ( + x = + string.length - i > newAr.length - 2 + ? newAr.length - 1 + : string.length - i; + x > 0; + x-- + ) { + if (newAr[x] === undefined) continue; + for (y = 0; y < newAr[x].length; y++) { + if (match(string, newAr[x][y], i, x)) { + key = newAr[x][y]; + y = newAr[x].length; + x = 0; + } + } + } + i += key.length - 1; + if (key === "") { + throw new Error("Can't understand after " + string.slice(i)); + } + nodes.push(mexp.tokens[indexOfToken(key, mexp.tokens)]); + } + return nodes; } export const lex = function (this: Mexp, inp: string, tokens: Token[]) { - 'use strict' - var changeSignObj: ParsedToken = { - value: this.math.changeSign, - type: 0, - precedence: 4, - show: '-', - } - var closingParObj: ParsedToken = { - value: ')', - show: ')', - type: 5, - precedence: 0, - } - var openingParObj: ParsedToken = { - value: '(', - type: 4, - precedence: 0, - show: '(', - } + "use strict"; + var changeSignObj: ParsedToken = { + value: this.math.changeSign, + type: tokenTypes.FUNCTION_WITH_ONE_ARG, + precedence: 4, + show: "-", + }; + var closingParObj: ParsedToken = { + value: ")", + show: ")", + type: tokenTypes.CLOSING_PARENTHESIS, + precedence: 0, + }; + var openingParObj: ParsedToken = { + value: "(", + type: tokenTypes.OPENING_PARENTHESIS, + precedence: 0, + show: "(", + }; - var str = [openingParObj] + var str = [openingParObj]; - var ptc = [] // Parenthesis to close at the beginning is after one token - var inpStr = inp - var allowed = type0 - var bracToClose = 0 - var asterick = empty - var prevKey = '' - var i - if (typeof tokens !== 'undefined') { - this.addToken(tokens) - } - var nodes = tokenize(this, inpStr) - for (i = 0; i < nodes.length; i++) { - var node = nodes[i] - if (node.type === 14) { - if ( - i > 0 && - i < nodes.length - 1 && - nodes[i + 1].type === 1 && - (nodes[i - 1].type === 1 || nodes[i - 1].type === 6) - ) { - throw new Error('Unexpected Space') - } - continue - } - var cToken = node.token - var cType = node.type - var cEv = node.value - var cPre = node.precedence - var cShow = node.show - var pre = str[str.length - 1] - var j - for (j = ptc.length; j--; ) { - // loop over ptc - if (ptc[j] === 0) { - if ([0, 2, 3, 4, 5, 9, 10, 11, 12, 13].indexOf(cType) !== -1) { - if (allowed[cType] !== true) { - throw new Error(cToken + ' is not allowed after ' + prevKey) - } - str.push(closingParObj) - allowed = type1 - asterick = type3Asterick - ptc.pop() - } - } else break - } - if (allowed[cType] !== true) { - throw new Error(cToken + ' is not allowed after ' + prevKey) - } - if (asterick[cType] === true) { - cType = 2 - cEv = this.math.mul - cShow = '×' - cPre = 3 - i = i - 1 - } - const obj = { - value: cEv, - type: cType, - precedence: cPre, - show: cShow, - numberOfArguments: node.numberOfArguments, - } - if (cType === 0) { - allowed = type0 - asterick = empty - inc(ptc, 2) - str.push(obj) - if (nodes[i + 1].type !== 4) { - str.push(openingParObj) - ptc.push(2) - } - // bracToClose++ - } else if (cType === 1) { - if (pre.type === 1) { - pre.value += cEv - inc(ptc, 1) - } else { - str.push(obj) - } - allowed = type1 - asterick = type1Asterick - } else if (cType === 2) { - allowed = type0 - asterick = empty - inc(ptc, 2) - str.push(obj) - } else if (cType === 3) { - // constant - str.push(obj) - allowed = type1 - asterick = type3Asterick - } else if (cType === 4) { - inc(ptc, 1) - bracToClose++ - allowed = type0 - asterick = empty - str.push(obj) - } else if (cType === 5) { - if (!bracToClose) { - throw new Error('Closing parenthesis are more than opening one, wait What!!!') - } - bracToClose-- - allowed = type1 - asterick = type3Asterick - str.push(obj) - inc(ptc, 1) - } else if (cType === 6) { - if (pre.hasDec) { - throw new Error('Two decimals are not allowed in one number') - } - if (pre.type !== 1) { - pre = { - show: '0', - value: 0, - type: 1, - precedence: 0, - } // pre needs to be changed as it will the last value now to be safe in later code - str.push(pre) - // inc(ptc, 1) - } - allowed = type6 - inc(ptc, 1) - asterick = empty - pre.value += cEv - pre.hasDec = true - } else if (cType === 7) { - allowed = type1 - asterick = type3Asterick - inc(ptc, 1) - str.push(obj) - } - if (cType === 8) { - allowed = type0 - asterick = empty - // @ts-ignore - inc(ptc, node.numberOfArguments + 2) - str.push(obj) - if (nodes[i + 1].type !== 4) { - str.push(openingParObj) - // @ts-ignore - ptc.push(node.numberOfArguments + 2) - } - } else if (cType === 9) { - if (pre.type === 9) { - if (pre.value === this.math.add) { - pre.value = cEv - pre.show = cShow - inc(ptc, 1) - } else if (pre.value === this.math.sub && cShow === '-') { - pre.value = this.math.add - pre.show = '+' - inc(ptc, 1) - } - } else if (pre.type !== 5 && pre.type !== 7 && pre.type !== 1 && pre.type !== 3 && pre.type !== 13) { - // changesign only when negative is found - if (cToken === '-') { - // do nothing for + token - // don't add with the above if statement as that will run the else statement of parent if on Ctoken + - allowed = type0 - asterick = empty - inc(ptc, 2).push(2) - str.push(changeSignObj) - str.push(openingParObj) - } - } else { - str.push(obj) - inc(ptc, 2) - } - allowed = type0 - asterick = empty - } else if (cType === 10) { - allowed = type0 - asterick = empty - inc(ptc, 2) - str.push(obj) - } else if (cType === 11) { - allowed = type0 - asterick = empty - str.push(obj) - } else if (cType === 12) { - allowed = type0 - asterick = empty - inc(ptc, 6) - str.push(obj) - if (nodes[i + 1].type !== 4) { - str.push(openingParObj) - ptc.push(6) - } - } else if (cType === 13) { - allowed = type1 - asterick = type3Asterick - str.push(obj) - } + var ptc = []; // Parenthesis to close at the beginning is after one token + var inpStr = inp; + var allowed = type0; + var bracToClose = 0; + var asterick = empty; + var prevKey = ""; + var i; + if (typeof tokens !== "undefined") { + this.addToken(tokens); + } + var nodes = tokenize(this, inpStr); + for (i = 0; i < nodes.length; i++) { + var node = nodes[i]; + if (node.type === 14) { + if ( + i > 0 && + i < nodes.length - 1 && + nodes[i + 1].type === 1 && + (nodes[i - 1].type === 1 || nodes[i - 1].type === 6) + ) { + throw new Error("Unexpected Space"); + } + continue; + } + var cToken = node.token; + var cType = node.type; + var cEv = node.value; + var cPre = node.precedence; + var cShow = node.show; + var pre = str[str.length - 1]; + var j; + for (j = ptc.length; j--; ) { + // loop over ptc + if (ptc[j] === 0) { + if ( + [ + tokenTypes.FUNCTION_WITH_ONE_ARG, + tokenTypes.BINARY_OPERATOR_HIGH_PRECENDENCE, + tokenTypes.CONSTANT, + tokenTypes.OPENING_PARENTHESIS, + tokenTypes.CLOSING_PARENTHESIS, + tokenTypes.BINARY_OPERATOR_LOW_PRECENDENCE, + tokenTypes.BINARY_OPERATOR_PERMUTATION, + tokenTypes.COMMA, + tokenTypes.EVALUATED_FUNCTION, + tokenTypes.EVALUATED_FUNCTION_PARAMETER, + ].indexOf(cType) !== -1 + ) { + if (allowed[cType] !== true) { + throw new Error(cToken + " is not allowed after " + prevKey); + } + str.push(closingParObj); + allowed = type1; + asterick = type3Asterick; + ptc.pop(); + } + } else break; + } + if (allowed[cType] !== true) { + throw new Error(cToken + " is not allowed after " + prevKey); + } + if (asterick[cType] === true) { + cType = tokenTypes.BINARY_OPERATOR_HIGH_PRECENDENCE; + cEv = this.math.mul; + cShow = "×"; + cPre = 3; + i = i - 1; + } + const obj = { + value: cEv, + type: cType, + precedence: cPre, + show: cShow, + numberOfArguments: node.numberOfArguments, + }; + if (cType === tokenTypes.FUNCTION_WITH_ONE_ARG) { + allowed = type0; + asterick = empty; + inc(ptc, 2); + str.push(obj); + if (nodes[i + 1].type !== tokenTypes.OPENING_PARENTHESIS) { + str.push(openingParObj); + ptc.push(2); + } + // bracToClose++ + } else if (cType === tokenTypes.NUMBER) { + if (pre.type === tokenTypes.NUMBER) { + pre.value += cEv; + inc(ptc, 1); + } else { + str.push(obj); + } + allowed = type1; + asterick = type1Asterick; + } else if (cType === tokenTypes.BINARY_OPERATOR_HIGH_PRECENDENCE) { + allowed = type0; + asterick = empty; + inc(ptc, 2); + str.push(obj); + } else if (cType === tokenTypes.CONSTANT) { + // constant + str.push(obj); + allowed = type1; + asterick = type3Asterick; + } else if (cType === tokenTypes.OPENING_PARENTHESIS) { + inc(ptc, 1); + bracToClose++; + allowed = type0; + asterick = empty; + str.push(obj); + } else if (cType === tokenTypes.CLOSING_PARENTHESIS) { + if (!bracToClose) { + throw new Error( + "Closing parenthesis are more than opening one, wait What!!!" + ); + } + bracToClose--; + allowed = type1; + asterick = type3Asterick; + str.push(obj); + inc(ptc, 1); + } else if (cType === tokenTypes.DECIMAL) { + if (pre.hasDec) { + throw new Error("Two decimals are not allowed in one number"); + } + if (pre.type !== tokenTypes.NUMBER) { + pre = { + show: "0", + value: 0, + type: tokenTypes.NUMBER, + precedence: 0, + }; // pre needs to be changed as it will the last value now to be safe in later code + str.push(pre); + // inc(ptc, 1) + } + allowed = type6; + inc(ptc, 1); + asterick = empty; + pre.value += cEv; + pre.hasDec = true; + } else if (cType === tokenTypes.POSTFIX_FUNCTION_WITH_ONE_ARG) { + allowed = type1; + asterick = type3Asterick; + inc(ptc, 1); + str.push(obj); + } + if (cType === tokenTypes.FUNCTION_WITH_N_ARGS) { + allowed = type0; + asterick = empty; + // @ts-ignore + inc(ptc, node.numberOfArguments + 2); + str.push(obj); + if (nodes[i + 1].type !== tokenTypes.OPENING_PARENTHESIS) { + str.push(openingParObj); + // @ts-ignore + ptc.push(node.numberOfArguments + 2); + } + } else if (cType === tokenTypes.BINARY_OPERATOR_LOW_PRECENDENCE) { + if (pre.type === tokenTypes.BINARY_OPERATOR_LOW_PRECENDENCE) { + if (pre.value === this.math.add) { + pre.value = cEv; + pre.show = cShow; + inc(ptc, 1); + } else if (pre.value === this.math.sub && cShow === "-") { + pre.value = this.math.add; + pre.show = "+"; + inc(ptc, 1); + } + } else if ( + pre.type !== tokenTypes.CLOSING_PARENTHESIS && + pre.type !== tokenTypes.POSTFIX_FUNCTION_WITH_ONE_ARG && + pre.type !== tokenTypes.NUMBER && + pre.type !== tokenTypes.CONSTANT && + pre.type !== tokenTypes.EVALUATED_FUNCTION_PARAMETER + ) { + // changesign only when negative is found + if (cToken === "-") { + // do nothing for + token + // don't add with the above if statement as that will run the else statement of parent if on Ctoken + + allowed = type0; + asterick = empty; + inc(ptc, 1).push(2); + str.push(changeSignObj); + str.push(openingParObj); + } + } else { + str.push(obj); + inc(ptc, 2); + } + allowed = type0; + asterick = empty; + } else if (cType === tokenTypes.BINARY_OPERATOR_PERMUTATION) { + allowed = type0; + asterick = empty; + inc(ptc, 2); + str.push(obj); + } else if (cType === tokenTypes.COMMA) { + allowed = type0; + asterick = empty; + str.push(obj); + } else if (cType === tokenTypes.EVALUATED_FUNCTION) { + allowed = type0; + asterick = empty; + inc(ptc, 6); + str.push(obj); + if (nodes[i + 1].type !== tokenTypes.OPENING_PARENTHESIS) { + str.push(openingParObj); + ptc.push(6); + } + } else if (cType === tokenTypes.EVALUATED_FUNCTION_PARAMETER) { + allowed = type1; + asterick = type3Asterick; + str.push(obj); + } - inc(ptc, -1) - prevKey = cToken - } - for (j = ptc.length; j--; ) { - // loop over ptc - str.push(closingParObj) - } - if (allowed[5] !== true) { - throw new Error('complete the expression') - } - while (bracToClose--) { - str.push(closingParObj) - } + inc(ptc, -1); + prevKey = cToken; + } + for (j = ptc.length; j--; ) { + // loop over ptc + str.push(closingParObj); + } + if (allowed[5] !== true) { + throw new Error("complete the expression"); + } + while (bracToClose--) { + str.push(closingParObj); + } - str.push(closingParObj) - // console.log(str); - return str -} + str.push(closingParObj); + return str; +}; diff --git a/test/index.js b/test/index.js index caec54d..60211a5 100644 --- a/test/index.js +++ b/test/index.js @@ -188,6 +188,12 @@ describe('Testing Unit', function () { it('checks multiple allowable operator', function () { assert.equal(mexp.eval('2+++-++-+-+3'), '-1') assert.equal(mexp.eval('2*+3'), '6') + }) + it("checks sign after function", function() { + assert.equal(mexp.eval('cos-0-cos0'), '0') + + + }) }) describe('These expression will check for types of returned result', function () {