'Math-Expression Evaluator 'Updated: Sept. 10, 2001 'This source code is simply extracted (with minor modifications) from the source code of QEDTukak 1.52 'originally written by Achilles B.Mina. ' 'You can use this file as a module in your program for the evaluation of 'user-entered mathematical expressions (like i.e. sin(.1)^2-3e2*ln(tan(.1*PI))). 'Simply call the function "EvaluateExpression(MathString$, DRGMode)" from your main program: 'MathString$ is the string to be evaluated (passed BYREF) 'DRGMode=0 for deg, DRGMode=1 for rad, DRGMode=2 for grad 'The global variable "EvaluateErrorFlag" can be used for error checking 'Have a look at eval_tst.qb as a simple example) $TYPECHECK ON ' Public DEFBYTE EvaluateErrorFlag '0:no error, 1:syntax error, 2:division by zero, 3:result undefined ' Private DIM MathString$(1 TO 36) AS STRING DEFSTR formula$, math$ DEFDBL CMode, CModeN, EvaluateAnswer ' Declare Sub routines DECLARE FUNCTION EvaluateExpression (BYREF MathString$ AS STRING, DRGMode AS BYTE) AS DOUBLE DECLARE SUB CheckSyntax DECLARE SUB ExpSolver DECLARE SUB Functions(par1 AS INTEGER, mathval$ AS STRING) DECLARE SUB ExpSolverEdit DECLARE SUB changeE DECLARE SUB CheckE(newstring$ AS STRING) DECLARE SUB DoInPrecision(value AS DOUBLE, value$ AS STRING) MathString$(1) = "sin" MathString$(2) = "cos" MathString$(3) = "tan" MathString$(4) = "cot" MathString$(5) = "sec" MathString$(6) = "csc" MathString$(7) = "sqrt" MathString$(8) = "log" MathString$(9) = "ln" MathString$(10) = "asin" MathString$(11) = "acos" MathString$(12) = "atan" MathString$(13) = "acot" MathString$(14) = "asec" MathString$(15) = "acsc" MathString$(16) = "sinh" MathString$(17) = "cosh" MathString$(18) = "tanh" MathString$(19) = "coth" MathString$(20) = "sech" MathString$(21) = "csch" MathString$(22) = "asinh" MathString$(23) = "acosh" MathString$(24) = "atanh" MathString$(25) = "acoth" MathString$(26) = "asech" MathString$(27) = "acsch" MathString$(28) = "inv" MathString$(29) = "exp" MathString$(30) = "abs" MathString$(31) = "int" MathString$(32) = "frac" MathString$(33) = "floor" MathString$(34) = "ceil" MathString$(35) = "cbrt" MathString$(36) = "sqr" FUNCTION EvaluateExpression (BYREF MathString$ AS STRING, DRGMode AS BYTE) AS DOUBLE EvaluateErrorFlag=0 formula$ = MathString$ IF DRGMode = 0 THEN CMode = 57.2957795130824 'converts to degrees CModeN = 0.0174532925199433 'converts to radians ELSEIF DRGMode = 2 THEN CMode = 63.6619772367584 'converts to grad CModeN =0.0157079632679489 'converts to radians ELSE CMode =1 CModeN =1 END IF CheckSyntax IF EvaluateErrorFlag=0 THEN ExpSolverEdit IF EvaluateErrorFlag=0 THEN EvaluateExpression = EvaluateAnswer ELSE EvaluateExpression = 0 END IF END FUNCTION SUB CheckSyntax DEFBYTE x DEFSHORT posops, startmath, wherepi DEFSTR math$, mathchar$, errmath$, oldmathchar$, ops$, afterendpar$ formula$ = LCASE$(REPLACESUBSTR$(formula$," ","")) formula$ = REPLACESUBSTR$(formula$,"pi","3.14159265358979") changeE math$ = "" startmath = 0 errmath$ = "" mathchar$ = "" IF TALLY(formula$,")(") <> 0 THEN MessageDlg("Invalid -> ')('. Please correct syntax.", mtError, mbOK, 0) EvaluateErrorFlag = 1 EXIT SUB END IF IF TALLY(formula$,"(") <> TALLY(formula$,")") THEN MessageDlg("Missing parenthesis. Please correct syntax.", mtError, mbOK, 0) EvaluateErrorFlag = 1 EXIT SUB ELSE DO INC startmath oldmathchar$ = mathchar$ mathchar$ = MID$(formula$,startmath,1) SELECT CASE mathchar$ CASE "e" SELECT CASE oldmathchar$ CASE "","^","c","s","-","+","*","/","\" math$ = math$ + mathchar$ errmath$ = math$ END SELECT CASE "a","b","c","f","g","h","i","l","n","o","p","q","r","s","t","v","x" math$ = math$ + mathchar$ errmath$ = errmath$ + mathchar$ SELECT CASE oldmathchar$ CASE "0" TO "9" MessageDlg(errmath$ + " is invalid. Please correct syntax.", mtError, mbOK, 0) EvaluateErrorFlag = 1 EXIT SUB END SELECT CASE "d","j","k","m","u","w","y","z" errmath$ = errmath$ + mathchar$ FOR x = 1 TO 5 ops$ = MID$("+-*/^",x,1) posops = RINSTR(errmath$,ops$) IF posops <> 0 THEN EXIT FOR NEXT x errmath$ = RIGHT$(errmath$,LEN(errmath$)-posops) MessageDlg(errmath$ + "?" + " Please correct syntax.", mtError, mbOK, 0) EvaluateErrorFlag = 1 EXIT SUB CASE ")" afterendpar$ = MID$(formula$,startmath+1,1) SELECT CASE afterendpar$ CASE "0" TO "9" MessageDlg(")"+ afterendpar$ + " is invalid." + " Please correct syntax.", mtError, mbOK, 0) EvaluateErrorFlag = 1 EXIT SUB END SELECT CASE "(" SELECT CASE oldmathchar$ CASE "0" TO "9" MessageDlg(oldmathchar$ + "( is invalid." + " Please correct syntax.", mtError, mbOK, 0) EvaluateErrorFlag = 1 EXIT SUB END SELECT IF math$ <> "" THEN FOR x = 1 TO 36 IF math$ = MathString$(x) THEN math$ = "" END IF NEXT x IF math$ <> "" AND math$ <> "pi" THEN 'if there's a match, skip this MessageDlg(errmath$ + "?" + " Please correct syntax.", mtError, mbOK, 0) EvaluateErrorFlag = 1 EXIT SUB END IF END IF CASE "0" TO "9" errmath$ = errmath$ + mathchar$ SELECT CASE oldmathchar$ CASE "a" TO "d", "f" TO "z" FOR x = 1 TO 5 ops$ = MID$("+-*/^",x,1) posops = RINSTR(errmath$,ops$) IF posops <> 0 THEN EXIT FOR NEXT x errmath$ = RIGHT$(errmath$,LEN(errmath$)-posops) MessageDlg(errmath$ + "?" + " Please correct syntax.", mtError, mbOK, 0) EvaluateErrorFlag = 1 EXIT SUB END SELECT CASE "+","-","*","/","^" IF (math$ <> "" AND math$ <> "pi") AND (INSTR(math$,"(") = 0 OR INSTR(math$,")") = 0) THEN FOR x = 1 TO 5 ops$ = MID$("+-*/^",x,1) posops = RINSTR(errmath$,ops$) IF posops <> 0 THEN EXIT FOR NEXT x math$ = RIGHT$(math$,LEN(math$)-posops) MessageDlg("Missing parenthesis. Please correct syntax.", mtError, mbOK, 0) EvaluateErrorFlag = 1 EXIT SUB END IF END SELECT LOOP UNTIL startmath = LEN(formula$) END IF END SUB SUB ExpSolverEdit DEFBYTE reformatted, formulaL, par1, par2, operator, preoperator, postoperator, start, x, y, i DEFSTR betweenpars$, pivotoperator$, operator$, oldstring$, newstring$, checkback$ DEFDBL preopnumber, postopnumber, newstring FOR i = 1 TO 1000 DO reformatted = 0 formulaL = LEN(formula$) par1 = RINSTR(formulaL,formula$,"(") par2 = INSTR(par1,formula$,")") IF par1 = 0 OR par2 = 0 THEN betweenpars$ = formula$ ELSE betweenpars$ = MID$(formula$,par1+1,(par2-1)-par1) END IF IF INSTR(betweenpars$,"^") <> 0 THEN pivotoperator$ = "^" operator = INSTR(betweenpars$,"^") ELSEIF INSTR(betweenpars$,"*") <> 0 THEN pivotoperator$ = "*" operator = INSTR(betweenpars$,"*") ELSEIF INSTR(betweenpars$,"/") <> 0 THEN pivotoperator$ = "/" operator = INSTR(betweenpars$,"/") ELSEIF (INSTR(betweenpars$,"+") <> 0 OR INSTR(betweenpars$,"-") <> 0) THEN 'AND INSTR(betweenpars$,"E") = 0 THEN IF MID$(betweenpars$,1,1) = "-" THEN start = 2 ELSE start = 1 END IF IF (TALLY(betweenpars$,"-") = 1 AND TALLY(betweenpars$,"+") = 0) AND MID$(betweenpars$,1,1) = "-" THEN checkback$ = MID$(formula$,par1-1,1) SELECT CASE checkback$ CASE "c","g","h","l","n","p","r","s","t","v" Functions(par1,betweenpars$) CASE ELSE formula$ = DELETE$(formula$,par1,1) formula$ = DELETE$(formula$,par2-1,1) END SELECT IF formula$ = betweenpars$ THEN EvaluateAnswer = VAL(formula$) EXIT SUB END IF reformatted = 1 ELSE FOR y = start TO LEN(betweenpars$) operator$ = MID$(betweenpars$,y,1) SELECT CASE operator$ CASE "+","-" operator = y pivotoperator$ = MID$(betweenpars$,operator,1) EXIT FOR END SELECT NEXT x END IF ELSE checkback$ = MID$(formula$,par1-1,1) SELECT CASE checkback$ CASE "c","g","h","l","n","p","r","s","t","v" Functions(par1,betweenpars$) CASE ELSE formula$ = DELETE$(formula$,par1,1) formula$ = DELETE$(formula$,par2-1,1) END SELECT IF formula$ = betweenpars$ THEN EvaluateAnswer = VAL(formula$) EXIT SUB END IF reformatted = 1 END IF formula$ = REPLACESUBSTR$(formula$,"--","+") LOOP UNTIL reformatted = 0 x = operator DO INC x IF x >= LEN(betweenpars$) THEN postoperator = x EXIT DO END IF operator$ = MID$(betweenpars$,x,1) SELECT CASE operator$ CASE "+","*","/","^" postoperator = x - 1 EXIT DO CASE "-" IF x <> operator + 1 THEN postoperator = x - 1 EXIT DO END IF END SELECT LOOP x = operator DO DEC x IF x <= 1 THEN preoperator = 1 EXIT DO END IF operator$ = MID$(betweenpars$,x,1) SELECT CASE operator$ CASE "+","-","*","/","^" preoperator = x + 1 EXIT DO END SELECT LOOP preopnumber = VAL(MID$(betweenpars$,preoperator,(operator-1)-(preoperator-1))) postopnumber = VAL(MID$(betweenpars$,operator+1,postoperator-operator)) oldstring$ = MID$(formula$,par1+preoperator,postoperator-(preoperator-1)) SELECT CASE pivotoperator$ CASE "^" newstring = preopnumber ^ postopnumber CASE "*" newstring = preopnumber * postopnumber CASE "/" IF postopnumber = 0 THEN MessageDlg("Can't divide by zero. Please correct entry.", mtError, mbOK, 0) EvaluateErrorFlag = 2 EXIT SUB END IF newstring = preopnumber / postopnumber CASE "+" newstring = preopnumber + postopnumber CASE "-" newstring = preopnumber - postopnumber END SELECT DoInPrecision(newstring,@newstring$) CheckE(@newstring$) formula$ = REPLACESUBSTR$(formula$,oldstring$,newstring$) NEXT x END SUB SUB Functions(par1 AS INTEGER,mathval$ AS STRING) DEFBYTE i DEFINT startmath DEFSTR mathchar$, oldval$, outformold$, insertval$, outform$ DEFDBL insertval startmath = par1 insertval$ = "" DO DEC startmath mathchar$ = MID$(formula$,startmath,1) SELECT CASE mathchar$ CASE "+","-","*","/","^","(" EXIT DO CASE "a","b","c","e","f","g","h","i","l","n","o","p","q","r","s","t","v","x" math$ = mathchar$ + math$ END SELECT LOOP UNTIL startmath <= 1 FOR i = 1 TO 36 IF math$ = MathString$(i) THEN math$ = "" oldval$ = MathString$(i) + "(" + mathval$ + ")" SELECT CASE i CASE 1 insertval = SIN(VAL(mathval$)*CModeN) CASE 2 insertval = COS(VAL(mathval$)*CModeN) CASE 3 insertval = TAN(VAL(mathval$)*CModeN) CASE 4 insertval = 1/(TAN(VAL(mathval$)*CModeN)) CASE 5 insertval = 1/(COS(VAL(mathval$)*CModeN)) CASE 6 insertval = 1/(SIN(VAL(mathval$)*CModeN)) CASE 7 insertval = SQR(VAL(mathval$)) CASE 8 insertval = LOG(VAL(mathval$))/LOG(10) CASE 9 insertval = LOG(VAL(mathval$)) CASE 10 IF VAL(mathval$) > 1 THEN outformold$ = outform$ outform$ = "undefined" EvaluateErrorFlag = 3 ELSEIF VAL(mathval$) = 1 THEN IF CMode = 1 THEN insertval = 1.5707963267949 ELSE insertval = 90 END IF ELSE insertval = ATN(VAL(mathval$)/SQR(-VAL(mathval$)*VAL(mathval$) +1))*CMode END IF CASE 11 IF VAL(mathval$) > 1 THEN outformold$ = outform$ outform$ = "undefined" EvaluateErrorFlag = 3 ELSEIF VAL(mathval$) = 0 THEN IF CMode = 1 THEN insertval = 1.5707963267949 ELSE insertval = 90 END IF ELSEIF VAL(mathval$) = 1 THEN insertval = 0 ELSE insertval = (-ATN(VAL(mathval$)/SQR(-VAL(mathval$)*VAL(mathval$) +1)) + 1.570796)*CMode END IF CASE 12 insertval = ATN(VAL(mathval$))*CMode CASE 13 insertval = (-ATN(VAL(mathval$)) + 1.570796)*CMode CASE 14 IF VAL(mathval$) = 0 THEN outformold$ = outform$ outform$ = "undefined" EvaluateErrorFlag = 3 ELSE insertval = (ATN(SQR(VAL(mathval$)*VAL(mathval$) -1)) + (SGN(VAL(mathval$)) -1)*1.570796)*CMode END IF CASE 15 IF VAL(mathval$) = 0 THEN outformold$ = outform$ outform$ = "undefined" EvaluateErrorFlag = 3 ELSEIF VAL(mathval$) = 1 THEN IF CMode = 1 THEN insertval = 1.5707963267949 ELSE insertval = 90 END IF ELSE insertval = (ATN(1/SQR(VAL(mathval$)*VAL(mathval$) -1)) + (SGN(VAL(mathval$)) -1)*1.570796)*CMode END IF CASE 16 insertval = (EXP(VAL(mathval$)) - EXP(-VAL(mathval$)))/2 CASE 17 insertval = (EXP(VAL(mathval$)) + EXP(-VAL(mathval$)))/2 CASE 18 insertval = -EXP(-VAL(mathval$)) /(EXP(VAL(mathval$)) + EXP(-VAL(mathval$)))*2 + 1 CASE 19 insertval = EXP(-VAL(mathval$))/(EXP(VAL(mathval$)) - EXP(-VAL(mathval$)))*2 + 1 CASE 20 insertval = 2/(EXP(VAL(mathval$)) + EXP(-VAL(mathval$))) CASE 21 insertval = 2/(EXP(VAL(mathval$)) - EXP(-VAL(mathval$))) CASE 22 insertval = LOG(VAL(mathval$) + SQR(VAL(mathval$)*VAL(mathval$) + 1)) CASE 23 insertval = LOG(VAL(mathval$) + SQR(VAL(mathval$)*VAL(mathval$) - 1)) CASE 24 insertval = LOG((1 + VAL(mathval$))/(1 - VAL(mathval$)))/2 CASE 25 insertval = LOG((VAL(mathval$) + 1)/(VAL(mathval$) - 1))/2 CASE 26 insertval = LOG((SGN(VAL(mathval$))*SQR(-VAL(mathval$)*VAL(mathval$) + 1) + 1)/VAL(mathval$)) CASE 27 insertval = LOG((SGN(VAL(mathval$))*SQR(VAL(mathval$)*VAL(mathval$) + 1) + 1)/VAL(mathval$)) CASE 28 insertval = 1/VAL(mathval$) CASE 29 insertval = EXP(VAL(mathval$)) CASE 30 insertval = ABS(VAL(mathval$)) CASE 31 insertval = INT(VAL(mathval$)) CASE 32 insertval = FRAC(VAL(mathval$)) CASE 33 insertval = FLOOR(VAL(mathval$)) CASE 34 insertval = CEIL(VAL(mathval$)) CASE 35 insertval = VAL(mathval$)^(1/3) CASE 36 insertval = VAL(mathval$)^2 END SELECT DoInPrecision(insertval,@insertval$) END IF NEXT i CheckE(@insertval$) formula$ = REPLACESUBSTR$(formula$,oldval$,insertval$) END SUB SUB DoInPrecision(value AS DOUBLE, value$ AS STRING) value$ = FORMAT$("%g",value) END SUB SUB changeE DEFBYTE posE, x DEFSTR E$ x = 0 DO INC x E$ = MID$(formula$,x,1) IF E$ = "" THEN EXIT SUB ELSEIF E$ = "e" THEN posE = x INC x E$ = MID$(formula$,x,1) SELECT CASE E$ CASE "0" TO "9" formula$ = DELETE$(formula$,posE,1) formula$ = INSERT$("*10^",formula$,posE) CASE "-" formula$ = DELETE$(formula$,posE,2) formula$ = INSERT$("*1/10^",formula$,posE) CASE "c","i","x" CASE ELSE SHOWMESSAGE "Syntax error." EvaluateErrorFlag = 1 EXIT SUB END SELECT END IF LOOP END SUB SUB CheckE(newstring$ AS STRING) DEFSTR powerEnd$, oldbase$, checkneg$, base$, power$, newbase$ DEFBYTE whereE, whereDot, x, move, powerEnd DEFSHORT power whereE = INSTR(newstring$,"E") x = whereE IF whereE = 0 THEN EXIT SUB ELSE whereDot = RINSTR(whereE,newstring$,".") IF whereDot < 2 THEN whereDot = 2 'since in scientific format DO INC x IF x >= LEN(newstring$) THEN powerEnd = x EXIT DO END IF powerEnd$ = MID$(newstring$,x,1) SELECT CASE powerEnd$ CASE "+","*","/",")" powerEnd = x - 1 EXIT DO CASE "-" IF x <> whereE + 1 THEN powerEnd = x - 1 EXIT DO END IF END SELECT LOOP oldbase$ = LEFT$(newstring$,whereE-1) base$ = REPLACESUBSTR$(oldbase$,".","") power$ = MID$(newstring$,whereE+1,powerEnd-whereE) power = VAL(power$) IF power => 14 THEN EXIT SUB checkneg$ = LEFT$(power$,1) IF checkneg$ = "-" THEN move = ABS(power) newbase$ = "." + STRING$(move-(whereDot-1),"0") + base$ END IF newstring$ = newbase$ END IF END SUB