### Author Topic: Steve's Math Evaluator  (Read 1524 times)

0 Members and 1 Guest are viewing this topic.

This topic contains a post which is marked as Best Answer or Most Recent Update. Press here if you would like to see it.

#### SMcNeill

• QB64 Developer
• Forum Resident
• Posts: 3623
##### Steve's Math Evaluator
« on: August 01, 2019, 12:58:22 PM »
Here's my little math evaluation routine, which everybody's been using for ages, even if they don't know they have!  :D

Code: [Select]
`REDIM SHARED OName(0) AS STRING 'Operation NameREDIM SHARED PL(0) AS INTEGER 'Priority LevelDIM SHARED QuickReturn AS INTEGERSet_OrderOfOperations 'This will also make certain our directories are valid, and if not make them.DO    INPUT math\$    PRINT Evaluate_Expression(math\$)LOOP'Steve Subs/Functins for _MATH support with CONSTFUNCTION Evaluate_Expression\$ (e\$)    t\$ = e\$ 'So we preserve our original data, we parse a temp copy of it    b = INSTR(UCASE\$(e\$), "EQL") 'take out assignment before the preparser sees it    IF b THEN t\$ = MID\$(e\$, b + 3): var\$ = UCASE\$(LTRIM\$(RTRIM\$(MID\$(e\$, 1, b - 1))))    QuickReturn = 0    PreParse t\$    IF QuickReturn THEN Evaluate_Expression\$ = t\$: EXIT FUNCTION    IF LEFT\$(t\$, 5) = "ERROR" THEN Evaluate_Expression\$ = t\$: EXIT FUNCTION    'Deal with brackets first    exp\$ = "(" + t\$ + ")" 'Starting and finishing brackets for our parse routine.    DO        Eval_E = INSTR(exp\$, ")")        IF Eval_E > 0 THEN            c = 0            DO UNTIL Eval_E - c <= 0                c = c + 1                IF Eval_E THEN                    IF MID\$(exp\$, Eval_E - c, 1) = "(" THEN EXIT DO                END IF            LOOP            s = Eval_E - c + 1            IF s < 1 THEN PRINT "ERROR -- BAD () Count": END            eval\$ = " " + MID\$(exp\$, s, Eval_E - s) + " " 'pad with a space before and after so the parser can pick up the values properly.            ParseExpression eval\$            eval\$ = LTRIM\$(RTRIM\$(eval\$))            IF LEFT\$(eval\$, 5) = "ERROR" THEN Evaluate_Expression\$ = eval\$: EXIT SUB            exp\$ = DWD(LEFT\$(exp\$, s - 2) + eval\$ + MID\$(exp\$, Eval_E + 1))            IF MID\$(exp\$, 1, 1) = "N" THEN MID\$(exp\$, 1) = "-"            temppp\$ = DWD(LEFT\$(exp\$, s - 2) + " ## " + eval\$ + " ## " + MID\$(exp\$, E + 1))        END IF    LOOP UNTIL Eval_E = 0    c = 0    DO        c = c + 1        SELECT CASE MID\$(exp\$, c, 1)            CASE "0" TO "9", ".", "-" 'At this point, we should only have number values left.            CASE ELSE: Evaluate_Expression\$ = "ERROR - Unknown Diagnosis: (" + exp\$ + ") ": EXIT SUB        END SELECT    LOOP UNTIL c >= LEN(exp\$)    Evaluate_Expression\$ = exp\$END FUNCTIONSUB ParseExpression (exp\$)    DIM num(10) AS STRING    'We should now have an expression with no () to deal with    IF MID\$(exp\$, 2, 1) = "-" THEN exp\$ = "0+" + MID\$(exp\$, 2)    FOR J = 1 TO 250        lowest = 0        DO UNTIL lowest = LEN(exp\$)            lowest = LEN(exp\$): OpOn = 0            FOR P = 1 TO UBOUND(OName)                'Look for first valid operator                IF J = PL(P) THEN 'Priority levels match                    IF LEFT\$(exp\$, 1) = "-" THEN op = INSTR(2, exp\$, OName(P)) ELSE op = INSTR(exp\$, OName(P))                    IF op > 0 AND op < lowest THEN lowest = op: OpOn = P                END IF            NEXT            IF OpOn = 0 THEN EXIT DO 'We haven't gotten to the proper PL for this OP to be processed yet.            IF LEFT\$(exp\$, 1) = "-" THEN op = INSTR(2, exp\$, OName(OpOn)) ELSE op = INSTR(exp\$, OName(OpOn))            numset = 0            '*** SPECIAL OPERATION RULESETS            IF OName(OpOn) = "-" THEN 'check for BOOLEAN operators before the -                SELECT CASE MID\$(exp\$, op - 3, 3)                    CASE "NOT", "XOR", "AND", "EQV", "IMP"                        EXIT DO 'Not an operator, it's a negative                END SELECT                IF MID\$(exp\$, op - 3, 2) = "OR" THEN EXIT DO 'Not an operator, it's a negative            END IF            IF op THEN                c = LEN(OName(OpOn)) - 1                DO                    SELECT CASE MID\$(exp\$, op + c + 1, 1)                        CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "N": numset = -1 'Valid digit                        CASE "-" 'We need to check if it's a minus or a negative                            IF OName(OpOn) = "_PI" OR numset THEN EXIT DO                        CASE ELSE 'Not a valid digit, we found our separator                            EXIT DO                    END SELECT                    c = c + 1                LOOP UNTIL op + c >= LEN(exp\$)                E = op + c                c = 0                DO                    c = c + 1                    SELECT CASE MID\$(exp\$, op - c, 1)                        CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "N" 'Valid digit                        CASE "-" 'We need to check if it's a minus or a negative                            c1 = c                            bad = 0                            DO                                c1 = c1 + 1                                SELECT CASE MID\$(exp\$, op - c1, 1)                                    CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."                                        bad = -1                                        EXIT DO 'It's a minus sign                                    CASE ELSE                                        'It's a negative sign and needs to count as part of our numbers                                END SELECT                            LOOP UNTIL op - c1 <= 0                            IF bad THEN EXIT DO 'We found our seperator                        CASE ELSE 'Not a valid digit, we found our separator                            EXIT DO                    END SELECT                LOOP UNTIL op - c <= 0                s = op - c                num(1) = MID\$(exp\$, s + 1, op - s - 1) 'Get our first number                num(2) = MID\$(exp\$, op + LEN(OName(OpOn)), E - op - LEN(OName(OpOn)) + 1) 'Get our second number                IF MID\$(num(1), 1, 1) = "N" THEN MID\$(num(1), 1) = "-"                IF MID\$(num(2), 1, 1) = "N" THEN MID\$(num(2), 1) = "-"                num(3) = EvaluateNumbers(OpOn, num())                IF MID\$(num(3), 1, 1) = "-" THEN MID\$(num(3), 1) = "N"                'PRINT "*************"                'PRINT num(1), OName(OpOn), num(2), num(3), exp\$                IF LEFT\$(num(3), 5) = "ERROR" THEN exp\$ = num(3): EXIT SUB                exp\$ = LTRIM\$(N2S(DWD(LEFT\$(exp\$, s) + RTRIM\$(LTRIM\$(num(3))) + MID\$(exp\$, E + 1))))                'PRINT exp\$            END IF            op = 0        LOOP    NEXTEND SUBSUB Set_OrderOfOperations    'PL sets our priortity level. 1 is highest to 65535 for the lowest.    'I used a range here so I could add in new priority levels as needed.    'OName ended up becoming the name of our commands, as I modified things.... Go figure!  LOL!    'Constants get evaluated first, with a Priority Level of 1    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_PI"    REDIM _PRESERVE PL(i): PL(i) = 1    'I'm not certain where exactly percentages should go.  They kind of seem like a special case to me.  COS10% should be COS.1 I'd think...    'I'm putting it here for now, and if anyone knows someplace better for it in our order of operations, let me know.    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "%"    REDIM _PRESERVE PL(i): PL(i) = 5    'Then Functions with PL 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ACOS"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ASIN"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ARCSEC"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ARCCSC"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ARCCOT"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_SECH"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_CSCH"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_COTH"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "COS"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "SIN"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "TAN"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "LOG"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "EXP"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ATN"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_D2R"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_D2G"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_R2D"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_R2G"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_G2D"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_G2R"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ABS"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "SGN"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "INT"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ROUND"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "FIX"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_SEC"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_CSC"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_COT"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ASC"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "CHR\$"    REDIM _PRESERVE PL(i): PL(i) = 10    'Exponents with PL 20    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "^"    REDIM _PRESERVE PL(i): PL(i) = 20    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "SQR"    REDIM _PRESERVE PL(i): PL(i) = 20    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ROOT"    REDIM _PRESERVE PL(i): PL(i) = 20    'Multiplication and Division PL 30    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "*"    REDIM _PRESERVE PL(i): PL(i) = 30    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "/"    REDIM _PRESERVE PL(i): PL(i) = 30    'Integer Division PL 40    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "\"    REDIM _PRESERVE PL(i): PL(i) = 40    'MOD PL 50    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "MOD"    REDIM _PRESERVE PL(i): PL(i) = 50    'Addition and Subtraction PL 60    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "+"    REDIM _PRESERVE PL(i): PL(i) = 60    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "-"    REDIM _PRESERVE PL(i): PL(i) = 60    'Relational Operators =, >, <, <>, <=, >=   PL 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "<>"    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "><" 'These next three are just reversed symbols as an attempt to help process a common typo    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "<="    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = ">="    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "=<" 'I personally can never keep these things straight.  Is it < = or = <...    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "=>" 'Who knows, check both!    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = ">"    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "<"    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "="    REDIM _PRESERVE PL(i): PL(i) = 70    'Logical Operations PL 80+    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "NOT"    REDIM _PRESERVE PL(i): PL(i) = 80    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "AND"    REDIM _PRESERVE PL(i): PL(i) = 90    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "OR"    REDIM _PRESERVE PL(i): PL(i) = 100    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "XOR"    REDIM _PRESERVE PL(i): PL(i) = 110    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "EQV"    REDIM _PRESERVE PL(i): PL(i) = 120    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "IMP"    REDIM _PRESERVE PL(i): PL(i) = 130END SUBFUNCTION EvaluateNumbers\$ (p, num() AS STRING)    DIM n1 AS _FLOAT, n2 AS _FLOAT, n3 AS _FLOAT    SELECT CASE OName(p) 'Depending on our operator..        CASE "_PI": n1 = 3.14159265358979323846264338327950288## 'Future compatable in case something ever stores extra digits for PI        CASE "%": n1 = (VAL(num(1))) / 100 'Note percent is a special case and works with the number BEFORE the % command and not after        CASE "_ACOS": n1 = _ACOS(VAL(num(2)))        CASE "_ASIN": n1 = _ASIN(VAL(num(2)))        CASE "_ARCSEC": n1 = _ARCSEC(VAL(num(2)))        CASE "_ARCCSC": n1 = _ARCCSC(VAL(num(2)))        CASE "_ARCCOT": n1 = _ARCCOT(VAL(num(2)))        CASE "_SECH": n1 = _SECH(VAL(num(2)))        CASE "_CSCH": n1 = _CSCH(VAL(num(2)))        CASE "_COTH": n1 = _COTH(VAL(num(2)))        CASE "COS": n1 = COS(VAL(num(2)))        CASE "SIN": n1 = SIN(VAL(num(2)))        CASE "TAN": n1 = TAN(VAL(num(2)))        CASE "LOG": n1 = LOG(VAL(num(2)))        CASE "EXP": n1 = EXP(VAL(num(2)))        CASE "ATN": n1 = ATN(VAL(num(2)))        CASE "_D2R": n1 = 0.0174532925 * (VAL(num(2)))        CASE "_D2G": n1 = 1.1111111111 * (VAL(num(2)))        CASE "_R2D": n1 = 57.2957795 * (VAL(num(2)))        CASE "_R2G": n1 = 0.015707963 * (VAL(num(2)))        CASE "_G2D": n1 = 0.9 * (VAL(num(2)))        CASE "_G2R": n1 = 63.661977237 * (VAL(num(2)))        CASE "ABS": n1 = ABS(VAL(num(2)))        CASE "SGN": n1 = SGN(VAL(num(2)))        CASE "INT": n1 = INT(VAL(num(2)))        CASE "_ROUND": n1 = _ROUND(VAL(num(2)))        CASE "FIX": n1 = FIX(VAL(num(2)))        CASE "_SEC": n1 = _SEC(VAL(num(2)))        CASE "_CSC": n1 = _CSC(VAL(num(2)))        CASE "_COT": n1 = _COT(VAL(num(2)))        CASE "^": n1 = VAL(num(1)) ^ VAL(num(2))        CASE "SQR": n1 = SQR(VAL(num(2)))        CASE "ROOT"            n1 = VAL(num(1)): n2 = VAL(num(2))            IF n2 = 1 THEN EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1))): EXIT FUNCTION            IF n1 < 0 AND n2 >= 1 THEN sign = -1: n1 = -n1 ELSE sign = 1            n3 = 1## / n2            IF n3 <> INT(n3) AND n2 < 1 THEN sign = SGN(n1): n1 = ABS(n1)            n1 = sign * (n1 ^ n3)        CASE "*": n1 = VAL(num(1)) * VAL(num(2))        CASE "/": n1 = VAL(num(1)) / VAL(num(2))        CASE "\"            IF VAL(num(2)) <> 0 THEN                n1 = VAL(num(1)) \ VAL(num(2))            ELSE                EvaluateNumbers\$ = "ERROR - Bad operation (We shouldn't see this)"                EXIT FUNCTION            END IF        CASE "MOD": n1 = VAL(num(1)) MOD VAL(num(2))        CASE "+": n1 = VAL(num(1)) + VAL(num(2))        CASE "-": n1 = VAL(num(1)) - VAL(num(2))        CASE "=": n1 = VAL(num(1)) = VAL(num(2))        CASE ">": n1 = VAL(num(1)) > VAL(num(2))        CASE "<": n1 = VAL(num(1)) < VAL(num(2))        CASE "<>", "><": n1 = VAL(num(1)) <> VAL(num(2))        CASE "<=", "=<": n1 = VAL(num(1)) <= VAL(num(2))        CASE ">=", "=>": n1 = VAL(num(1)) >= VAL(num(2))        CASE "NOT": n1 = NOT VAL(num(2))        CASE "AND": n1 = VAL(num(1)) AND VAL(num(2))        CASE "OR": n1 = VAL(num(1)) OR VAL(num(2))        CASE "XOR": n1 = VAL(num(1)) XOR VAL(num(2))        CASE "EQV": n1 = VAL(num(1)) EQV VAL(num(2))        CASE "IMP": n1 = VAL(num(1)) IMP VAL(num(2))        CASE ELSE            EvaluateNumbers\$ = "ERROR - Bad operation (We shouldn't see this)" 'Let's say we're bad...    END SELECT    EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1)))END FUNCTIONFUNCTION DWD\$ (exp\$) 'Deal With Duplicates    'To deal with duplicate operators in our code.    'Such as --  becomes a +    '++ becomes a +    '+- becomes a -    '-+ becomes a -    t\$ = exp\$    DO        bad = 0        DO            l = INSTR(t\$, "++")            IF l THEN t\$ = LEFT\$(t\$, l - 1) + "+" + MID\$(t\$, l + 2): bad = -1        LOOP UNTIL l = 0        DO            l = INSTR(t\$, "+-")            IF l THEN t\$ = LEFT\$(t\$, l - 1) + "-" + MID\$(t\$, l + 2): bad = -1        LOOP UNTIL l = 0        DO            l = INSTR(t\$, "-+")            IF l THEN t\$ = LEFT\$(t\$, l - 1) + "-" + MID\$(t\$, l + 2): bad = -1        LOOP UNTIL l = 0        DO            l = INSTR(t\$, "--")            IF l THEN t\$ = LEFT\$(t\$, l - 1) + "+" + MID\$(t\$, l + 2): bad = -1        LOOP UNTIL l = 0    LOOP UNTIL NOT bad    DWD\$ = t\$    VerifyString t\$END FUNCTIONSUB PreParse (e\$)    DIM f AS _FLOAT    t\$ = e\$    'First strip all spaces    t\$ = ""    FOR i = 1 TO LEN(e\$)        IF MID\$(e\$, i, 1) <> " " THEN t\$ = t\$ + MID\$(e\$, i, 1)    NEXT    t\$ = UCASE\$(t\$)    IF t\$ = "" THEN e\$ = "ERROR -- NULL string; nothing to evaluate": EXIT SUB    'ERROR CHECK by counting our brackets    l = 0    DO        l = INSTR(l + 1, t\$, "("): IF l THEN c = c + 1    LOOP UNTIL l = 0    l = 0    DO        l = INSTR(l + 1, t\$, ")"): IF l THEN c1 = c1 + 1    LOOP UNTIL l = 0    IF c <> c1 THEN e\$ = "ERROR -- Bad Parenthesis:" + STR\$(c) + "( vs" + STR\$(c1) + ")": EXIT SUB    'Modify so that NOT will process properly    l = 0    DO        l = INSTR(l + 1, t\$, "NOT")        IF l THEN            'We need to work magic on the statement so it looks pretty.            ' 1 + NOT 2 + 1 is actually processed as 1 + (NOT 2 + 1)            'Look for something not proper            l1 = INSTR(l + 1, t\$, "AND")            IF l1 = 0 OR (INSTR(l + 1, t\$, "OR") > 0 AND INSTR(l + 1, t\$, "OR") < l1) THEN l1 = INSTR(l + 1, t\$, "OR")            IF l1 = 0 OR (INSTR(l + 1, t\$, "XOR") > 0 AND INSTR(l + 1, t\$, "XOR") < l1) THEN l1 = INSTR(l + 1, t\$, "XOR")            IF l1 = 0 OR (INSTR(l + 1, t\$, "EQV") > 0 AND INSTR(l + 1, t\$, "EQV") < l1) THEN l1 = INSTR(l + 1, t\$, "EQV")            IF l1 = 0 OR (INSTR(l + 1, t\$, "IMP") > 0 AND INSTR(l + 1, t\$, "IMP") < l1) THEN l1 = INSTR(l + 1, t\$, "IMP")            IF l1 = 0 THEN l1 = LEN(t\$) + 1            t\$ = LEFT\$(t\$, l - 1) + "(" + MID\$(t\$, l, l1 - l) + ")" + MID\$(t\$, l + l1 - l)            l = l + 3            'PRINT t\$        END IF    LOOP UNTIL l = 0    'Check for bad operators before a ( bracket    l = 0    DO        l = INSTR(l + 1, t\$, "(")        IF l AND l > 2 THEN 'Don't check the starting bracket; there's nothing before it.            good = 0            FOR i = 1 TO UBOUND(OName)                IF MID\$(t\$, l - LEN(OName(i)), LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)            NEXT            IF NOT good THEN e\$ = "ERROR - Improper operations before (.": EXIT SUB            l = l + 1        END IF    LOOP UNTIL l = 0    'Check for bad operators after a ) bracket    l = 0    DO        l = INSTR(l + 1, t\$, ")")        IF l AND l < LEN(t\$) THEN            good = 0            FOR i = 1 TO UBOUND(OName)                IF MID\$(t\$, l + 1, LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)            NEXT            IF MID\$(t\$, l + 1, 1) = ")" THEN good = -1            IF NOT good THEN e\$ = "ERROR - Improper operations after ).": EXIT SUB            l = l + 1        END IF    LOOP UNTIL l = 0 OR l = LEN(t\$) 'last symbol is a bracket    'Turn all &H (hex) numbers into decimal values for the program to process properly    l = 0    DO        l = INSTR(t\$, "&H")        IF l THEN            E = l + 1: finished = 0            DO                E = E + 1                comp\$ = MID\$(t\$, E, 1)                SELECT CASE comp\$                    CASE "0" TO "9", "A" TO "F" 'All is good, our next digit is a number, continue to add to the hex\$                    CASE ELSE                        good = 0                        FOR i = 1 TO UBOUND(OName)                            IF MID\$(t\$, E, LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)                        NEXT                        IF NOT good THEN e\$ = "ERROR - Improper &H value. (" + comp\$ + ")": EXIT SUB                        E = E - 1                        finished = -1                END SELECT            LOOP UNTIL finished OR E = LEN(t\$)            t\$ = LEFT\$(t\$, l - 1) + LTRIM\$(RTRIM\$(STR\$(VAL(MID\$(t\$, l, E - l + 1))))) + MID\$(t\$, E + 1)        END IF    LOOP UNTIL l = 0    'Turn all &B (binary) numbers into decimal values for the program to process properly    l = 0    DO        l = INSTR(t\$, "&B")        IF l THEN            E = l + 1: finished = 0            DO                E = E + 1                comp\$ = MID\$(t\$, E, 1)                SELECT CASE comp\$                    CASE "0", "1" 'All is good, our next digit is a number, continue to add to the hex\$                    CASE ELSE                        good = 0                        FOR i = 1 TO UBOUND(OName)                            IF MID\$(t\$, E, LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)                        NEXT                        IF NOT good THEN e\$ = "ERROR - Improper &B value. (" + comp\$ + ")": EXIT SUB                        E = E - 1                        finished = -1                END SELECT            LOOP UNTIL finished OR E = LEN(t\$)            bin\$ = MID\$(t\$, l + 2, E - l - 1)            FOR i = 1 TO LEN(bin\$)                IF MID\$(bin\$, i, 1) = "1" THEN f = f + 2 ^ (LEN(bin\$) - i)            NEXT            t\$ = LEFT\$(t\$, l - 1) + LTRIM\$(RTRIM\$(STR\$(f))) + MID\$(t\$, E + 1)        END IF    LOOP UNTIL l = 0    t\$ = N2S(t\$)    VerifyString t\$    e\$ = t\$END SUBSUB VerifyString (t\$)    'ERROR CHECK for unrecognized operations    j = 1    DO        comp\$ = MID\$(t\$, j, 1)        SELECT CASE comp\$            CASE "0" TO "9", ".", "(", ")": j = j + 1            CASE ELSE                good = 0                FOR i = 1 TO UBOUND(OName)                    IF MID\$(t\$, j, LEN(OName(i))) = OName(i) THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)                NEXT                IF NOT good THEN t\$ = "ERROR - Bad Operational value. (" + comp\$ + ")": EXIT SUB                j = j + LEN(OName(i))        END SELECT    LOOP UNTIL j > LEN(t\$)END SUBFUNCTION N2S\$ (exp\$) 'scientific Notation to String    t\$ = LTRIM\$(RTRIM\$(exp\$))    IF LEFT\$(t\$, 1) = "-" THEN sign\$ = "-": t\$ = MID\$(t\$, 2)    dp = INSTR(t\$, "D+"): dm = INSTR(t\$, "D-")    ep = INSTR(t\$, "E+"): em = INSTR(t\$, "E-")    check1 = SGN(dp) + SGN(dm) + SGN(ep) + SGN(em)    IF check1 < 1 OR check1 > 1 THEN N2S = exp\$: EXIT SUB 'If no scientic notation is found, or if we find more than 1 type, it's not SN!    SELECT CASE l 'l now tells us where the SN starts at.        CASE IS < dp: l = dp        CASE IS < dm: l = dm        CASE IS < ep: l = ep        CASE IS < em: l = em    END SELECT    l\$ = LEFT\$(t\$, l - 1) 'The left of the SN    r\$ = MID\$(t\$, l + 1): r&& = VAL(r\$) 'The right of the SN, turned into a workable long    IF INSTR(l\$, ".") THEN 'Location of the decimal, if any        IF r&& > 0 THEN            r&& = r&& - LEN(l\$) + 2        ELSE            r&& = r&& + 1        END IF        l\$ = LEFT\$(l\$, 1) + MID\$(l\$, 3)    END IF    SELECT CASE r&&        CASE 0 'what the heck? We solved it already?            'l\$ = l\$        CASE IS < 0            FOR i = 1 TO -r&&                l\$ = "0" + l\$            NEXT            l\$ = "0." + l\$        CASE ELSE            FOR i = 1 TO r&&                l\$ = l\$ + "0"            NEXT    END SELECT    N2S\$ = sign\$ + l\$END SUB`
If you look inside QB64.bas, you'll see these routines, which are what the IDE uses to calculate math values for use with CONST and then substitute the finished product into your code.

