PageRenderTime 104ms CodeModel.GetById 16ms app.highlight 76ms RepoModel.GetById 2ms app.codeStats 0ms

/WCFWebApi/src/Microsoft.ApplicationServer.Http/Microsoft/ApplicationServer/Query/DynamicQueryable.cs

#
C# | 2292 lines | 2129 code | 130 blank | 33 comment | 470 complexity | f152c28c7f61f7927d0c126ac0352ad7 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1// <copyright>
   2//   Copyright (c) Microsoft Corporation.  All rights reserved.
   3// </copyright>
   4
   5namespace Microsoft.ApplicationServer.Query
   6{
   7    using System;
   8    using System.Collections.Generic;
   9    using System.Globalization;
  10    using System.Linq;
  11    using System.Linq.Expressions;
  12    using System.Reflection;
  13    using Microsoft.ApplicationServer.Http;
  14
  15    internal static class DynamicQueryable
  16    {
  17        public static IQueryable Where(this IQueryable source, string predicate, QueryResolver queryResolver)
  18        {
  19            if (source == null)
  20                throw new ArgumentNullException("source");
  21            if (predicate == null)
  22                throw new ArgumentNullException("predicate");
  23            LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, queryResolver);
  24            return source.Provider.CreateQuery(
  25                Expression.Call(
  26                    typeof(Queryable), "Where",
  27                    new Type[] { source.ElementType },
  28                    source.Expression, Expression.Quote(lambda)));
  29        }
  30
  31        public static IQueryable OrderBy(this IQueryable source, string ordering, QueryResolver queryResolver)
  32        {
  33            if (source == null)
  34                throw new ArgumentNullException("source");
  35            if (ordering == null)
  36                throw new ArgumentNullException("ordering");
  37            ParameterExpression[] parameters = new ParameterExpression[] {
  38                Expression.Parameter(source.ElementType, "") };
  39            ExpressionParser parser = new ExpressionParser(parameters, ordering, queryResolver);
  40            IEnumerable<DynamicOrdering> orderings = parser.ParseOrdering();
  41            Expression queryExpr = source.Expression;
  42            string methodAsc = "OrderBy";
  43            string methodDesc = "OrderByDescending";
  44            foreach (DynamicOrdering o in orderings)
  45            {
  46                queryExpr = Expression.Call(
  47                    typeof(Queryable), o.Ascending ? methodAsc : methodDesc,
  48                    new Type[] { source.ElementType, o.Selector.Type },
  49                    queryExpr, Expression.Quote(DynamicExpression.Lambda(o.Selector, parameters)));
  50                methodAsc = "ThenBy";
  51                methodDesc = "ThenByDescending";
  52            }
  53            return source.Provider.CreateQuery(queryExpr);
  54        }
  55
  56        public static IQueryable Take(this IQueryable source, int count)
  57        {
  58            if (source == null)
  59                throw new ArgumentNullException("source");
  60            return source.Provider.CreateQuery(
  61                Expression.Call(
  62                    typeof(Queryable), "Take",
  63                    new Type[] { source.ElementType },
  64                    source.Expression, Expression.Constant(count)));
  65        }
  66
  67        public static IQueryable Skip(this IQueryable source, int count)
  68        {
  69            if (source == null)
  70                throw new ArgumentNullException("source");
  71            return source.Provider.CreateQuery(
  72                Expression.Call(
  73                    typeof(Queryable), "Skip",
  74                    new Type[] { source.ElementType },
  75                    source.Expression, Expression.Constant(count)));
  76        }
  77    }
  78
  79    internal static class DynamicExpression
  80    {
  81        static readonly Type[] funcTypes = new Type[] {
  82            typeof(Func<>),
  83            typeof(Func<,>),
  84            typeof(Func<,,>),
  85            typeof(Func<,,,>),
  86            typeof(Func<,,,,>)
  87        };
  88
  89        public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, QueryResolver queryResolver)
  90        {
  91            return ParseLambda(new ParameterExpression[] { Expression.Parameter(itType, "") }, resultType, expression, queryResolver);
  92        }
  93
  94        public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, QueryResolver queryResolver)
  95        {
  96            ExpressionParser parser = new ExpressionParser(parameters, expression, queryResolver);
  97            return Lambda(parser.Parse(resultType), parameters);
  98        }
  99
 100        public static LambdaExpression Lambda(Expression body, params ParameterExpression[] parameters)
 101        {
 102            int paramCount = parameters == null ? 0 : parameters.Length;
 103            Type[] typeArgs = new Type[paramCount + 1];
 104            for (int i = 0; i < paramCount; i++)
 105                typeArgs[i] = parameters[i].Type;
 106            typeArgs[paramCount] = body.Type;
 107            return Expression.Lambda(GetFuncType(typeArgs), body, parameters);
 108        }
 109
 110        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "Arguments are provided internally by the parser's ParserLambda methods.")]
 111        public static Type GetFuncType(params Type[] typeArgs)
 112        {
 113            if (typeArgs == null || typeArgs.Length < 1 || typeArgs.Length > 5)
 114                throw new ArgumentException();
 115            return funcTypes[typeArgs.Length - 1].MakeGenericType(typeArgs);
 116        }
 117    }
 118
 119    internal class DynamicOrdering
 120    {
 121        public Expression Selector;
 122        public bool Ascending;
 123    }
 124
 125    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", Justification = "Exception is intended to only be used by the dynamic parser.")]
 126    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic", Justification = "Exception is intended to only be used by the dynamic parser.")]
 127    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable", Justification = "Exception is intended to only be used by the dynamic parser.")]
 128    internal class ParseException : Exception
 129    {
 130        public ParseException(string message, int position)
 131            : base(string.Format(CultureInfo.InvariantCulture, SR.ParseExceptionFormat(message, position)))
 132        {
 133        }
 134    }
 135
 136    internal class ExpressionParser
 137    {
 138        struct Token
 139        {
 140            public TokenId id;
 141            public string text;
 142            public int pos;
 143        }
 144
 145        enum TokenId
 146        {
 147            Unknown,
 148            End,
 149            Identifier,
 150            StringLiteral,
 151            IntegerLiteral,
 152            RealLiteral,
 153            Exclamation,
 154            Percent,
 155            Amphersand,
 156            OpenParen,
 157            CloseParen,
 158            Asterisk,
 159            Plus,
 160            Comma,
 161            Minus,
 162            Dot,
 163            Slash,
 164            Colon,
 165            LessThan,
 166            Equal,
 167            GreaterThan,
 168            Question,
 169            OpenBracket,
 170            CloseBracket,
 171            Bar,
 172            ExclamationEqual,
 173            DoubleAmphersand,
 174            LessThanEqual,
 175            LessGreater,
 176            DoubleEqual,
 177            GreaterThanEqual,
 178            DoubleBar
 179        }
 180
 181        internal class MappedMemberInfo
 182        {
 183            public MappedMemberInfo(Type mappedType, string memberName, bool isStatic, bool isMethod)
 184            {
 185                MappedType = mappedType;
 186                MemberName = memberName;
 187                IsStatic = isStatic;
 188                IsMethod = isMethod;
 189            }
 190
 191            public Type MappedType { get; private set; }
 192            public string MemberName { get; private set; }
 193            public bool IsStatic { get; private set; }
 194            public bool IsMethod { get; private set; }
 195        }
 196
 197        interface ILogicalSignatures
 198        {
 199            void F(bool x, bool y);
 200            void F(bool? x, bool? y);
 201        }
 202
 203        interface IArithmeticSignatures
 204        {
 205            void F(int x, int y);
 206            void F(uint x, uint y);
 207            void F(long x, long y);
 208            void F(ulong x, ulong y);
 209            void F(float x, float y);
 210            void F(double x, double y);
 211            void F(decimal x, decimal y);
 212            void F(int? x, int? y);
 213            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA908:AvoidTypesThatRequireJitCompilationInPrecompiledAssemblies", Justification = "Legacy code.")]
 214            void F(uint? x, uint? y);
 215            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA908:AvoidTypesThatRequireJitCompilationInPrecompiledAssemblies", Justification = "Legacy code.")]
 216            void F(long? x, long? y);
 217            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA908:AvoidTypesThatRequireJitCompilationInPrecompiledAssemblies", Justification = "Legacy code.")]
 218            void F(ulong? x, ulong? y);
 219            void F(float? x, float? y);
 220            void F(double? x, double? y);
 221            void F(decimal? x, decimal? y);
 222        }
 223
 224        interface IRelationalSignatures : IArithmeticSignatures
 225        {
 226            void F(string x, string y);
 227            void F(char x, char y);
 228            void F(DateTime x, DateTime y);
 229            void F(TimeSpan x, TimeSpan y);
 230            void F(char? x, char? y);
 231            void F(DateTime? x, DateTime? y);
 232            void F(TimeSpan? x, TimeSpan? y);
 233            void F(DateTimeOffset x, DateTimeOffset y);
 234            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA908:AvoidTypesThatRequireJitCompilationInPrecompiledAssemblies", Justification = "Legacy code.")]
 235            void F(DateTimeOffset? x, DateTimeOffset? y);
 236        }
 237
 238        interface IEqualitySignatures : IRelationalSignatures
 239        {
 240            void F(bool x, bool y);
 241            void F(bool? x, bool? y);
 242            void F(Guid x, Guid y);
 243            void F(Guid? x, Guid? y);
 244        }
 245
 246        interface IAddSignatures : IArithmeticSignatures
 247        {
 248            void F(DateTime x, TimeSpan y);
 249            void F(TimeSpan x, TimeSpan y);
 250            void F(DateTime? x, TimeSpan? y);
 251            void F(TimeSpan? x, TimeSpan? y);
 252            void F(DateTimeOffset x, TimeSpan y);
 253            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA908:AvoidTypesThatRequireJitCompilationInPrecompiledAssemblies", Justification = "Legacy code.")]
 254            void F(DateTimeOffset? x, TimeSpan? y);
 255        }
 256
 257        interface ISubtractSignatures : IAddSignatures
 258        {
 259            void F(DateTime x, DateTime y);
 260            void F(DateTime? x, DateTime? y);
 261            void F(DateTimeOffset x, DateTimeOffset y);
 262            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA908:AvoidTypesThatRequireJitCompilationInPrecompiledAssemblies", Justification = "Legacy code.")]
 263            void F(DateTimeOffset? x, DateTimeOffset? y);
 264        }
 265
 266        interface INegationSignatures
 267        {
 268            void F(int x);
 269            void F(long x);
 270            void F(float x);
 271            void F(double x);
 272            void F(decimal x);
 273            void F(int? x);
 274            void F(long? x);
 275            void F(float? x);
 276            void F(double? x);
 277            void F(decimal? x);
 278        }
 279
 280        interface INotSignatures
 281        {
 282            void F(bool x);
 283            void F(bool? x);
 284        }
 285
 286        interface IEnumerableSignatures
 287        {
 288            void Where(bool predicate);
 289            void Any();
 290            void Any(bool predicate);
 291            void All(bool predicate);
 292            void Count();
 293            void Count(bool predicate);
 294            void Min(object selector);
 295            void Max(object selector);
 296            void Sum(int selector);
 297            void Sum(int? selector);
 298            void Sum(long selector);
 299            void Sum(long? selector);
 300            void Sum(float selector);
 301            void Sum(float? selector);
 302            void Sum(double selector);
 303            void Sum(double? selector);
 304            void Sum(decimal selector);
 305            void Sum(decimal? selector);
 306            void Average(int selector);
 307            void Average(int? selector);
 308            void Average(long selector);
 309            void Average(long? selector);
 310            void Average(float selector);
 311            void Average(float? selector);
 312            void Average(double selector);
 313            void Average(double? selector);
 314            void Average(decimal selector);
 315            void Average(decimal? selector);
 316        }
 317
 318        static readonly Type[] predefinedTypes = {
 319            typeof(Object),
 320            typeof(Boolean),
 321            typeof(Char),
 322            typeof(String),
 323            typeof(SByte),
 324            typeof(Byte),
 325            typeof(Int16),
 326            typeof(UInt16),
 327            typeof(Int32),
 328            typeof(UInt32),
 329            typeof(Int64),
 330            typeof(UInt64),
 331            typeof(Single),
 332            typeof(Double),
 333            typeof(Decimal),
 334            typeof(DateTime),
 335            typeof(DateTimeOffset),
 336            typeof(TimeSpan),
 337            typeof(Guid),
 338            typeof(Math),
 339            typeof(Convert),
 340            typeof(StringComparison),
 341            typeof(Uri)
 342        };
 343
 344        static readonly Expression trueLiteral = Expression.Constant(true);
 345        static readonly Expression falseLiteral = Expression.Constant(false);
 346        static readonly Expression nullLiteral = Expression.Constant(null);
 347
 348        const string keywordIt = "it";
 349        const string keywordIif = "iif";
 350
 351        static Dictionary<string, object> keywords;
 352
 353        Dictionary<string, object> symbols;
 354        Dictionary<Expression, string> literals;
 355        ParameterExpression it;
 356        string text;
 357        int textPos;
 358        int textLen;
 359        char ch;
 360        Token token;
 361        QueryResolver queryResolver;
 362
 363        public ExpressionParser(ParameterExpression[] parameters, string expression, QueryResolver queryResolver)
 364        {
 365            if (expression == null)
 366                throw new ArgumentNullException("expression");
 367            if (keywords == null)
 368                keywords = CreateKeywords();
 369            this.queryResolver = queryResolver;
 370            symbols = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
 371            literals = new Dictionary<Expression, string>();
 372            if (parameters != null)
 373                ProcessParameters(parameters);
 374            text = expression;
 375            textLen = text.Length;
 376            SetTextPos(0);
 377            NextToken();
 378        }
 379
 380        void ProcessParameters(ParameterExpression[] parameters)
 381        {
 382            foreach (ParameterExpression pe in parameters)
 383                if (!String.IsNullOrEmpty(pe.Name))
 384                    AddSymbol(pe.Name, pe);
 385            if (parameters.Length == 1 && String.IsNullOrEmpty(parameters[0].Name))
 386                it = parameters[0];
 387        }
 388
 389        void AddSymbol(string name, object value)
 390        {
 391            if (symbols.ContainsKey(name))
 392                throw ParseError(SR.DuplicateIdentifier(name));
 393            symbols.Add(name, value);
 394        }
 395
 396        public Expression Parse(Type resultType)
 397        {
 398            int exprPos = token.pos;
 399            Expression expr = ParseExpression();
 400            if (resultType != null)
 401                if ((expr = PromoteExpression(expr, resultType, true)) == null)
 402                    throw ParseError(exprPos, SR.ExpressionTypeMismatch(GetTypeName(resultType)));
 403            ValidateToken(TokenId.End, SR.SyntaxError);
 404            return expr;
 405        }
 406
 407#pragma warning disable 0219
 408        public IEnumerable<DynamicOrdering> ParseOrdering()
 409        {
 410            List<DynamicOrdering> orderings = new List<DynamicOrdering>();
 411            while (true)
 412            {
 413                Expression expr = ParseExpression();
 414                bool ascending = true;
 415                if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending"))
 416                {
 417                    NextToken();
 418                }
 419                else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending"))
 420                {
 421                    NextToken();
 422                    ascending = false;
 423                }
 424                orderings.Add(new DynamicOrdering
 425                {
 426                    Selector = expr,
 427                    Ascending = ascending
 428                });
 429                if (token.id != TokenId.Comma)
 430                    break;
 431                NextToken();
 432            }
 433            ValidateToken(TokenId.End, SR.SyntaxError);
 434            return orderings;
 435        }
 436#pragma warning restore 0219
 437
 438        // ?: operator
 439        Expression ParseExpression()
 440        {
 441            int errorPos = token.pos;
 442            Expression expr = ParseLogicalOr();
 443            if (token.id == TokenId.Question)
 444            {
 445                NextToken();
 446                Expression expr1 = ParseExpression();
 447                ValidateToken(TokenId.Colon, SR.ColonExpected);
 448                NextToken();
 449                Expression expr2 = ParseExpression();
 450                expr = GenerateConditional(expr, expr1, expr2, errorPos);
 451            }
 452            return expr;
 453        }
 454
 455        // ||, or operator
 456        Expression ParseLogicalOr()
 457        {
 458            Expression left = ParseLogicalAnd();
 459            while (token.id == TokenId.DoubleBar || TokenIdentifierIs("or"))
 460            {
 461                Token op = token;
 462                NextToken();
 463                Expression right = ParseLogicalAnd();
 464                CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos);
 465                left = Expression.OrElse(left, right);
 466            }
 467            return left;
 468        }
 469
 470        // &&, and operator
 471        Expression ParseLogicalAnd()
 472        {
 473            Expression left = ParseComparison();
 474            while (token.id == TokenId.DoubleAmphersand || TokenIdentifierIs("and"))
 475            {
 476                Token op = token;
 477                NextToken();
 478                Expression right = ParseComparison();
 479                CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos);
 480                left = Expression.AndAlso(left, right);
 481            }
 482            return left;
 483        }
 484
 485        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Legacy code.")]
 486        // =, ==, !=, <>, >, >=, <, <= operators
 487        Expression ParseComparison()
 488        {
 489            Expression left = ParseAdditive();
 490            while (token.id == TokenId.Equal || token.id == TokenId.DoubleEqual ||
 491                token.id == TokenId.ExclamationEqual || token.id == TokenId.LessGreater ||
 492                token.id == TokenId.GreaterThan || token.id == TokenId.GreaterThanEqual ||
 493                token.id == TokenId.LessThan || token.id == TokenId.LessThanEqual)
 494            {
 495                Token op = token;
 496                NextToken();
 497                Expression right = ParseAdditive();
 498                bool isEquality = op.id == TokenId.Equal || op.id == TokenId.DoubleEqual ||
 499                    op.id == TokenId.ExclamationEqual || op.id == TokenId.LessGreater;
 500                if (isEquality && !left.Type.IsValueType && !right.Type.IsValueType)
 501                {
 502                    if (left.Type != right.Type)
 503                    {
 504                        if (left.Type.IsAssignableFrom(right.Type))
 505                        {
 506                            right = Expression.Convert(right, left.Type);
 507                        }
 508                        else if (right.Type.IsAssignableFrom(left.Type))
 509                        {
 510                            left = Expression.Convert(left, right.Type);
 511                        }
 512                        else
 513                        {
 514                            throw IncompatibleOperandsError(op.text, left, right, op.pos);
 515                        }
 516                    }
 517                }
 518                else if (IsEnumType(left.Type) || IsEnumType(right.Type))
 519                {
 520                    // convert enum expressions to their underlying values for comparison
 521                    left = ConvertEnumExpression(left, right);
 522                    right = ConvertEnumExpression(right, left);
 523
 524                    CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures),
 525                        op.text, ref left, ref right, op.pos);
 526                }
 527                else
 528                {
 529                    CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures),
 530                        op.text, ref left, ref right, op.pos);
 531                }
 532                switch (op.id)
 533                {
 534                    case TokenId.Equal:
 535                    case TokenId.DoubleEqual:
 536                        left = GenerateEqual(left, right);
 537                        break;
 538                    case TokenId.ExclamationEqual:
 539                    case TokenId.LessGreater:
 540                        left = GenerateNotEqual(left, right);
 541                        break;
 542                    case TokenId.GreaterThan:
 543                        left = GenerateGreaterThan(left, right);
 544                        break;
 545                    case TokenId.GreaterThanEqual:
 546                        left = GenerateGreaterThanEqual(left, right);
 547                        break;
 548                    case TokenId.LessThan:
 549                        left = GenerateLessThan(left, right);
 550                        break;
 551                    case TokenId.LessThanEqual:
 552                        left = GenerateLessThanEqual(left, right);
 553                        break;
 554                }
 555            }
 556            return left;
 557        }
 558
 559        /// <summary>
 560        /// We perform comparisons against enums using the underlying type
 561        /// because a more complete set of comparisons can be performed.
 562        /// </summary>
 563        static Expression ConvertEnumExpression(Expression expr, Expression otherExpr)
 564        {
 565            if (!IsEnumType(expr.Type))
 566            {
 567                return expr;
 568            }
 569
 570            Type underlyingType;
 571            if (IsNullableType(expr.Type) ||
 572                (otherExpr.NodeType == ExpressionType.Constant && ((ConstantExpression)otherExpr).Value == null))
 573            {
 574                // if the enum expression itself is nullable or is being compared against null
 575                // we use a nullable type
 576                underlyingType = typeof(Nullable<>).MakeGenericType(Enum.GetUnderlyingType(GetNonNullableType(expr.Type)));
 577            }
 578            else
 579            {
 580                underlyingType = Enum.GetUnderlyingType(expr.Type);
 581            }
 582
 583            return Expression.Convert(expr, underlyingType);
 584        }
 585
 586        // +, -, & operators
 587        Expression ParseAdditive()
 588        {
 589            Expression left = ParseMultiplicative();
 590            while (token.id == TokenId.Plus || token.id == TokenId.Minus ||
 591                token.id == TokenId.Amphersand)
 592            {
 593                Token op = token;
 594                NextToken();
 595                Expression right = ParseMultiplicative();
 596                switch (op.id)
 597                {
 598                    case TokenId.Plus:
 599                        if (left.Type == typeof(string) || right.Type == typeof(string))
 600                            goto case TokenId.Amphersand;
 601                        CheckAndPromoteOperands(typeof(IAddSignatures), op.text, ref left, ref right, op.pos);
 602                        left = GenerateAdd(left, right);
 603                        break;
 604                    case TokenId.Minus:
 605                        CheckAndPromoteOperands(typeof(ISubtractSignatures), op.text, ref left, ref right, op.pos);
 606                        left = GenerateSubtract(left, right);
 607                        break;
 608                    case TokenId.Amphersand:
 609                        left = GenerateStringConcat(left, right);
 610                        break;
 611                }
 612            }
 613            return left;
 614        }
 615
 616        // *, /, %, mod operators
 617        Expression ParseMultiplicative()
 618        {
 619            Expression left = ParseUnary();
 620            while (token.id == TokenId.Asterisk || token.id == TokenId.Slash ||
 621                token.id == TokenId.Percent || TokenIdentifierIs("mod"))
 622            {
 623                Token op = token;
 624                NextToken();
 625                Expression right = ParseUnary();
 626                CheckAndPromoteOperands(typeof(IArithmeticSignatures), op.text, ref left, ref right, op.pos);
 627                switch (op.id)
 628                {
 629                    case TokenId.Asterisk:
 630                        left = Expression.Multiply(left, right);
 631                        break;
 632                    case TokenId.Slash:
 633                        left = Expression.Divide(left, right);
 634                        break;
 635                    case TokenId.Percent:
 636                    case TokenId.Identifier:
 637                        left = Expression.Modulo(left, right);
 638                        break;
 639                }
 640            }
 641            return left;
 642        }
 643
 644        // -, !, not unary operators
 645        Expression ParseUnary()
 646        {
 647            if (token.id == TokenId.Minus || token.id == TokenId.Exclamation ||
 648                TokenIdentifierIs("not"))
 649            {
 650                Token op = token;
 651                NextToken();
 652                if (op.id == TokenId.Minus && (token.id == TokenId.IntegerLiteral ||
 653                    token.id == TokenId.RealLiteral))
 654                {
 655                    token.text = "-" + token.text;
 656                    token.pos = op.pos;
 657                    return ParsePrimary();
 658                }
 659                Expression expr = ParseUnary();
 660                if (op.id == TokenId.Minus)
 661                {
 662                    CheckAndPromoteOperand(typeof(INegationSignatures), op.text, ref expr, op.pos);
 663                    expr = Expression.Negate(expr);
 664                }
 665                else
 666                {
 667                    CheckAndPromoteOperand(typeof(INotSignatures), op.text, ref expr, op.pos);
 668                    expr = Expression.Not(expr);
 669                }
 670                return expr;
 671            }
 672            return ParsePrimary();
 673        }
 674
 675        Expression ParsePrimary()
 676        {
 677            Expression expr = ParsePrimaryStart();
 678            while (true)
 679            {
 680                if (token.id == TokenId.Dot)
 681                {
 682                    NextToken();
 683                    expr = ParseMemberAccess(null, expr);
 684                }
 685                else if (token.id == TokenId.OpenBracket)
 686                {
 687                    expr = ParseElementAccess(expr);
 688                }
 689                else
 690                {
 691                    break;
 692                }
 693            }
 694            return expr;
 695        }
 696
 697        Expression ParsePrimaryStart()
 698        {
 699            switch (token.id)
 700            {
 701                case TokenId.Identifier:
 702                    return ParseIdentifier();
 703                case TokenId.StringLiteral:
 704                    return ParseStringLiteral();
 705                case TokenId.IntegerLiteral:
 706                    return ParseIntegerLiteral();
 707                case TokenId.RealLiteral:
 708                    return ParseRealLiteral();
 709                case TokenId.OpenParen:
 710                    return ParseParenExpression();
 711                default:
 712                    throw ParseError(SR.ExpressionExpected);
 713            }
 714        }
 715
 716        Expression ParseStringLiteral()
 717        {
 718            ValidateToken(TokenId.StringLiteral);
 719            char quote = token.text[0];
 720            // Unwrap string (remove surrounding quotes) and unwrap backslashes.
 721            string s = token.text.Substring(1, token.text.Length - 2).Replace("\\\\", "\\");
 722
 723            if (quote == '\'')
 724            {
 725                // Unwrap single quotes.
 726                s = s.Replace("\\\'", "\'");
 727            }
 728            else
 729            {
 730                // Unwrap double quotes.
 731                s = s.Replace("\\\"", "\"");
 732
 733                // TODO : do we need this code anymore?
 734            }
 735
 736            NextToken();
 737            return CreateLiteral(s, s);
 738        }
 739
 740        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", Justification = "Legacy code.")]
 741        Expression ParseIntegerLiteral()
 742        {
 743            ValidateToken(TokenId.IntegerLiteral);
 744            string text = token.text;
 745            if (text[0] != '-')
 746            {
 747                ulong value;
 748                if (!UInt64.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out value))
 749                    throw ParseError(SR.InvalidIntegerLiteral(text));
 750                NextToken();
 751                if (value <= (ulong)Int32.MaxValue)
 752                    return CreateLiteral((int)value, text);
 753                if (value <= (ulong)UInt32.MaxValue)
 754                    return CreateLiteral((uint)value, text);
 755                if (value <= (ulong)Int64.MaxValue)
 756                    return CreateLiteral((long)value, text);
 757                return CreateLiteral(value, text);
 758            }
 759            else
 760            {
 761                long value;
 762                if (!Int64.TryParse(text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out value))
 763                    throw ParseError(SR.InvalidIntegerLiteral(text));
 764                NextToken();
 765                if (value >= Int32.MinValue && value <= Int32.MaxValue)
 766                    return CreateLiteral((int)value, text);
 767                return CreateLiteral(value, text);
 768            }
 769        }
 770
 771        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", Justification = "Legacy code.")]
 772        Expression ParseRealLiteral()
 773        {
 774            ValidateToken(TokenId.RealLiteral);
 775            string text = token.text;
 776            object value = null;
 777            char last = text[text.Length - 1];
 778            if (last == 'F' || last == 'f')
 779            {
 780                float f;
 781                if (Single.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out f))
 782                    value = f;
 783            }
 784            else if (last == 'M' || last == 'm')
 785            {
 786                decimal m;
 787                if (Decimal.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out m))
 788                    value = m;
 789            }
 790            else if (last == 'D' || last == 'd')
 791            {
 792                double d;
 793                if (Double.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out d))
 794                    value = d;
 795            }
 796            else
 797            {
 798                double d;
 799                if (Double.TryParse(text, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out d))
 800                    value = d;
 801            }
 802            if (value == null)
 803                throw ParseError(SR.InvalidRealLiteral(text));
 804            NextToken();
 805            return CreateLiteral(value, text);
 806        }
 807
 808        Expression CreateLiteral(object value, string valueAsString)
 809        {
 810            ConstantExpression expr = Expression.Constant(value);
 811            literals.Add(expr, valueAsString);
 812            return expr;
 813        }
 814
 815        Expression ParseParenExpression()
 816        {
 817            ValidateToken(TokenId.OpenParen, SR.OpenParenExpected);
 818            NextToken();
 819            Expression e = ParseExpression();
 820            ValidateToken(TokenId.CloseParen, SR.CloseParenOrOperatorExpected);
 821            NextToken();
 822            return e;
 823        }
 824
 825        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Legacy code.")]
 826        Expression ParseIdentifier()
 827        {
 828            ValidateToken(TokenId.Identifier);
 829            object value;
 830            if (keywords.TryGetValue(token.text, out value))
 831            {
 832                if (value is Type)
 833                    return ParseTypeAccess((Type)value);
 834                if (value == (object)keywordIt)
 835                    return ParseIt();
 836                if (value == (object)keywordIif)
 837                    return ParseIif();
 838                NextToken();
 839                return (Expression)value;
 840            }
 841
 842            if (symbols.TryGetValue(token.text, out value))
 843            {
 844                Expression expr = value as Expression;
 845                if (expr == null)
 846                {
 847                    expr = Expression.Constant(value);
 848                }
 849                NextToken();
 850                return expr;
 851            }
 852
 853            // See if the token is a mapped function call
 854            MappedMemberInfo mappedFunction = MapFunction(token.text);
 855            if (mappedFunction != null)
 856            {
 857                return ParseMappedFunction(mappedFunction);
 858            }
 859
 860            if (it != null)
 861                return ParseMemberAccess(null, it);
 862
 863            throw ParseError(SR.UnknownIdentifier(token.text));
 864        }
 865
 866        MappedMemberInfo MapFunction(string functionName)
 867        {
 868            MappedMemberInfo mappedMember = MapStringFunction(functionName);
 869            if (mappedMember != null)
 870            {
 871                return mappedMember;
 872            }
 873
 874            mappedMember = MapDateFunction(functionName);
 875            if (mappedMember != null)
 876            {
 877                return mappedMember;
 878            }
 879
 880            mappedMember = MapMathFunction(functionName);
 881            if (mappedMember != null)
 882            {
 883                return mappedMember;
 884            }
 885
 886            return null;
 887        }
 888
 889        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Legacy code.")]
 890        MappedMemberInfo MapStringFunction(string functionName)
 891        {
 892            if (functionName == "startswith")
 893            {
 894                return new MappedMemberInfo(typeof(string), "StartsWith", false, true);
 895            }
 896            else if (functionName == "endswith")
 897            {
 898                return new MappedMemberInfo(typeof(string), "EndsWith", false, true);
 899            }
 900            else if (functionName == "length")
 901            {
 902                return new MappedMemberInfo(typeof(string), "Length", false, false);
 903            }
 904            else if (functionName == "toupper")
 905            {
 906                return new MappedMemberInfo(typeof(string), "ToUpper", false, true);
 907            }
 908            else if (functionName == "tolower")
 909            {
 910                return new MappedMemberInfo(typeof(string), "ToLower", false, true);
 911            }
 912            else if (functionName == "substringof")
 913            {
 914                return new MappedMemberInfo(typeof(string), "Contains", false, true);
 915            }
 916            else if (functionName == "indexof")
 917            {
 918                return new MappedMemberInfo(typeof(string), "IndexOf", false, true);
 919            }
 920            else if (functionName == "replace")
 921            {
 922                return new MappedMemberInfo(typeof(string), "Replace", false, true);
 923            }
 924            else if (functionName == "substring")
 925            {
 926                return new MappedMemberInfo(typeof(string), "Substring", false, true);
 927            }
 928            else if (functionName == "trim")
 929            {
 930                return new MappedMemberInfo(typeof(string), "Trim", false, true);
 931            }
 932            else if (functionName == "concat")
 933            {
 934                return new MappedMemberInfo(typeof(string), "Concat", true, true);
 935            }
 936
 937            return null;
 938        }
 939
 940        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Legacy code.")]
 941        MappedMemberInfo MapDateFunction(string functionName)
 942        {
 943            // date functions
 944            if (functionName == "day")
 945            {
 946                return new MappedMemberInfo(typeof(DateTime), "Day", false, false);
 947            }
 948            else if (functionName == "month")
 949            {
 950                return new MappedMemberInfo(typeof(DateTime), "Month", false, false);
 951            }
 952            else if (functionName == "year")
 953            {
 954                return new MappedMemberInfo(typeof(DateTime), "Year", false, false);
 955            }
 956            else if (functionName == "hour")
 957            {
 958                return new MappedMemberInfo(typeof(DateTime), "Hour", false, false);
 959            }
 960            else if (functionName == "minute")
 961            {
 962                return new MappedMemberInfo(typeof(DateTime), "Minute", false, false);
 963            }
 964            else if (functionName == "second")
 965            {
 966                return new MappedMemberInfo(typeof(DateTime), "Second", false, false);
 967            }
 968
 969            return null;
 970        }
 971
 972        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Legacy code.")]
 973        MappedMemberInfo MapMathFunction(string functionName)
 974        {
 975            if (functionName == "round")
 976            {
 977                return new MappedMemberInfo(typeof(Math), "Round", true, true);
 978            }
 979            if (functionName == "floor")
 980            {
 981                return new MappedMemberInfo(typeof(Math), "Floor", true, true);
 982            }
 983            if (functionName == "ceiling")
 984            {
 985                return new MappedMemberInfo(typeof(Math), "Ceiling", true, true);
 986            }
 987
 988            return null;
 989        }
 990
 991        Expression ParseIt()
 992        {
 993            if (it == null)
 994                throw ParseError(SR.NoItInScope);
 995            NextToken();
 996            return it;
 997        }
 998
 999        Expression ParseIif()
