This demo shows how the VSFlexString control can be used to implement a mathematical expression evaluator. You can use this project as is, to allow users to enter expressions instead of numeric constants, or use it as a starting point for a more sophisticated evaluator with variables and custom functions.
Here is how the final application will look:
Step 1: Create the Controls
Start a new Visual Basic project. Right-click on the Toolbox and select Components, then pick the VSFlexString control from the list. The VSFlexString icon will be added to the Visual Basic Toolbox. (If the control does not appear on the list, it hasn't been registered on your computer. In this case, click the Browse button and select the VSSTR8.OCX file to register it.)
Create a VSFlexString object on the form by clicking the icon on the Toolbox, then dropping it on the form. Also create two text boxes and a command button. Arrange the controls and resize the form so it looks like the picture above.
Click on the VSFlexString control and use the Visual Basic Property window to change its name to "fs".
Step 2: Evaluating Expressions
This project consists mainly of a single recursive function that uses the VSFlexString control to evaluate the expressions typed in the text box.
This function, which we will write later, needs to be called when the user clicks the button. Double-click the button and type the following into the event handler:
' Evaluate the expression in Text1 and show the result in Text2
Sub Command1_Click ()
Text2 = Format(Eval(Text1), "0.00")
End Sub
That leaves only the Eval function, which takes a string containing a mathematical expression as a parameter and returns a value. Here is the code:
Function Eval(ByVal s As String) As Double
Dim s1$, s2$, s3$
Dim v#
' get ready to parse
fs = Trim(s) ' set breakpoint on this line
' interpret sub-expressions enclosed in parentheses
fs.Pattern = "{.*}({[^()]*}){.*}"
If fs.MatchCount > 0 Then
s1 = fs.TagString(0) ' stuff to the left
s2 = fs.TagString(1) ' sub-expression
s3 = fs.TagString(2) ' stuff to the right
Debug.Print "match: "; s1; " #<(># "; s2; " #<)># "; s3
v = Eval(s2) ' evaluate sub-expression
Eval = Eval(s1 & Format(v) & s3)
Exit Function
End If
' add and subtract (high-priority operators)
fs.Pattern = "{.+}{[+-]}{.+}"
If fs.MatchCount > 0 Then
s1 = fs.TagString(0) ' operand 1
s2 = fs.TagString(2) ' operand 2
Debug.Print "match: "; s1; " #<+-># "; s2
Select Case fs.TagString(1)
Case "+": Eval = Eval(s1) + Eval(s2)
Case "-": Eval = Eval(s1) - Eval(s2)
End Select
Exit Function
End If
' multiply and divide (lower-priority operators)
fs.Pattern = "{.+}{[*/]}{.+}"
If fs.MatchCount > 0 Then
s1 = fs.TagString(0) ' operand 1
s2 = fs.TagString(2) ' operand 2
Debug.Print "match: "; s1; " #<*/># "; s2
Select Case fs.TagString(1)
Case "*": Eval = Eval(s1) * Eval(s2)
Case "/": Eval = Eval(s1) / Eval(s2)
End Select
Exit Function
End If
' power (lowest-priority operator)
fs.Pattern = "{.+}^{.+}"
If fs.MatchCount > 0 Then
s1 = fs.TagString(0) ' operand 1
s2 = fs.TagString(1) ' operand 2
Debug.Print "match: "; s1; "#<^>#"; s2
Eval = Eval(s1) ^ Eval(s2)
Exit Function
End If
' number (nothing else matched, so this should be a number)
fs.Pattern = "^-?[0-9]+\.?[0-9]*$"
If fs.MatchCount > 0 Then
Eval = Val(s)
Else
Debug.Print "Eval Error: "; fs: Beep
End If
End Function
This routine handles all basic operators taking into account their precedence (that is, power before division before sum). It also handles sub-expressions contained in parentheses.
The Eval function consists of a pattern that repeats itself. The VSFlexString is used to parse each expression into its components, according to operator priority rules, and Eval is called recursively to evaluate each component. This process continues until a number is found and evaluated using VB's built-in Val function.
The typical pattern has this format: "{.+}{[*/]}{.+}". The "{+.}" at the start and end of the pattern match runs of one or more characters. The "{[*/]}" matches an operator that separates the left and right parts of the expression.
Step 3: Trying it out
Press F5 to run the project and type an expression such as "(2*(5+3)+144^0.5)/7". Then click the command button and the result (4) will appear on the second text box. The debug window will show a trace of the Eval function. Here's a commented version of the output:
match: (2* #<(># 5+3 #<)># +144^0.5)/7 found sub-expression
match: 5 #<+-># 3 found +
match: #<(># 2*8+144^0.5 #<)># /7 found sub-expression
match: 2*8 #<+-># 144^0.5 found +
match: 144 #<^># 0.5 found ^
match: 2 #<*/># 8 found *
match: 28 #<*/># 7 found /
The trace shows the order in which matches were found and operations executed. You may want to place a breakpoint at the top of the Eval routine and see what happens after each match.
Step 4: Extending the Evaluator
As an exercise for the reader, try adding support for user-defined variables and functions such as Sin and Cos.