KovtunV / 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.
MIT License
28 stars 10 forks source link

Use without extra dependencies #1

Closed RobertBeekman closed 3 years ago

RobertBeekman commented 3 years ago

Hello,

I'd love to use this library in a dekstop application but I'd really rather stay away from a dependency on Microsoft.Extensions.DependencyInjection and friends.

Is it possible to use this without adding that dependency? I'm using Ninject.

Thanks in advance!

KovtunV commented 3 years ago

Hello @SpoinkyNL,

for sure you can, I suggest two ways:

1) Using Ninject

public class Program
{
    static async Task Main(string[] args)
    {
        var kernel = new StandardKernel();
        kernel.Load(Assembly.GetExecutingAssembly());

        var eval = kernel.Get<INoStringEvaluator>();

        var res1 = eval.CalcNumber("5 - 36"); // -31
        var res2 = eval.CalcWord("5 + \"happy\""); // !5happy!
        var res3 = eval.CalcNumber("max(1; 5; 5; 3; 45; 7) - min(5; 7; 5; 2; 4)"); // 43
        var res4 = eval.CalcWord("YouAre('Vitaly'; 26)"); // !Hello, Vitaly. After 10 years you will be 36 y.o.!
    }
}

NinjectModule

public class NoStringNinjectModule : NinjectModule
{
    public override void Load()
    {
        // Pooling
        Bind<ObjectPool<Stack<InternalEvaluatorValue>>>()
            .ToConstant(ObjectPool.Create<Stack<InternalEvaluatorValue>>())
            .InSingletonScope();

        Bind<ObjectPool<List<InternalEvaluatorValue>>>()
            .ToConstant(ObjectPool.Create<List<InternalEvaluatorValue>>())
            .InSingletonScope();

        Bind<ObjectPool<ExtraTypeIdContainer>>()
            .ToConstant(ObjectPool.Create<ExtraTypeIdContainer>())
            .InSingletonScope();

        // Parser
        Bind<IFormulaCache>().To<FormulaCache>().InSingletonScope();
        Bind<IFunctionReader>().To<FunctionReader>().InSingletonScope();
        Bind<IFormulaParser>().To<FormulaParser>().InSingletonScope();

        // Checker
        Bind<IFormulaChecker>().To<FormulaChecker>().InSingletonScope();

        // Evaluator
        Bind<INoStringEvaluator>().To<NoStringEvaluator>().InSingletonScope();

        // Options
        var opt = new NoStringEvaluatorOptions().SetWordQuotationMark("!");
        opt.UpdateConstants();

        // If needed
        InjectUserDefinedFunctions();
    }

    private void InjectUserDefinedFunctions()
    {
        var functionReader = Kernel.GetRequiredService<IFunctionReader>();
        NoStringFunctionsInitializer.InitializeFunctions(functionReader, typeof(NoStringNinjectModule));
    }
}

2) Using manual initialization - not recommended, but it's possible :smiley:

public class Program
{
    static async Task Main(string[] args)
    {
        var evalInstance = new EvaluatorInstance();
        var eval = evalInstance.Evaluator;

        var res1 = eval.CalcNumber("5 - 36"); // -31
        var res2 = eval.CalcWord("5 + \"happy\""); // !5happy!
        var res3 = eval.CalcNumber("max(1; 5; 5; 3; 45; 7) - min(5; 7; 5; 2; 4)"); // 43
        var res4 = eval.CalcWord("YouAre('Vitaly'; 26)"); // !Hello, Vitaly. After 10 years you will be 36 y.o.!
    }
}

EvaluatorInstance

public class EvaluatorInstance
{
    private readonly ObjectPool<Stack<InternalEvaluatorValue>> _stackPool;
    private readonly ObjectPool<List<InternalEvaluatorValue>> _argsPool;
    private readonly ObjectPool<ExtraTypeIdContainer> _extraTypeIdPool;

    public EvaluatorInstance()
    {
        // Pooling
        _stackPool = ObjectPool.Create<Stack<InternalEvaluatorValue>>();
        _argsPool = ObjectPool.Create<List<InternalEvaluatorValue>>();
        _extraTypeIdPool = ObjectPool.Create<ExtraTypeIdContainer>();

        // Parser
        FunctionReader = new FunctionReader();
        FormulaParser = new FormulaParser(FunctionReader);
        FormulaCache = new FormulaCache(FormulaParser);

        // Checker
        FormulaChecker = new FormulaChecker(FormulaParser);

        // Evaluator
        Evaluator = new NoStringEvaluator(_stackPool, _argsPool, _extraTypeIdPool, FormulaCache);

        // Options
        var opt = new NoStringEvaluatorOptions().SetWordQuotationMark("!");
        opt.UpdateConstants();

        // If needed
        InitializeUserDefinedFunctions();
    }

    public INoStringEvaluator Evaluator { get; }

    public IFunctionReader FunctionReader { get; }

    public IFormulaParser FormulaParser { get; }

    public IFormulaCache FormulaCache { get; }

    public IFormulaChecker FormulaChecker { get; }

    private void InitializeUserDefinedFunctions()
    {
        NoStringFunctionsInitializer.InitializeFunctions(FunctionReader, typeof(NoStringNinjectModule));
    }
}
KovtunV commented 3 years ago

BTW, @SpoinkyNL, if Microsoft.Extensions.DependencyInjection dependency shouldn't be, I can split nuget and make two

Would it be good?

RobertBeekman commented 3 years ago

Hey, thanks for the reply and code sample!

A separate Nuget package sounds great, if it's not too much trouble.

The dependency on ObjectPool is ofcourse no problem :)

I'll be using this to power a Maths node in our node-based visual scripting system for https://github.com/Artemis-RGB/Artemis

KovtunV commented 3 years ago

@SpoinkyNL

I've just launched 1) v2.2.1 https://www.nuget.org/packages/NoStringEvaluating 2) Microsoft DI https://www.nuget.org/packages/NoStringEvaluating.Extensions.Microsoft.DependencyInjection

If you need anything else let me know, please 😊 Have a good day!

RobertBeekman commented 3 years ago

Hi, this turned out great, thank you!

img