When you type:  CONST P = _PI(2), it's these routines which substitute 6.28 instead of _PI(2), so the code is processed as CONST P = 6.24....

Feel free to plug it in and use it for any of your needs.  It's rather simple, just call  Evaluate_Expression\$ with your string formula for it to solve, like in the above:      PRINT Evaluate_Expression(math\$)

https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

#### BSpinoza

• Newbie
• Posts: 69
##### Re: Steve's Math Evaluator
« Reply #1 on: September 02, 2019, 03:27:35 PM »
Thanks!
« Last Edit: September 04, 2019, 06:15:00 AM by BSpinoza »
"Ich sage euch: man muss noch Chaos in sich haben, um einen tanzenden Stern gebären zu können. Ich sage euch: ihr habt noch Chaos in euch." (from Friedrich Nietzsche: "Also sprach Zarathustra")

#### SMcNeill

• QB64 Developer
• Forum Resident
• Posts: 3623
##### Re: Steve's Math Evaluator
« Reply #2 on: January 05, 2020, 12:05:59 PM »
An updated version of the code, for testing purposes as I expand it:

Code: [Select]
`REDIM SHARED OName(0) AS STRING 'Operation NameREDIM SHARED PL(0) AS INTEGER 'Priority LevelDIM SHARED QuickReturn AS INTEGERSet_OrderOfOperationsDO    i\$ = INPUT\$(1)    CLS    SELECT CASE i\$        CASE CHR\$(8)            eval\$ = LEFT\$(eval\$, LEN(eval\$) - 1)        CASE CHR\$(13)            eval\$ = ""        CASE CHR\$(27)            SYSTEM        CASE ELSE            eval\$ = eval\$ + i\$    END SELECT    PRINT eval\$    PRINT Evaluate_Expression(eval\$)LOOP'Steve Subs/Functins for _MATH support with CONSTFUNCTION Evaluate_Expression\$ (e\$)    t\$ = e\$ 'So we preserve our original data, we parse a temp copy of it    b = INSTR(UCASE\$(e\$), "EQL") 'take out assignment before the preparser sees it    IF b THEN t\$ = MID\$(e\$, b + 3): var\$ = UCASE\$(LTRIM\$(RTRIM\$(MID\$(e\$, 1, b - 1))))    QuickReturn = 0    PreParse t\$    IF QuickReturn THEN Evaluate_Expression\$ = t\$: EXIT FUNCTION    IF LEFT\$(t\$, 5) = "ERROR" THEN Evaluate_Expression\$ = t\$: EXIT FUNCTION    'Deal with brackets first    exp\$ = "(" + t\$ + ")" 'Starting and finishing brackets for our parse routine.    DO        Eval_E = INSTR(exp\$, ")")        IF Eval_E > 0 THEN            c = 0            DO UNTIL Eval_E - c <= 0                c = c + 1                IF Eval_E THEN                    IF MID\$(exp\$, Eval_E - c, 1) = "(" THEN EXIT DO                END IF            LOOP            s = Eval_E - c + 1            IF s < 1 THEN Evaluate_Expression\$ = "ERROR -- BAD () Count": EXIT SUB            eval\$ = " " + MID\$(exp\$, s, Eval_E - s) + " " 'pad with a space before and after so the parser can pick up the values properly.            ParseExpression eval\$            eval\$ = LTRIM\$(RTRIM\$(eval\$))            IF LEFT\$(eval\$, 5) = "ERROR" THEN Evaluate_Expression\$ = eval\$: EXIT SUB            exp\$ = DWD(LEFT\$(exp\$, s - 2) + eval\$ + MID\$(exp\$, Eval_E + 1))            IF MID\$(exp\$, 1, 1) = "N" THEN MID\$(exp\$, 1) = "-"            temppp\$ = DWD(LEFT\$(exp\$, s - 2) + " ## " + eval\$ + " ## " + MID\$(exp\$, E + 1))        END IF    LOOP UNTIL Eval_E = 0    c = 0    DO        c = c + 1        SELECT CASE MID\$(exp\$, c, 1)            CASE "0" TO "9", ".", "-" 'At this point, we should only have number values left.            CASE ELSE: Evaluate_Expression\$ = "ERROR - Unknown Diagnosis: (" + exp\$ + ") ": EXIT SUB        END SELECT    LOOP UNTIL c >= LEN(exp\$)    Evaluate_Expression\$ = exp\$END FUNCTIONSUB ParseExpression (exp\$)    DIM num(10) AS STRING    'PRINT exp\$    'We should now have an expression with no () to deal with    IF MID\$(exp\$, 2, 1) = "-" THEN exp\$ = "0+" + MID\$(exp\$, 2)    FOR J = 1 TO 250        lowest = 0        DO UNTIL lowest = LEN(exp\$)            lowest = LEN(exp\$): OpOn = 0            FOR P = 1 TO UBOUND(OName)                'Look for first valid operator                IF J = PL(P) THEN 'Priority levels match                    IF LEFT\$(exp\$, 1) = "-" THEN op = INSTR(2, exp\$, OName(P)) ELSE op = INSTR(exp\$, OName(P))                    IF op > 0 AND op < lowest THEN lowest = op: OpOn = P                END IF            NEXT            IF OpOn = 0 THEN EXIT DO 'We haven't gotten to the proper PL for this OP to be processed yet.            IF LEFT\$(exp\$, 1) = "-" THEN op = INSTR(2, exp\$, OName(OpOn)) ELSE op = INSTR(exp\$, OName(OpOn))            numset = 0            '*** SPECIAL OPERATION RULESETS            IF OName(OpOn) = "-" THEN 'check for BOOLEAN operators before the -                SELECT CASE MID\$(exp\$, op - 3, 3)                    CASE "NOT", "XOR", "AND", "EQV", "IMP"                        EXIT DO 'Not an operator, it's a negative                END SELECT                IF MID\$(exp\$, op - 3, 2) = "OR" THEN EXIT DO 'Not an operator, it's a negative            END IF            IF op THEN                c = LEN(OName(OpOn)) - 1                DO                    SELECT CASE MID\$(exp\$, op + c + 1, 1)                        CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "N": numset = -1 'Valid digit                        CASE "-" 'We need to check if it's a minus or a negative                            IF OName(OpOn) = "_PI" OR numset THEN EXIT DO                        CASE ",": numset = 0                        CASE ELSE 'Not a valid digit, we found our separator                            EXIT DO                    END SELECT                    c = c + 1                LOOP UNTIL op + c >= LEN(exp\$)                E = op + c                c = 0                DO                    c = c + 1                    SELECT CASE MID\$(exp\$, op - c, 1)                        CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "N" 'Valid digit                        CASE "-" 'We need to check if it's a minus or a negative                            c1 = c                            bad = 0                            DO                                c1 = c1 + 1                                SELECT CASE MID\$(exp\$, op - c1, 1)                                    CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."                                        bad = -1                                        EXIT DO 'It's a minus sign                                    CASE ELSE                                        'It's a negative sign and needs to count as part of our numbers                                END SELECT                            LOOP UNTIL op - c1 <= 0                            IF bad THEN EXIT DO 'We found our seperator                        CASE ELSE 'Not a valid digit, we found our separator                            EXIT DO                    END SELECT                LOOP UNTIL op - c <= 0                s = op - c                num(1) = MID\$(exp\$, s + 1, op - s - 1) 'Get our first number                num(2) = MID\$(exp\$, op + LEN(OName(OpOn)), E - op - LEN(OName(OpOn)) + 1) 'Get our second number                IF MID\$(num(1), 1, 1) = "N" THEN MID\$(num(1), 1) = "-"                IF MID\$(num(2), 1, 1) = "N" THEN MID\$(num(2), 1) = "-"                num(3) = EvaluateNumbers(OpOn, num())                IF MID\$(num(3), 1, 1) = "-" THEN MID\$(num(3), 1) = "N"                'PRINT "*************"                'PRINT num(1), OName(OpOn), num(2), num(3), exp\$                IF LEFT\$(num(3), 5) = "ERROR" THEN exp\$ = num(3): EXIT SUB                exp\$ = LTRIM\$(N2S(DWD(LEFT\$(exp\$, s) + RTRIM\$(LTRIM\$(num(3))) + MID\$(exp\$, E + 1))))                'PRINT exp\$            END IF            op = 0        LOOP    NEXTEND SUBSUB Set_OrderOfOperations    'PL sets our priortity level. 1 is highest to 65535 for the lowest.    'I used a range here so I could add in new priority levels as needed.    'OName ended up becoming the name of our commands, as I modified things.... Go figure!  LOL!    'Constants get evaluated first, with a Priority Level of 1    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_PI"    REDIM _PRESERVE PL(i): PL(i) = 1    'I'm not certain where exactly percentages should go.  They kind of seem like a special case to me.  COS10% should be COS.1 I'd think...    'I'm putting it here for now, and if anyone knows someplace better for it in our order of operations, let me know.    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "%"    REDIM _PRESERVE PL(i): PL(i) = 5    'Then Functions with PL 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ACOS"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ASIN"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ARCSEC"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ARCCSC"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ARCCOT"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_SECH"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_CSCH"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_COTH"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "COS"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "SIN"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "TAN"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "LOG"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "EXP"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ATN"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_D2R"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_D2G"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_R2D"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_R2G"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_G2D"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_G2R"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ABS"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "SGN"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "INT"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ROUND"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "FIX"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_SEC"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_CSC"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_COT"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ASC"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "CHR\$"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RGB32"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RGBA32"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RGB"    REDIM _PRESERVE PL(i): PL(i) = 10    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RGBA"    REDIM _PRESERVE PL(i): PL(i) = 10    'Exponents with PL 20    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "^"    REDIM _PRESERVE PL(i): PL(i) = 20    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "SQR"    REDIM _PRESERVE PL(i): PL(i) = 20    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ROOT"    REDIM _PRESERVE PL(i): PL(i) = 20    'Multiplication and Division PL 30    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "*"    REDIM _PRESERVE PL(i): PL(i) = 30    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "/"    REDIM _PRESERVE PL(i): PL(i) = 30    'Integer Division PL 40    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "\"    REDIM _PRESERVE PL(i): PL(i) = 40    'MOD PL 50    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "MOD"    REDIM _PRESERVE PL(i): PL(i) = 50    'Addition and Subtraction PL 60    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "+"    REDIM _PRESERVE PL(i): PL(i) = 60    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "-"    REDIM _PRESERVE PL(i): PL(i) = 60    'Relational Operators =, >, <, <>, <=, >=   PL 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "<>"    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "><" 'These next three are just reversed symbols as an attempt to help process a common typo    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "<="    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = ">="    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "=<" 'I personally can never keep these things straight.  Is it < = or = <...    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "=>" 'Who knows, check both!    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = ">"    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "<"    REDIM _PRESERVE PL(i): PL(i) = 70    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "="    REDIM _PRESERVE PL(i): PL(i) = 70    'Logical Operations PL 80+    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "NOT"    REDIM _PRESERVE PL(i): PL(i) = 80    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "AND"    REDIM _PRESERVE PL(i): PL(i) = 90    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "OR"    REDIM _PRESERVE PL(i): PL(i) = 100    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "XOR"    REDIM _PRESERVE PL(i): PL(i) = 110    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "EQV"    REDIM _PRESERVE PL(i): PL(i) = 120    i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "IMP"    REDIM _PRESERVE PL(i): PL(i) = 130END SUBFUNCTION EvaluateNumbers\$ (p, num() AS STRING)    DIM n1 AS _FLOAT, n2 AS _FLOAT, n3 AS _FLOAT    IF INSTR(num(1), ",") THEN EvaluateNumbers\$ = "ERROR - Invalid comma (" + num(1) + ")": EXIT FUNCTION    IF INSTR(num(2), ",") THEN        SELECT CASE OName(p) 'only certain commands should pass a comma value            CASE "_RGB32", "_RGBA32", "_RGB", "_RGBA"            CASE ELSE                EvaluateNumbers\$ = "ERROR - Invalid comma (" + num(2) + ")": EXIT FUNCTION        END SELECT    END IF    SELECT CASE OName(p) 'Depending on our operator..        CASE "_PI": n1 = 3.14159265358979323846264338327950288## 'Future compatable in case something ever stores extra digits for PI        CASE "%": n1 = (VAL(num(1))) / 100 'Note percent is a special case and works with the number BEFORE the % command and not after        CASE "_ACOS": n1 = _ACOS(VAL(num(2)))        CASE "_ASIN": n1 = _ASIN(VAL(num(2)))        CASE "_ARCSEC": n1 = _ARCSEC(VAL(num(2)))        CASE "_ARCCSC": n1 = _ARCCSC(VAL(num(2)))        CASE "_ARCCOT": n1 = _ARCCOT(VAL(num(2)))        CASE "_SECH": n1 = _SECH(VAL(num(2)))        CASE "_CSCH": n1 = _CSCH(VAL(num(2)))        CASE "_COTH": n1 = _COTH(VAL(num(2)))        CASE "_RGB32"            n\$ = num(2)            IF n\$ = "" THEN EvaluateNumbers\$ = "ERROR - Invalid null _RGB32": EXIT FUNCTION            c1 = INSTR(n\$, ",")            IF c1 THEN c2 = INSTR(c1 + 1, n\$, ",")            IF c2 THEN c3 = INSTR(c2 + 1, n\$, ",")            IF c3 THEN c4 = INSTR(c2 + 1, n\$, ",")            IF c1 = 0 THEN 'there's no comma in the command to parse.  It's a grayscale value                n = VAL(num(2))                n1 = _RGB32(n, n, n)            ELSEIF c2 = 0 THEN 'there's one comma and not 2.  It's grayscale with alpha.                n = VAL(LEFT\$(num(2), c1))                n2 = VAL(MID\$(num(2), c1 + 1))                n1 = _RGBA32(n, n, n, n2)            ELSEIF c3 = 0 THEN 'there's two commas.  It's _RGB values                n = VAL(LEFT\$(num(2), c1))                n2 = VAL(MID\$(num(2), c1 + 1))                n3 = VAL(MID\$(num(2), c2 + 1))                n1 = _RGB32(n, n2, n3)            ELSEIF c4 = 0 THEN 'there's three commas.  It's _RGBA values                n = VAL(LEFT\$(num(2), c1))                n2 = VAL(MID\$(num(2), c1 + 1))                n3 = VAL(MID\$(num(2), c2 + 1))                n4 = VAL(MID\$(num(2), c3 + 1))                n1 = _RGBA32(n, n2, n3, n4)            ELSE 'we have more than three commas.  I have no idea WTH type of values got passed here!                EvaluateNumbers\$ = "ERROR - Invalid comma count (" + num(2) + ")": EXIT FUNCTION            END IF        CASE "_RGBA32"            n\$ = num(2)            IF n\$ = "" THEN EvaluateNumbers\$ = "ERROR - Invalid null _RGBA32": EXIT FUNCTION            c1 = INSTR(n\$, ",")            IF c1 THEN c2 = INSTR(c1 + 1, n\$, ",")            IF c2 THEN c3 = INSTR(c2 + 1, n\$, ",")            IF c3 THEN c4 = INSTR(c3 + 1, n\$, ",")            IF c3 = 0 OR c4 <> 0 THEN EvaluateNumbers\$ = "ERROR - Invalid comma count (" + num(2) + ")": EXIT FUNCTION            'we have to have 3 commas; not more, not less.            n = VAL(LEFT\$(num(2), c1))            n2 = VAL(MID\$(num(2), c1 + 1))            n3 = VAL(MID\$(num(2), c2 + 1))            n4 = VAL(MID\$(num(2), c3 + 1))            n1 = _RGBA32(n, n2, n3, n4)        CASE "_RGB"            n\$ = num(2)            IF n\$ = "" THEN EvaluateNumbers\$ = "ERROR - Invalid null _RGB": EXIT FUNCTION            c1 = INSTR(n\$, ",")            IF c1 THEN c2 = INSTR(c1 + 1, n\$, ",")            IF c2 THEN c3 = INSTR(c2 + 1, n\$, ",")            IF c3 THEN c4 = INSTR(c3 + 1, n\$, ",")            IF c3 = 0 OR c4 <> 0 THEN EvaluateNumbers\$ = "ERROR - Invalid comma count (" + num(2) + "). _RGB requires 4 parameters for Red, Green, Blue, ScreenMode.": EXIT FUNCTION            'we have to have 3 commas; not more, not less.            n = VAL(LEFT\$(num(2), c1))            n2 = VAL(MID\$(num(2), c1 + 1))            n3 = VAL(MID\$(num(2), c2 + 1))            n4 = VAL(MID\$(num(2), c3 + 1))            SELECT CASE n4                CASE 0 TO 2, 7 TO 13, 256, 32 'these are the good screen values                CASE ELSE                    EvaluateNumbers\$ = "ERROR - Invalid Screen Mode (" + STR\$(n4) + ")": EXIT FUNCTION            END SELECT            t = _NEWIMAGE(1, 1, n4)            n1 = _RGB(n, n2, n3, t)            _FREEIMAGE t        CASE "_RGBA"            n\$ = num(2)            IF n\$ = "" THEN EvaluateNumbers\$ = "ERROR - Invalid null _RGBA": EXIT FUNCTION            c1 = INSTR(n\$, ",")            IF c1 THEN c2 = INSTR(c1 + 1, n\$, ",")            IF c2 THEN c3 = INSTR(c2 + 1, n\$, ",")            IF c3 THEN c4 = INSTR(c3 + 1, n\$, ",")            IF c4 THEN c5 = INSTR(c4 + 1, n\$, ",")            IF c4 = 0 OR c5 <> 0 THEN EvaluateNumbers\$ = "ERROR - Invalid comma count (" + num(2) + "). _RGBA requires 5 parameters for Red, Green, Blue, Alpha, ScreenMode.": EXIT FUNCTION            'we have to have 4 commas; not more, not less.            n = VAL(LEFT\$(num(2), c1))            n2 = VAL(MID\$(num(2), c1 + 1))            n3 = VAL(MID\$(num(2), c2 + 1))            n4 = VAL(MID\$(num(2), c3 + 1))            n5 = VAL(MID\$(num(2), c4 + 1))            SELECT CASE n5                CASE 0 TO 2, 7 TO 13, 256, 32 'these are the good screen values                CASE ELSE                    EvaluateNumbers\$ = "ERROR - Invalid Screen Mode (" + STR\$(n5) + ")": EXIT FUNCTION            END SELECT            t = _NEWIMAGE(1, 1, n5)            n1 = _RGBA(n, n2, n3, n4, t)            _FREEIMAGE t        CASE "COS": n1 = COS(VAL(num(2)))        CASE "SIN": n1 = SIN(VAL(num(2)))        CASE "TAN": n1 = TAN(VAL(num(2)))        CASE "LOG": n1 = LOG(VAL(num(2)))        CASE "EXP": n1 = EXP(VAL(num(2)))        CASE "ATN": n1 = ATN(VAL(num(2)))        CASE "_D2R": n1 = 0.0174532925 * (VAL(num(2)))        CASE "_D2G": n1 = 1.1111111111 * (VAL(num(2)))        CASE "_R2D": n1 = 57.2957795 * (VAL(num(2)))        CASE "_R2G": n1 = 0.015707963 * (VAL(num(2)))        CASE "_G2D": n1 = 0.9 * (VAL(num(2)))        CASE "_G2R": n1 = 63.661977237 * (VAL(num(2)))        CASE "ABS": n1 = ABS(VAL(num(2)))        CASE "SGN": n1 = SGN(VAL(num(2)))        CASE "INT": n1 = INT(VAL(num(2)))        CASE "_ROUND": n1 = _ROUND(VAL(num(2)))        CASE "FIX": n1 = FIX(VAL(num(2)))        CASE "_SEC": n1 = _SEC(VAL(num(2)))        CASE "_CSC": n1 = _CSC(VAL(num(2)))        CASE "_COT": n1 = _COT(VAL(num(2)))        CASE "^": n1 = VAL(num(1)) ^ VAL(num(2))        CASE "SQR": n1 = SQR(VAL(num(2)))        CASE "ROOT"            n1 = VAL(num(1)): n2 = VAL(num(2))            IF n2 = 1 THEN EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1))): EXIT FUNCTION            IF n1 < 0 AND n2 >= 1 THEN sign = -1: n1 = -n1 ELSE sign = 1            n3 = 1## / n2            IF n3 <> INT(n3) AND n2 < 1 THEN sign = SGN(n1): n1 = ABS(n1)            n1 = sign * (n1 ^ n3)        CASE "*": n1 = VAL(num(1)) * VAL(num(2))        CASE "/": n1 = VAL(num(1)) / VAL(num(2))        CASE "\"            IF VAL(num(2)) <> 0 THEN                n1 = VAL(num(1)) \ VAL(num(2))            ELSE                EvaluateNumbers\$ = "ERROR - Bad operation (We shouldn't see this)"                EXIT FUNCTION            END IF        CASE "MOD": n1 = VAL(num(1)) MOD VAL(num(2))        CASE "+": n1 = VAL(num(1)) + VAL(num(2))        CASE "-": n1 = VAL(num(1)) - VAL(num(2))        CASE "=": n1 = VAL(num(1)) = VAL(num(2))        CASE ">": n1 = VAL(num(1)) > VAL(num(2))        CASE "<": n1 = VAL(num(1)) < VAL(num(2))        CASE "<>", "><": n1 = VAL(num(1)) <> VAL(num(2))        CASE "<=", "=<": n1 = VAL(num(1)) <= VAL(num(2))        CASE ">=", "=>": n1 = VAL(num(1)) >= VAL(num(2))        CASE "NOT": n1 = NOT VAL(num(2))        CASE "AND": n1 = VAL(num(1)) AND VAL(num(2))        CASE "OR": n1 = VAL(num(1)) OR VAL(num(2))        CASE "XOR": n1 = VAL(num(1)) XOR VAL(num(2))        CASE "EQV": n1 = VAL(num(1)) EQV VAL(num(2))        CASE "IMP": n1 = VAL(num(1)) IMP VAL(num(2))        CASE ELSE            EvaluateNumbers\$ = "ERROR - Bad operation (We shouldn't see this)" 'Let's say we're bad...    END SELECT    EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1)))END FUNCTIONFUNCTION DWD\$ (exp\$) 'Deal With Duplicates    'To deal with duplicate operators in our code.    'Such as --  becomes a +    '++ becomes a +    '+- becomes a -    '-+ becomes a -    t\$ = exp\$    DO        bad = 0        DO            l = INSTR(t\$, "++")            IF l THEN t\$ = LEFT\$(t\$, l - 1) + "+" + MID\$(t\$, l + 2): bad = -1        LOOP UNTIL l = 0        DO            l = INSTR(t\$, "+-")            IF l THEN t\$ = LEFT\$(t\$, l - 1) + "-" + MID\$(t\$, l + 2): bad = -1        LOOP UNTIL l = 0        DO            l = INSTR(t\$, "-+")            IF l THEN t\$ = LEFT\$(t\$, l - 1) + "-" + MID\$(t\$, l + 2): bad = -1        LOOP UNTIL l = 0        DO            l = INSTR(t\$, "--")            IF l THEN t\$ = LEFT\$(t\$, l - 1) + "+" + MID\$(t\$, l + 2): bad = -1        LOOP UNTIL l = 0    LOOP UNTIL NOT bad    DWD\$ = t\$    VerifyString t\$END FUNCTIONSUB PreParse (e\$)    DIM f AS _FLOAT    t\$ = e\$    'First strip all spaces    t\$ = ""    FOR i = 1 TO LEN(e\$)        IF MID\$(e\$, i, 1) <> " " THEN t\$ = t\$ + MID\$(e\$, i, 1)    NEXT    t\$ = UCASE\$(t\$)    IF t\$ = "" THEN e\$ = "ERROR -- NULL string; nothing to evaluate": EXIT SUB    'ERROR CHECK by counting our brackets    l = 0    DO        l = INSTR(l + 1, t\$, "("): IF l THEN c = c + 1    LOOP UNTIL l = 0    l = 0    DO        l = INSTR(l + 1, t\$, ")"): IF l THEN c1 = c1 + 1    LOOP UNTIL l = 0    IF c <> c1 THEN e\$ = "ERROR -- Bad Parenthesis:" + STR\$(c) + "( vs" + STR\$(c1) + ")": EXIT SUB    'Modify so that NOT will process properly    l = 0    DO        l = INSTR(l + 1, t\$, "NOT")        IF l THEN            'We need to work magic on the statement so it looks pretty.            ' 1 + NOT 2 + 1 is actually processed as 1 + (NOT 2 + 1)            'Look for something not proper            l1 = INSTR(l + 1, t\$, "AND")            IF l1 = 0 OR (INSTR(l + 1, t\$, "OR") > 0 AND INSTR(l + 1, t\$, "OR") < l1) THEN l1 = INSTR(l + 1, t\$, "OR")            IF l1 = 0 OR (INSTR(l + 1, t\$, "XOR") > 0 AND INSTR(l + 1, t\$, "XOR") < l1) THEN l1 = INSTR(l + 1, t\$, "XOR")            IF l1 = 0 OR (INSTR(l + 1, t\$, "EQV") > 0 AND INSTR(l + 1, t\$, "EQV") < l1) THEN l1 = INSTR(l + 1, t\$, "EQV")            IF l1 = 0 OR (INSTR(l + 1, t\$, "IMP") > 0 AND INSTR(l + 1, t\$, "IMP") < l1) THEN l1 = INSTR(l + 1, t\$, "IMP")            IF l1 = 0 THEN l1 = LEN(t\$) + 1            t\$ = LEFT\$(t\$, l - 1) + "(" + MID\$(t\$, l, l1 - l) + ")" + MID\$(t\$, l + l1 - l)            l = l + 3            'PRINT t\$        END IF    LOOP UNTIL l = 0    'Check for bad operators before a ( bracket    l = 0    DO        l = INSTR(l + 1, t\$, "(")        IF l AND l > 2 THEN 'Don't check the starting bracket; there's nothing before it.            good = 0            FOR i = 1 TO UBOUND(OName)                IF MID\$(t\$, l - LEN(OName(i)), LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)            NEXT            IF NOT good THEN e\$ = "ERROR - Improper operations before (.": EXIT SUB            l = l + 1        END IF    LOOP UNTIL l = 0    'Check for bad operators after a ) bracket    l = 0    DO        l = INSTR(l + 1, t\$, ")")        IF l AND l < LEN(t\$) THEN            good = 0            FOR i = 1 TO UBOUND(OName)                IF MID\$(t\$, l + 1, LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)            NEXT            IF MID\$(t\$, l + 1, 1) = ")" THEN good = -1            IF NOT good THEN e\$ = "ERROR - Improper operations after ).": EXIT SUB            l = l + 1        END IF    LOOP UNTIL l = 0 OR l = LEN(t\$) 'last symbol is a bracket    'Turn all &H (hex) numbers into decimal values for the program to process properly    l = 0    DO        l = INSTR(t\$, "&H")        IF l THEN            E = l + 1: finished = 0            DO                E = E + 1                comp\$ = MID\$(t\$, E, 1)                SELECT CASE comp\$                    CASE "0" TO "9", "A" TO "F" 'All is good, our next digit is a number, continue to add to the hex\$                    CASE ELSE                        good = 0                        FOR i = 1 TO UBOUND(OName)                            IF MID\$(t\$, E, LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)                        NEXT                        IF NOT good THEN e\$ = "ERROR - Improper &H value. (" + comp\$ + ")": EXIT SUB                        E = E - 1                        finished = -1                END SELECT            LOOP UNTIL finished OR E = LEN(t\$)            t\$ = LEFT\$(t\$, l - 1) + LTRIM\$(RTRIM\$(STR\$(VAL(MID\$(t\$, l, E - l + 1))))) + MID\$(t\$, E + 1)        END IF    LOOP UNTIL l = 0    'Turn all &B (binary) numbers into decimal values for the program to process properly    l = 0    DO        l = INSTR(t\$, "&B")        IF l THEN            E = l + 1: finished = 0            DO                E = E + 1                comp\$ = MID\$(t\$, E, 1)                SELECT CASE comp\$                    CASE "0", "1" 'All is good, our next digit is a number, continue to add to the hex\$                    CASE ELSE                        good = 0                        FOR i = 1 TO UBOUND(OName)                            IF MID\$(t\$, E, LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)                        NEXT                        IF NOT good THEN e\$ = "ERROR - Improper &B value. (" + comp\$ + ")": EXIT SUB                        E = E - 1                        finished = -1                END SELECT            LOOP UNTIL finished OR E = LEN(t\$)            bin\$ = MID\$(t\$, l + 2, E - l - 1)            FOR i = 1 TO LEN(bin\$)                IF MID\$(bin\$, i, 1) = "1" THEN f = f + 2 ^ (LEN(bin\$) - i)            NEXT            t\$ = LEFT\$(t\$, l - 1) + LTRIM\$(RTRIM\$(STR\$(f))) + MID\$(t\$, E + 1)        END IF    LOOP UNTIL l = 0    t\$ = N2S(t\$)    VerifyString t\$    e\$ = t\$END SUBSUB VerifyString (t\$)    'ERROR CHECK for unrecognized operations    j = 1    DO        comp\$ = MID\$(t\$, j, 1)        SELECT CASE comp\$            CASE "0" TO "9", ".", "(", ")", ",": j = j + 1            CASE ELSE                good = 0                FOR i = 1 TO UBOUND(OName)                    IF MID\$(t\$, j, LEN(OName(i))) = OName(i) THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)                NEXT                IF NOT good THEN t\$ = "ERROR - Bad Operational value. (" + comp\$ + ")": EXIT SUB                j = j + LEN(OName(i))        END SELECT    LOOP UNTIL j > LEN(t\$)END SUBFUNCTION N2S\$ (exp\$) 'scientific Notation to String    t\$ = LTRIM\$(RTRIM\$(exp\$))    IF LEFT\$(t\$, 1) = "-" THEN sign\$ = "-": t\$ = MID\$(t\$, 2)    dp = INSTR(t\$, "D+"): dm = INSTR(t\$, "D-")    ep = INSTR(t\$, "E+"): em = INSTR(t\$, "E-")    check1 = SGN(dp) + SGN(dm) + SGN(ep) + SGN(em)    IF check1 < 1 OR check1 > 1 THEN N2S = exp\$: EXIT SUB 'If no scientic notation is found, or if we find more than 1 type, it's not SN!    SELECT CASE l 'l now tells us where the SN starts at.        CASE IS < dp: l = dp        CASE IS < dm: l = dm        CASE IS < ep: l = ep        CASE IS < em: l = em    END SELECT    l\$ = LEFT\$(t\$, l - 1) 'The left of the SN    r\$ = MID\$(t\$, l + 1): r&& = VAL(r\$) 'The right of the SN, turned into a workable long    IF INSTR(l\$, ".") THEN 'Location of the decimal, if any        IF r&& > 0 THEN            r&& = r&& - LEN(l\$) + 2        ELSE            r&& = r&& + 1        END IF        l\$ = LEFT\$(l\$, 1) + MID\$(l\$, 3)    END IF    SELECT CASE r&&        CASE 0 'what the heck? We solved it already?            'l\$ = l\$        CASE IS < 0            FOR i = 1 TO -r&&                l\$ = "0" + l\$            NEXT            l\$ = "0." + l\$        CASE ELSE            FOR i = 1 TO r&&                l\$ = l\$ + "0"            NEXT    END SELECT    N2S\$ = sign\$ + l\$END SUB`
For you guys who don't know, this routine is something which you probably already use a lot of the time without even realizing it -- it's the way CONST does our math calculations for us in QB64, when we type something like CONST x = 123 + 456 * 789 + COS(.1) + SIN(_D2R(45))

