VSFlexString Introduction > Calculator Demo |
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:
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 properties window to change its name to "fs".
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:
Example Title |
Copy Code
|
---|---|
' 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:
Example Title |
Copy 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 (i.e., 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.
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:
Example Title |
Copy Code
|
---|---|
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.
As an exercise for the reader, try adding support for user-defined variables and functions such as Sin and Cos.