mastersign / Mastersign.Expressions

A parser and compiler for a small EXCEL like expression language for .NET applications.
Other
4 stars 1 forks source link

Customer operation & function #17

Closed kaifuzi closed 3 years ago

kaifuzi commented 3 years ago

Is it possible to add customer operation & function? Becuase I have "IIF"(from DataColumn.Expression) and "Ceiling"(from NCalc), I want Mastersign.Expressions can be compatible with them, other wise I have to modify all formulas...

mastersign commented 3 years ago

Custom functions are supported: Just call the EvaluationContext.AddFunction() method. You can find one example in the Simple Usage Scenario, where the custom function neg is added. You can also look at the implementation of the Load*Package() methods to see more examples. AddFunction() takes a FunctionHandle class, which can be constructed with a delegate, or a MethodInfo. For convenience, delegates and method infos are casted implicitly into a FunctionHandle. The method info can point to a static method or an instance method. If it points to an instance method, the FunctionHandle also needs a target object, where the method should be called on.

A major limitation is however, that generic methods can not be added as custom functions. Your IIF() function might be such a case. I tried to implement the mechanics of generic functions, but the implementation grow to complex. That is why I implemented the if() function directly in the system. As a way out, I could make the name of the if() function configurable.

I am afraid, operators can not be added, because they have a more complicated syntactic context and must be handled by precedence correctly. If you have suggestions for additional operators, I am happy to consider them to be implemented. But I will not likely change the meaning of already existing operator symbols or add alias symbols. Because that would either break compatibility with existing users or deviate from my goal of keeping the language simple.

kaifuzi commented 3 years ago

Here is what I did: // Handle special function if (expression.IndexOf("IIF(", StringComparison.InvariantCultureIgnoreCase) >= 0) { expression = Regex.Replace(expression, Regex.Escape("IIF("), "IF(", RegexOptions.IgnoreCase); } evalContext.AddFunction("CEILING", (Func<Double, Double>)(v => Math.Ceiling(v))); //Can be Double or Decimal, but Double is enough in here

For IIF, I just replace it by IF, it's not very safe, but it's a simple soltion for now. After you make the if() function configurable, I will change this solution. For CEILING, it works perfectly with AddFunction!

I fully agree with you to keep Mastersign.Expressions simple and easy to use!

mastersign commented 3 years ago

In the upcoming verion 0.6 you can set the name of the conditional function as follows:

var context = new EvaluationContext
{
    Options = new LanguageOptionsBuilder()
        .WithConditionalName("iif")
        .IgnoreFunctionNameCase()
        .Build(),
};

you can check it out already by checking out the current master.

kaifuzi commented 3 years ago

If I use .WithConditionalName("iif"), does if still work?

mastersign commented 3 years ago

No, the conditional function can only have one name.

kaifuzi commented 3 years ago

I download the master branch and test, I found I only can make iif or if working. I want to use if as condition as normal, but I also want to it can support iif for legacy reason. Maybe this request is not common, if it's hard or it will break your code structure, I think can you give up it. Thanks!

mastersign commented 3 years ago

I actually do not want to support two names for the same integrated function for simplicity reasons. It would be bad hygene for the expression code, if you could use two different names for something essential as the integrated conditional function.

kaifuzi commented 3 years ago

Yes, you are right! Beucase I changed from DataColumn.Expression to more powerful Mastersign.Expressions, iif is a legacy condition name. Then as you said, only keep one name for same integrated function, it's more clear and simple.

mastersign commented 3 years ago

Can we close this issue after the release of 0.6.0?

kaifuzi commented 3 years ago

Yes, we can close this issue for 0.6.0.