JSribar.MathematicalExpressionEvaluator 1.0.0

dotnet add package JSribar.MathematicalExpressionEvaluator --version 1.0.0
NuGet\Install-Package JSribar.MathematicalExpressionEvaluator -Version 1.0.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="JSribar.MathematicalExpressionEvaluator" Version="1.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add JSribar.MathematicalExpressionEvaluator --version 1.0.0
#r "nuget: JSribar.MathematicalExpressionEvaluator, 1.0.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install JSribar.MathematicalExpressionEvaluator as a Cake Addin
#addin nuget:?package=JSribar.MathematicalExpressionEvaluator&version=1.0.0

// Install JSribar.MathematicalExpressionEvaluator as a Cake Tool
#tool nuget:?package=JSribar.MathematicalExpressionEvaluator&version=1.0.0

Mathematical Expression Evaluator

C# library for parsing and evaluation of mathematical expressions with one or more variables. Written to support .NET 4.5 (and higher), .NET 5.0 and .NET 6.0.

Project was initially created as a demonstration of Interpreter design pattern but was later extended with parser, employing Shunting yard algorithm.

Features

Basic features of the library:

  • Supports basic arithmentic operations, using both ASCII characters and Unicode mathematical signs:
    • addition (<code>+</code>),
    • subtraction (<code>-</code>, U+2212 <code>−</code>),
    • multiplication (<code>*</code>, U+00D7 <code>×</code>, U+22C5 <code>⋅</code>),
    • division (<code>/</code>, <code>:</code>, U+00F7 <code>÷</code>),
    • exponentiation (<code>^</code>).
  • Allows extra sign (<code>-</code>, U+2212 <code>−</code> or <code>+</code>) preceding a value, function or variable.
  • Takes care of operator precedence.
  • Supports parentheses to override operator precedence.
  • Supports standard mathematical functions with one (e.g. sin, sqrt) or two arguments (e.g. atan2, pow) that can be invoked using corresponding identifiers.
  • Supports identifiers for basic mathematical constants (e, π).
  • Numeric values in expression can be in floating point (e.g. 3.14159) or in scientific format (e.g. 314159e-5). Note: only decimal point is allowed as a decimal separator because comma is used as function argument separator.
  • Additional custom functions and constants can be defined in the runtime.
  • Parser reports error for invalid expression, with exact position where error occured.

Basic Usage

Below are some introductory examples of library usage.

Simple expression with a single variable

How to evaluate expression

x + 3

for a value of x=2:

  1. Create <code>Parser</code> object.
  2. Invoke <code>Parse</code> method and pass the string with mathematical expression. On success, <code>Parse</code> method returns final <code>IExpression</code> object evaluated as a composition of expressions from operations parsed.
  3. Invoke <code>Evaluate</code> method of the object returned by <code>Parse</code> method in step 2. Value of the variable must be passed as argument to the method and method returns the value of mathematical expression for the value of variable x provided.
using JSribar.MathematicalExpressionEvaluator;
...
var parser = new Parser();
var mathExpression = "x + 3";
var expression = parser.Parse(mathExpression);
var x = 2;
var result = expression.Evaluate(x);
Console.WriteLine($"Value of {mathExpression} for x={x} is {result}");

Note 1: Spaces around operators and operands are optional. Mathematical expression in the example above could be also written as:

//...
var mathExpression = " x+3 ";

Note 2: Expression object returned by <code>Parse</code> method can be reused to evaluate expression for different values of the variable. For example, to evaluate the expression for a range of x:

var parser = new Parser();
var mathExpression = "x + 3";
var expression = parser.Parse(mathExpression);
Console.WriteLine($"Values of {mathExpression}{Environment.NewLine}x\tvalue");
for (int x = 0; x <= 10; ++x)
{
    Console.WriteLine($"{x}\t{expression.Evaluate(x)}");
}

Expression with operators of different precedence

Evaluate expression

12 − 8 × 2<sup>x</sup> ÷ 4

for a value of x=2 (result should be 4):

using JSribar.MathematicalExpressionEvaluator;
// ...
var parser = new Parser();
var mathExpression = "12 - 8 * 2 ^ x / 4";
var expression = parser.Parse(mathExpression);
var x = 2;
var result = expression.Evaluate(x);
Console.WriteLine($"Value of {mathExpression} for x={x} is {result}");

Expression with parentheses to override operator precedence

Evaluate expression

12 − (8 × 2)<sup>(x ÷ 4)</sup>

for x=2 (result should be 8):

using JSribar.MathematicalExpressionEvaluator;
// ...
var parser = new Parser();
var mathExpression = "12 - (8 * 2) ^ (x / 4)";
var expression = parser.Parse(mathExpression);
var x = 2;
var result = expression.Evaluate(x);
Console.WriteLine($"Value of {mathExpression} for x={x} is {result}");

Expression with mathematical function

Evaluate expression

x + tan(π/x)

for x=4 (result should be 5):

// ...
var mathExpression = "x + tan(PI / x)";
var result = mathExpression.Evaluate(4);
// ...

Note 1: Left parenthesis must follow function name immediately, otherwise <code>ParserException</code> is thrown.

Note 2: Mathematical constant π can be written also as a Greek character π in the expression string, i.e. <code>"x + tan(π / x)"</code>.

Expression with preceding sign

Evaluate expression

x + −tan(π/x)

for x=4 (result should be −5):

// ...
var mathExpression = "-x +-tan(PI / x)";
var result = mathExpression.Evaluate(4); 
// ...

Note: Preceding sign must be followed immediately by constant, variable or function name (no whitespaces are allowed), otherwise <code>ParserException</code> is thrown.

Adding custom function and constant

