Introduction
This is another math expression evaluator with which you can let the user type an expression in text format with up to three variables (x, y, z) and calculate the result according
to the variable values.
The main idea
Parse the text function and build a tree-like structure because a math expression is always an operator with "something" left and "something" right.
"something" can be a variable or a number or an expression itself. Functions like sin, cos... are special nodes whose argument is an expression. Simple, isn't it?
For example: 2*x - 4/sin(3*x) according to the operator precedence is a subtraction, if we build a tree like this...
-----
| - |
-----
_______| | |_______
| ----- |
----- -----
| * | | / |
----- -----
_| | |_ _| | |_
| ----- | | ----- |
----- ----- ----- -----
| 2 | | X | | 4 | | sin |
| nr. | | var | | nr. | |-----|
----- ----- ----- | |
--|--
|
-----
| * |
-----
_| | |_
| ----- |
----- -----
| 3 | | X |
| nr. | | var |
----- -----
... we can read the original expression by scanning the tree from left to right.
Building the tree
Each type of node (operator, operand, function) is implemented by a proper class.
Private Class clsFunction
Protected mName As String
Protected mArgument As Object
Public Sub New()
mName = ""
mArgument = Nothing
End Sub
Property Name() As String
Get
Return mName
End Get
Set(ByVal Value As String)
mName = Value
End Set
End Property
Property Argument() As Object
Get
Return mArgument
End Get
Set(ByVal Value As Object)
mArgument = Value
End Set
End Property
End Class
Private Class clsOperator
Protected mOperator As String
Protected mLeft As Object
Protected mRight As Object
Public Sub New()
mLeft = Nothing
mRight = Nothing
End Sub
Property Operator() As String
Get
Return mOperator
End Get
Set(ByVal Value As String)
mOperator = Value
End Set
End Property
Property Left() As Object
Get
Return mLeft
End Get
Set(ByVal Value As Object)
mLeft = Value
End Set
End Property
Property Right() As Object
Get
Return mRight
End Get
Set(ByVal Value As Object)
mRight = Value
End Set
End Property
End Class
Private Class clsOperand
Protected mIsVariable As Boolean
Protected mVariable As String
Protected mOperand As Decimal
Property IsVariable() As Boolean
Get
Return mIsVariable
End Get
Set(ByVal Value As Boolean)
mIsVariable = Value
End Set
End Property
Property Variable() As String
Get
Return mVariable
End Get
Set(ByVal Value As String)
mVariable = Value
End Set
End Property
Property Operand() As Decimal
Get
Return mOperand
End Get
Set(ByVal Value As Decimal)
mOperand = Value
End Set
End Property
End Class
We build the expression tree according to the operator precedence this way:
- Function
Expression
handles left sum|sub right by calling the function Term
for each side.
- Function
Term
handles left mult|div right by calling the function Factor
for each side.
- Function
Factor
handles left power right by calling the function Power
for each side.
- Function
Power
ends the work by detecting a new expression (if brackets), function (3 letters), variable, or number.
Calculate the result
Once we have the tree, scanning it from left to right to calculate the result is quite simple:
Protected Overridable Function GetResult(ByVal objNode As Object) As Decimal
If objNode Is Nothing Then Return 0
If TypeOf objNode Is clsFunction Then
Select Case objNode.Name.ToLower
Case "sin"
Return System.Math.Sin(GetResult(objNode.Argument))
Case "cos"
Return System.Math.Cos(GetResult(objNode.Argument))
Case "tan"
Return System.Math.Tan(GetResult(objNode.Argument))
Case "atn"
Return System.Math.Atan(GetResult(objNode.Argument))
Case "lne"
Return System.Math.Log(GetResult(objNode.Argument))
Case "log"
Return System.Math.Log10(GetResult(objNode.Argument))
Case "sqr"
Return System.Math.Sqrt(GetResult(objNode.Argument))
End Select
ElseIf TypeOf objNode Is clsOperator Then
Select Case objNode.operator
Case "+"
Return GetResult(objNode.Left) + GetResult(objNode.Right)
Case "-"
Return GetResult(objNode.Left) - GetResult(objNode.Right)
Case "*"
Return GetResult(objNode.Left) * GetResult(objNode.Right)
Case "/"
Return GetResult(objNode.Left) / GetResult(objNode.Right)
Case "^"
Return GetResult(objNode.Left) ^ GetResult(objNode.Right)
End Select
ElseIf objNode.isvariable Then
Select Case objNode.variable
Case "X"
Return mX
Case "Y"
Return mY
Case "Z"
Return mZ
End Select
Else
Return objNode.operand
End If
End Function
See the source code for more comments.
Using the code
dim LtFn as New LTFunctions.Functions
With LtFn
.Function = "sin(x)+2*p"
.X =-1
.Y = 0
.Z = 0
Messagebox.Show (Format(.Result, "standard"))
End With