Skip to content

Advanced Customization and Hacking

Coding Seb edited this page Mar 12, 2020 · 10 revisions

From version 1.4.0.0 ExpressionEvaluator allow some deeper customizations of the parsing process. It is mostly available by inheritance. Most of the ExpressionEvaluator class is now in protected visibility in place of private and the majority of init and parsing methods are now virtual.

This section show some of these hacks.

Warning : all uses of these inheritance hacks are at your own risk.
It's your responsability to avoid unwanted conflicts between the different parts of the parsing and evaluating process.
Also be aware that these stuff will be more sensitive to future versions changes of ExpressionEvaluator than the other parts of this wiki

Redefine existing operators

For left only, right only and 2 operands operators. (For more advanced stuff look how to redefine parsing process)

The following example show how to replace the C# logical operators && and || by the corresponding literals operators and and or:

Live example here

Inherits ExpressionEvaluator

public class XExpressionEvaluator : ExpressionEvaluator
{
    protected override void Init()
    {
        operatorsDictionary.Add("and", ExpressionOperator.ConditionalAnd);
        operatorsDictionary.Add("or", ExpressionOperator.ConditionalOr);
        operatorsDictionary.Remove("&&");
        operatorsDictionary.Remove("||");
    }
}

And use it like this

ExpressionEvaluator evaluator = new XExpressionEvaluator();

evaluator.Evaluate(expression);

Results :

true and true
true

true and false
false

false and true
true

false and false
false

true && true
throw an exception

true or true
true

true or false
true

false or true
true

false or false
false

true || true
throw an exception

Remark : Some operators need to be parsed an other way and are not in operatorsDictionary so they can't be redefined this way.
No exaustive examples condition ? expIfTrue : expIfFalse, (Type)variableToCast or nullVariable ?? other variable

Add your own simple operators

For left only, right only and 2 operands operators. (For more advanced stuff look how to redefine parsing process)

The following example show how to add 2 new operators :

  • A # operator to apply after a numerical value that elevate to the power of minus itself.
  • A love operator that do a binary or (|) of 2 values and and a 1 binary left shift <<

Live example here

Inherits ExpressionOperator to define new operators

public class XExpressionOperator : ExpressionOperator
{
    public static readonly ExpressionOperator Sharp = new XExpressionOperator();
    public static readonly ExpressionOperator Love = new XExpressionOperator();
}

Inherits ExpressionEvaluator to define operators priorities and implementation

public class XExpressionEvaluator : ExpressionEvaluator
{
    protected new static readonly IList<ExpressionOperator> leftOperandOnlyOperatorsEvaluationDictionary =
        ExpressionEvaluator.leftOperandOnlyOperatorsEvaluationDictionary
            .ToList()
            .FluidAdd(XExpressionOperator.Sharp);

    //protected new static readonly IList<ExpressionOperator> rightOperandOnlyOperatorsEvaluationDictionary = 
    //    ExpressionEvaluator.rightOperandOnlyOperatorsEvaluationDictionary
    //        .ToList();

    protected new static readonly IList<IDictionary<ExpressionOperator, Func<dynamic, dynamic, object>>> operatorsEvaluations =
        ExpressionEvaluator.operatorsEvaluations
            .Copy()
            .AddOperatorEvaluationAtNewLevelAfter(XExpressionOperator.Sharp, (left, _) => Math.Pow(left, -left), ExpressionOperator.UnaryPlus)
            .AddOperatorEvaluationAtLevelOf(XExpressionOperator.Love, (left, right) => (left | right) << 1, ExpressionOperator.ShiftBitsLeft);

    protected override IList<ExpressionOperator> LeftOperandOnlyOperatorsEvaluationDictionary => leftOperandOnlyOperatorsEvaluationDictionary;

    // protected override IList<ExpressionOperator> RightOperandOnlyOperatorsEvaluationDictionary => rightOperandOnlyOperatorsEvaluationDictionary;

    protected override IList<IDictionary<ExpressionOperator, Func<dynamic, dynamic, object>>> OperatorsEvaluations => operatorsEvaluations;

    protected override void Init()
    {
        operatorsDictionary.Add("#", XExpressionOperator.Sharp);
        operatorsDictionary.Add("love", XExpressionOperator.Love);
    }
}

And use it like this

ExpressionEvaluator evaluator = new XExpressionEvaluator();

evaluator.Evaluate(expression);

Results :

1#
1

2#
0.25

-4# - 6
250

1 love 2
6

1 love 2 >> 1
3

Add a complex operator or change the parsing process

The following example show how to add 2 new complex operators (or syntax) :

  • A ternary operator that do a string.Replace like input ° replace @ replacement
  • A syntax to declare inline DateTime objects like #year-month-day -> 2019-08-15

Live example here

Inherits ExpressionEvaluator to implement some parsing methods and add them to the ParsingMethods List

public class XExpressionEvaluator : ExpressionEvaluator
{
    protected override void Init()
    {
        ParsingMethods.Insert(0, EvaluateDateTimeSyntax);
        ParsingMethods.Add(EvaluateSpecialTernaryOperator);
    }

    /// <summary>
    /// To evaluate DateTimes objects with #year-month-day syntax (#2019-05-28)
    /// </summary>
    protected virtual bool EvaluateDateTimeSyntax(string expression, Stack<object> stack, ref int i)
    {
        Match match = Regex.Match(expression.Substring(i), @"^\s*#(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})");

        if(match.Success)
        {
            int year = int.Parse(match.Groups["year"].Value);
            int month = int.Parse(match.Groups["month"].Value);
            int day = int.Parse(match.Groups["day"].Value);

            DateTime dateTime = new DateTime(year,month, day);

            stack.Push(dateTime);

            i += match.Length - 1;

            return true;
        }

        return false;
    }

    /// <summary>
    /// To evaluate a string replace with custom ternary indicator
    /// </summary>
    protected virtual bool EvaluateSpecialTernaryOperator(string expression, Stack<object> stack, ref int i)
    {
        if (expression.Substring(i, 1).Equals("°"))
        {
            string input = (string)ProcessStack(stack);

            string restOfExpression = expression.Substring(i + 1);

            for (int j = 0; j < restOfExpression.Length; j++)
            {
                string s2 = restOfExpression.Substring(j, 1);

                Match internalStringMatch = stringBeginningRegex.Match(restOfExpression.Substring(j));

                if (internalStringMatch.Success)
                {
                    string innerString = internalStringMatch.Value + GetCodeUntilEndOfString(restOfExpression.Substring(j + internalStringMatch.Length), internalStringMatch);
                    j += innerString.Length - 1;
                }
                else if (s2.Equals("("))
                {
                    j++;
                    GetExpressionsBetweenParenthesesOrOtherImbricableBrackets(restOfExpression, ref j, false);
                }
                else if (s2.Equals("@"))
                {
                    stack.Clear();

                    stack.Push(input.Replace(Evaluate<string>(restOfExpression.Substring(1, j - 1)), Evaluate<string>(restOfExpression.Substring(j + 1))));

                    i = expression.Length;

                    return true;
                }
            }
        }

        return false;
    }
}

And use it like this

ExpressionEvaluator evaluator = new XExpressionEvaluator();

evaluator.Evaluate(expression);

Results :

"A sentence where a word must be replaced where it is" ° "replaced" @ "kept"
A sentence where a word must be kept where it is

#1985-09-11.Year
1985

#1985-09-11.Month
9

#1985-09-11.Day
11

#1985-09-11.Equals(new DateTime(1985,9,11))
true

Table Of Content

Clone this wiki locally