Update - May 30, 2012
The API has been slightly changed. As of changeset d28640eceb5c
instead of creating an instance of the Parser
class, you should create an instance of the CompiledExpression
The difference between the generic version and the non-generic version is that the non-generic version incurs the additional overhead of boxing the result into type Object
. It helps that if you actually know what type you are expecting beforehand, you should have a slight performance increase. This is untested, but I just wanted to have a cleaner API. Otherwise most everything else on the API side remains the same.
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.