Recent releases of QB64 have had issues with CONST acting up with a few different things (in particular the color commands), and if you check the original post for this routine, you might be able to see why fairly easily:  They weren't included in the basic math handler here, so they had to be processed manually via a different method.

The interaction of this substitution method and that substitution method, along with commas and multiple variables being on the same line...   just got lost, muddled, and corrupted somewhere.

CONST Red = _RGB32(255,0,0) works as it should.
CONST Red = _RGB32(255,0,0), Yellow = _RGB32(0,255,0) <-- this gets corrupted between all the various substitution methods and goofs up for us.

So, as a simple way to streamline the process and clean up the CONST command so it won't be as hard to both expand and debug in the future, I've expanded the math evaluator.

In the past, we only worked with basic single values.   SIN(x) for example, or "2 + 3".  We could work with one operator (plus, minus, SIN, ect), and one value to the left of it, and one value to the right of it.

1 + 2 <-- this used the left 1, the right 2, and the operator +
SIN(.2) <-- this just used the right .2, and the operator SIN

Commands with multiple parameters didn't work, so we couldn't use them.
_RGB32(255,0,0) <--- the commas in here separate our three values, and would error out...

This overhaul of the math evaluator removes that restriction.  We can now teach it to parse and process functions with multiple parameters, such as _RGB, _RGB32, and all.