Custom function can be added in runtime by <code>AddFunction</code> or <code>AddFunction2</code> methods, for functions with single or two arguments, respectively. Custom constants can be added by <code>AddConstant</code> method.

// User defined function:
static double Hypotenuse(double a, double b)
{
    return Math.Sqrt(a * a + b * b);
}
// ...
var parser = new Parser();
// Add user defined mathematical function 'Hypotenuse':
parser.AddFuncion2("hypotenuse", Hypotenuse);
// Add mathematical constant 'two' with value of 2:
parser.AddConstant("two", 2);
var expression = parser.Parse("hypotenuse(x, 2 * two)");
var result = expression.Evaluate(3); 
// ...

Note 1: Functions and constants must be added before invoking <code>Parse</code> method with expression that uses them in order to parse the expression correctly.

Note 2: If name of function or constant is already used for existing function or constant, parser throws <code>IdentifierException</code>.

Using different identifier for a variable

Parser assumes that variable is named x by default. If you need to use different identifier for a variable, simply provide the identifier to the <code>Parser</code> constructor:

// Use 'time' instead of default 'x' identifier for variable:
var parser = new Parser("time");
var expression = parser.Parse("sin(time / (2 * PI))");
// ...

Note: If name of identifier is already used for existing function or constant, parser throws <code>IdentifierException</code>.

Using multiple variables

To evaluate expression with multiple variables, variable identifiers must be passed to the <code>Parser</code> as a collection of strings. Actual values of variables for a given context must be passed to the <code>Evaluate</code> method as a collection of <code>string</code> - <code>double</code> tuples, with variable identifiers and corresponding values.

// Use 'x' and 'y' identifiers:
var parser = new Parser("x", "y");
// Expression with 2 variables:
var expression = parser.Parse("sin(x + y)");
// Provide values: x=2, y=3:
var result = expression.Evaluate(("x", 2), ("y", 3));
// ...

Built-in Functions

Following functions are built-in and directly available: | Identifier | Function invoked | Remark | | ------------------------------------ | ---------------------------------------------------------------------------------------- | ------------------- | | <code>abs</code> | <code>Math.Abs</code> | | | <code>acos</code> | <code>Math.Acos</code> | | | <code>acosh</code> | <code>Math.Acosh</code> | .NET 5.0 or higher | | <code>asin</code> | <code>Math.Asin</code> | | | <code>asinh</code> | <code>Math.Asinh</code> | .NET 5.0 or higher | | <code>atan</code> | <code>Math.Atan</code> | | | <code>atan2</code> | <code>Math.Atan2</code> | two arguments | | <code>atanh</code> | <code>Math.Atanh</code> | .NET 5.0 or higher | | <code>cbrt</code> | <code>Math.Cbrt</code> | .NET 5.0 or higher | | <code>cos</code> | <code>Math.Cos</code> | | | <code>cosh</code> | <code>Math.Cosh</code> | | | <code>exp</code> | <code>Math.Exp</code> | | | <code>ln</code> | <code>Math.Log</code> | | | <code>log</code>, <code>log10</code> | <code>Math.Log10</code> | | | <code>log2</code> | <code>Math.Log2</code> | .NET 5.0 or higher | | <code>pow</code> | <code>Math.Atan2</code> | two arguments | | <code>sin</code> | <code>Math.Sin</code> | | | <code>sinh</code> | <code>Math.Sinh</code> | | | <code>sqrt</code> | <code>Math.Sqrt</code> | | | <code>tan</code> , <code>tg</code> | <code>Math.Tan</code> | | | <code>tanh</code> | <code>Math.Tanh</code> | |

Built-in Mathematical Constants

Following mathematical constants are built-in and directly available: | Identifier | Field invoked | | ------------------------------------ | ----------------------------------------------------------------------------------- | | <code>E</code> | <code>Math.E</code> | | <code>PI</code>, <code>π</code> | <code>Math.PI</code> |

Error Reporting

Errors are reported through <code>ParserException</code> and <code>IdentifierException</code>.

<code>ParserException</code>

This exception will be thrown by <code>Parse</code> method when error is encountered in expression string being parsed. <code>ParserException</code> contains two properties/fields:

  • <code>Message</code> - string with error message,
  • <code>Position</code> - integer with the position in input expression string where error occured.

<code>IdentifierException</code>

This exception will be thrown:

  1. When custom identifier passed to the <code>Parser</code> has invalid format (it must not be empty, must start with letter followed by a sequence of letters and digits only).
  2. When user defined function or constant identifier passed to <code>AddConstant</code>, <code>AddFunction</code> or <code>AddFunction2</code> method has invalid format or is already used for some function or constant.
  3. When not all identifier values are supplied to the <code>Evaluate</code> method.

Exception contains two properties/fields:

  • <code>Message</code> - string with error message,
  • <code>Identifier</code> - string with identifier that caused the error.

How to Add New Function or Constant into Code

To add a new function, open <code>Parser.Functions.cs</code> file:

  1. Append new <code>Operator</code> enumeration value for the function.
  2. Append entry to <code>functionTokenMap</code> dictionary with new function identifer as a key and enumeration value from the step 1 as a value.
  3. Append entry to <code>functionMap</code> dictionary with enumeration value from step 1 as a key and the function as a value. Note: if function accepts two arguments, it should be appended to <code>functionMap2</code> dictionary.

To add a new constant, open <code>Parser.Constants.cs</code> file and append new entry to <code>mathematicalConstantsMap</code> with new constant identifier as dictionary key and constant value as entry value.

Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Framework net45 is compatible.  net451 was computed.  net452 was computed.  net46 was computed.  net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.0 197 9/13/2022

Initial release