SkillAgentSearch skills...

NoStringEvaluating

Fast low memory consuming mathematical evaluation without endless string parsing! Parses string formula once and uses its object sequence in each evaluation. Moreover, provides user defined functions and variables.

Install / Use

/learn @KovtunV/NoStringEvaluating

README

Fast low memory consuming mathematical evaluation without endless string parsing! Parses string formula once and uses its object sequence in each evaluation. Moreover provides user defined functions and variables. <br/> Build Status NuGet version NuGet Download codecov Tweet <br/>

Microsoft.Extensions.DependencyInjection <br/> Build Status NuGet version NuGet Download <br/>

Quick Links

<!--ts--> <!--te-->

Features

  • Fast math evaluation
  • Zero-allocation code (object pooling)
  • User defined functions
  • User defined variables with any chars
  • Mixed result type
  • Scientific notation

Performance

Compared with a good solution mXparser

  • In general, x5 faster!

Testing formulas

| № | Formula | | ------------ | ------------ | | Empty | | | NumberOnly | 3 | | 1 | 3 * 9 | | 2 | 3 * 9 / 456 * 32 + 12 / 17 - 3 | | 3 | 3 * (9 / 456 * (32 + 12)) / 17 - 3 | | 4 | (2 + 6 - (13 * 24 + 5 / (123 - 364 + 23))) - (2 + 6 - (13 * 24 + 5 / (123 - 364 + 23))) + (2 + 6 - (13 * 24 + 5 / (123 - 364 + 23))) * 345 * ((897 - 323)/ 23) | | 5 | Arg1 * Arg2 + Arg3 - Arg4 | | 6 | Arg1 * (Arg2 + Arg3) - Arg4 / (Arg5 - Arg6 + 1) + 45 * Arg7 + ((Arg8 * 56 + (12 + Arg9))) - Arg10 | | 7 | add(1; 2; 3) | | 8 | add(add(5; 1) - add(5; 2; 3)) | | 9 | if(Arg1 > 0; add(56 + 9 / 12 * 123.596; or(78; 9; 5; 2; 4; 5; 8; 7); 45;5); 9) * 24 + 52 -33 | | 10 | kov(1; 2; 3) - kovt(8; 9) |

1 000 000 calculations

Less is better image graph image table

Benchmark results

Both

Conclusion

As you can see this solution is faster in all cases, furthermore there isn't any garbage collection.

Benchmark code - src/ConsoleApp/Benchmark/BenchNumbers.cs

Benchmark excel - benchResults/Benchmark.xlsx

Quick start

Initialization

There are two ways to use evaluator:

  1. Static initialization
public void SomeMethod()
{
    var facade = NoStringEvaluator.CreateFacade();
    var evaluator = facade.Evaluator;
}
  1. DI from the package
public void ConfigureServices(IServiceCollection services)
{
    // ......
    services.AddNoStringEvaluator();
}

Usage

Add INoStringEvaluator to your controller, service, etc...

And just send string or FormulaNodes to evaluation:

public class MyService
{
    private INoStringEvaluator _noStringEvaluator;

    public MyService(INoStringEvaluator noStringEvaluator)
    {
        _noStringEvaluator = noStringEvaluator;
    }

    public double CalcNumber(string formula)
    {
        return _noStringEvaluator.CalcNumber(formula);
    }
    
    public string CalcWord(string formula)
    {
        return _noStringEvaluator.CalcWord(formula);
    }
    
    public EvaluatorValue Calc(string formula)
    {
        return _noStringEvaluator.Calc(formula);
    }
}

If you have variables, you can send IDictionary or your IVariablesContainer implementation:

public class MyService
{
    private INoStringEvaluator _noStringEvaluator;

    public MyService(INoStringEvaluator noStringEvaluator)
    {
        _noStringEvaluator = noStringEvaluator;
    }

    public double Calc(string formula, IDictionary<string, EvaluatorValue> variables)
    {
        return _noStringEvaluator.CalcNumber(formula, variables);
    }
}

User defined functions

If you need your function, just implement the interface IFunction If you want to returnt extra type, use factory.

As an argument's separator can be:

  • ;
  • ,

For instance, usage function "YouAre('Vitaly'; 26)":

public class MyFunction : IFunction
{
    public string Name { get; } = "YouAre";

    public bool CanHandleNullArguments { get; }

    public InternalEvaluatorValue Execute(List<InternalEvaluatorValue> args, ValueFactory factory)
    {
        var name = args[0].Word;
        var age = args[1];

        var ageAfterDecade = age + 10;
        var result = $"Hello, {name}. After 10 years you will be {ageAfterDecade} y.o.";

        return factory.Word.Create(result);
    }
}

And don't forget to initialize your functions via options or directly in IFunctionReader

public void SomeMethod()
{
    // NoStringEvaluator.CreateFacade(opt => opt.WithFunctionsFrom(<type from source assembly>));
    // NoStringEvaluator.CreateFacade(opt => opt.WithFunctionsFrom(<source assembly>));
    // NoStringEvaluator.CreateFacade(opt => opt.WithFunctions(new MyFunction()));

    // same with DI
    // services.AddNoStringEvaluator(opt => opt.WithFunctions(new MyFunction()));
}

Extra types

Apart from double calculations you can work with types:

  • Boolean
  • DateTime
  • String
  • List of string
  • List of double
  • Null
  • Object

Object is a special type to allow using, for example, services inside function.

public void Should_Evaluate_Service()
{
    // arrange
    var service = _serviceFactory(null);
    var args = new Dictionary<string, EvaluatorValue>
    {
        ["myService"] = new EvaluatorValue(new MyService()),
        ["myNum"] = 10
    };
    var expected = 50.5;

    // act
    var actual = service.CalcNumber("TestService(myService; myNum)", args);

    // assert
    actual.Should().BeApproximatelyNumber(expected);
}

private class ServiceFunction : IFunction
{
    public string Name { get; } = "TestService";

    public bool CanHandleNullArguments { get; }

    public InternalEvaluatorValue Execute(List<InternalEvaluatorValue> args, ValueFactory factory)
    {
        return args[0].GetObject<MyService>().GetTemperature() + args[1];
    }
}

private class MyService
{
    public double GetTemperature()
    {
        return 40.5;
    }
}

List description

You can describe a list inside the formula

| Example | Result | | ------------ | ------------ | | IsMember({'printer', 'computer', 'monitor'}; 'computer') | 1 | | Unique({'NEW','OLD','NEW','HEAVEN','OLD'}) | {'NEW','OLD','HEAVEN'} | | Add({1, 2, 3, 10, 3}) | 19 |

Variables

You can use two types of variables:

  • Simple variable
  • Bordered variable

Simple variable

Simple variable means that it named without unique symbols and starts with a letter. Only one extra symbol is possible, it's "_"

Some examples:

  • "25 + myArgument - 1"
  • "25 + myArg1 - 2"
  • "arg5684argArg_arg"
  • "25 + myArgument_newAge - 3"

Bordered variable

Bordered variable means that it has a tricky name with any symbols, except for square brackets.

Some examples:

  • "25 + [myVariable and some words] - 1"
  • "25 + [
View on GitHub
GitHub Stars31
CategoryDevelopment
Updated5mo ago
Forks12

Languages

C#

Security Score

92/100

Audited on Oct 28, 2025

No findings