In the long run, this will end up being something which I'll swap out to expand/replace our current math evaluation routine, but it could use some extra user testing to make certain nothing glitches out, locks up, or returns false values first.

So...

Kindly test it out.  Toss it all sorts of values (particularly any related to the various _RGB, _RGB32, _RGBA, _RGBA32 commands, and any with stray commas anywhere you'd like), and see if it works as intended.  This thing actually does a good bit of error checking for us, so you'll get a whole variety of error messages to help you diagnose what's wrong with your formula as you type it -- if any seem inappropriate, are worded in a confusing manner, or wrong, kindly report those back to me and I'll try and make them more concise to help debug the issue.

Test it, try to break it, and report back on your results, while I go ahead and assume it's working as intended and expand it to work with several more commands/values which it's currently missing like _RED, _GREEN, _BLUE, _RED32, _GREEN32, _BLUE32, _ALPHA....
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

#### FellippeHeitor

• QB64 Developer
• Forum Resident
• Posts: 3077
• Let it go, this too shall pass.
##### Re: Steve's Math Evaluator
« Reply #3 on: January 05, 2020, 03:21:17 PM »
It's giving invalid comma count when passing alpha to _RGB32:

Line 335:

Code: Text: [Select]
1. IF c3 THEN c4 = INSTR(c3 + 1, n\$, ",") 'instead of IF c3 THEN c4 = INSTR(c2 + 1, n\$, ",")
« Last Edit: January 05, 2020, 03:26:38 PM by FellippeHeitor »

#### SMcNeill

• QB64 Developer
• Forum Resident
• Posts: 3623
##### Re: Steve's Math Evaluator
« Reply #4 on: January 05, 2020, 03:40:39 PM »
Corrected.

Code: QB64: [Select]
1. REDIM SHARED OName(0) AS STRING 'Operation Name
2. REDIM SHARED PL(0) AS INTEGER 'Priority Level
3. DIM SHARED QuickReturn AS INTEGER
4. Set_OrderOfOperations
5.
6.     i\$ = INPUT\$(1)
7.         CASE CHR\$(8)
8.             eval\$ = LEFT\$(eval\$, LEN(eval\$) - 1)
9.         CASE CHR\$(13)
10.             eval\$ = ""
11.         CASE CHR\$(27)
12.             eval\$ = eval\$ + i\$
13.     PRINT eval\$
14.     PRINT Evaluate_Expression(eval\$)
15.
16.
17. 'Steve Subs/Functins for _MATH support with CONST
18. FUNCTION Evaluate_Expression\$ (e\$)
19.     t\$ = e\$ 'So we preserve our original data, we parse a temp copy of it
20.
21.     b = INSTR(UCASE\$(e\$), "EQL") 'take out assignment before the preparser sees it
22.     IF b THEN t\$ = MID\$(e\$, b + 3): var\$ = UCASE\$(LTRIM\$(RTRIM\$(MID\$(e\$, 1, b - 1))))
23.
24.     QuickReturn = 0
25.     PreParse t\$
26.
27.     IF QuickReturn THEN Evaluate_Expression\$ = t\$: EXIT FUNCTION
28.
29.     IF LEFT\$(t\$, 5) = "ERROR" THEN Evaluate_Expression\$ = t\$: EXIT FUNCTION
30.
31.     'Deal with brackets first
32.     EXP\$ = "(" + t\$ + ")" 'Starting and finishing brackets for our parse routine.
33.
34.         Eval_E = INSTR(EXP\$, ")")
35.         IF Eval_E > 0 THEN
36.             c = 0
37.             DO UNTIL Eval_E - c <= 0
38.                 c = c + 1
39.                 IF Eval_E THEN
40.                     IF MID\$(EXP\$, Eval_E - c, 1) = "(" THEN EXIT DO
41.             s = Eval_E - c + 1
42.             IF s < 1 THEN Evaluate_Expression\$ = "ERROR -- BAD () Count": EXIT SUB
43.             eval\$ = " " + MID\$(EXP\$, s, Eval_E - s) + " " 'pad with a space before and after so the parser can pick up the values properly.
44.             ParseExpression eval\$
45.
46.             eval\$ = LTRIM\$(RTRIM\$(eval\$))
47.             IF LEFT\$(eval\$, 5) = "ERROR" THEN Evaluate_Expression\$ = eval\$: EXIT SUB
48.             EXP\$ = DWD(LEFT\$(EXP\$, s - 2) + eval\$ + MID\$(EXP\$, Eval_E + 1))
49.             IF MID\$(EXP\$, 1, 1) = "N" THEN MID\$(EXP\$, 1) = "-"
50.
51.             temppp\$ = DWD(LEFT\$(EXP\$, s - 2) + " ## " + eval\$ + " ## " + MID\$(EXP\$, E + 1))
52.     LOOP UNTIL Eval_E = 0
53.     c = 0
54.         c = c + 1
55.         SELECT CASE MID\$(EXP\$, c, 1)
56.             CASE "0" TO "9", ".", "-" 'At this point, we should only have number values left.
57.             CASE ELSE: Evaluate_Expression\$ = "ERROR - Unknown Diagnosis: (" + EXP\$ + ") ": EXIT SUB
58.     LOOP UNTIL c >= LEN(EXP\$)
59.
60.     Evaluate_Expression\$ = EXP\$
61.
62.
63.
64. SUB ParseExpression (EXP\$)
65.     DIM num(10) AS STRING
66.     'PRINT exp\$
67.     'We should now have an expression with no () to deal with
68.     IF MID\$(EXP\$, 2, 1) = "-" THEN EXP\$ = "0+" + MID\$(EXP\$, 2)
69.     FOR J = 1 TO 250
70.         lowest = 0
71.         DO UNTIL lowest = LEN(EXP\$)
72.             lowest = LEN(EXP\$): OpOn = 0
73.             FOR P = 1 TO UBOUND(OName)
74.                 'Look for first valid operator
75.                 IF J = PL(P) THEN 'Priority levels match
76.                     IF LEFT\$(EXP\$, 1) = "-" THEN op = INSTR(2, EXP\$, OName(P)) ELSE op = INSTR(EXP\$, OName(P))
77.                     IF op > 0 AND op < lowest THEN lowest = op: OpOn = P
78.             IF OpOn = 0 THEN EXIT DO 'We haven't gotten to the proper PL for this OP to be processed yet.
79.             IF LEFT\$(EXP\$, 1) = "-" THEN op = INSTR(2, EXP\$, OName(OpOn)) ELSE op = INSTR(EXP\$, OName(OpOn))
80.             numset = 0
81.
82.             '*** SPECIAL OPERATION RULESETS
83.             IF OName(OpOn) = "-" THEN 'check for BOOLEAN operators before the -
84.                 SELECT CASE MID\$(EXP\$, op - 3, 3)
85.                     CASE "NOT", "XOR", "AND", "EQV", "IMP"
86.                         EXIT DO 'Not an operator, it's a negative
87.                 IF MID\$(EXP\$, op - 3, 2) = "OR" THEN EXIT DO 'Not an operator, it's a negative
88.
89.             IF op THEN
90.                 c = LEN(OName(OpOn)) - 1
91.                     SELECT CASE MID\$(EXP\$, op + c + 1, 1)
92.                         CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "N": numset = -1 'Valid digit
93.                         CASE "-" 'We need to check if it's a minus or a negative
94.                             IF OName(OpOn) = "_PI" OR numset THEN EXIT DO
95.                         CASE ",": numset = 0
96.                         CASE ELSE 'Not a valid digit, we found our separator
97.                     c = c + 1
98.                 LOOP UNTIL op + c >= LEN(EXP\$)
99.                 E = op + c
100.
101.                 c = 0
102.                     c = c + 1
103.                     SELECT CASE MID\$(EXP\$, op - c, 1)
104.                         CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "N" 'Valid digit
105.                         CASE "-" 'We need to check if it's a minus or a negative
106.                             c1 = c
107.                             bad = 0
108.                                 c1 = c1 + 1
109.                                 SELECT CASE MID\$(EXP\$, op - c1, 1)
110.                                     CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."
111.                                         bad = -1
112.                                         EXIT DO 'It's a minus sign
113.                                         'It's a negative sign and needs to count as part of our numbers
114.                             LOOP UNTIL op - c1 <= 0
115.                             IF bad THEN EXIT DO 'We found our seperator
116.                         CASE ELSE 'Not a valid digit, we found our separator
117.                 LOOP UNTIL op - c <= 0
118.                 s = op - c
119.                 num(1) = MID\$(EXP\$, s + 1, op - s - 1) 'Get our first number
120.                 num(2) = MID\$(EXP\$, op + LEN(OName(OpOn)), E - op - LEN(OName(OpOn)) + 1) 'Get our second number
121.                 IF MID\$(num(1), 1, 1) = "N" THEN MID\$(num(1), 1) = "-"
122.                 IF MID\$(num(2), 1, 1) = "N" THEN MID\$(num(2), 1) = "-"
123.                 num(3) = EvaluateNumbers(OpOn, num())
124.                 IF MID\$(num(3), 1, 1) = "-" THEN MID\$(num(3), 1) = "N"
125.                 'PRINT "*************"
126.                 'PRINT num(1), OName(OpOn), num(2), num(3), exp\$
127.                 IF LEFT\$(num(3), 5) = "ERROR" THEN EXP\$ = num(3): EXIT SUB
128.                 EXP\$ = LTRIM\$(N2S(DWD(LEFT\$(EXP\$, s) + RTRIM\$(LTRIM\$(num(3))) + MID\$(EXP\$, E + 1))))
129.                 'PRINT exp\$
130.             op = 0
131.
132.
133.
134.
135. SUB Set_OrderOfOperations
136.     'PL sets our priortity level. 1 is highest to 65535 for the lowest.
137.     'I used a range here so I could add in new priority levels as needed.
138.     'OName ended up becoming the name of our commands, as I modified things.... Go figure!  LOL!
139.
140.     'Constants get evaluated first, with a Priority Level of 1
141.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_PI"
142.     REDIM _PRESERVE PL(i): PL(i) = 1
143.     'I'm not certain where exactly percentages should go.  They kind of seem like a special case to me.  COS10% should be COS.1 I'd think...
144.     'I'm putting it here for now, and if anyone knows someplace better for it in our order of operations, let me know.
145.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "%"
146.     REDIM _PRESERVE PL(i): PL(i) = 5
147.     'Then Functions with PL 10
148.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ACOS"
149.     REDIM _PRESERVE PL(i): PL(i) = 10
150.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ASIN"
151.     REDIM _PRESERVE PL(i): PL(i) = 10
152.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ARCSEC"
153.     REDIM _PRESERVE PL(i): PL(i) = 10
154.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ARCCSC"
155.     REDIM _PRESERVE PL(i): PL(i) = 10
156.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ARCCOT"
157.     REDIM _PRESERVE PL(i): PL(i) = 10
158.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_SECH"
159.     REDIM _PRESERVE PL(i): PL(i) = 10
160.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_CSCH"
161.     REDIM _PRESERVE PL(i): PL(i) = 10
162.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_COTH"
163.     REDIM _PRESERVE PL(i): PL(i) = 10
164.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "COS"
165.     REDIM _PRESERVE PL(i): PL(i) = 10
166.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "SIN"
167.     REDIM _PRESERVE PL(i): PL(i) = 10
168.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "TAN"
169.     REDIM _PRESERVE PL(i): PL(i) = 10
170.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "LOG"
171.     REDIM _PRESERVE PL(i): PL(i) = 10
172.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "EXP"
173.     REDIM _PRESERVE PL(i): PL(i) = 10
174.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ATN"
175.     REDIM _PRESERVE PL(i): PL(i) = 10
176.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_D2R"
177.     REDIM _PRESERVE PL(i): PL(i) = 10
178.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_D2G"
179.     REDIM _PRESERVE PL(i): PL(i) = 10
180.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_R2D"
181.     REDIM _PRESERVE PL(i): PL(i) = 10
182.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_R2G"
183.     REDIM _PRESERVE PL(i): PL(i) = 10
184.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_G2D"
185.     REDIM _PRESERVE PL(i): PL(i) = 10
186.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_G2R"
187.     REDIM _PRESERVE PL(i): PL(i) = 10
188.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ABS"
189.     REDIM _PRESERVE PL(i): PL(i) = 10
190.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "SGN"
191.     REDIM _PRESERVE PL(i): PL(i) = 10
192.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "INT"
193.     REDIM _PRESERVE PL(i): PL(i) = 10
194.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ROUND"
195.     REDIM _PRESERVE PL(i): PL(i) = 10
196.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "FIX"
197.     REDIM _PRESERVE PL(i): PL(i) = 10
198.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_SEC"
199.     REDIM _PRESERVE PL(i): PL(i) = 10
200.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_CSC"
201.     REDIM _PRESERVE PL(i): PL(i) = 10
202.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_COT"
203.     REDIM _PRESERVE PL(i): PL(i) = 10
204.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ASC"
205.     REDIM _PRESERVE PL(i): PL(i) = 10
206.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "CHR\$"
207.     REDIM _PRESERVE PL(i): PL(i) = 10
208.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RGB32"
209.     REDIM _PRESERVE PL(i): PL(i) = 10
210.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RGBA32"
211.     REDIM _PRESERVE PL(i): PL(i) = 10
212.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RGB"
213.     REDIM _PRESERVE PL(i): PL(i) = 10
214.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RGBA"
215.     REDIM _PRESERVE PL(i): PL(i) = 10
216.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RED"
217.     REDIM _PRESERVE PL(i): PL(i) = 10
218.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_GREEN"
219.     REDIM _PRESERVE PL(i): PL(i) = 10
220.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_BLUE"
221.     REDIM _PRESERVE PL(i): PL(i) = 10
222.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ALPHA"
223.     REDIM _PRESERVE PL(i): PL(i) = 10
224.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RED32"
225.     REDIM _PRESERVE PL(i): PL(i) = 10
226.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_GREEN32"
227.     REDIM _PRESERVE PL(i): PL(i) = 10
228.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_BLUE32"
229.     REDIM _PRESERVE PL(i): PL(i) = 10
230.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ALPHA32"
231.     REDIM _PRESERVE PL(i): PL(i) = 10
232.
233.     'Exponents with PL 20
234.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "^"
235.     REDIM _PRESERVE PL(i): PL(i) = 20
236.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "SQR"
237.     REDIM _PRESERVE PL(i): PL(i) = 20
238.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ROOT"
239.     REDIM _PRESERVE PL(i): PL(i) = 20
240.     'Multiplication and Division PL 30
241.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "*"
242.     REDIM _PRESERVE PL(i): PL(i) = 30
243.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "/"
244.     REDIM _PRESERVE PL(i): PL(i) = 30
245.     'Integer Division PL 40
246.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "\"
247.     REDIM _PRESERVE PL(i): PL(i) = 40
248.     'MOD PL 50
249.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "MOD"
250.     REDIM _PRESERVE PL(i): PL(i) = 50
251.     'Addition and Subtraction PL 60
252.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "+"
253.     REDIM _PRESERVE PL(i): PL(i) = 60
254.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "-"
255.     REDIM _PRESERVE PL(i): PL(i) = 60
256.
257.     'Relational Operators =, >, <, <>, <=, >=   PL 70
258.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "<>"
259.     REDIM _PRESERVE PL(i): PL(i) = 70
260.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "><" 'These next three are just reversed symbols as an attempt to help process a common typo
261.     REDIM _PRESERVE PL(i): PL(i) = 70
262.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "<="
263.     REDIM _PRESERVE PL(i): PL(i) = 70
264.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = ">="
265.     REDIM _PRESERVE PL(i): PL(i) = 70
266.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "=<" 'I personally can never keep these things straight.  Is it < = or = <...
267.     REDIM _PRESERVE PL(i): PL(i) = 70
268.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "=>" 'Who knows, check both!
269.     REDIM _PRESERVE PL(i): PL(i) = 70
270.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = ">"
271.     REDIM _PRESERVE PL(i): PL(i) = 70
272.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "<"
273.     REDIM _PRESERVE PL(i): PL(i) = 70
274.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "="
275.     REDIM _PRESERVE PL(i): PL(i) = 70
276.     'Logical Operations PL 80+
277.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "NOT"
278.     REDIM _PRESERVE PL(i): PL(i) = 80
279.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "AND"
280.     REDIM _PRESERVE PL(i): PL(i) = 90
281.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "OR"
282.     REDIM _PRESERVE PL(i): PL(i) = 100
283.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "XOR"
284.     REDIM _PRESERVE PL(i): PL(i) = 110
285.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "EQV"
286.     REDIM _PRESERVE PL(i): PL(i) = 120
287.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "IMP"
288.     REDIM _PRESERVE PL(i): PL(i) = 130
289.
290.
291. FUNCTION EvaluateNumbers\$ (p, num() AS STRING)
292.     DIM n1 AS _FLOAT, n2 AS _FLOAT, n3 AS _FLOAT
293.     IF INSTR(num(1), ",") THEN EvaluateNumbers\$ = "ERROR - Invalid comma (" + num(1) + ")": EXIT FUNCTION
294.     IF INSTR(num(2), ",") THEN
295.         SELECT CASE OName(p) 'only certain commands should pass a comma value
296.             CASE "_RGB32", "_RGBA32", "_RGB", "_RGBA", "_RED", "_GREEN", "_BLUE", "_ALPHA"
297.                 EvaluateNumbers\$ = "ERROR - Invalid comma (" + num(2) + ")": EXIT FUNCTION
298.
299.     SELECT CASE PL(p) 'divide up the work so we want do as much case checking
300.         CASE 10 'functions
301.             SELECT CASE OName(p) 'Depending on our operator..
302.                 CASE "_ACOS": n1 = _ACOS(VAL(num(2)))
303.                 CASE "_ASIN": n1 = _ASIN(VAL(num(2)))
304.                 CASE "_ARCSEC": n1 = _ARCSEC(VAL(num(2)))
305.                 CASE "_ARCCSC": n1 = _ARCCSC(VAL(num(2)))
306.                 CASE "_ARCCOT": n1 = _ARCCOT(VAL(num(2)))
307.                 CASE "_SECH": n1 = _SECH(VAL(num(2)))
308.                 CASE "_CSCH": n1 = _CSCH(VAL(num(2)))
309.                 CASE "_COTH": n1 = _COTH(VAL(num(2)))
310.                 CASE "_RGB32"
311.                     n\$ = num(2)
312.                     IF n\$ = "" THEN EvaluateNumbers\$ = "ERROR - Invalid null _RGB32": EXIT FUNCTION
313.                     c1 = INSTR(n\$, ",")
314.                     IF c1 THEN c2 = INSTR(c1 + 1, n\$, ",")
315.                     IF c2 THEN c3 = INSTR(c2 + 1, n\$, ",")
316.                     IF c3 THEN c4 = INSTR(c3 + 1, n\$, ",")
317.                     IF c1 = 0 THEN 'there's no comma in the command to parse.  It's a grayscale value
318.                         n = VAL(num(2))
319.                         n1 = _RGB32(n, n, n)
320.                     ELSEIF c2 = 0 THEN 'there's one comma and not 2.  It's grayscale with alpha.
321.                         n = VAL(LEFT\$(num(2), c1))
322.                         n2 = VAL(MID\$(num(2), c1 + 1))
323.                         n1 = _RGBA32(n, n, n, n2)
324.                     ELSEIF c3 = 0 THEN 'there's two commas.  It's _RGB values
325.                         n = VAL(LEFT\$(num(2), c1))
326.                         n2 = VAL(MID\$(num(2), c1 + 1))
327.                         n3 = VAL(MID\$(num(2), c2 + 1))
328.                         n1 = _RGB32(n, n2, n3)
329.                     ELSEIF c4 = 0 THEN 'there's three commas.  It's _RGBA values
330.                         n = VAL(LEFT\$(num(2), c1))
331.                         n2 = VAL(MID\$(num(2), c1 + 1))
332.                         n3 = VAL(MID\$(num(2), c2 + 1))
333.                         n4 = VAL(MID\$(num(2), c3 + 1))
334.                         n1 = _RGBA32(n, n2, n3, n4)
335.                     ELSE 'we have more than three commas.  I have no idea WTH type of values got passed here!
336.                         EvaluateNumbers\$ = "ERROR - Invalid comma count (" + num(2) + ")": EXIT FUNCTION
337.                 CASE "_RGBA32"
338.                     n\$ = num(2)
339.                     IF n\$ = "" THEN EvaluateNumbers\$ = "ERROR - Invalid null _RGBA32": EXIT FUNCTION
340.                     c1 = INSTR(n\$, ",")
341.                     IF c1 THEN c2 = INSTR(c1 + 1, n\$, ",")
342.                     IF c2 THEN c3 = INSTR(c2 + 1, n\$, ",")
343.                     IF c3 THEN c4 = INSTR(c3 + 1, n\$, ",")
344.                     IF c3 = 0 OR c4 <> 0 THEN EvaluateNumbers\$ = "ERROR - Invalid comma count (" + num(2) + ")": EXIT FUNCTION
345.                     'we have to have 3 commas; not more, not less.
346.                     n = VAL(LEFT\$(num(2), c1))
347.                     n2 = VAL(MID\$(num(2), c1 + 1))
348.                     n3 = VAL(MID\$(num(2), c2 + 1))
349.                     n4 = VAL(MID\$(num(2), c3 + 1))
350.                     n1 = _RGBA32(n, n2, n3, n4)
351.                 CASE "_RGB"
352.                     n\$ = num(2)
353.                     IF n\$ = "" THEN EvaluateNumbers\$ = "ERROR - Invalid null _RGB": EXIT FUNCTION
354.                     c1 = INSTR(n\$, ",")
355.                     IF c1 THEN c2 = INSTR(c1 + 1, n\$, ",")
356.                     IF c2 THEN c3 = INSTR(c2 + 1, n\$, ",")
357.                     IF c3 THEN c4 = INSTR(c3 + 1, n\$, ",")
358.                     IF c3 = 0 OR c4 <> 0 THEN EvaluateNumbers\$ = "ERROR - Invalid comma count (" + num(2) + "). _RGB requires 4 parameters for Red, Green, Blue, ScreenMode.": EXIT FUNCTION
359.                     'we have to have 3 commas; not more, not less.
360.                     n = VAL(LEFT\$(num(2), c1))
361.                     n2 = VAL(MID\$(num(2), c1 + 1))
362.                     n3 = VAL(MID\$(num(2), c2 + 1))
363.                     n4 = VAL(MID\$(num(2), c3 + 1))
364.                         CASE 0 TO 2, 7 TO 13, 256, 32 'these are the good screen values
365.                             EvaluateNumbers\$ = "ERROR - Invalid Screen Mode (" + STR\$(n4) + ")": EXIT FUNCTION
366.                     t = _NEWIMAGE(1, 1, n4)
367.                     n1 = _RGB(n, n2, n3, t)
368.                 CASE "_RGBA"
369.                     n\$ = num(2)
370.                     IF n\$ = "" THEN EvaluateNumbers\$ = "ERROR - Invalid null _RGBA": EXIT FUNCTION
371.                     c1 = INSTR(n\$, ",")
372.                     IF c1 THEN c2 = INSTR(c1 + 1, n\$, ",")
373.                     IF c2 THEN c3 = INSTR(c2 + 1, n\$, ",")
374.                     IF c3 THEN c4 = INSTR(c3 + 1, n\$, ",")
375.                     IF c4 THEN c5 = INSTR(c4 + 1, n\$, ",")
376.                     IF c4 = 0 OR c5 <> 0 THEN EvaluateNumbers\$ = "ERROR - Invalid comma count (" + num(2) + "). _RGBA requires 5 parameters for Red, Green, Blue, Alpha, ScreenMode.": EXIT FUNCTION
377.                     'we have to have 4 commas; not more, not less.
378.                     n = VAL(LEFT\$(num(2), c1))
379.                     n2 = VAL(MID\$(num(2), c1 + 1))
380.                     n3 = VAL(MID\$(num(2), c2 + 1))
381.                     n4 = VAL(MID\$(num(2), c3 + 1))
382.                     n5 = VAL(MID\$(num(2), c4 + 1))
383.                         CASE 0 TO 2, 7 TO 13, 256, 32 'these are the good screen values
384.                             EvaluateNumbers\$ = "ERROR - Invalid Screen Mode (" + STR\$(n5) + ")": EXIT FUNCTION
385.                     t = _NEWIMAGE(1, 1, n5)
386.                     n1 = _RGBA(n, n2, n3, n4, t)
387.                 CASE "_RED", "_GREEN", "_BLUE", "_ALPHA"
388.                     n\$ = num(2)
389.                     IF n\$ = "" THEN EvaluateNumbers\$ = "ERROR - Invalid null " + OName(p): EXIT FUNCTION
390.                     c1 = INSTR(n\$, ",")
391.                     IF c1 = 0 THEN EvaluateNumbers\$ = "ERROR - " + OName(p) + " requires 2 parameters for Color, ScreenMode.": EXIT FUNCTION
392.                     IF c1 THEN c2 = INSTR(c1 + 1, n\$, ",")
393.                     IF c2 THEN EvaluateNumbers\$ = "ERROR - " + OName(p) + " requires 2 parameters for Color, ScreenMode.": EXIT FUNCTION
394.                     n = VAL(LEFT\$(num(2), c1))
395.                     n2 = VAL(MID\$(num(2), c1 + 1))
396.                         CASE 0 TO 2, 7 TO 13, 256, 32 'these are the good screen values
397.                             EvaluateNumbers\$ = "ERROR - Invalid Screen Mode (" + STR\$(n2) + ")": EXIT FUNCTION
398.                     t = _NEWIMAGE(1, 1, n4)
399.                     SELECT CASE OName(p)
400.                         CASE "_RED": n1 = _RED(n, t)
401.                         CASE "_BLUE": n1 = _BLUE(n, t)
402.                         CASE "_GREEN": n1 = _GREEN(n, t)
403.                         CASE "_ALPHA": n1 = _ALPHA(n, t)
404.                 CASE "_RED32", "_GREEN32", "_BLUE32", "_ALPHA32"
405.                     n\$ = num(2)
406.                     IF n\$ = "" THEN EvaluateNumbers\$ = "ERROR - Invalid null " + OName(p): EXIT FUNCTION
407.                     n = VAL(num(2))
408.                     SELECT CASE OName(p)
409.                         CASE "_RED32": n1 = _RED32(n)
410.                         CASE "_BLUE32": n1 = _BLUE32(n)
411.                         CASE "_GREEN32": n1 = _GREEN32(n)
412.                         CASE "_ALPHA32": n1 = _ALPHA32(n)
413.                 CASE "COS": n1 = COS(VAL(num(2)))
414.                 CASE "SIN": n1 = SIN(VAL(num(2)))
415.                 CASE "TAN": n1 = TAN(VAL(num(2)))
416.                 CASE "LOG": n1 = LOG(VAL(num(2)))
417.                 CASE "EXP": n1 = EXP(VAL(num(2)))
418.                 CASE "ATN": n1 = ATN(VAL(num(2)))
419.                 CASE "_D2R": n1 = 0.0174532925 * (VAL(num(2)))
420.                 CASE "_D2G": n1 = 1.1111111111 * (VAL(num(2)))
421.                 CASE "_R2D": n1 = 57.2957795 * (VAL(num(2)))
422.                 CASE "_R2G": n1 = 0.015707963 * (VAL(num(2)))
423.                 CASE "_G2D": n1 = 0.9 * (VAL(num(2)))
424.                 CASE "_G2R": n1 = 63.661977237 * (VAL(num(2)))
425.                 CASE "ABS": n1 = ABS(VAL(num(2)))
426.                 CASE "SGN": n1 = SGN(VAL(num(2)))
427.                 CASE "INT": n1 = INT(VAL(num(2)))
428.                 CASE "_ROUND": n1 = _ROUND(VAL(num(2)))
429.                 CASE "FIX": n1 = FIX(VAL(num(2)))
430.                 CASE "_SEC": n1 = _SEC(VAL(num(2)))
431.                 CASE "_CSC": n1 = _CSC(VAL(num(2)))
432.                 CASE "_COT": n1 = _COT(VAL(num(2)))
433.         CASE 20 TO 60 'Math Operators
434.             SELECT CASE OName(p) 'Depending on our operator..
435.                 CASE "^": n1 = VAL(num(1)) ^ VAL(num(2))
436.                 CASE "SQR": n1 = SQR(VAL(num(2)))
437.                 CASE "ROOT"
438.                     n1 = VAL(num(1)): n2 = VAL(num(2))
439.                     IF n2 = 1 THEN EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1))): EXIT FUNCTION
440.                     IF n1 < 0 AND n2 >= 1 THEN sign = -1: n1 = -n1 ELSE sign = 1
441.                     n3 = 1## / n2
442.                     IF n3 <> INT(n3) AND n2 < 1 THEN sign = SGN(n1): n1 = ABS(n1)
443.                     n1 = sign * (n1 ^ n3)
444.                 CASE "*": n1 = VAL(num(1)) * VAL(num(2))
445.                 CASE "/": n1 = VAL(num(1)) / VAL(num(2))
446.                 CASE "\"
447.                     IF VAL(num(2)) <> 0 THEN
448.                         n1 = VAL(num(1)) \ VAL(num(2))
449.                         EvaluateNumbers\$ = "ERROR - Bad operation (We shouldn't see this)"
450.                 CASE "MOD": n1 = VAL(num(1)) MOD VAL(num(2))
451.                 CASE "+": n1 = VAL(num(1)) + VAL(num(2))
452.                 CASE "-": n1 = VAL(num(1)) - VAL(num(2))
453.         CASE 70 'Relational Operators =, >, <, <>, <=, >=
454.             SELECT CASE OName(p) 'Depending on our operator..
455.                 CASE "=": n1 = VAL(num(1)) = VAL(num(2))
456.                 CASE ">": n1 = VAL(num(1)) > VAL(num(2))
457.                 CASE "<": n1 = VAL(num(1)) < VAL(num(2))
458.                 CASE "<>", "><": n1 = VAL(num(1)) <> VAL(num(2))
459.                 CASE "<=", "=<": n1 = VAL(num(1)) <= VAL(num(2))
460.                 CASE ">=", "=>": n1 = VAL(num(1)) >= VAL(num(2))
461.         CASE ELSE 'a value we haven't processed elsewhere
462.             SELECT CASE OName(p) 'Depending on our operator..
463.                 CASE "_PI": n1 = 3.14159265358979323846264338327950288## 'Future compatable in case something ever stores extra digits for PI
464.                 CASE "%": n1 = (VAL(num(1))) / 100 'Note percent is a special case and works with the number BEFORE the % command and not after
465.                 CASE "NOT": n1 = NOT VAL(num(2))
466.                 CASE "AND": n1 = VAL(num(1)) AND VAL(num(2))
467.                 CASE "OR": n1 = VAL(num(1)) OR VAL(num(2))
468.                 CASE "XOR": n1 = VAL(num(1)) XOR VAL(num(2))
469.                 CASE "EQV": n1 = VAL(num(1)) EQV VAL(num(2))
470.                 CASE "IMP": n1 = VAL(num(1)) IMP VAL(num(2))
471.
472.     EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1)))
473.
474. FUNCTION DWD\$ (EXP\$) 'Deal With Duplicates
475.     'To deal with duplicate operators in our code.
476.     'Such as --  becomes a +
477.     '++ becomes a +
478.     '+- becomes a -
479.     '-+ becomes a -
480.     t\$ = EXP\$
481.         bad = 0
482.             l = INSTR(t\$, "++")
483.             IF l THEN t\$ = LEFT\$(t\$, l - 1) + "+" + MID\$(t\$, l + 2): bad = -1
484.         LOOP UNTIL l = 0
485.             l = INSTR(t\$, "+-")
486.             IF l THEN t\$ = LEFT\$(t\$, l - 1) + "-" + MID\$(t\$, l + 2): bad = -1
487.         LOOP UNTIL l = 0
488.             l = INSTR(t\$, "-+")
489.             IF l THEN t\$ = LEFT\$(t\$, l - 1) + "-" + MID\$(t\$, l + 2): bad = -1
490.         LOOP UNTIL l = 0
491.             l = INSTR(t\$, "--")
492.             IF l THEN t\$ = LEFT\$(t\$, l - 1) + "+" + MID\$(t\$, l + 2): bad = -1
493.         LOOP UNTIL l = 0
494.     LOOP UNTIL NOT bad
495.     DWD\$ = t\$
496.     VerifyString t\$
497.
498. SUB PreParse (e\$)
499.
500.     t\$ = e\$
501.
502.     'First strip all spaces
503.     t\$ = ""
504.     FOR i = 1 TO LEN(e\$)
505.         IF MID\$(e\$, i, 1) <> " " THEN t\$ = t\$ + MID\$(e\$, i, 1)
506.
507.     t\$ = UCASE\$(t\$)
508.     IF t\$ = "" THEN e\$ = "ERROR -- NULL string; nothing to evaluate": EXIT SUB
509.
510.     'ERROR CHECK by counting our brackets
511.     l = 0
512.         l = INSTR(l + 1, t\$, "("): IF l THEN c = c + 1
513.     LOOP UNTIL l = 0
514.     l = 0
515.         l = INSTR(l + 1, t\$, ")"): IF l THEN c1 = c1 + 1
516.     LOOP UNTIL l = 0
517.     IF c <> c1 THEN e\$ = "ERROR -- Bad Parenthesis:" + STR\$(c) + "( vs" + STR\$(c1) + ")": EXIT SUB
518.
519.     'Modify so that NOT will process properly
520.     l = 0
521.         l = INSTR(l + 1, t\$, "NOT")
522.             'We need to work magic on the statement so it looks pretty.
523.             ' 1 + NOT 2 + 1 is actually processed as 1 + (NOT 2 + 1)
524.             'Look for something not proper
525.             l1 = INSTR(l + 1, t\$, "AND")
526.             IF l1 = 0 OR (INSTR(l + 1, t\$, "OR") > 0 AND INSTR(l + 1, t\$, "OR") < l1) THEN l1 = INSTR(l + 1, t\$, "OR")
527.             IF l1 = 0 OR (INSTR(l + 1, t\$, "XOR") > 0 AND INSTR(l + 1, t\$, "XOR") < l1) THEN l1 = INSTR(l + 1, t\$, "XOR")
528.             IF l1 = 0 OR (INSTR(l + 1, t\$, "EQV") > 0 AND INSTR(l + 1, t\$, "EQV") < l1) THEN l1 = INSTR(l + 1, t\$, "EQV")
529.             IF l1 = 0 OR (INSTR(l + 1, t\$, "IMP") > 0 AND INSTR(l + 1, t\$, "IMP") < l1) THEN l1 = INSTR(l + 1, t\$, "IMP")
530.             IF l1 = 0 THEN l1 = LEN(t\$) + 1
531.             t\$ = LEFT\$(t\$, l - 1) + "(" + MID\$(t\$, l, l1 - l) + ")" + MID\$(t\$, l + l1 - l)
532.             l = l + 3
533.             'PRINT t\$
534.     LOOP UNTIL l = 0
535.
536.     'Check for bad operators before a ( bracket
537.     l = 0
538.         l = INSTR(l + 1, t\$, "(")
539.         IF l AND l > 2 THEN 'Don't check the starting bracket; there's nothing before it.
540.             good = 0
541.             FOR i = 1 TO UBOUND(OName)
542.                 IF MID\$(t\$, l - LEN(OName(i)), LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)
543.             IF NOT good THEN e\$ = "ERROR - Improper operations before (.": EXIT SUB
544.             l = l + 1
545.     LOOP UNTIL l = 0
546.
547.     'Check for bad operators after a ) bracket
548.     l = 0
549.         l = INSTR(l + 1, t\$, ")")
550.         IF l AND l < LEN(t\$) THEN
551.             good = 0
552.             FOR i = 1 TO UBOUND(OName)
553.                 IF MID\$(t\$, l + 1, LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)
554.             IF MID\$(t\$, l + 1, 1) = ")" THEN good = -1
555.             IF NOT good THEN e\$ = "ERROR - Improper operations after ).": EXIT SUB
556.             l = l + 1
557.     LOOP UNTIL l = 0 OR l = LEN(t\$) 'last symbol is a bracket
558.
559.     'Turn all &H (hex) numbers into decimal values for the program to process properly
560.     l = 0
561.         l = INSTR(t\$, "&H")
562.             E = l + 1: finished = 0
563.                 E = E + 1
564.                 comp\$ = MID\$(t\$, E, 1)
565.                 SELECT CASE comp\$
566.                     CASE "0" TO "9", "A" TO "F" 'All is good, our next digit is a number, continue to add to the hex\$
567.                         good = 0
568.                         FOR i = 1 TO UBOUND(OName)
569.                             IF MID\$(t\$, E, LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)
570.                         IF NOT good THEN e\$ = "ERROR - Improper &H value. (" + comp\$ + ")": EXIT SUB
571.                         E = E - 1
572.                         finished = -1
573.             LOOP UNTIL finished OR E = LEN(t\$)
574.             t\$ = LEFT\$(t\$, l - 1) + LTRIM\$(RTRIM\$(STR\$(VAL(MID\$(t\$, l, E - l + 1))))) + MID\$(t\$, E + 1)
575.     LOOP UNTIL l = 0
576.
577.     'Turn all &B (binary) numbers into decimal values for the program to process properly
578.     l = 0
579.         l = INSTR(t\$, "&B")
580.             E = l + 1: finished = 0
581.                 E = E + 1
582.                 comp\$ = MID\$(t\$, E, 1)
583.                 SELECT CASE comp\$
584.                     CASE "0", "1" 'All is good, our next digit is a number, continue to add to the hex\$
585.                         good = 0
586.                         FOR i = 1 TO UBOUND(OName)
587.                             IF MID\$(t\$, E, LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)
588.                         IF NOT good THEN e\$ = "ERROR - Improper &B value. (" + comp\$ + ")": EXIT SUB
589.                         E = E - 1
590.                         finished = -1
591.             LOOP UNTIL finished OR E = LEN(t\$)
592.             bin\$ = MID\$(t\$, l + 2, E - l - 1)
593.             FOR i = 1 TO LEN(bin\$)
594.                 IF MID\$(bin\$, i, 1) = "1" THEN f = f + 2 ^ (LEN(bin\$) - i)
595.             t\$ = LEFT\$(t\$, l - 1) + LTRIM\$(RTRIM\$(STR\$(f))) + MID\$(t\$, E + 1)
596.     LOOP UNTIL l = 0
597.
598.     t\$ = N2S(t\$)
599.     VerifyString t\$
600.
601.     e\$ = t\$
602.
603.
604.
605. SUB VerifyString (t\$)
606.     'ERROR CHECK for unrecognized operations
607.     j = 1
608.         comp\$ = MID\$(t\$, j, 1)
609.         SELECT CASE comp\$
610.             CASE "0" TO "9", ".", "(", ")", ",": j = j + 1
611.                 good = 0
612.                 FOR i = 1 TO UBOUND(OName)
613.                     IF MID\$(t\$, j, LEN(OName(i))) = OName(i) THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)
614.                 IF NOT good THEN t\$ = "ERROR - Bad Operational value. (" + comp\$ + ")": EXIT SUB
615.                 j = j + LEN(OName(i))
616.     LOOP UNTIL j > LEN(t\$)
617.
618. FUNCTION N2S\$ (EXP\$) 'scientific Notation to String
619.     t\$ = LTRIM\$(RTRIM\$(EXP\$))
620.     IF LEFT\$(t\$, 1) = "-" THEN sign\$ = "-": t\$ = MID\$(t\$, 2)
621.
622.     dp = INSTR(t\$, "D+"): dm = INSTR(t\$, "D-")
623.     ep = INSTR(t\$, "E+"): em = INSTR(t\$, "E-")
624.     check1 = SGN(dp) + SGN(dm) + SGN(ep) + SGN(em)
625.     IF check1 < 1 OR check1 > 1 THEN N2S = EXP\$: EXIT SUB 'If no scientic notation is found, or if we find more than 1 type, it's not SN!
626.
627.     SELECT CASE l 'l now tells us where the SN starts at.
628.         CASE IS < dp: l = dp
629.         CASE IS < dm: l = dm
630.         CASE IS < ep: l = ep
631.         CASE IS < em: l = em
632.
633.     l\$ = LEFT\$(t\$, l - 1) 'The left of the SN
634.     r\$ = MID\$(t\$, l + 1): r&& = VAL(r\$) 'The right of the SN, turned into a workable long
635.
636.
637.     IF INSTR(l\$, ".") THEN 'Location of the decimal, if any
638.         IF r&& > 0 THEN
639.             r&& = r&& - LEN(l\$) + 2
640.             r&& = r&& + 1
641.         l\$ = LEFT\$(l\$, 1) + MID\$(l\$, 3)
642.
643.     SELECT CASE r&&
644.         CASE 0 'what the heck? We solved it already?
645.             'l\$ = l\$
646.         CASE IS < 0
647.             FOR i = 1 TO -r&&
648.                 l\$ = "0" + l\$
649.             l\$ = "0." + l\$
650.             FOR i = 1 TO r&&
651.                 l\$ = l\$ + "0"
652.
653.     N2S\$ = sign\$ + l\$
654.

This  now also has _RED, _GREEN, _BLUE, _ALPHA, _RED32, _GREEN32, _ALPHA32, _BLUE32...

Also did a little restructuring of the SELECT CASE which we use to finally calculate values, in an attempt to try and reduce a little CPU usage and processing time once it's swapped back into QB64.

QB64 parses everything we type over and over and over, with each and every keypress which we hit in the IDE.  This little routine has now grown to where it contains over 60 different functions which it can calculate values for...  IF the value we're looking for is down at the bottom of the list, that's 60+ IF calculations which we check and compare against with every keypress...

To reduce that burden, this now breaks things down by first the priority level and then the operation.   In a stand-alone evaluation program, like the demo here, it doesn't make that much of a difference, but I'm hoping the internal change might help inside QB64 itself,  as it reduces the amount of comparisons which it has to do over and over inside CONST for us.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

#### STxAxTIC

• Library Staff
• Forum Resident
• Posts: 1062
• TOXIC
##### Re: Steve's Math Evaluator
« Reply #5 on: January 05, 2020, 04:17:36 PM »
I always enjoyed playing with this. My favorite interaction (with result) is

Code: QB64: [Select]
1. ((6=6)-(5=5))^((4=4)-(3=3))
2. 1

Anyway, I noticed that 0--7 correctly returns 7, but 0---7 returns 0. If you make the function that replaces "--" with "-" recursive or loopy you can deal with this whole class if problems. Of course, three minus signs is kinda rare, but someone could easily try to pass three euqal signs in a row if they confuse QB64 with JavaScript. Hm... maybe you don't need this suggestion...

