Page 1 of 2 12 LastLast
Results 1 to 40 of 57

Thread: simple math string parser

  1. #1

    Thread Starter
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    simple math string parser

    I need a fast and easy way to parse a math expression that only contains addition and multiplication, the numbers can have decimals.
    the result is a whole number, so the decimals are only used inside the parser. here's my take:
    Code:
    Private Function doMath&(ByVal Exp$)
        Dim i&, j&, a&, b&, am!, bm!, rm!, f$
    
        f = "x"
        For j = 1 To 2
        Do
            i = InStr(1, Exp, f)
            If i = 0 Then
                Exit Do
            Else
                a = i - 1
                b = i + 1
                Do
                    If Mid(Exp, a, 1) = "+" Then Exit Do Else a = a - 1
                Loop Until a = 0
                Do
                    If Mid(Exp, b, 1) = "+" Then Exit Do Else b = b + 1
                Loop Until b > Len(Exp)
                am = CSng(Mid(Exp, a + 1, i - a - 1))
                bm = CSng(Mid(Exp, i + 1, b - i - 1))
                If j = 1 Then rm = am * bm Else rm = am + bm
                Exp = Left(Exp, a) & CStr(rm) & Mid(Exp, b)
            End If
        Loop
        f = "+"
        Next j
        doMath = CLng(Exp)
    End Function
    and no, it should not require a reference/component/ocx. maybe a windows API (need to be available xp to 10)
    so my question is, if theres a better way to do it, smarter and faster?
    as I wrote, only * and +, but if you include / - and its better I dont mind, but Its not required.
    the importance is speed more than length of code.

  2. #2
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: simple math string parser

    I just put together a sample grammar for VbPeg that impl +/- and mul/div on doubles along with parentheses for explicit operations precedence and then compiled the parser to mdCalc.bas.

    In the sample project the grammar is called from Sub Main evaluating command line arguments and dumping the result (double value) and optionally an error from the expression parser (string).

    At 497 LOC this parser seems a bit bloated for your case but works reasonable w/o external deps, here are samples of error reporting along w/ intermediate results on failure:

    Code:
    C:\Work\Temp\vbpeg\test\calc2>Project1.exe 1+999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999992.1237+67
    Result: 68
    Error: Overflow
    Code:
    C:\Work\Temp\vbpeg\test\calc2>Project1.exe ((1+2)*3)/2***
    Result: 4.5
    Error: Extra characters: ***
    You can also use VbPeg's `-public` or `-private` options to generates a public/private class module from the sample grammar (instead of a global .bas) for better encapsulation.

    Code:
    Usage: VbPeg.exe [options] <in_file.peg>
    
    Options:
      -o OUTFILE      write result to OUTFILE [default: stdout]
      -tree           output parse tree
      -ir             output intermediate represetation
      -public         emit public VB6 class module
      -private        emit private VB6 class module
      -module NAME    VB6 class/module name [default: OUTFILE]
      -userdata TYPE  parser context's UserData member data-type [default: Variant]
      -q              in quiet operation outputs only errors
    cheers,
    </wqw>

  3. #3

    Thread Starter
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: simple math string parser

    Doesn't work,

    I input 3+5*9+7+2*5
    but I get the result 11, it should be 65

  4. #4

    Thread Starter
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: simple math string parser

    doing more tests, using Byref speed up the parsing a lot,
    also, copying char by char is faster than using mid$() strangely enough.
    also slipping it to multiple functions makes it a bit faster as well.

    edit: byref is not an option, destroys the string as Schmidt pointed out!
    Last edited by baka; Mar 19th, 2018 at 11:57 AM.

  5. #5
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: simple math string parser

    Here is a simple evaluating expression-parser, which can do a bit more than just "plus and mul"
    (it is not performance-optimized, but quite short code-wise - able to support even simple function-calls):

    Into a *.bas-Module
    Code:
    Option Explicit
    
    Public Function Eval(ByVal Expr As String)
    Dim L As String, R As String
      Do While HandleParentheses(Expr): Loop
    
      If 0 Then
        ElseIf Spl(Expr, "Or", L, R) Then:   Eval = Eval(L) Or Eval(R)
        ElseIf Spl(Expr, "And", L, R) Then:  Eval = Eval(L) And Eval(R)
        ElseIf Spl(Expr, ">=", L, R) Then:   Eval = Eval(L) >= Eval(R)
        ElseIf Spl(Expr, "<=", L, R) Then:   Eval = Eval(L) <= Eval(R)
        ElseIf Spl(Expr, "=", L, R) Then:    Eval = Eval(L) = Eval(R)
        ElseIf Spl(Expr, ">", L, R) Then:    Eval = Eval(L) > Eval(R)
        ElseIf Spl(Expr, "<", L, R) Then:    Eval = Eval(L) < Eval(R)
        ElseIf Spl(Expr, "Like", L, R) Then: Eval = Eval(L) Like Eval(R)
        ElseIf Spl(Expr, "&", L, R) Then:    Eval = Eval(L) & Eval(R)
        ElseIf Spl(Expr, "-", L, R) Then:    Eval = Eval(L) - Eval(R)
        ElseIf Spl(Expr, "+", L, R) Then:    Eval = Eval(L) + Eval(R)
        ElseIf Spl(Expr, "Mod", L, R) Then:  Eval = Eval(L) Mod Eval(R)
        ElseIf Spl(Expr, "\", L, R) Then:    Eval = Eval(L) \ Eval(R)
        ElseIf Spl(Expr, "*", L, R) Then:    Eval = Eval(L) * Eval(R)
        ElseIf Spl(Expr, "/", L, R) Then:    Eval = Eval(L) / Eval(R)
        ElseIf Spl(Expr, "^", L, R) Then:    Eval = Eval(L) ^ Eval(R)
        ElseIf Trim(Expr) >= "A" Then:       Eval = Fnc(Expr)
        ElseIf Len(Expr) Then:               Eval = IIf(InStr(Expr, "'"), _
                                Replace(Trim(Expr), "'", ""), Val(Expr))
      End If
    End Function
    
    Private Function HandleParentheses(Expr As String) As Boolean
    Dim P As Long, i As Long, C As Long
      P = InStr(Expr, "(")
      If P Then HandleParentheses = True Else Exit Function
    
      For i = P To Len(Expr)
        If Mid(Expr, i, 1) = "(" Then C = C + 1
        If Mid(Expr, i, 1) = ")" Then C = C - 1
        If C = 0 Then Exit For
      Next i
    
      Expr = Left(Expr, P - 1) & Str(Eval(Mid(Expr, P + 1, i - P - 1))) & Mid(Expr, i + 1)
    End Function
    
    Private Function Spl(Expr As String, Op$, L$, R$) As Boolean
    Dim P As Long
      P = InStrRev(Expr, Op, , 1)
      If P Then Spl = True Else Exit Function
      If P < InStrRev(Expr, "'") And InStr("*-", Op) Then P = InStrRev(Expr, "'", P) - 1
    
      R = Mid(Expr, P + Len(Op))
      L = Trim(Left$(Expr, IIf(P > 0, P - 1, 0)))
    
      Select Case Right(L, 1)
        Case "", "+", "*", "/", "A" To "z": Spl = False
        Case "-": R = "-" & R
      End Select
    End Function
    
    Private Function Fnc(Expr As String)
      Expr = LCase(Trim(Expr))
    
      Select Case Left(Expr, 3)
        Case "abs": Fnc = Abs(Val(Mid$(Expr, 4)))
        Case "sin": Fnc = Sin(Val(Mid$(Expr, 4)))
        Case "cos": Fnc = Cos(Val(Mid$(Expr, 4)))
        Case "atn": Fnc = Atn(Val(Mid$(Expr, 4)))
        Case "log": Fnc = Log(Val(Mid$(Expr, 4)))
        Case "exp": Fnc = Exp(Val(Mid$(Expr, 4)))
        'etc...
      End Select
    End Function
    Form-Code (with a few testcases - comparing outputs with the VB6-expression-solver)
    Code:
    Option Explicit
    
    'just a group of Test-calls for the simple Evaluator (in comparison to VB-outputs)
    'the VB6-resolved expression is always located directly below the Eval-String to
    'be able to compare the test-expressions more easily ...
    '(results are printed side-by-side and should come out the same in all test-cases)
    
    Private Sub Form_Load()
      'simple operator-precedence without parentheses
      Debug.Print Eval("3 + 5 * 9 + 7 + 2 * 5"), _
                        3 + 5 * 9 + 7 + 2 * 5
                        
      Debug.Print Eval("1 + 6 / 3 - 7"), _
                        1 + 6 / 3 - 7
    
      'unary-operator test
      Debug.Print Eval("-1 + -6 / 3 - -7 "), _
                        -1 + -6 / 3 - -7
                        
      'simple parentheses test
      Debug.Print Eval("-14 / 7 * -(1 + 2)"), _
                        -14 / 7 * -(1 + 2)
    
      'a complex case, including exponent-handling
      Debug.Print Eval("((1 + -2) * -3 + 4) * 2 / 7 * 216 ^ (-1 / -3) "), _
                        ((1 + -2) * -3 + 4) * 2 / 7 * 216 ^ (-1 / -3)
    
      'operator-precedence (mainly to test Mod and Div operators)
      Debug.Print Eval("27 / 3 Mod (5 \ 2) + 23"), _
                        27 / 3 Mod (5 \ 2) + 23
    
      'function-calls
      Debug.Print Eval("43 + -(-2 - 3) * Abs(Cos(4 * Atn(1)))"), _
                        43 + -(-2 - 3) * Abs(Cos(4 * Atn(1)))
      
      'simple case, but mixed with a string-concat (math-ops have precedence)
      Debug.Print Eval("5 + 3 & 2"), _
                        5 + 3 & 2
                        
      'simple case of a string-concat (notation for string-literals as in SQL)
      Debug.Print Eval("'abc ' & '123 ' & 'xyz'"), _
                        "abc " & "123 " & "xyz"
      
      'simple comparison-ops follow... (starting with a string-comparison)
      Debug.Print Eval("'abc' > '123'"), _
                        "abc" > "123"
                        
      'comparison of numbers... math-ops have precedence
      Debug.Print Eval("3 = 1 + 2"), _
                        3 = 1 + 2
                        
      'comparison of strings per Like-Operator...
      Debug.Print Eval("'abc' Like '*b*'"), _
                        "abc" Like "*b*"
                 
      'comparison of strings per Like-Operator (using the "in-range" notation)
      Debug.Print Eval("'3xB..foo' Like '[1-5]?[A-C]*o'"), _
                        "3xB..foo" Like "[1-5]?[A-C]*o"
      
      'and here logical comparisons, involving the And-Operator (a mix of String- and Value-Compares)
      Debug.Print Eval("'foobar' Like 'foo*' And 'foobar' Like '*bar' And (1 + 2) * 4 - 1 = 11"), _
                        "foobar" Like "foo*" And "foobar" Like "*bar" And (1 + 2) * 4 - 1 = 11
    End Sub
    HTH

    Olaf

  6. #6
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: simple math string parser

    @Olaf: Impressively short code!

    FYI, tweaked a test case to "-1 + -6 / 3 - --7" and got a diff in the test results in immediate window.

    Quote Originally Posted by baka View Post
    Doesn't work,

    I input 3+5*9+7+2*5
    but I get the result 11, it should be 65
    Sorry about that. Had a bug in the VbPeg code generator -- fixed in 0.3.2.

    cheers,
    </wqw>

  7. #7

    Thread Starter
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: simple math string parser

    thx for the evaluator, its quite neat and includes a lot of equations.
    added it to one project to try the speed, but the result time is not satisfactory, so i tried to strip down your code to just work with + & * to see if i could increase performance.
    this is the stripped down version:
    Code:
    Public Function Eval(Expr As String)
        Dim L As String, R As String
    
        If 0 Then
            ElseIf Spl(Expr, "+", L, R) Then:    Eval = Eval(L) + Eval(R)
            ElseIf Spl(Expr, "*", L, R) Then:    Eval = Eval(L) * Eval(R)
            ElseIf Len(Expr) Then:               Eval = Val(Expr)
        End If
    End Function
    
    Private Function Spl(Expr As String, Op$, L$, R$) As Boolean
        Dim P As Long
        
        P = InStrRev(Expr, Op, , 1)
        If P Then Spl = True Else Exit Function
    
        R = Mid(Expr, P + Len(Op))
        L = Trim(Left$(Expr, IIf(P > 0, P - 1, 0)))
    
        Select Case Right(L, 1)
            Case "", "+", "*": Spl = False
        End Select
    End Function
    still comparing the code im using, its a bit slower.
    doing 1.000.000 evaluations.
    my code: average 160 milliseconds
    your stripped down: average 19500 milliseconds.
    my code:
    Code:
    Private Function solveAdd$(Expr$)
        Dim X&, i&, a&, b&, s$, teA$, teB$
        
        X = InStr(1, Expr, "+")
        a = X - 1
        b = X + 1
        Do
            s = Mid$(Expr, a, 1)
            If s = "+" Then Exit Do Else teA = s & teA: a = a - 1
        Loop Until a = 0
        Do
            s = Mid$(Expr, b, 1)
            If s = "+" Then Exit Do Else teB = teB & s: b = b + 1
        Loop Until b > Len(Expr)
        solveAdd = Left(Expr, a) & CStr(CSng(teA) + CSng(teB)) & Mid(Expr, b)
    End Function
    
    Private Function solveMul$(Expr$)
        Dim X&, i&, a&, b&, s$, teA$, teB$
    
        X = InStr(1, Expr, "x")
        a = X - 1
        b = X + 1
        Do
            s = Mid$(Expr, a, 1)
            If s = "x" Or s = "+" Then Exit Do Else teA = s & teA: a = a - 1
        Loop Until a = 0
        Do
            s = Mid$(Expr, b, 1)
            If s = "x" Or s = "+" Then Exit Do Else teB = teB & s: b = b + 1
        Loop Until b > Len(Expr)
        solveMul = Left(Expr, a) & CStr(CSng(teA) * CSng(teB)) & Mid(Expr, b)
    End Function
    
    Public Function doMath&(Expr$)
        Do
            If InStr(Expr, "x") = 0 Then Exit Do Else Expr = solveMul(Expr)
        Loop
        Do
            If InStr(Expr, "+") = 0 Then Exit Do Else Expr = solveAdd(Expr)
        Loop
        doMath = CLng(Expr)
    End Function
    as I only need + * x

    the reason I only need + and x is because its different formulas inside the game.
    its information and part of the string have a "formula" that need to be converted into numbers before it show.
    it could look like this: &Lx0,75& or &Lx5& or &Lx0,5+20& where L is a number of the current level, so I use another function to find the &___& and do the Replace of L than I call the math parser.
    Last edited by baka; Mar 17th, 2018 at 10:32 AM.

  8. #8
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: simple math string parser

    @baka: 160 ms is impressive! On my machine evaluating 1 mil times vResult = CDbl("0.75") (VB6 code) takes 137 ms

    cheers,
    </wqw>

  9. #9

    Thread Starter
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: simple math string parser

    yeah, the result is not saying anything as its based on your computer specs, but the comparison do.
    that is why i need as fast possible, im sure that even Olaf's eval works in the game, its not that I need to call it 1 million times haha...
    but its more that that, theres a lot going on in a game, so I always try to find the fastest solution for anything.
    so far the method I use is good. I don't need division because, 5 / 2 is equal to 5 * 0,5 and Im the one adding the formulas.

    anyway, your suggestions are not in vain, olaf' eval is very neat and if this thread stick around Im sure others could have use for it.
    or even use the one I posted, to add / and - is not that hard.

  10. #10
    PowerPoster ChrisE's Avatar
    Join Date
    Jun 2017
    Location
    Frankfurt
    Posts
    3,046

    Re: simple math string parser

    Hi,

    here my 2cents...
    Code:
    Option Explicit
    
    Private Sub Command1_Click()
        Dim scrObj As Object
        Dim xResult$
        Dim xText$
        
        'Text1= '3+5*9+7+2*5
        
        xText = Text1.Text
        Set scrObj = CreateObject("ScriptControl")
        scrObj.Language = "VBScript"
        scrObj.ExecuteStatement ("xResult = " & xText)
        
        xResult = scrObj.Eval("xResult")
        Text2.Text = "the result from " & xText & " is " & xResult
    End Sub
    
    Private Sub Form_Load()
    Text1.Text = "3+5*9+7+2*5"
    End Sub
    regards
    Chris
    to hunt a species to extinction is not logical !
    since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.

  11. #11

    Thread Starter
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: simple math string parser

    yeah, we can use scriptcontrol, word, database, ocx, etc to evaluate, but I posted that I didn't want to use that, just pure vb.
    anyway, to try the speed, I added the scrObj on its own to just call it once.
    so the only lines are scrObj.ExecuteStatement ("xResult = " & xText) and the result.

    comparison: 10,000 loops. took around 6 milliseconds using my code and 140-150 milliseconds using ScriptControl.

  12. #12
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: simple math string parser

    Quote Originally Posted by baka View Post
    ... tried to strip down your code to just work with + & * to see if i could increase performance.
    this is the stripped down version:
    Code:
    Public Function Eval(Expr As String)
        Dim L As String, R As String
    
        If 0 Then
            ElseIf Spl(Expr, "+", L, R) Then:    Eval = Eval(L) + Eval(R)
            ElseIf Spl(Expr, "*", L, R) Then:    Eval = Eval(L) * Eval(R)
            ElseIf Len(Expr) Then:               Eval = Val(Expr)
        End If
    End Function
    
    Private Function Spl(Expr As String, Op$, L$, R$) As Boolean
        Dim P As Long
        
        P = InStrRev(Expr, Op, , 1)
        If P Then Spl = True Else Exit Function
    
        R = Mid(Expr, P + Len(Op))
        L = Trim(Left$(Expr, IIf(P > 0, P - 1, 0)))
    
        Select Case Right(L, 1)
            Case "", "+", "*": Spl = False
        End Select
    End Function
    A better "stripped down"-version could look this way:

    Code:
    Public Function Eval(Expr As String) As Double
      Dim L As String, R As String
      If Spl(Expr, "+", L, R) Then Eval = Eval(L) + Eval(R): Exit Function
      If Spl(Expr, "*", L, R) Then Eval = Eval(L) * Eval(R): Exit Function
      If Len(Expr) Then Eval = Val(Expr)
    End Function
    
    Private Function Spl(Expr As String, Op$, L$, R$) As Long
      Spl = InStrRev(Expr, Op)
      If Spl Then R = Mid$(Expr, Spl + Len(Op)): L = Left$(Expr, Spl - 1)
    End Function
    With that change, my test-results (doing 100000 iterations) come out as:
    - Eval (0.85 sec)
    - doMath (1.45 sec)

    And native compiled (all Options):
    - Eval (0.62 sec)
    - doMath (1.22 sec)

    So your code is about 2 times slower - here is the test-code I was using (in a Form):
    Code:
    Option Explicit
    
    Private Sub Form_Click()
      AutoRedraw = True: Cls
      
      Dim i As Long, T!, Result
      T = Timer
        For i = 1 To 100000
          Result = Eval("3+5*9+7+2*5+3")
        Next
      Print "Eval", Timer - T, Result
      
      T = Timer
        For i = 1 To 100000
          Result = doMath("3+5x9+7+2x5+3")
        Next
      Print "doMath", Timer - T, Result
    End Sub
    Edit:
    And another speedup (about factor 2 again) can be achieved, when the Spl-Subfunction-calls are avoided:

    Code:
    Public Function Eval(Expr As String) As Double 'that's all what is needed
      Dim P As Long
      P = InStr(Expr, "+"): If P Then Eval = Eval(Left$(Expr, P - 1)) + Eval(Mid$(Expr, P + 1)): Exit Function
      P = InStr(Expr, "*"): If P Then Eval = Eval(Left$(Expr, P - 1)) * Eval(Mid$(Expr, P + 1)): Exit Function
      If Len(Expr) Then Eval = Val(Expr)
    End Function

    Olaf

  13. #13

    Thread Starter
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: simple math string parser

    hmm, not sure how you got those numbers. trying in my project i get
    (1,000,000) loops, 325 from doMath and 5765 from (new) Eval
    maybe you can take a look.

    edit: I did change doMath a bit, as you can see in the posts, i figure out that Byref and Copy single ascii is faster than Byval and using Mid$.
    Attached Files Attached Files
    Last edited by baka; Mar 17th, 2018 at 04:38 PM.

  14. #14
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: simple math string parser

    This class isn't super-rich. It handles only +, -, *, /, and ^ but it does do ( and ) as well as hex and octal and can be used locale-blind or locale-aware, even for a specific locale. Whitespace toleration seems pretty good.

    Not fully tested. Not a performance screamer but probably good enough for many purposes.

    Should be possible to add more operators and even named variables (by adding an = assignment operator). VFormat class also included, to balance locale handling (output as well as input).


    Name:  sshot1.png
Views: 2012
Size:  4.0 KB


    Name:  sshot2.png
Views: 2031
Size:  4.2 KB


    Reposted

    Changes made to a Property name (and the demo Form UI to match).

    Bug in parsing ")" corrected.

    Operators for integer divide \ and modulus % added though I could be happier about using % instead of Mod but right now I didn't want to add multicharacter operators.

    Replaced VarXXX() OLE API calls by VB6 operations.

    Other minor optimizations such as replacing some Select Case statements with On x GoTo statements. Those are slightly quicker but not really all that much so they may be a dubious choice.
    Attached Files Attached Files
    Last edited by dilettante; Mar 18th, 2018 at 02:46 PM.

  15. #15
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: simple math string parser

    Quote Originally Posted by baka View Post
    hmm, not sure how you got those numbers.
    By using the test-routine I've posted in #12 (you should try it).

    Quote Originally Posted by baka View Post
    trying in my project i get
    (1,000,000) loops, 325 from doMath and 5765 from (new) Eval
    maybe you can take a look.
    I did that - and what you do in your test-loop is:
    Not taking into account, that your routine *does* destroy the (ByRef) Input-Value - in a way,
    that already after the first round, you will not evaluate "the original expression-formula" (6+4x6+8+2x7) anymore,
    but the result (52) as the new "to evaluate input" instead.

    My testcode in #12 did avoid this, by using a String-Literal for the expression (which will get "passed along" unchanged then, in the test-loop).

    An easy fix for that (in your test-code) would be, when you change the signature of your Public Function to:
    Public Function doMath&(ByVal Expr$)

    Then you will get the same results as I was getting with my test-routine:
    - doMath being about 3 times slower in IDE-(PCode)-Mode
    - and about factor 3.5 - 4 slower when native compiled with all options

    Olaf

  16. #16
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: simple math string parser

    Quote Originally Posted by wqweto View Post
    @Olaf: Impressively short code!
    FYI, tweaked a test case to "-1 + -6 / 3 - --7" and got a diff in the test results in immediate window.
    Thanks, the (also slightly performance-optimized) version below can now handle that case of "multiple-unary-minus":
    Code:
    Option Explicit
    
    Public Function Eval(ByVal Expr As String)
      Do While HandleParentheses(Expr): Loop
      
      Dim L As String, R As String
      If Spl(Expr, "Or", L, R) Then:  Eval = Eval(L) Or Eval(R): Exit Function
      If Spl(Expr, "And", L, R) Then: Eval = Eval(L) And Eval(R): Exit Function
      If Spl(Expr, ">=", L, R) Then:  Eval = Eval(L) >= Eval(R): Exit Function
      If Spl(Expr, "<=", L, R) Then:  Eval = Eval(L) <= Eval(R): Exit Function
      If Spl(Expr, "=", L, R) Then:   Eval = Eval(L) = Eval(R): Exit Function
      If Spl(Expr, ">", L, R) Then:   Eval = Eval(L) > Eval(R): Exit Function
      If Spl(Expr, "<", L, R) Then:   Eval = Eval(L) < Eval(R): Exit Function
      If Spl(Expr, "Like", L, R) Then Eval = Eval(L) Like Eval(R): Exit Function
      If Spl(Expr, "&", L, R) Then:   Eval = Eval(L) & Eval(R): Exit Function
      If Spl(Expr, "-", L, R) Then:   Eval = Eval(L) - Eval(R): Exit Function
      If Spl(Expr, "+", L, R) Then:   Eval = Eval(L) + Eval(R): Exit Function
      If Spl(Expr, "Mod", L, R) Then: Eval = Eval(L) Mod Eval(R): Exit Function
      If Spl(Expr, "\", L, R) Then:   Eval = Eval(L) \ Eval(R): Exit Function
      If Spl(Expr, "*", L, R) Then:   Eval = Eval(L) * Eval(R): Exit Function
      If Spl(Expr, "/", L, R) Then:   Eval = Eval(L) / Eval(R): Exit Function
      If Spl(Expr, "^", L, R) Then:   Eval = Eval(L) ^ Eval(R): Exit Function
      If Trim$(Expr) >= "A" Then:     Eval = Fnc(Trim$(Expr)): Exit Function
      If InStr(Expr, "'") Then Eval = Replace(Trim$(Expr), "'", ""): Exit Function
      If Len(Expr) Then Eval = Val(Replace(Expr, "--", ""))
    End Function
    
    Private Function HandleParentheses(Expr As String) As Boolean
    Dim P As Long, i As Long, C As Long
      P = InStr(Expr, "(")
      If P Then HandleParentheses = True Else Exit Function
    
      For i = P To Len(Expr)
        If Mid$(Expr, i, 1) = "(" Then C = C + 1
        If Mid$(Expr, i, 1) = ")" Then C = C - 1
        If C = 0 Then Exit For
      Next i
    
      Expr = Left$(Expr, P - 1) & Str(Eval(Mid$(Expr, P + 1, i - P - 1))) & Mid$(Expr, i + 1)
    End Function
    
    Private Function Spl(Expr As String, Op$, L$, R$) As Boolean
    Dim P As Long
        P = InStrRev(Expr, Op, , 1)
     If P Then Spl = True Else Exit Function
     If P < InStrRev(Expr, "'") And InStr("*-", Op) Then P = InStrRev(Expr, "'", P) - 1
    
     L = Trim$(Left$(Expr, P - 1)): R = Mid$(Expr, P + Len(Op))
     
     Do
        Select Case Right$(L, 1)
          Case "", "+", "*", "/", "A" To "z": Spl = False: Exit Do
          Case "-": L = Trim$(Left$(L, Len(L) - 1)): R = "-" & R
          Case Else: Exit Do
        End Select
     Loop
    End Function
    
    Private Function Fnc(Expr As String)
      Select Case LCase$(Left$(Expr, 3))
        Case "abs": Fnc = Abs(Val(Mid$(Expr, 4)))
        Case "sin": Fnc = Sin(Val(Mid$(Expr, 4)))
        Case "cos": Fnc = Cos(Val(Mid$(Expr, 4)))
        Case "atn": Fnc = Atn(Val(Mid$(Expr, 4)))
        Case "log": Fnc = Log(Val(Mid$(Expr, 4)))
        Case "exp": Fnc = Exp(Val(Mid$(Expr, 4)))
        'etc...
      End Select
    End Function
    Olaf

  17. #17

    Thread Starter
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: simple math string parser

    that explained why it go so fast Schmidt! thanks for finding that error.
    in my test project, doing 1M loops, doMath makes to 7731 and your Eval to 2409.

    this thread should definitely go to codebank, especially Schmidt's code its very good.

  18. #18
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: simple math string parser

    I should have named the property "ReturnedTypes" something like "ParsedTypes" because it applies to the parsed numeric values in the expression rather than the calculated result. This can have an impact on the calculation's final data type, but it does not specify what type or types the result should be limited to.

  19. #19
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: simple math string parser

    Found a serious flaw in post #14's attachment code. Working on a fix and some optimizations.

    Changed version re-posted above to replace the original.
    Last edited by dilettante; Mar 18th, 2018 at 02:46 PM.

  20. #20

    Thread Starter
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: simple math string parser

    very impressive dilettante. i can't understand anything on the source code but its superfast and can do a lot!
    definitely a keeper if you need an expression evaluator.

  21. #21
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: simple math string parser

    New version in case anyone who stumbles upon this thread needs it.

    A little slower, but now it can work with variables (case-insensitive, but stored uppercased, letter and letters and digits or underscore):

    Name:  sshot1.png
Views: 2000
Size:  5.2 KB


    Name:  sshot2.png
Views: 1941
Size:  5.8 KB

    May still have lingering bugs... or a few new ones
    Attached Files Attached Files
    Last edited by dilettante; Mar 22nd, 2018 at 10:39 AM. Reason: replaced attachment with v. 2.2

  22. #22
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: simple math string parser

    Quote Originally Posted by baka View Post
    ... i can't understand anything on the source code but its superfast and can do a lot!
    Well, the code roughly follows this process to create a queue of operands and postfix operators from the input:

    https://en.wikipedia.org/wiki/Shunti...l_illustration

    Then it evaluates this.


    For the input:

    Code:
    a=(3+5)*9\2
    The queue looks like:

    Code:
    Operand: "A" (Variable name)
    Operand:  3  (Integer)
    Operand:  5  (Integer)
    Operator: +
    Operand:  9  (Integer)
    Operator: *
    Operand:  2  (Integer)
    Operator: \
    Operator: =

  23. #23
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: simple math string parser

    Once I am comfortable that the logic has been debugged more thoroughly I might combine the parsing and evaluation, eliminating the queue itself entirely using a second small operand stack instead of reusing the one stack there now. That may run even faster if evaluation is coded inline, saving a procedure call for each parsed token. Not sure I'll revisit this further though.

    There may be refinements or alternatives to the shunting algorithm (or better coding) that will run faster. Perhaps the oleaut32.dll value parsing could be replaced with something less generalized using inline code. That could be both faster and far more portable for non-Windows usage. Coping with locale conventions would seem to be of value, especially for programs processing user input. Chopping that out as a requirement would make VB6-code number parsing a lot simpler. Dropping the handling of E-notation for real number values and "thousands separators" simplifies things more. Making everything Double values instead of Variant could speed things a bit more.

    But my days of tinkering with compiler and interpreter construction are far behind me. I haven't taken a serious look at this stuff since school back in the 1970s.


    Ripping out the On X GoTo statements and putting Select Case back in would be a little slower. But not by much and it might make the code a bit clearer for anyone who wanted to study it or re-engineer it. There are a lot of unused OLE Const values that could be ripped out as well.

  24. #24
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: simple math string parser

    Oh yeah:

    The "FIFI" LCID (Finnish, Finland) was included as an example of another number-punctuation convention.

    English:

    Code:
    4,294,967,295.125
    Finnish:

    Code:
    4 294 967 295,125

    Name:  sshot.png
Views: 1916
Size:  5.3 KB

  25. #25
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: simple math string parser

    @dilettante: FYI, latest VEval 2.0.zip seems to fail on "1 + -(1)" w/ "Bad Expression"

    Here is my try on the shunting-yard algorithm
    Code:
    Option Explicit
    
    Private Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (Ptr() As Any) As Long
    Private Declare Sub GetMem4 Lib "msvbvm60" (Source As Any, Destination As Any)
    
    Private Function SimpleEval(sText As String) As Double
        Const TOK_FINAL     As Long = 0
        Const TOK_RPAREN    As Long = 1
        Const TOK_ADD       As Long = 2
        Const TOK_MOD       As Long = 3
        Const TOK_IDIV      As Long = 4
        Const TOK_MUL       As Long = 5
        Const TOK_UNARY     As Long = 6
        Const TOK_POWER     As Long = 7
        Const TOK_LPAREN    As Long = 8
        Const TOK_NUM       As Long = 9
        Const TOK_WHITE     As Long = 10
        Static aLookup(0 To 255) As Long
        Dim uSafeArray(0 To 5) As Long
        Dim aTextBuf()      As Integer
        Dim lPos            As Long
        Dim nChar           As Long
        Dim nNumChar        As Long
        Dim lNumSize        As Long
        Dim aValStack(0 To 1000) As Double
        Dim lValIdx         As Long
        Dim aOpStack(0 To 1000) As Long
        Dim lOpIdx          As Long
        Dim lTokPreced      As Long
        Dim lPrevPreced     As Long
    
        On Error GoTo EH
        '--- one-time init of token type lookup
        If aLookup(32) = 0 Then
            For nChar = 0 To UBound(aLookup)
                Select Case nChar
                Case 40         ' "("
                    aLookup(nChar) = TOK_LPAREN
                Case 41         ' ")"
                    aLookup(nChar) = TOK_RPAREN
                Case 43, 45     ' "+", "-"
                    aLookup(nChar) = TOK_ADD
                Case 42, 47     ' "*", "/"
                    aLookup(nChar) = TOK_MUL
                Case 94         ' "^"
                    aLookup(nChar) = TOK_POWER
                Case 92         ' "\"
                    aLookup(nChar) = TOK_IDIV
                Case 37         ' "%"
                    aLookup(nChar) = TOK_MOD
                Case 48 To 57, 46 ' "0" To "9", "."
                    aLookup(nChar) = TOK_NUM
                Case 32, 10, 13 ' " ", vbCrLf
                    aLookup(nChar) = TOK_WHITE
                Case Else
                    aLookup(nChar) = TOK_WHITE
                End Select
            Next
        End If
        '--- point aTextBuf -> sText w/o copying
        uSafeArray(0) = 1                   ' cDims
        uSafeArray(1) = 2                   ' cbElements
        uSafeArray(3) = StrPtr(sText)       ' pvData
        uSafeArray(4) = Len(sText) + 1      ' cElements
        Call GetMem4(VarPtr(uSafeArray(0)), ByVal ArrPtr(aTextBuf))
        For lPos = 0 To Len(sText) - 1
            nChar = aTextBuf(lPos)
            If nChar >= 256 Then
                GoTo Continue
            End If
            lTokPreced = aLookup(nChar)
            If lTokPreced = TOK_NUM Then
                For lNumSize = 1 To 100
                    nNumChar = aTextBuf(lPos + lNumSize)
                    If nNumChar >= 256 Or aLookup(nNumChar And &HFF&) <> TOK_NUM Then
                        Exit For
                    End If
                Next
                lValIdx = lValIdx + 1
                aValStack(lValIdx) = Val(Mid$(sText, lPos + 1, lNumSize))
                lTokPreced = TOK_NUM
                lPos = lPos + lNumSize - 1
            ElseIf lTokPreced = TOK_ADD Then
                If lPrevPreced >= TOK_ADD And lPrevPreced < TOK_NUM Then
                    lTokPreced = TOK_UNARY
                End If
            ElseIf lTokPreced = TOK_WHITE Then
                GoTo Continue
            End If
            If lTokPreced >= TOK_ADD And lTokPreced < TOK_NUM Then
                If lTokPreced <> TOK_UNARY Then '--- right assoc
                    GoSub EvalOpStack
                End If
                lOpIdx = lOpIdx + 1
                aOpStack(lOpIdx) = lTokPreced * &H10000 + nChar
            End If
            lPrevPreced = lTokPreced
    Continue:
        Next
        lTokPreced = TOK_FINAL
        GoSub EvalOpStack
        SimpleEval = aValStack(lValIdx)
    EH:
        '--- un-peek sText
        Call GetMem4(0&, ByVal ArrPtr(aTextBuf))
        Exit Function
    EvalOpStack:
        For lOpIdx = lOpIdx To 1 Step -1
            If aOpStack(lOpIdx) < lTokPreced * &H10000 Then
                Exit For
            End If
            lValIdx = lValIdx - 1
            Select Case aOpStack(lOpIdx) And &HFFFF&
            Case 43         ' "+"
                If aOpStack(lOpIdx) > TOK_UNARY * &H10000 Then
                    lValIdx = lValIdx + 1
                Else
                    aValStack(lValIdx) = aValStack(lValIdx) + aValStack(lValIdx + 1)
                End If
            Case 45         ' "-"
                If aOpStack(lOpIdx) > TOK_UNARY * &H10000 Then
                    lValIdx = lValIdx + 1
                    aValStack(lValIdx) = -aValStack(lValIdx)
                Else
                    aValStack(lValIdx) = aValStack(lValIdx) - aValStack(lValIdx + 1)
                End If
            Case 42         ' "*"
                aValStack(lValIdx) = aValStack(lValIdx) * aValStack(lValIdx + 1)
            Case 47         ' "/"
                aValStack(lValIdx) = aValStack(lValIdx) / aValStack(lValIdx + 1)
            Case 94         ' "^"
                aValStack(lValIdx) = aValStack(lValIdx) ^ aValStack(lValIdx + 1)
            Case 92         ' "\"
                aValStack(lValIdx) = aValStack(lValIdx) \ aValStack(lValIdx + 1)
            Case 37         ' "%"
                aValStack(lValIdx) = aValStack(lValIdx) Mod aValStack(lValIdx + 1)
            Case 40         ' "("
                lValIdx = lValIdx + 1
                If lTokPreced = TOK_RPAREN Then
                    lOpIdx = lOpIdx - 1
                    Exit For
                ElseIf lTokPreced > TOK_RPAREN Then
                    Exit For
                End If
            End Select
        Next
        Return
    End Function
    
    Private Sub Form_Load()
        Debug.Assert SimpleEval("(3.5) + 2.9 * (2 + -(1 + 2))") = (3.5) + 2.9 * (2 + -(1 + 2))
        Debug.Assert SimpleEval("2 \ 3 / 3") = 2 \ 3 / 3
        Debug.Assert SimpleEval("2 ^ -3 ^ 4") = 2 ^ -3 ^ 4
        Debug.Assert SimpleEval("2 ^ 3 \ 5 ^ 2 / 5 / 3") = 2 ^ 3 \ 5 ^ 2 / 5 / 3
        
        Dim sExpr           As String
        Dim dblTimer        As Double
        Dim lIdx            As Long
        Dim dblResult       As Double
        
        sExpr = "(3.5) + 2.9 * (2 + -(1 + 2))"
        dblTimer = Timer
        For lIdx = 1 To 100000
            dblResult = SimpleEval(sExpr)
        Next
        Debug.Print "SimpleEval: " & dblResult, Format$(Timer - dblTimer, "0.000")
    End Sub
    This supports same operators w/ no variables but is about as fast as VEval class and is ~150 LOC in total.

    cheers,
    </wqw>
    Last edited by wqweto; Mar 21st, 2018 at 10:21 AM.

  26. #26
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: simple math string parser

    Thanks.

    I was dealing with unary minus in a trivial and naive way. I'll have to look at correcting that.



    Ok, I replaced post #21 version 2.0 attachment with version 2.1 which seems to fix that issue. If I'm lucky I haven't broken something else with that patch.
    Last edited by dilettante; Mar 20th, 2018 at 10:03 AM.

  27. #27
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: simple math string parser

    I just updated SimpleEval above to support rest of the arith operators (on par with VEval) and noticed a couple of edge case for version 2.1 e.g.

    1) 2 \ 3 / 3
    2) 2 ^ -3 ^ 4

    Noticed that Integer division has an intresting precedence in Arithmetic Operator Precedence table.

    cheers,
    </wqw>

  28. #28
    Frenzied Member
    Join Date
    Apr 2012
    Posts
    1,253

    Re: simple math string parser

    Quote Originally Posted by dilettante View Post
    If I'm lucky I haven't broken something else with that patch.
    Ah, the Microsoft approach to issuing patches.
    If you don't know where you're going, any road will take you there...

    My VB6 love-children: Vee-Hive and Vee-Launcher

  29. #29
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: simple math string parser

    Good feedback.

    The precedence fix should be easy enough. I think exponentiation needs to be handled as right-associative so I can look at that too. See Operator associativity.

    Thanks.

  30. #30
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: simple math string parser

    I thought so too, but apparently the power (^) operator is left-associative in VB6.

    E.g. check the output of ? 2 ^ 3 ^ 4, (2 ^ 3) ^ 4, 2 ^ (3 ^ 4)

    But unary minus complicates matters a bit (it has lesser precedence): ? 2 ^ -3 ^ 4, (2 ^ -3) ^ 4, 2 ^ (-3 ^ 4)

    cheers,
    </wqw>

  31. #31
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: simple math string parser

    I haven't followed every word of this thread, but y'all are bumping up against a problem I've fought in the past: negation.

    In some math books, it's taught that negation has the highest order of precedence. However, that's not how most (if not all) programming languages work. Furthermore, we must distinguish between a variable containing a negative value and a negated variable (or constant or literal constant). In other words, there's a fundamental difference between the two printed Debug statements in the following:

    Code:
    
        Dim i As Long
        Dim j As Long
    
        i = -5
        j = 5
    
        Debug.Print i ^ 2
        Debug.Print -j ^ 2
    
    To fully understand negation, we must conceptualize a negative one variable (or constant)...

    Code:
    
        Const NegOne = -1
    
    Now, with this constant, we can conceptualize negation as a number times this constant. For instance, in the above, we can conceptualize...

    Code:
    
        Dim i As Long
        Dim j As Long
        Const NegOne = -1
    
        i = NegOne * 5
        j = 5
    
        Debug.Print i ^ 2
        Debug.Print NegOne * j ^ 2
    
    And now, we can "see" that the precedence order of operations makes perfect sense. Every negation must be "conceptualized" as the number being multiplied by -1 (with the -1 on the left-hand-side, as is the negation symbol).

    And, I suppose these expression evaluators should work the same way.

    Best Regards,
    Elroy
    Last edited by Elroy; Mar 21st, 2018 at 05:32 PM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  32. #32
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: simple math string parser

    I'll try to get back to this soon. Fixes shouldn't be very hard.

    Hit a deer last night and the insurance co. totaled out my car so I'm dealing with all of that right now.

  33. #33
    Frenzied Member
    Join Date
    Apr 2012
    Posts
    1,253

    Re: simple math string parser

    Quote Originally Posted by dilettante View Post
    Hit a deer last night and the insurance co. totaled out my car so I'm dealing with all of that right now.
    Wow. Same deal happened to me here (UK) about 5 years ago. Pretty horrifying experience...
    If you don't know where you're going, any road will take you there...

    My VB6 love-children: Vee-Hive and Vee-Launcher

  34. #34
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: simple math string parser

    Quote Originally Posted by dilettante View Post
    Hit a deer last night and the insurance co. totaled out my car so I'm dealing with all of that right now.
    Oh crud. Sorry to hear that, dilettante. I've hit three deer in the past, and one coyote. Two of the deer and the coyote were all in the same car, Chrysler Sebring. In fact, both deer, they hit me more than me hitting them. I finally figured out that a good horn does wonders to shake them out of their headlight daze.

    Best of luck with getting everything sorted.

    Elroy
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  35. #35
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: simple math string parser

    Eh, this was on a country road at 55 MPH at a point where the road is narrow and trees and brush are in quite close. Little more than a flash and the deer was right there, and I was watching intently for just this thing too.

    At least the (old) car was wedge-shaped enough that the deer rolled up the hood, the windshield, and over the roof. Until I got to a phone at a 24 hour gas station (didn't have my cellphone with me) I expected minor damage. Turned out the hood was pretty messed up though it stayed down but the right headlamp cluster was gone. Minor coolant leak, but driveable. Didn't pop any airbags.

    Got a repair estimate, then the insurance appraiser came out and said "It's dead, Jim" i.e. totaled. If I had made it to May the car would be 16 years old. Longer than I was with my ex wife, sad to see a "member of the family" go like that.

    By the time I got home today in a new car the tow truck was just hauling the old car from my driveway. Almost done aside from getting a lien-release letter I can't find my copy of so I can turn it over to the salvage yard with the title. Well that and getting the bad news from my insurance about my new premium rates when I get my token compensation.

    Amazing what they think you need in a car these days. Web browser? Seriously? BTW: this new car was built in a UK plant. Just glad to be in the position to whip out the checkbook for this sort of thing. A lot of people would be tempted to pay the $400 buyout and then get the old car duct-taped back together.

    I'll have to baby it through the break-in period so I can put 300K miles on this one too. I just need to avoid making the turbo kick in and be easy on braking.

    Too late I remembered I had about $5 in toll change I forgot to grab when I cleaned out the old buggy. At least I had just renewed my registration so I won't have to pay the higher rates on the new car for the next 11 months or so.


    I'll add that though the collision didn't bother me the money has left me more than a little rattled, being cheaper than Jack Benny. But I'd been casually car shopping for years and I did all the math as well as understanding how bad a choice it is to repair cars post-collision. Been there, done that before... after a semi tractor drove over my previous car while it was parked in a grocery store parking lot. That one I drove home too, but after repairs it was never quite right and I got rid of it.
    Last edited by dilettante; Mar 21st, 2018 at 10:32 PM.

  36. #36
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: simple math string parser

    Quote Originally Posted by Elroy View Post
    And now, we can "see" that the precedence order of operations makes perfect sense. Every negation must be "conceptualized" as the number being multiplied by -1 (with the -1 on the left-hand-side, as is the negation symbol).

    And, I suppose these expression evaluators should work the same way.
    Try these counter-samples
    Code:
        Debug.Print 2 ^ -j ^ 2
        Debug.Print 2 ^ NegOne * j ^ 2
        Debug.Print 2 ^ (NegOne * j) ^ 2
    All 3 return different results and you didn't mention using parens at all. It's obviously not just syntactic substition.

    The correct precedence seems to be Debug.Print 2 ^ (NegOne * j ^ 2)

    cheers,
    </wqw>

  37. #37
    Frenzied Member
    Join Date
    Apr 2012
    Posts
    1,253

    Re: simple math string parser

    Quote Originally Posted by Elroy View Post
    Two of the deer and the coyote were all in the same car, Chrysler Sebring.
    Crazy Americans. That's what happens when you let animals drive.
    If you don't know where you're going, any road will take you there...

    My VB6 love-children: Vee-Hive and Vee-Launcher

  38. #38
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: simple math string parser

    See post #21 above. New atttachment that should handle unary minus, integer divide, etc. better.

    Operator Precedence is for VBA but the same as the VB6 information that I can't find online right now.

  39. #39
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: simple math string parser

    Quote Originally Posted by wqweto View Post
    Try these counter-samples
    Code:
        Debug.Print 2 ^ -j ^ 2
        Debug.Print 2 ^ NegOne * j ^ 2
        Debug.Print 2 ^ (NegOne * j) ^ 2
    All 3 return different results and you didn't mention using parens at all. It's obviously not just syntactic substition.

    The correct precedence seems to be Debug.Print 2 ^ (NegOne * j ^ 2)

    cheers,
    </wqw>
    Yeah, I actually thought about that after I made that post. When a negation actually IS the exponent, it apparently does have a higher precedence. However, your example is just weird. Although, with enough thought, it does make sense, sort of. It places a double-condition on negation. Basically, the expression is evaluated for negation to see if anything can be executed with negation having lower precedence. And then, it's re-evaluated to see if it can be evaluated with negation having higher precedence. And apparently, that's independent of any left-to-right evaluation. And that's basically what you outlined in your last sentence.

    Best Regards,
    Elroy
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  40. #40
    Hyperactive Member
    Join Date
    Aug 2017
    Posts
    380

    Re: simple math string parser

    Quote Originally Posted by dilettante View Post
    Operator Precedence is for VBA but the same as the VB6 information that I can't find online right now.
    Bookmark this: Office 2010's Operator Precedence documentation.

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width