Jace.NET – Just another calculation engine for .NET
I work as a .NET technical architect in the financial industry and I get in touch with all sorts of mathematical formulas representing insurance or banking products. Most of the time, these formulas are hard coded by programmers and are thus not very flexible to be change dynamically at runtime by end-users.
This inspired me to start developing a calculation engine for the .NET platform that can execute any mathematical formula stored as a string both with or without variables. I wiped the dust of my university book: “Modern compiler implementations” and started the development a couple of weeks ago. I decided to name the calculation engine Jace.NET: Just another calculation engine for .NET.
Jace.NET is designed to run on .NET 4.0, Windows Phone 7, Windows Phone 8 and Windows RT.
How to use it
Jace.NET can be used in a couple of ways:
By directly executing a given mathematical function using the provided variables:
Dictionary<string, double> variables = new Dictionary<string, double>(); variables.Add("var1", 2.5); variables.Add("var2", 3.4); CalculationEngine engine = new CalculationEngine(); double result = engine.Calculate("var1*var2", variables);
By building a .NET Func accepting a dictionary as input containing the values for each variable:
CalculationEngine engine = new CalculationEngine(); Func<Dictionary<string, double>, double> function = engine.Build("var1+2/(3*otherVariable)"); Dictionary<string, double> variables = new Dictionary<string, double>(); variables.Add("var1", 2); variables.Add("otherVariable", 4.2); double result = function(variables);
By building a typed .NET Func:
CalculationEngine engine = new CalculationEngine(); Func<int, double, double> function = (Func<int, double, double>)engine.Function("var1+2/(3*otherVariable)") .Parameter("var1", DataType.Integer) .Parameter("otherVariable", DataType.FloatingPoint) .Result(DataType.FloatingPoint) .Build(); double result = function(2, 4.2);
Jace.NET has an architecture similar to the one of modern compilers: interpretation and execution are performed in a number of steps. Each step focuses on one aspect of the parsing and interpretation of the formula. This keeps the overall complexity manageable.
The process starts with the tokenizing phase. During this phase the input string is converted into the various allowed tokens: integers, doubles, operations and variables. If a part of the input string contains text that does not match with any type of token, an exception is thrown and Jace will halt.
When tokenizing is successfully finished, an abstract syntax tree (AST) is constructed. This abstract syntax tree is a tree like data model that unambiguously represents the mathematical formula in memory. All mathematical precedence rules are taking into account when constructing the abstract syntax tree. Jace uses an algorithm inspired by the shunting-yard algorithm of Dijkstra to create this AST.
After AST creation, the optimizer will try to simplify the abstract syntax tree: if a part of the formula does not depend on variables but solely on constants. This part of the tree is already calculated and replaced by a constant in the tree.
The final phase is the OpCode generation. During this phase, a .NET dynamic method is created and the necessary MSIL is generated to execute the formula. This dynamic method is cached in memory. If the same formula is executed again in the future with other values for the variables. The interpretation steps are skipped and the dynamic method is directly executed. If the formulas of the calculations are frequently reoccurring, Jace.NET has near compiled code performance.
If you are interested in the source code, please have a look at: