Example #1
0
        internal static void LastIndexOfOpArray(List <Token> tokens, string[] ops, EvalConfiguration config, out int matchIndex, out string match)
        {
            var    pos       = -1;
            string bestMatch = null;

            for (var i = 0; i < ops.Length; i++)
            {
                var item = ops[i];
                int opIndex;

                if (config.RightAssociativeOps.Contains(item))
                {
                    opIndex = IndexOfOpInTokens(tokens, item);
                }
                else
                {
                    opIndex = LastIndexOfOpInTokens(tokens, item);
                }

                if (opIndex == -1)
                {
                    continue;
                }

                if (pos == -1 || opIndex > pos)
                {
                    pos       = opIndex;
                    bestMatch = item;
                }
            }

            matchIndex = pos;
            match      = bestMatch;
        }
Example #2
0
        public EvalConfiguration Clone(bool deep = false)
        {
            var config = new EvalConfiguration(NumericType);

            config.OperatorOrder       = OperatorOrder;
            config.PrefixOperators     = deep ? new HashSet <string>(PrefixOperators) : PrefixOperators;
            config.SuffixOperators     = deep ? new HashSet <string>(SuffixOperators) : SuffixOperators;
            config.RightAssociativeOps = deep ? new HashSet <string>(RightAssociativeOps) : RightAssociativeOps;
            config.VarNameChars        = deep ? new HashSet <char>(VarNameChars) : VarNameChars;
            config.GenericConstants    = deep ? new Dictionary <string, object>(GenericConstants) : GenericConstants;
            config.GenericFunctions    = deep ? new Dictionary <string, EvalFunctionDelegate>(GenericFunctions) : GenericFunctions;
            config.Constants           = deep ? new Dictionary <string, object>(Constants) : Constants;
            config.Functions           = deep ? new Dictionary <string, EvalFunctionDelegate>(Functions) : Functions;
            config.ConstProvider       = ConstProvider;
            return(config);
        }
Example #3
0
        internal static object EvaluateFunction(Token token, EvalConfiguration configuration)
        {
            var fname = token.Value;

            var args = new List <object>();

            for (var i = 0; i < token.Arguments.Count; i++)
            {
                if (token.Arguments[i] == null)
                {
                    args.Add(null);
                }
                else
                {
                    args.Add(EvaluateToken(token.Arguments[i], configuration));
                }
            }

            if (configuration.Functions.ContainsKey(fname))
            {
                return(configuration.Functions[fname](args.ToArray()));
            }

            if (configuration.Functions.ContainsKey(fname.ToUpperInvariant()))
            {
                return(configuration.Functions[fname.ToUpperInvariant()](args.ToArray()));
            }

            if (configuration.GenericFunctions.ContainsKey(fname))
            {
                return(configuration.GenericFunctions[fname](args.ToArray()));
            }

            if (configuration.GenericFunctions.ContainsKey(fname.ToUpperInvariant()))
            {
                return(configuration.GenericFunctions[fname.ToUpperInvariant()](args.ToArray()));
            }

            throw new Exception("Function named \"" + fname + "\" was not found");
        }
Example #4
0
        internal static string OpAtPosition(string expression, int start, EvalConfiguration configuration)
        {
            string op = null;

            var allOperators = configuration._AllOperators;

            for (int j = 0, jlen = allOperators.Length; j < jlen; j++)
            {
                var item = allOperators[j];

                if (op != null && (op == item || item.Length <= op.Length))
                {
                    continue;
                }

                if (expression.Substring(start, item.Length) == item)
                {
                    op = item;
                }
            }

            return(op);
        }
Example #5
0
 public static object Execute(string expression, EvalConfiguration configuration)
 {
     return(Execute(Compile(expression, configuration)));
 }
Example #6
0
        internal static object EvaluateToken(Token token, EvalConfiguration configuration)
        {
            var value = token.Value;

            switch (token.Type)
            {
            case TokenType.String:
                return(value);

            case TokenType.Number:
                return(configuration.ConvertToNumber(value));

            case TokenType.Var:

                if (configuration.ConstProvider != null)
                {
                    var val = configuration.ConstProvider(value);
                    if (val != null)
                    {
                        return(val);
                    }
                }

                if (configuration.Constants.ContainsKey(value))
                {
                    return(configuration.Constants[value]);
                }

                if (configuration.Constants.ContainsKey(value.ToUpperInvariant()))
                {
                    return(configuration.Constants[value.ToUpperInvariant()]);
                }

                if (configuration.GenericConstants.ContainsKey(value))
                {
                    return(configuration.GenericConstants[value]);
                }

                if (configuration.GenericConstants.ContainsKey(value.ToUpperInvariant()))
                {
                    return(configuration.GenericConstants[value.ToUpperInvariant()]);
                }

                return(null);

            case TokenType.Call:
                return(EvaluateFunction(token, configuration));

            case TokenType.Op:
                switch (token.Value)
                {
                case "!":                   // Factorial or Not
                    if (token.Left != null) // Factorial (i.e. 5!)
                    {
                        return(configuration.Factorial(EvaluateToken(token.Right, configuration)));
                    }
                    else         // Not (i.e. !5)
                    {
                        return(configuration.LogicalNot(EvaluateToken(token.Right, configuration)));
                    }

                case "/":         // Divide
                case "\\":
                    return(configuration.Divide(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "*":         // Multiply
                    return(configuration.Multiply(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "+":         // Add
                    return(configuration.Add(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "-":         // Subtract
                    return(configuration.Subtract(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "<<":         // Shift left
                    return(configuration.BitShiftLeft(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case ">>":         // Shift right
                    return(configuration.BitShiftRight(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "<":         // Less than
                    return(configuration.LessThan(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "<=":         // Less than or equals to
                    return(configuration.LessThanOrEqualsTo(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case ">":         // Greater than
                    return(configuration.GreaterThan(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case ">=":         // Greater than or equals to
                    return(configuration.GreaterThanOrEqualsTo(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "==":         // Equals to
                case "=":
                    return(configuration.EqualsTo(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "!=":         // Not equals to
                case "<>":
                    return(configuration.NotEqualsTo(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "**":         // Power
                    return(configuration.Pow(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "%":         // Mod
                    return(configuration.Mod(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "&":         // Bitwise AND
                    return(configuration.BitAnd(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "^":         // Bitwise XOR
                    return(configuration.BitXor(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "|":         // Bitwise OR
                    return(configuration.BitOr(EvaluateToken(token.Left, configuration), EvaluateToken(token.Right, configuration)));

                case "&&":         // Logical AND
                {
                    var res = EvaluateToken(token.Left, configuration);
                    if (configuration.IsTruthy(res))
                    {
                        return(EvaluateToken(token.Right, configuration));
                    }
                    return(res);
                }

                case "||":         // Logical OR
                {
                    var res = EvaluateToken(token.Left, configuration);
                    if (!configuration.IsTruthy(res))
                    {
                        return(EvaluateToken(token.Right, configuration));
                    }
                    return(res);
                }
                }
                break;
            }

            throw new Exception("An unexpected error occurred while evaluating expression");
        }
Example #7
0
        internal static Token BuildTree(List <Token> tokens, EvalConfiguration configuration)
        {
            var order      = configuration.OperatorOrder;
            int orderCount = order.Length;
            var prefixOps  = configuration.PrefixOperators;
            var suffixOps  = configuration.SuffixOperators;

            for (int i = orderCount - 1; i >= 0; i--)
            {
                var cs = order[i];

                int    pos;
                string op;
                LastIndexOfOpArray(tokens, cs, configuration, out pos, out op);

                if (pos != -1)
                {
                    var token = tokens[pos];

                    List <Token> left;
                    List <Token> right;

                    if (prefixOps.Contains(op) || suffixOps.Contains(op))
                    {
                        left  = null;
                        right = null;

                        if (prefixOps.Contains(op) && pos == 0)
                        {
                            right = tokens.GetRange(pos + 1, tokens.Count - (pos + 1));
                        }
                        else if (suffixOps.Contains(op) && pos > 0)
                        {
                            left = tokens.GetRange(0, pos);
                        }

                        if (left == null && right == null)
                        {
                            throw new Exception("Operator " + token.Value.ToString() + " is unexpected at index " + token.Position);
                        }
                    }
                    else
                    {
                        left  = tokens.GetRange(0, pos);
                        right = tokens.GetRange(pos + 1, tokens.Count - (pos + 1));

                        if (left.Count == 0 && (op == "-" || op == "+"))
                        {
                            left = null;
                        }
                    }

                    if ((left != null && left.Count == 0) ||
                        (right != null && right.Count == 0))
                    {
                        throw new Exception("Invalid expression, missing operand");
                    }

                    if (left == null && op == "-")
                    {
                        left = new List <Token> {
                            new Token {
                                Type = TokenType.Number, Value = "0"
                            }
                        };
                    }
                    else if (left == null && op == "+")
                    {
                        return(BuildTree(right, configuration));
                    }

                    if (left != null)
                    {
                        token.Left = BuildTree(left, configuration);
                    }

                    if (right != null)
                    {
                        token.Right = BuildTree(right, configuration);
                    }

                    return(token);
                }
            }

            if (tokens.Count > 1)
            {
                throw new Exception("Invalid expression, missing operand or operator at " + tokens[1].Position);
            }

            if (tokens.Count == 0)
            {
                throw new Exception("Invalid expression, missing operand or operator.");
            }

            var singleToken = tokens[0];

            if (singleToken.Type == TokenType.Group)
            {
                singleToken = BuildTree(singleToken.Tokens, configuration);
            }
            else if (singleToken.Type == TokenType.Call)
            {
                singleToken.Arguments = new List <Token>();
                for (int a = 0, arglen = singleToken.ArgumentsGroups.Count; a < arglen; a++)
                {
                    if (singleToken.ArgumentsGroups[a].Count == 0)
                    {
                        singleToken.Arguments.Add(null);
                    }
                    else
                    {
                        singleToken.Arguments.Add(BuildTree(singleToken.ArgumentsGroups[a], configuration));
                    }
                }
            }
            else if (singleToken.Type == TokenType.Comma)
            {
                throw new Exception("Unexpected character at index " + singleToken.Position);
            }

            return(singleToken);
        }
Example #8
0
        internal static List <Token> TokenizeExpression(string expression, EvalConfiguration configuration)
        {
            var varNameChars = configuration.VarNameChars;

            var tokens = new List <Token>();

            int i = 0;

            for (int len = expression.Length; i < len; i++)
            {
                var c = expression[i];

                var isDigit = c >= '0' && c <= '9';

                if (isDigit || c == '.')
                {
                    // Starting a number
                    int nextIndex;
                    var parsedNumber = ParseNumber(expression, i, out nextIndex);
                    tokens.Add(new Token
                    {
                        Type     = TokenType.Number,
                        Position = i,
                        Value    = parsedNumber
                    });
                    i = nextIndex - 1;
                    continue;
                }

                var isVarChars = varNameChars.Contains(c);

                if (isVarChars)
                {
                    // Starting a variable name - can start only with A-Z_

                    var token = "";

                    while (i < len)
                    {
                        c          = expression[i];
                        isVarChars = varNameChars.Contains(c);
                        if (!isVarChars)
                        {
                            break;
                        }

                        token += c;
                        i++;
                    }

                    tokens.Add(new Token
                    {
                        Type     = TokenType.Var,
                        Position = i - token.Length,
                        Value    = token
                    });

                    i--; // Step back to continue loop from correct place

                    continue;
                }

                if (c == '\'' || c == '\"')
                {
                    int nextIndex;
                    var parsedString = ParseString(expression, i, false, true, out nextIndex);
                    tokens.Add(new Token
                    {
                        Type     = TokenType.String,
                        Position = i,
                        Value    = parsedString
                    });
                    i = nextIndex - 1;
                    continue;
                }

                if (c == '(')
                {
                    tokens.Add(new Token
                    {
                        Type     = TokenType.LeftParen,
                        Position = i
                    });
                    continue;
                }

                if (c == ')')
                {
                    tokens.Add(new Token
                    {
                        Type     = TokenType.RightParen,
                        Position = i
                    });
                    continue;
                }

                if (c == ',')
                {
                    tokens.Add(new Token
                    {
                        Type     = TokenType.Comma,
                        Position = i
                    });
                    continue;
                }

                if (c == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\n')
                {
                    // Whitespace, skip
                    continue;
                }

                var op = OpAtPosition(expression, i, configuration);
                if (op != null)
                {
                    tokens.Add(new Token
                    {
                        Type     = TokenType.Op,
                        Position = i,
                        Value    = op
                    });
                    i += op.Length - 1;
                    continue;
                }

                throw new FormatException("Unexpected token at index " + i);
            }

            return(tokens);
        }
Example #9
0
        public static CompiledExpression Compile(string expression, EvalConfiguration configuration)
        {
            var tokens = TokenizeExpression(expression, configuration);

            // Compact +-
            for (int i = 1, len = tokens.Count; i < len; i++)
            {
                var token     = tokens[i];
                var prevToken = tokens[i - 1];

                if (token.Type == TokenType.Op &&
                    (token.Value == "-" || token.Value == "+") &&
                    prevToken.Type == TokenType.Op &&
                    (prevToken.Value == "-" || prevToken.Value == "+"))
                {
                    if (prevToken.Value != "+")
                    {
                        if (token.Value == "-")
                        {
                            token.Value = "+";
                        }
                        else
                        {
                            token.Value = "-";
                        }
                    }

                    tokens.RemoveAt(i - 1);
                    i--;
                    len = tokens.Count;

                    continue;
                }

                if (token.Type == TokenType.Number &&
                    prevToken.Type == TokenType.Op &&
                    (prevToken.Value == "-" || prevToken.Value == "+") &&
                    ((i > 1 && tokens[i - 2].Type == TokenType.Op) || i == 1)
                    )
                {
                    if (prevToken.Value == "-")
                    {
                        token.Value = prevToken.Value + token.Value;
                    }
                    tokens.RemoveAt(i - 1);
                    i--;
                    len = tokens.Count;

                    continue;
                }
            }

            // Take care of groups (including function calls)
            for (int i = 0, len = tokens.Count; i < len; i++)
            {
                var token = tokens[i];

                if (token.Type == TokenType.LeftParen)
                {
                    GroupTokens(tokens, i);
                    len = tokens.Count;
                    i--;
                }
            }

            // Build the tree
            var tree = BuildTree(tokens, configuration);

            return(new CompiledExpression {
                Root = tree, Configuration = configuration
            });
        }