EDIT:

Found a few errors when a minus sign leads the expression. Here's a simple one or two:
Code: QB64: [Select]
1. -(COS(3))
2. 0

Code: QB64: [Select]
1. -(-3+2)
2. 0

« Last Edit: January 05, 2020, 04:27:07 PM by STxAxTIC »
TOXIC

#### SMcNeill

• QB64 Developer
• Forum Resident
• Posts: 3623
##### Re: Steve's Math Evaluator
« Reply #6 on: January 05, 2020, 05:03:07 PM »
I always enjoyed playing with this. My favorite interaction (with result) is

Code: QB64: [Select]
1. ((6=6)-(5=5))^((4=4)-(3=3))
2. 1

Anyway, I noticed that 0--7 correctly returns 7, but 0---7 returns 0. If you make the function that replaces "--" with "-" recursive or loopy you can deal with this whole class if problems. Of course, three minus signs is kinda rare, but someone could easily try to pass three euqal signs in a row if they confuse QB64 with JavaScript. Hm... maybe you don't need this suggestion...

EDIT:

Found a few errors when a minus sign leads the expression. Here's a simple one or two:
Code: QB64: [Select]
1. -(COS(3))
2. 0

Code: QB64: [Select]
1. -(-3+2)
2. 0

I’m surprised no one has caught the - - - bug before!  I’ll sort on this as well for us, while I’m in here working on things.  There’s a DWD (deal with duplicates) function that should process and handle these things.  I’ll have to see why it’s ignoring them for us.  ;)

