Doing math in vb.net like Eval in javascript
Is there any way to parse a string in vb.net (like, built in methods), that can do math like Eval can? For example, 3+(7/3.5) as a string would return 2.
I am not asking for you to code this for me, I just want to know if there is a built in way to do this, if there is not I will code it myself.
I can wager that it would not be able to parse stuff like Sin(90) on its own, and I understand that would need to be replaced by Math.Sin(90).
If there is a built in method, how do you use it?
Solution 1:
There's a shortcut for limited (ie. simple) math expressions by using the DataTable.Compute method. Obviously, this isn't robust (limited functionality) and feels hackish to misuse the DataTable for this purpose, but I figured I would add to the current answers.
Example:
var result = new DataTable().Compute("3+(7/3.5)", null); // 5
"Sin(90)" wouldn't work with this approach. Refer to the DataColumn.Expression Property page for a list of supported functions, specifically under the "Aggregates" section.
Using the System.CodeDom namespace is an option.
Some helpful links:
- CodeDom Calculator
- Evaluating mathematical expressions using CodeDom
- Related SO question: Is there a string math evaluator in .NET?
EDIT: to address your comment, here is an approach to demonstrate replacing trigonometric functions with their equivalent Math class methods.
C#
string expression = "(Sin(0) + Cos(0)+Tan(0)) * 10";
string updatedExpression = Regex.Replace(expression, @"(?<func>Sin|Cos|Tan)\((?<arg>.*?)\)", match =>
match.Groups["func"].Value == "Sin" ? Math.Sin(Int32.Parse(match.Groups["arg"].Value)).ToString() :
match.Groups["func"].Value == "Cos" ? Math.Cos(Int32.Parse(match.Groups["arg"].Value)).ToString() :
Math.Tan(Int32.Parse(match.Groups["arg"].Value)).ToString()
);
var result = new DataTable().Compute(updatedExpression, null); // 10
VB.NET
Dim expression As String = "(Sin(0) + Cos(0)+Tan(0)) * 10"
Dim updatedExpression As String = Regex.Replace(expression, "(?<func>Sin|Cos|Tan)\((?<arg>.*?)\)", Function(match As Match) _
If(match.Groups("func").Value = "Sin", Math.Sin(Int32.Parse(match.Groups("arg").Value)).ToString(), _
If(match.Groups("func").Value = "Cos", Math.Cos(Int32.Parse(match.Groups("arg").Value)).ToString(), _
Math.Tan(Int32.Parse(match.Groups("arg").Value)).ToString())) _
)
Dim result = New DataTable().Compute(updatedExpression, Nothing)
Note, however, that you need to know the contents of the "arg" group. I know they are ints, so I used Int32.Parse on them. If they are a combination of items then this simple approach won't work. I suspect you will constantly need to band-aid the solution if it gets too complicated with more unsupported function calls, in which case the CodeDom approach or others may be more suitable.
Solution 2:
Here is a way to evaluate an expression that I haven't seen mentioned anywhere else: use a WebBrowser
control and JavaScript's eval()
:
Option Strict On
Imports System.Security.Permissions
<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _
<System.Runtime.InteropServices.ComVisibleAttribute(True)>
Public Class Form1
Dim browser As New WebBrowser
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
browser.ObjectForScripting = Me
'browser.ScriptErrorsSuppressed = True
browser.DocumentText = "<script>function evalIt(x) { return eval(x); }</script>"
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim result = browser.Document.InvokeScript("evalIt", New String() {"3+4*5"})
If result IsNot Nothing Then
MessageBox.Show(result.ToString()) '23
End If
End Sub
End Class