private static bool TryAssignBinaryOpArgs(ListSegment <Token> leftArgTokens, ListSegment <Token> rightArgTokens, List <Expression> contexts, Type recommendedType, out Expression argLeft, out Expression argRight) { argLeft = null; argRight = null; try { argLeft = BuildExpression(leftArgTokens, contexts, recommendedType); argRight = BuildExpression(rightArgTokens, contexts, argLeft.Type); return(true); } catch (ExpressionTypeException) { return(false); } }
/// <summary> /// Simply finds index of lowest priority Operator Token among all tokens /// </summary> private static int FindLowestPriorityOperatorTokenIndex(ListSegment <Token> tokens) { OperatorToken operatorToken = null; int operatorTokenIndex = -1; for (int i = 0; i < tokens.Count; ++i) { OperatorToken opToken = tokens[i] as OperatorToken; if (opToken != null && (operatorToken == null || opToken.Priority < operatorToken.Priority)) { operatorTokenIndex = i; operatorToken = opToken; } } return(operatorTokenIndex); }
/// <summary> /// Builds expressions related to Property extraction, Function call, Type conversion and Constants /// </summary> /// <param name="tokens">Tokens list</param> /// <param name="contexts">List of ParameterExpressions - variables which will be passed as Funtor's arguments</param> /// <param name="recommendedType">If not null, tryes to convert resulted type of expression to recommendedType (no 100% guarantee)</param> /// <returns>Expression</returns> private static Expression BuildMembersExpression(ListSegment <Token> tokens, List <Expression> contexts, Type recommendedType) { int state = 0; //0-separator, 1-expression, 2-function Expression result = contexts[0]; MemberOrConstantToken methodToken = null; MemberOrConstantToken conversionToken = null; for (int i = 0; i < tokens.Count; ++i) { switch (state) { case 0: { MemberOrConstantToken mcToken = tokens[i] as MemberOrConstantToken; if (mcToken != null) { if (mcToken.IsString) { if (i == 0) { result = Expression.Constant(mcToken.StrValue); } else { throw new BuilderException(mcToken.Position, BuilderException.BuilderExceptionType.UnexpectedConstant, mcToken.StrValue); } } else if (i == tokens.Count - 1 || tokens[i + 1] is PointToken) { if (i == 0 && mcToken.StrValue.Length > 2 && mcToken.StrValue.Substring(0, 3).ToLower() == "arg") { string numstr = mcToken.StrValue.Substring(3); if (numstr != string.Empty) { int num = 0; try { num = Convert.ToInt32(numstr); } catch { throw new BuilderException(mcToken.Position, BuilderException.BuilderExceptionType.WrongArgFormat); } if (num >= contexts.Count) { throw new BuilderException(mcToken.Position, BuilderException.BuilderExceptionType.ArgNumberExceedsMax); } result = contexts[num]; } } else { PropertyInfo pi = TypeUtils.GetPropertyInfo(result.Type, mcToken.StrValue); if (pi != null) { result = Expression.Property(result, pi); } else if (i == 0) { result = CreateConstantExpression(mcToken, tokens.Count == 1 ? recommendedType : null); } else { throw new BuilderException(mcToken.Position, BuilderException.BuilderExceptionType.PropertyNotExists, pi.Name); } } } else { if (i == 0 && mcToken.IsConversion) { conversionToken = mcToken; state = 2; } else { if (TypeUtils.MethodExists(result.Type, mcToken.StrValue)) { state = 2; methodToken = mcToken; } else if (i == 0) { result = CreateConstantExpression(mcToken, tokens.Count == 1 ? recommendedType : null); } else { throw new BuilderException(mcToken.Position, BuilderException.BuilderExceptionType.FunctionNotExists, mcToken.StrValue); } } } if (state == 0) { state = 1; } break; } BracketsToken brToken = tokens[i] as BracketsToken; if (brToken != null) { result = BuildExpression(new ListSegment <Token>(brToken.Tokens), contexts, null); state = 1; break; } throw new BuilderException(tokens[i].Position, BuilderException.BuilderExceptionType.IncorrectExpression); } case 1: if (tokens[i] is PointToken) { state = 0; } else { throw new BuilderException(tokens[i].Position, BuilderException.BuilderExceptionType.UnexpectedExpression); } break; case 2: { BracketsToken brToken = tokens[i] as BracketsToken; if (brToken != null) { int argsCount = GetParamsNumber(brToken); if (methodToken != null) { MethodInfo mi = TypeUtils.GetMethodInfo(result.Type, methodToken.StrValue, 0); if (mi == null) { throw new BuilderException(methodToken.Position, BuilderException.BuilderExceptionType.NoFunctionFound); } if (argsCount == 0) { result = Expression.Call(result, mi); } else { ParameterInfo[] pinfos = mi.GetParameters(); List <Type> recommendedTypes = new List <Type>(); for (int j = 0; j < pinfos.Length; ++j) { if (!pinfos[j].IsOut && !pinfos[j].IsRetval) { recommendedTypes.Add(pinfos[j].ParameterType); } else { throw new BuilderException(tokens[i].Position, BuilderException.BuilderExceptionType.ParameterTypeNotSupported, pinfos[j].Name); } } List <Expression> args = BuildFunctionArgumentsList(brToken, contexts, recommendedTypes); result = Expression.Call(result, mi, args); } } else if (conversionToken != null) { if (argsCount != 1) { throw new BuilderException(conversionToken.Position, BuilderException.BuilderExceptionType.WrongArgumentsNumber, "1 expected"); } List <Type> recommendedTypes = new List <Type>(); recommendedTypes.Add(null); List <Expression> args = BuildFunctionArgumentsList(brToken, contexts, recommendedTypes); result = conversionToken.CreateConversion(args[0]); } else { throw new BuilderException(brToken.Position, BuilderException.BuilderExceptionType.UnexpectedError); } methodToken = null; conversionToken = null; state = 1; } else { throw new BuilderException(tokens[i].Position, BuilderException.BuilderExceptionType.FunctionArgumentsExpected); } } break; } } if (state == 2) { throw new BuilderException(tokens[tokens.Count - 1].Position, BuilderException.BuilderExceptionType.FunctionArgumentsExpected); } return(result); }
/// <summary> /// Builds expression based on provided list of Tokens. Uses contexts to access parameters that will be passed externally. /// </summary> /// <param name="tokens">Tokens list</param> /// <param name="contexts">List of ParameterExpressions - variables which will be passed as Funtor's arguments</param> /// <param name="recommendedType">If not null, tryes to convert resulted type of expression to recommendedType (no 100% guarantee)</param> /// <returns>Expression</returns> private static Expression BuildExpression(ListSegment <Token> tokens, List <Expression> contexts, Type recommendedType) { if (tokens == null || tokens.Count == 0) { return(null); } Expression result = null; int operatorTokenIndex = FindLowestPriorityOperatorTokenIndex(tokens); if (operatorTokenIndex != -1) { OperatorToken operatorToken = tokens[operatorTokenIndex] as OperatorToken; if (operatorToken.IsBinary) { if (operatorTokenIndex == 0) { throw new BuilderException(operatorToken.Position, BuilderException.BuilderExceptionType.NoLeftOperand); } if (operatorTokenIndex == tokens.Count - 1) { throw new BuilderException(operatorToken.Position, BuilderException.BuilderExceptionType.NoRightOperand); } ListSegment <Token> leftArgTokens = tokens.GetSegment(0, operatorTokenIndex); ListSegment <Token> rightArgTokens = tokens.GetSegment(operatorTokenIndex + 1, tokens.Count - operatorTokenIndex - 1); Expression argLeft = null; Expression argRight = null; switch (operatorToken.RecommendedInputTypes) { case RecommendedInputTypes.Boolean: TryAssignBinaryOpArgs(leftArgTokens, rightArgTokens, contexts, typeof(bool), out argLeft, out argRight); break; case RecommendedInputTypes.Comparable: case RecommendedInputTypes.Numeric: if (TryAssignBinaryOpArgs(leftArgTokens, rightArgTokens, contexts, null, out argLeft, out argRight)) { break; } if (TryAssignBinaryOpArgs(rightArgTokens, leftArgTokens, contexts, null, out argLeft, out argRight)) { ListSegment <Token> temp = leftArgTokens; leftArgTokens = rightArgTokens; rightArgTokens = temp; break; } if (TryAssignBinaryOpArgs(leftArgTokens, rightArgTokens, contexts, typeof(int), out argLeft, out argRight)) { break; } if (TryAssignBinaryOpArgs(leftArgTokens, rightArgTokens, contexts, typeof(float), out argLeft, out argRight)) { break; } if (TryAssignBinaryOpArgs(leftArgTokens, rightArgTokens, contexts, typeof(bool), out argLeft, out argRight)) { break; } if (TryAssignBinaryOpArgs(leftArgTokens, rightArgTokens, contexts, typeof(DateTime), out argLeft, out argRight)) { break; } if (TryAssignBinaryOpArgs(leftArgTokens, rightArgTokens, contexts, typeof(string), out argLeft, out argRight)) { break; } break; default: if (TryAssignBinaryOpArgs(leftArgTokens, rightArgTokens, contexts, null, out argLeft, out argRight)) { break; } break; } result = operatorToken.CreateBinaryExpression(argLeft, argRight); } else { if (operatorTokenIndex != 0 || tokens.Count != 2) { throw new BuilderException(operatorToken.Position, BuilderException.BuilderExceptionType.IncorrectUnaryOperatorPosition); } Expression arg = BuildExpression(tokens.GetSegment(1, 1), contexts, null); result = operatorToken.CreateUnaryExpression(arg); } } else { result = BuildMembersExpression(tokens, contexts, recommendedType); } if (recommendedType != null && result.Type != recommendedType) { throw new ExpressionTypeException(operatorTokenIndex == -1 ? tokens[0].Position : tokens[operatorTokenIndex].Position, recommendedType, result.Type); } return(result); }