I also noticed that % is a percent symbol, which we can’t use in QB64, as it’s a type indicator for us.  I’m kinda surprised that’s never affected anyone either.

1.1% * 100 should be 100, not 1.1.
« Last Edit: January 05, 2020, 05:09:17 PM by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

#### FellippeHeitor

• QB64 Developer
• Forum Resident
• Posts: 3077
• Let it go, this too shall pass.
##### Re: Steve's Math Evaluator
« Reply #7 on: January 05, 2020, 08:49:41 PM »
_PI should take parameters like the function does (multipliers), like _PI(.5), _PI(2).

#### Pete

• Forum Resident
• Posts: 2576
• Cuz I sez so, varmint!
##### Re: Steve's Math Evaluator
« Reply #8 on: January 05, 2020, 09:30:22 PM »
You fellers er talkin' over my head agin... And I'z be a wearing my hat, too!

- Sam

#### FellippeHeitor

• QB64 Developer
• Forum Resident
• Posts: 3077
• Let it go, this too shall pass.
##### Re: Steve's Math Evaluator
« Reply #9 on: January 05, 2020, 09:46:13 PM »
_CEIL is missing.

#### SMcNeill

• QB64 Developer
• Forum Resident
• Posts: 3623
##### Re: Steve's Math Evaluator
« Reply #10 on: January 05, 2020, 09:56:54 PM »
_PI should take parameters like the function does (multipliers), like _PI(.5), _PI(2).

