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; }
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); }
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"); }
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); }
public static object Execute(string expression, EvalConfiguration configuration) { return(Execute(Compile(expression, configuration))); }
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"); }
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); }
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); }
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 }); }