1000        {
1001            int errorPos = token.pos;
1002            NextToken();
1003            Expression[] args = ParseArgumentList();
1004            if (args.Length != 3)
1005                throw ParseError(errorPos, SR.IifRequiresThreeArgs);
1006            return GenerateConditional(args[0], args[1], args[2], errorPos);
1007        }
1008
1009        Expression GenerateConditional(Expression test, Expression expr1, Expression expr2, int errorPos)
1010        {
1011            if (test.Type != typeof(bool))
1012                throw ParseError(errorPos, SR.FirstExprMustBeBool);
1013            if (expr1.Type != expr2.Type)
1014            {
1015                Expression expr1as2 = expr2 != nullLiteral ? PromoteExpression(expr1, expr2.Type, true) : null;
1016                Expression expr2as1 = expr1 != nullLiteral ? PromoteExpression(expr2, expr1.Type, true) : null;
1017                if (expr1as2 != null && expr2as1 == null)
1018                {
1019                    expr1 = expr1as2;
1020                }
1021                else if (expr2as1 != null && expr1as2 == null)
1022                {
1023                    expr2 = expr2as1;
1024                }
1025                else
1026                {
1027                    string type1 = expr1 != nullLiteral ? expr1.Type.Name : "null";
1028                    string type2 = expr2 != nullLiteral ? expr2.Type.Name : "null";
1029                    if (expr1as2 != null && expr2as1 != null)
1030                        throw ParseError(errorPos, SR.BothTypesConvertToOther(type1, type2));
1031                    throw ParseError(errorPos, SR.NeitherTypeConvertsToOther(type1, type2));
1032                }
1033            }
1034            return Expression.Condition(test, expr1, expr2);
1035        }
1036
1037        Expression ParseTypeAccess(Type type)
1038        {
1039            int errorPos = token.pos;
1040            NextToken();
1041            if (token.id == TokenId.Question)
1042            {
1043                if (!type.IsValueType || IsNullableType(type))
1044                    throw ParseError(errorPos, SR.TypeHasNoNullableForm(GetTypeName(type)));
1045                type = typeof(Nullable<>).MakeGenericType(type);
1046                NextToken();
1047            }
1048            if (token.id == TokenId.OpenParen)
1049            {
1050                Expression[] args = ParseArgumentList();
1051                MethodBase method;
1052                switch (FindBestMethod(type.GetConstructors(), args, out method))
1053                {
1054                    case 0:
1055                        if (args.Length == 1)
1056                            return GenerateConversion(args[0], type, errorPos);
1057                        throw ParseError(errorPos, SR.NoMatchingConstructor(GetTypeName(type)));
1058                    case 1:
1059                        return Expression.New((ConstructorInfo)method, args);
1060                    default:
1061                        throw ParseError(errorPos, SR.AmbiguousConstructorInvocation(GetTypeName(type)));
1062                }
1063            }
1064            ValidateToken(TokenId.Dot, SR.DotOrOpenParenExpected);
1065            NextToken();
1066            return ParseMemberAccess(type, null);
1067        }
1068
1069        static Expression GenerateConversion(Expression expr, Type type, int errorPos)
1070        {
1071            Type exprType = expr.Type;
1072            if (exprType == type)
1073                return expr;
1074            if (exprType.IsValueType && type.IsValueType)
1075            {
1076                if ((IsNullableType(exprType) || IsNullableType(type)) &&
1077                    GetNonNullableType(exprType) == GetNonNullableType(type))
1078                    return Expression.Convert(expr, type);
1079                if ((IsNumericType(exprType) || IsEnumType(exprType)) &&
1080                    (IsNumericType(type) || IsEnumType(type)))
1081                    return Expression.ConvertChecked(expr, type);
1082            }
1083            if (exprType.IsAssignableFrom(type) || type.IsAssignableFrom(exprType) ||
1084                exprType.IsInterface || type.IsInterface)
1085                return Expression.Convert(expr, type);
1086            throw ParseError(errorPos, SR.CannotConvertValue(
1087                GetTypeName(exprType), GetTypeName(type)));
1088        }
1089
1090        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Legacy code.")]
1091        Expression ParseMappedFunction(MappedMemberInfo mappedMember)
1092        {
1093            Type type = mappedMember.MappedType;
1094            string mappedMemberName = mappedMember.MemberName;
1095
1096            int errorPos = token.pos;
1097            Expression[] args = null;
1098            Expression paramArg = null;
1099            Expression instance = null;
1100
1101            NextToken();
1102            if (token.id == TokenId.OpenParen)
1103            {
1104                args = ParseArgumentList();
1105
1106                // TODO : verify the first argument is the param
1107                // Really what we'll want to do here is allow either to be the param
1108                paramArg = args[0];
1109                instance = paramArg;
1110
1111                // static methods need to include the target
1112                if (!mappedMember.IsStatic)
1113                {
1114                    args = args.Skip(1).ToArray();
1115                }
1116                else
1117                {
1118                    instance = null;
1119                }
1120            }
1121
1122            if (mappedMember.IsMethod)
1123            {
1124                // a mapped function
1125                MethodBase mb;
1126                switch (FindMethod(type, mappedMemberName, mappedMember.IsStatic, args, out mb))
1127                {
1128                    case 0:
1129                        throw ParseError(errorPos, SR.NoApplicableMethod(
1130                            mappedMemberName, GetTypeName(type)));
1131                    case 1:
1132                        MethodInfo method = (MethodInfo)mb;
1133                        if (!IsPredefinedType(method.DeclaringType))
1134                            throw ParseError(errorPos, SR.MethodsAreInaccessible(GetTypeName(method.DeclaringType)));
1135                        if (method.ReturnType == typeof(void))
1136                            throw ParseError(errorPos, SR.MethodIsVoid(
1137                                mappedMemberName, GetTypeName(method.DeclaringType)));
1138                        return Expression.Call(instance, (MethodInfo)method, args);
1139                    default:
1140                        throw ParseError(errorPos, SR.AmbiguousMethodInvocation(
1141                            mappedMemberName, GetTypeName(type)));
1142                }
1143            }
1144            else
1145            {
1146                // a mapped Property/Field
1147                MemberInfo member = FindPropertyOrField(type, mappedMemberName, mappedMember.IsStatic);
1148                if (member == null)
1149                {
1150                    if (this.queryResolver != null)
1151                    {
1152                        MemberExpression mex = queryResolver.ResolveMember(type, mappedMemberName, instance);
1153                        if (mex != null)
1154                        {
1155                            return mex;
1156                        }
1157                    }
1158                    throw ParseError(errorPos, SR.UnknownPropertyOrField(
1159                        mappedMemberName, GetTypeName(type)));
1160                }
1161
1162                return member is PropertyInfo ?
1163                    Expression.Property(instance, (PropertyInfo)member) :
1164                    Expression.Field(instance, (FieldInfo)member);
1165            }
1166        }
1167
1168        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Legacy code.")]
1169        Expression ParseMemberAccess(Type type, Expression instance)
1170        {
1171            if (instance != null)
1172                type = instance.Type;
1173            int errorPos = token.pos;
1174            string id = GetIdentifier();
1175            NextToken();
1176            if (token.id == TokenId.OpenParen)
1177            {
1178                if (instance != null && type != typeof(string))
1179                {
1180                    Type enumerableType = FindGenericType(typeof(IEnumerable<>), type);
1181                    if (enumerableType != null)
1182                    {
1183                        Type elementType = enumerableType.GetGenericArguments()[0];
1184                        return ParseAggregate(instance, elementType, id, errorPos);
1185                    }
1186                }
1187                Expression[] args = ParseArgumentList();
1188                MethodBase mb;
1189                switch (FindMethod(type, id, instance == null, args, out mb))
1190                {
1191                    case 0:
1192                        throw ParseError(errorPos, SR.NoApplicableMethod(
1193                            id, GetTypeName(type)));
1194                    case 1:
1195                        MethodInfo method = (MethodInfo)mb;
1196                        if (!IsPredefinedType(method.DeclaringType))
1197                            throw ParseError(errorPos, SR.MethodsAreInaccessible(GetTypeName(method.DeclaringType)));
1198                        if (method.ReturnType == typeof(void))
1199                            throw ParseError(errorPos, SR.MethodIsVoid(
1200                                id, GetTypeName(method.DeclaringType)));
1201                        return Expression.Call(instance, (MethodInfo)method, args);
1202                    default:
1203                        throw ParseError(errorPos, SR.AmbiguousMethodInvocation(
1204                            id, GetTypeName(type)));
1205                }
1206            }
1207            else
1208            {
1209                MemberInfo member = FindPropertyOrField(type, id, instance == null);
1210                if (member == null)
1211                {
1212                    if (this.queryResolver != null)
1213                    {
1214                        MemberExpression mex = queryResolver.ResolveMember(type, id, instance);
1215                        if (mex != null)
1216                        {
1217                            return mex;
1218                        }
1219                    }
1220                    throw ParseError(errorPos, SR.UnknownPropertyOrField(
1221                        id, GetTypeName(type)));
1222                }
1223                return member is PropertyInfo ?
1224                    Expression.Property(instance, (PropertyInfo)member) :
1225                    Expression.Field(instance, (FieldInfo)member);
1226            }
1227        }
1228
1229        static Type FindGenericType(Type generic, Type type)
1230        {
1231            while (type != null && type != typeof(object))
1232            {
1233                if (type.IsGenericType && type.GetGenericTypeDefinition() == generic)
1234                    return typ

Large files files are truncated, but you can click here to view the full file