Code: QB64: [Select]
1. SCREEN _NEWIMAGE(1024, 720, 32)
2. REDIM SHARED OName(0) AS STRING 'Operation Name
3. REDIM SHARED PL(0) AS INTEGER 'Priority Level
4. DIM SHARED QuickReturn AS INTEGER
5. Set_OrderOfOperations
6.
7.     i\$ = INPUT\$(1)
8.         CASE CHR\$(8)
9.             eval\$ = LEFT\$(eval\$, LEN(eval\$) - 1)
10.         CASE CHR\$(13)
11.             eval\$ = ""
12.         CASE CHR\$(27)
13.             eval\$ = eval\$ + i\$
14.     PRINT eval\$
15.     PRINT Evaluate_Expression(eval\$)
16.
17.
18. 'Steve Subs/Functins for _MATH support with CONST
19. FUNCTION Evaluate_Expression\$ (e\$)
20.     t\$ = e\$ 'So we preserve our original data, we parse a temp copy of it
21.
22.     b = INSTR(UCASE\$(e\$), "EQL") 'take out assignment before the preparser sees it
23.     IF b THEN t\$ = MID\$(e\$, b + 3): var\$ = UCASE\$(LTRIM\$(RTRIM\$(MID\$(e\$, 1, b - 1))))
24.
25.     QuickReturn = 0
26.     PreParse t\$
27.
28.     IF QuickReturn THEN Evaluate_Expression\$ = t\$: EXIT FUNCTION
29.
30.     IF LEFT\$(t\$, 5) = "ERROR" THEN Evaluate_Expression\$ = t\$: EXIT FUNCTION
31.
32.     'Deal with brackets first
33.     EXP\$ = "(" + t\$ + ")" 'Starting and finishing brackets for our parse routine.
34.
35.         Eval_E = INSTR(EXP\$, ")")
36.         IF Eval_E > 0 THEN
37.             c = 0
38.             DO UNTIL Eval_E - c <= 0
39.                 c = c + 1
40.                 IF Eval_E THEN
41.                     IF MID\$(EXP\$, Eval_E - c, 1) = "(" THEN EXIT DO
42.             s = Eval_E - c + 1
43.             IF s < 1 THEN Evaluate_Expression\$ = "ERROR -- BAD () Count": EXIT SUB
44.             eval\$ = " " + MID\$(EXP\$, s, Eval_E - s) + " " 'pad with a space before and after so the parser can pick up the values properly.
45.             ParseExpression eval\$
46.
47.             eval\$ = LTRIM\$(RTRIM\$(eval\$))
48.             IF LEFT\$(eval\$, 5) = "ERROR" THEN Evaluate_Expression\$ = eval\$: EXIT SUB
49.             EXP\$ = DWD(LEFT\$(EXP\$, s - 2) + eval\$ + MID\$(EXP\$, Eval_E + 1))
50.             IF MID\$(EXP\$, 1, 1) = "N" THEN MID\$(EXP\$, 1) = "-"
51.
52.             temppp\$ = DWD(LEFT\$(EXP\$, s - 2) + " ## " + eval\$ + " ## " + MID\$(EXP\$, E + 1))
53.     LOOP UNTIL Eval_E = 0
54.     c = 0
55.         c = c + 1
56.         SELECT CASE MID\$(EXP\$, c, 1)
57.             CASE "0" TO "9", ".", "-" 'At this point, we should only have number values left.
58.             CASE ELSE: Evaluate_Expression\$ = "ERROR - Unknown Diagnosis: (" + EXP\$ + ") ": EXIT SUB
59.     LOOP UNTIL c >= LEN(EXP\$)
60.
61.     Evaluate_Expression\$ = EXP\$
62.
63.
64.
65. SUB ParseExpression (EXP\$)
66.     DIM num(10) AS STRING
67.     'PRINT exp\$
68.     'We should now have an expression with no () to deal with
69.     IF MID\$(EXP\$, 2, 1) = "-" THEN EXP\$ = "0+" + MID\$(EXP\$, 2)
70.     FOR J = 1 TO 250
71.         lowest = 0
72.         DO UNTIL lowest = LEN(EXP\$)
73.             lowest = LEN(EXP\$): OpOn = 0
74.             FOR P = 1 TO UBOUND(OName)
75.                 'Look for first valid operator
76.                 IF J = PL(P) THEN 'Priority levels match
77.                     IF LEFT\$(EXP\$, 1) = "-" THEN op = INSTR(2, EXP\$, OName(P)) ELSE op = INSTR(EXP\$, OName(P))
78.                     IF op > 0 AND op < lowest THEN lowest = op: OpOn = P
79.             IF OpOn = 0 THEN EXIT DO 'We haven't gotten to the proper PL for this OP to be processed yet.
80.             IF LEFT\$(EXP\$, 1) = "-" THEN op = INSTR(2, EXP\$, OName(OpOn)) ELSE op = INSTR(EXP\$, OName(OpOn))
81.             numset = 0
82.
83.             '*** SPECIAL OPERATION RULESETS
84.             IF OName(OpOn) = "-" THEN 'check for BOOLEAN operators before the -
85.                 SELECT CASE MID\$(EXP\$, op - 3, 3)
86.                     CASE "NOT", "XOR", "AND", "EQV", "IMP"
87.                         EXIT DO 'Not an operator, it's a negative
88.                 IF MID\$(EXP\$, op - 3, 2) = "OR" THEN EXIT DO 'Not an operator, it's a negative
89.
90.             IF op THEN
91.                 c = LEN(OName(OpOn)) - 1
92.                     SELECT CASE MID\$(EXP\$, op + c + 1, 1)
93.                         CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "N": numset = -1 'Valid digit
94.                         CASE "-" 'We need to check if it's a minus or a negative
95.                             IF OName(OpOn) = "_PI" OR numset THEN EXIT DO
96.                         CASE ",": numset = 0
97.                         CASE ELSE 'Not a valid digit, we found our separator
98.                     c = c + 1
99.                 LOOP UNTIL op + c >= LEN(EXP\$)
100.                 E = op + c
101.
102.                 c = 0
103.                     c = c + 1
104.                     SELECT CASE MID\$(EXP\$, op - c, 1)
105.                         CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "N" 'Valid digit
106.                         CASE "-" 'We need to check if it's a minus or a negative
107.                             c1 = c
108.                             bad = 0
109.                                 c1 = c1 + 1
110.                                 SELECT CASE MID\$(EXP\$, op - c1, 1)
111.                                     CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."
112.                                         bad = -1
113.                                         EXIT DO 'It's a minus sign
114.                                         'It's a negative sign and needs to count as part of our numbers
115.                             LOOP UNTIL op - c1 <= 0
116.                             IF bad THEN EXIT DO 'We found our seperator
117.                         CASE ELSE 'Not a valid digit, we found our separator
118.                 LOOP UNTIL op - c <= 0
119.                 s = op - c
120.                 num(1) = MID\$(EXP\$, s + 1, op - s - 1) 'Get our first number
121.                 num(2) = MID\$(EXP\$, op + LEN(OName(OpOn)), E - op - LEN(OName(OpOn)) + 1) 'Get our second number
122.                 IF MID\$(num(1), 1, 1) = "N" THEN MID\$(num(1), 1) = "-"
123.                 IF MID\$(num(2), 1, 1) = "N" THEN MID\$(num(2), 1) = "-"
124.                 num(3) = EvaluateNumbers(OpOn, num())
125.                 IF MID\$(num(3), 1, 1) = "-" THEN MID\$(num(3), 1) = "N"
126.                 'PRINT "*************"
127.                 'PRINT num(1), OName(OpOn), num(2), num(3), exp\$
128.                 IF LEFT\$(num(3), 5) = "ERROR" THEN EXP\$ = num(3): EXIT SUB
129.                 EXP\$ = LTRIM\$(N2S(DWD(LEFT\$(EXP\$, s) + RTRIM\$(LTRIM\$(num(3))) + MID\$(EXP\$, E + 1))))
130.                 'PRINT exp\$
131.             op = 0
132.
133.
134.
135.
136. SUB Set_OrderOfOperations
137.     'PL sets our priortity level. 1 is highest to 65535 for the lowest.
138.     'I used a range here so I could add in new priority levels as needed.
139.     'OName ended up becoming the name of our commands, as I modified things.... Go figure!  LOL!
140.
141.     'Constants get evaluated first, with a Priority Level of 1
142.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_PI"
143.     REDIM _PRESERVE PL(i): PL(i) = 10
144.     'I'm not certain where exactly percentages should go.  They kind of seem like a special case to me.  COS10% should be COS.1 I'd think...
145.     'I'm putting it here for now, and if anyone knows someplace better for it in our order of operations, let me know.
146.
147.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_UOF" 'convert to unsigned offset
148.     REDIM _PRESERVE PL(i): PL(i) = 5
149.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_OF" 'convert to offset
150.     REDIM _PRESERVE PL(i): PL(i) = 5
151.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_UBY" 'convert to unsigned byte
152.     REDIM _PRESERVE PL(i): PL(i) = 5
153.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_BY" 'convert to byte
154.     REDIM _PRESERVE PL(i): PL(i) = 5
155.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_UIN" 'convert to unsigned integer
156.     REDIM _PRESERVE PL(i): PL(i) = 5
157.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_IN" 'convert to integer
158.     REDIM _PRESERVE PL(i): PL(i) = 5
159.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_UIF" 'convert to unsigned int64
160.     REDIM _PRESERVE PL(i): PL(i) = 5
161.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_IF" 'convert to int64
162.     REDIM _PRESERVE PL(i): PL(i) = 5
163.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_ULO" 'convert to unsigned long
164.     REDIM _PRESERVE PL(i): PL(i) = 5
165.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_LO" 'convert to long
166.     REDIM _PRESERVE PL(i): PL(i) = 5
167.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_SI" 'convert to single
168.     REDIM _PRESERVE PL(i): PL(i) = 5
169.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_FL" 'convert to float
170.     REDIM _PRESERVE PL(i): PL(i) = 5
171.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_DO" 'convert to double
172.     REDIM _PRESERVE PL(i): PL(i) = 5
173.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_UBI" 'convert to unsigned bit
174.     REDIM _PRESERVE PL(i): PL(i) = 5
175.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "C_BI" 'convert to bit
176.     REDIM _PRESERVE PL(i): PL(i) = 5
177.
178.     'Then Functions with PL 10
179.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ACOS"
180.     REDIM _PRESERVE PL(i): PL(i) = 10
181.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ASIN"
182.     REDIM _PRESERVE PL(i): PL(i) = 10
183.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ARCSEC"
184.     REDIM _PRESERVE PL(i): PL(i) = 10
185.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ARCCSC"
186.     REDIM _PRESERVE PL(i): PL(i) = 10
187.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ARCCOT"
188.     REDIM _PRESERVE PL(i): PL(i) = 10
189.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_SECH"
190.     REDIM _PRESERVE PL(i): PL(i) = 10
191.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_CSCH"
192.     REDIM _PRESERVE PL(i): PL(i) = 10
193.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_COTH"
194.     REDIM _PRESERVE PL(i): PL(i) = 10
195.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "COS"
196.     REDIM _PRESERVE PL(i): PL(i) = 10
197.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "SIN"
198.     REDIM _PRESERVE PL(i): PL(i) = 10
199.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "TAN"
200.     REDIM _PRESERVE PL(i): PL(i) = 10
201.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "LOG"
202.     REDIM _PRESERVE PL(i): PL(i) = 10
203.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "EXP"
204.     REDIM _PRESERVE PL(i): PL(i) = 10
205.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ATN"
206.     REDIM _PRESERVE PL(i): PL(i) = 10
207.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_D2R"
208.     REDIM _PRESERVE PL(i): PL(i) = 10
209.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_D2G"
210.     REDIM _PRESERVE PL(i): PL(i) = 10
211.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_R2D"
212.     REDIM _PRESERVE PL(i): PL(i) = 10
213.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_R2G"
214.     REDIM _PRESERVE PL(i): PL(i) = 10
215.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_G2D"
216.     REDIM _PRESERVE PL(i): PL(i) = 10
217.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_G2R"
218.     REDIM _PRESERVE PL(i): PL(i) = 10
219.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ABS"
220.     REDIM _PRESERVE PL(i): PL(i) = 10
221.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "SGN"
222.     REDIM _PRESERVE PL(i): PL(i) = 10
223.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "INT"
224.     REDIM _PRESERVE PL(i): PL(i) = 10
225.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ROUND"
226.     REDIM _PRESERVE PL(i): PL(i) = 10
227.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "FIX"
228.     REDIM _PRESERVE PL(i): PL(i) = 10
229.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_SEC"
230.     REDIM _PRESERVE PL(i): PL(i) = 10
231.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_CSC"
232.     REDIM _PRESERVE PL(i): PL(i) = 10
233.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_COT"
234.     REDIM _PRESERVE PL(i): PL(i) = 10
235.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ASC"
236.     REDIM _PRESERVE PL(i): PL(i) = 10
237.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "CHR\$"
238.     REDIM _PRESERVE PL(i): PL(i) = 10
239.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RGB32"
240.     REDIM _PRESERVE PL(i): PL(i) = 10
241.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RGBA32"
242.     REDIM _PRESERVE PL(i): PL(i) = 10
243.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RGB"
244.     REDIM _PRESERVE PL(i): PL(i) = 10
245.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RGBA"
246.     REDIM _PRESERVE PL(i): PL(i) = 10
247.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RED"
248.     REDIM _PRESERVE PL(i): PL(i) = 10
249.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_GREEN"
250.     REDIM _PRESERVE PL(i): PL(i) = 10
251.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_BLUE"
252.     REDIM _PRESERVE PL(i): PL(i) = 10
253.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ALPHA"
254.     REDIM _PRESERVE PL(i): PL(i) = 10
255.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_RED32"
256.     REDIM _PRESERVE PL(i): PL(i) = 10
257.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_GREEN32"
258.     REDIM _PRESERVE PL(i): PL(i) = 10
259.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_BLUE32"
260.     REDIM _PRESERVE PL(i): PL(i) = 10
261.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "_ALPHA32"
262.     REDIM _PRESERVE PL(i): PL(i) = 10
263.
264.     'Exponents with PL 20
265.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "^"
266.     REDIM _PRESERVE PL(i): PL(i) = 20
267.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "SQR"
268.     REDIM _PRESERVE PL(i): PL(i) = 20
269.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "ROOT"
270.     REDIM _PRESERVE PL(i): PL(i) = 20
271.     'Multiplication and Division PL 30
272.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "*"
273.     REDIM _PRESERVE PL(i): PL(i) = 30
274.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "/"
275.     REDIM _PRESERVE PL(i): PL(i) = 30
276.     'Integer Division PL 40
277.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "\"
278.     REDIM _PRESERVE PL(i): PL(i) = 40
279.     'MOD PL 50
280.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "MOD"
281.     REDIM _PRESERVE PL(i): PL(i) = 50
282.     'Addition and Subtraction PL 60
283.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "+"
284.     REDIM _PRESERVE PL(i): PL(i) = 60
285.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "-"
286.     REDIM _PRESERVE PL(i): PL(i) = 60
287.
288.     'Relational Operators =, >, <, <>, <=, >=   PL 70
289.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "<>"
290.     REDIM _PRESERVE PL(i): PL(i) = 70
291.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "><" 'These next three are just reversed symbols as an attempt to help process a common typo
292.     REDIM _PRESERVE PL(i): PL(i) = 70
293.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "<="
294.     REDIM _PRESERVE PL(i): PL(i) = 70
295.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = ">="
296.     REDIM _PRESERVE PL(i): PL(i) = 70
297.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "=<" 'I personally can never keep these things straight.  Is it < = or = <...
298.     REDIM _PRESERVE PL(i): PL(i) = 70
299.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "=>" 'Who knows, check both!
300.     REDIM _PRESERVE PL(i): PL(i) = 70
301.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = ">"
302.     REDIM _PRESERVE PL(i): PL(i) = 70
303.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "<"
304.     REDIM _PRESERVE PL(i): PL(i) = 70
305.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "="
306.     REDIM _PRESERVE PL(i): PL(i) = 70
307.     'Logical Operations PL 80+
308.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "NOT"
309.     REDIM _PRESERVE PL(i): PL(i) = 80
310.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "AND"
311.     REDIM _PRESERVE PL(i): PL(i) = 90
312.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "OR"
313.     REDIM _PRESERVE PL(i): PL(i) = 100
314.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "XOR"
315.     REDIM _PRESERVE PL(i): PL(i) = 110
316.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "EQV"
317.     REDIM _PRESERVE PL(i): PL(i) = 120
318.     i = i + 1: REDIM _PRESERVE OName(i): OName(i) = "IMP"
319.     REDIM _PRESERVE PL(i): PL(i) = 130
320.
321.
322.
323. FUNCTION EvaluateNumbers\$ (p, num() AS STRING)
324.     DIM n1 AS _FLOAT, n2 AS _FLOAT, n3 AS _FLOAT
325.     IF INSTR(num(1), ",") THEN EvaluateNumbers\$ = "ERROR - Invalid comma (" + num(1) + ")": EXIT FUNCTION
326.     IF INSTR(num(2), ",") THEN
327.         SELECT CASE OName(p) 'only certain commands should pass a comma value
328.             CASE "_RGB32", "_RGBA32", "_RGB", "_RGBA", "_RED", "_GREEN", "_BLUE", "_ALPHA"
329.                 EvaluateNumbers\$ = "ERROR - Invalid comma (" + num(2) + ")": EXIT FUNCTION
330.
331.     SELECT CASE PL(p) 'divide up the work so we want do as much case checking
332.         CASE 5 'Type conversions
333.             'Note, these are special cases and work with the number BEFORE the command and not after
334.             SELECT CASE OName(p) 'Depending on our operator..
335.                 CASE "C_UOF": n1~%& = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1~%&)))
336.                 CASE "C_ULO": n1%& = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1%&)))
337.                 CASE "C_UBY": n1~%% = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1~%%)))
338.                 CASE "C_UIN": n1~% = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1~%)))
339.                 CASE "C_BY": n1%% = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1%%)))
340.                 CASE "C_IN": n1% = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1%)))
341.                 CASE "C_UIF": n1~&& = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1~&&)))
342.                 CASE "C_OF": n1~& = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1~&)))
343.                 CASE "C_IF": n1&& = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1&&)))
344.                 CASE "C_LO": n1& = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1&)))
345.                 CASE "C_UBI": n1~` = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1~`)))
346.                 CASE "C_BI": n1` = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1`)))
347.                 CASE "C_FL": n1## = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1##)))
348.                 CASE "C_DO": n1# = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1#)))
349.                 CASE "C_SI": n1! = VAL(num(1)): EvaluateNumbers\$ = RTRIM\$(LTRIM\$(STR\$(n1!)))
350.         CASE 10 'functions
351.             SELECT CASE OName(p) 'Depending on our operator..
352.                 CASE "_PI"
353.                     n1 = 3.14159265358979323846264338327950288## 'Future compatable in case something ever stores extra digits for PI
354.                     IF num(2) <> "" THEN n1 = n1 * VAL(num(2))
355.                 CASE "_ACOS": n1 = _ACOS(VAL(num(2)))
356.                 CASE "_ASIN": n1 = _ASIN(VAL(num(2)))
357.                 CASE "_ARCSEC": n1 = _ARCSEC(VAL(num(2)))
358.                 CASE "_ARCCSC": n1 = _ARCCSC(VAL(num(2)))
359.                 CASE "_ARCCOT": n1 = _ARCCOT(VAL(num(2)))
360.                 CASE "_SECH": n1 = _SECH(VAL(num(2)))
361.                 CASE "_CSCH": n1 = _CSCH(VAL(num(2)))
362.                 CASE "_COTH": n1 = _COTH(VAL(num(2)))
363.                 CASE "_RGB32"
364.                     n\$ = num(2)
365.                     IF n\$ = "" THEN EvaluateNumbers\$ = "ERROR - Invalid null _RGB32": EXIT FUNCTION
366.                     c1 = INSTR(n\$, ",")
367.                     IF c1 THEN c2 = INSTR(c1 + 1, n\$, ",")
368.                     IF c2 THEN c3 = INSTR(c2 + 1, n\$, ",")
369.                     IF c3 THEN c4 = INSTR(c3 + 1, n\$, ",")
370.                     IF c1 = 0 THEN 'there's no comma in the command to parse.  It's a grayscale value
371.                         n = VAL(num(2))
372.                         n1 = _RGB32(n, n, n)
373.                     ELSEIF c2 = 0 THEN 'there's one comma and not 2.  It's grayscale with alpha.
374.                         n = VAL(LEFT\$(num(2), c1))
375.                         n2 = VAL(MID\$(num(2), c1 + 1))
376.                         n1 = _RGBA32(n, n, n, n2)
377.                     ELSEIF c3 = 0 THEN 'there's two commas.  It's _RGB values
378.                         n = VAL(LEFT\$(num(2), c1))
379.                         n2 = VAL(MID\$(num(2), c1 + 1))
380.                         n3 = VAL(MID\$(num(2), c2 + 1))
381.                         n1 = _RGB32(n, n2, n3)
382.                     ELSEIF c4 = 0 THEN 'there's three commas.  It's _RGBA values
383.                         n = VAL(LEFT\$(num(2), c1))
384.                         n2 = VAL(MID\$(num(2), c1 + 1))
385.                         n3 = VAL(MID\$(num(2), c2 + 1))
386.                         n4 = VAL(MID\$(num(2), c3 + 1))
387.                         n1 = _RGBA32(n, n2, n3, n4)
388.                     ELSE 'we have more than three commas.  I have no idea WTH type of values got passed here!
389.                         EvaluateNumbers\$ = "ERROR - Invalid comma count (" + num(2) + ")": EXIT FUNCTION
390.                 CASE "_RGBA32"
391.                     n\$ = num(2)
392.                     IF n\$ = "" THEN EvaluateNumbers\$ = "ERROR - Invalid null _RGBA32": EXIT FUNCTION
393.                     c1 = INSTR(n\$, ",")
394.                     IF c1 THEN c2 = INSTR(c1 + 1, n\$, ",")
395.                     IF c2 THEN c3 = INSTR(c2 + 1, n\$, ",")
396.                     IF c3 THEN c4 = INSTR(c3 + 1, n\$, ",")
397.                     IF c3 = 0 OR c4 <> 0 THEN EvaluateNumbers\$ = "ERROR - Invalid comma count (" + num(2) + ")": EXIT FUNCTION
398.                     'we have to have 3 commas; not more, not less.
399.                     n = VAL(LEFT\$(num(2), c1))
400.                     n2 = VAL(MID\$(num(2), c1 + 1))
401.                     n3 = VAL(MID\$(num(2), c2 + 1))
402.                     n4 = VAL(MID\$(num(2), c3 + 1))
403.                     n1 = _RGBA32(n, n2, n3, n4)
404.                 CASE "_RGB"
405.                     n\$ = num(2)
406.                     IF n\$ = "" THEN EvaluateNumbers\$ = "ERROR - Invalid null _RGB": EXIT FUNCTION
407.                     c1 = INSTR(n\$, ",")
408.                     IF c1 THEN c2 = INSTR(c1 + 1, n\$, ",")
409.                     IF c2 THEN c3 = INSTR(c2 + 1, n\$, ",")