This is a simple mathematical and logical C# expression evaluator using Expression trees.
C# does not include a built-in method to evaluate a string during runtime, like VBScript's Eval(). I wanted to be able to define a condition "x = c" where x was a property of an runtime object that would vary over calls, and c was a constant, and this condition should be defined in an XML file where it could be changed if needed. I also wanted to ensure that any further conditions could also be defined in this manner, without the need to write any code.
One of the options I found around the internet was to compile the code into a class, load it into memory and call the function through reflection, but I immediately rejected this solution as it was clunky and inelegant.
I then came across Pascal Ganaye's Eval3 library
. The library supported passing external variables through a class - just what I needed. It performed well enough (I had to modify the existing code to accommodate the equals operator) but I felt that it was a bit difficult to maintain (had to write a function for each operator-combination type) and I was trying to move away from VB.NET.
I then started working with LINQ and Expression trees and realized I could build my own parser, create an Expression tree and compile it, and even cache the function delegate for optimum speed.
With a bit of help from Wikipedia, I borrowed an implementation of Dijkstra's Shunting-yard algorithm
to handle the parsing, rewrote it in C#, threw in Expressions and had the evaluator up and running.
- C# style operators
- Arithmetic operators: +- * / % ^
- Relational operators: = != < > <= >=
- Logical Operators: ! & | (bitwise logic) && || (short circuit logic)
- Brackets ( )
- Index accessor [ ]
- External variables through a class
- Strings: enclosed in 'single quotes' and string concatenation
- DateTimes: #any valid date format#, #Now# returns DateTime.Now
- true, false, null literals
- Declarative typing of numbers as double and float using the d and f suffixes
- Implicit conversion of numerical expressions
- Member access operator (.) for any valid expression. Access properties, fields and methods of types, objects and expressions
- Registry of external types and objects
- Some built-in default types (bool, int, double, float, char, string, DateTime, Convert, Math)
- Nested function calls (x.method(y.method(z.method()), y.method2()))
- Compiled expression is cached for speed, useful if expression needs to be evaluated multiple times
and Sample Expressions
The parser code is in the get-it-working state and may contain bugs.