Skip to content

Advanced Customization and Hacking

Sébastien Geiser edited this page May 28, 2019 · 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)

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 redifined 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)

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

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((string)Evaluate(restOfExpression.Substring(1, j - 1)), (string)Evaluate(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