Best and shortest way to evaluate mathematical expressions
Asked Answered
H

8

18

There are many algorithms to evaluate expressions, for example:

  1. By Recursive Descent
  2. Shunting-yard algorithm
  3. Reverse Polish notation

Is there any way to evaluate any mathematical expression using C# .net reflection or other modern .net technology?

Hypallage answered 17/9, 2009 at 10:27 Comment(2)
I asked a similar question a while back. You may want to look at some of those answers: #234717Wick
Have you found a way to link to the variables used in the rest of the "static/pre-compiled" code?Battleship
S
19

Further to Thomas's answer, it's actually possible to access the (deprecated) JScript libraries directly from C#, which means you can use the equivalent of JScript's eval function.

using Microsoft.JScript;        // needs a reference to Microsoft.JScript.dll
using Microsoft.JScript.Vsa;    // needs a reference to Microsoft.Vsa.dll

// ...

string expr = "7 + (5 * 4)";
Console.WriteLine(JScriptEval(expr));    // displays 27

// ...

public static double JScriptEval(string expr)
{
    // error checking etc removed for brevity
    return double.Parse(Eval.JScriptEvaluate(expr, _engine).ToString());
}

private static readonly VsaEngine _engine = VsaEngine.CreateEngine();
Semiautomatic answered 17/9, 2009 at 13:15 Comment(1)
Shame it doesn't support the caret ^ for exponentiation.Blown
C
13

It's certainly possible. The CodeSnippetCompileUnit class does basically this. I wrote you some example usage code. You'll need to include these namespaces:

  • System.CodeDom.Compiler;
  • System.CodeDom;
  • Microsoft.CSharp;
  • System.Reflection;

Here's the code:

string source = @"
class MyType
{
    public static int Evaluate(<!parameters!>)
    {
        return <!expression!>;
    }
}
";

string parameters = "int a, int b, int c";
string expression = "a + b * c";

string finalSource = source.Replace("<!parameters!>", parameters).Replace("<!expression!>", expression);

CodeSnippetCompileUnit compileUnit = new CodeSnippetCompileUnit(finalSource);
CodeDomProvider provider = new CSharpCodeProvider();

CompilerParameters parameters = new CompilerParameters();

CompilerResults results = provider.CompileAssemblyFromDom(parameters, compileUnit);

Type type = results.CompiledAssembly.GetType("MyType");
MethodInfo method = type.GetMethod("Evaluate");

// The first parameter is the instance to invoke the method on. Because our Evaluate method is static, we pass null.
int result = (int)method.Invoke(null, new object[] { 4, -3, 2 });

Replace 'parameters' and 'expression' by whatever, and you've got yourself a general expression evaluator.

If you get a FileNotFoundException in results.CompiledAssembly, then the snippet failed to compile.

You might also want to take a look at the System.CodeDom.CodeSnippetExpression class. It's used for more specifically reading expressions, but an expression by itself can't be compiled, so you would need to use more CodeDom to build a working class and method around it. This is useful if you want to be able to programmatically manipulate what kind of class you're generating. CodeSnippetCompileUnit is nice to generate an entire working class at once (and simpler for an example) but to manipulate it you would have to do inconvenient string manipulations.

Choli answered 17/9, 2009 at 11:49 Comment(1)
For the record, the performance of this solution over using ncalc is HUGE, I tested it for a grapher and some multi variable functions took over 500s to be ploted, with this it took me less than 5s ploting over 400,000 points. Great solution!Moldau
U
4

ncalc is the best. you can find it in codeplex also in nugget.
NCalc is a mathematical expressions evaluator in .NET. NCalc can parse any expression and evaluate the result, including static or dynamic parameters and custom functions.

Unsung answered 30/10, 2012 at 21:19 Comment(0)
N
3

Although using compiler services is a simple and efficient solution, it raises serious security issues if the expression is entered by a user, because it could execute virtually anything.

There's another very simple solution that is much more secure : take advantage of the JScript Eval function. You just need to follow these steps :

Create a js file named JsMath.js :

class JsMath
{
    static function Eval(expression : String) : double
    {
        return eval(expression);
    };
}

Compile it into a class library :

jsc /t:library JsMath.js

Reference the JsMath library in your C# project, and use it like that :

double result = JsMath.Eval(expression);
Nonobservance answered 17/9, 2009 at 12:20 Comment(2)
It's actually possible to access the eval function directly from C#, without the intermediate JScript compilation step. See my answer for details.Semiautomatic
To avoid security issues using compiler services I use ANTL to pre parse user expression and avoid any weird input. If you're looking for performance the eval() function might not work.Moldau
S
3

For me Vici.Parser works extremely well: check it out here , it's the most flexible expression parser I've found so far.

(we've used it to set up 'human-readable' business rules, with data provided by an SQL server database)

Examples are available and there's a very good support by the developer (check the website's forum).

Simile answered 17/9, 2009 at 12:43 Comment(1)
@Simile - Link is dead.Occultism
W
1

I think this is the best way of all. Petar Repac's answer is amazing. Using the 'expression' argument of the DataColumn object solves incredibly and easily the topic:

static double Evaluate(string expression)
{
    var loDataTable = new DataTable();
    var loDataColumn = new DataColumn("Eval", typeof(double), expression);
    loDataTable.Columns.Add(loDataColumn);
    loDataTable.Rows.Add(0);
    return (double)(loDataTable.Rows[0]["Eval"]);
}
Warhead answered 24/9, 2014 at 22:6 Comment(0)
C
0

You can use Math-Expression-Evaluator library which implements Shunting Yard algorithm that I am author of. It supports simple expressions such as 2.5+5.9, 17.89-2.47+7.16, 5/2/2+1.5*3+4.58, expressions with parentheses (((9-6/2)*2-4)/2-6-1)/(2+24/(2+4)) and expressions with variables:

var a = 6;
var b = 4.32m;
var c = 24.15m;
var engine = new ExpressionEvaluator();
engine.Evaluate("(((9-a/2)*2-b)/2-a-1)/(2+c/(2+4))", new { a, b, c});

You can also pass parameters as named variables:

dynamic dynamicEngine = new ExpressionEvaluator();

var a = 6;
var b = 4.5m;
var c = 2.6m;

dynamicEngine.Evaluate("(c+b)*a", a: 6, b: 4.5, c: 2.6);

It supports .Net Standard 2.0 so can be used from .Net Core as well as .Net Full Framework projects and it doesn't have any external dependencies.

Codify answered 1/6, 2020 at 0:27 Comment(0)
S
0

To dynamically compile code using the new Roslyn API's, and load the assembly in a .net core project;

    string finalSource = ...;
    IEnumerable<Assembly> references = ...;

    var compilation = CSharpCompilation.Create("Dynamic",
        new[] { 
            SyntaxFactory.ParseSyntaxTree(
                finalSource,
                CSharpParseOptions.Default
                    .WithLanguageVersion(LanguageVersion.Latest)
            ) },
        references.Select(a => MetadataReference.CreateFromFile(a.Location)),

        new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
            .WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default)
    );

    using var ms = new MemoryStream();
    var e = compilation.Emit(ms);
    if (!e.Success)
        throw new Exception("Compilation failed");
    ms.Seek(0, SeekOrigin.Begin);

    var context = new AssemblyLoadContext(null, true);
    var assembly = context.LoadFromStream(ms);

Note that along with any other types required by the source you are compiling. In order to load the compiled assembly within the same process, references will need to include;

    AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name == "netstandard").Single(),
    typeof(object).Assembly
Salivation answered 1/6, 2020 at 0:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.