PageRenderTime 13ms CodeModel.GetById 16ms app.highlight 73ms RepoModel.GetById 1ms app.codeStats 1ms

/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
   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 type;
1235                if (generic.IsInterface)
1236                {
1237                    foreach (Type intfType in type.GetInterfaces())
1238                    {
1239                        Type found = FindGenericType(generic, intfType);
1240                        if (found != null)
1241                            return found;
1242                    }
1243                }
1244                type = type.BaseType;
1245            }
1246            return null;
1247        }
1248
1249        Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos)
1250        {
1251            ParameterExpression outerIt = it;
1252            ParameterExpression innerIt = Expression.Parameter(elementType, "");
1253            it = innerIt;
1254            Expression[] args = ParseArgumentList();
1255            it = outerIt;
1256            MethodBase signature;
1257            if (FindMethod(typeof(IEnumerableSignatures), methodName, false, args, out signature) != 1)
1258                throw ParseError(errorPos, SR.NoApplicableAggregate(methodName));
1259            Type[] typeArgs;
1260            if (signature.Name == "Min" || signature.Name == "Max")
1261            {
1262                typeArgs = new Type[] { elementType, args[0].Type };
1263            }
1264            else
1265            {
1266                typeArgs = new Type[] { elementType };
1267            }
1268            if (args.Length == 0)
1269            {
1270                args = new Expression[] { instance };
1271            }
1272            else
1273            {
1274                args = new Expression[] { instance, DynamicExpression.Lambda(args[0], innerIt) };
1275            }
1276            return Expression.Call(typeof(Enumerable), signature.Name, typeArgs, args);
1277        }
1278
1279        Expression[] ParseArgumentList()
1280        {
1281            ValidateToken(TokenId.OpenParen, SR.OpenParenExpected);
1282            NextToken();
1283            Expression[] args = token.id != TokenId.CloseParen ? ParseArguments() : new Expression[0];
1284            ValidateToken(TokenId.CloseParen, SR.CloseParenOrCommaExpected);
1285            NextToken();
1286            return args;
1287        }
1288
1289        Expression[] ParseArguments()
1290        {
1291            List<Expression> argList = new List<Expression>();
1292            while (true)
1293            {
1294                argList.Add(ParseExpression());
1295                if (token.id != TokenId.Comma)
1296                    break;
1297                NextToken();
1298            }
1299            return argList.ToArray();
1300        }
1301
1302        Expression ParseElementAccess(Expression expr)
1303        {
1304            int errorPos = token.pos;
1305            ValidateToken(TokenId.OpenBracket, SR.OpenParenExpected);
1306            NextToken();
1307            Expression[] args = ParseArguments();
1308            ValidateToken(TokenId.CloseBracket, SR.CloseBracketOrCommaExpected);
1309            NextToken();
1310            if (expr.Type.IsArray)
1311            {
1312                if (expr.Type.GetArrayRank() != 1 || args.Length != 1)
1313                    throw ParseError(errorPos, SR.CannotIndexMultiDimArray);
1314                Expression index = PromoteExpression(args[0], typeof(int), true);
1315                if (index == null)
1316                    throw ParseError(errorPos, SR.InvalidIndex);
1317                return Expression.ArrayIndex(expr, index);
1318            }
1319            else
1320            {
1321                MethodBase mb;
1322                switch (FindIndexer(expr.Type, args, out mb))
1323                {
1324                    case 0:
1325                        throw ParseError(errorPos, SR.NoApplicableIndexer(
1326                            GetTypeName(expr.Type)));
1327                    case 1:
1328                        return Expression.Call(expr, (MethodInfo)mb, args);
1329                    default:
1330                        throw ParseError(errorPos, SR.AmbiguousIndexerInvocation(
1331                            GetTypeName(expr.Type)));
1332                }
1333            }
1334        }
1335
1336        static bool IsPredefinedType(Type type)
1337        {
1338            type = GetNonNullableType(type);
1339            foreach (Type t in predefinedTypes)
1340                if (t == type)
1341                    return true;
1342            return false;
1343        }
1344
1345        static bool IsNullableType(Type type)
1346        {
1347            return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
1348        }
1349
1350        static Type GetNonNullableType(Type type)
1351        {
1352            return IsNullableType(type) ? type.GetGenericArguments()[0] : type;
1353        }
1354
1355        internal static string GetTypeName(Type type)
1356        {
1357            Type baseType = GetNonNullableType(type);
1358            string s = baseType.Name;
1359            if (type != baseType)
1360                s += '?';
1361            return s;
1362        }
1363
1364        static bool IsNumericType(Type type)
1365        {
1366            return GetNumericTypeKind(type) != 0;
1367        }
1368
1369        static bool IsSignedIntegralType(Type type)
1370        {
1371            return GetNumericTypeKind(type) == 2;
1372        }
1373
1374        static bool IsUnsignedIntegralType(Type type)
1375        {
1376            return GetNumericTypeKind(type) == 3;
1377        }
1378
1379        static int GetNumericTypeKind(Type type)
1380        {
1381            type = GetNonNullableType(type);
1382            if (type.IsEnum)
1383                return 0;
1384            switch (Type.GetTypeCode(type))
1385            {
1386                case TypeCode.Char:
1387                case TypeCode.Single:
1388                case TypeCode.Double:
1389                case TypeCode.Decimal:
1390                    return 1;
1391                case TypeCode.SByte:
1392                case TypeCode.Int16:
1393                case TypeCode.Int32:
1394                case TypeCode.Int64:
1395                    return 2;
1396                case TypeCode.Byte:
1397                case TypeCode.UInt16:
1398                case TypeCode.UInt32:
1399                case TypeCode.UInt64:
1400                    return 3;
1401                default:
1402                    return 0;
1403            }
1404        }
1405
1406        static bool IsEnumType(Type type)
1407        {
1408            return GetNonNullableType(type).IsEnum;
1409        }
1410
1411        void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos)
1412        {
1413            Expression[] args = new Expression[] { expr };
1414            MethodBase method;
1415            if (FindMethod(signatures, "F", false, args, out method) != 1)
1416                throw ParseError(errorPos, SR.IncompatibleOperand(
1417                    opName, GetTypeName(args[0].Type)));
1418            expr = args[0];
1419        }
1420
1421        void CheckAndPromoteOperands(Type signatures, string opName, ref Expression left, ref Expression right, int errorPos)
1422        {
1423            Expression[] args = new Expression[] { left, right };
1424            MethodBase method;
1425            if (FindMethod(signatures, "F", false, args, out method) != 1)
1426                throw IncompatibleOperandsError(opName, left, right, errorPos);
1427            left = args[0];
1428            right = args[1];
1429        }
1430
1431        static Exception IncompatibleOperandsError(string opName, Expression left, Expression right, int pos)
1432        {
1433            return ParseError(pos, SR.IncompatibleOperands(
1434                opName, GetTypeName(left.Type), GetTypeName(right.Type)));
1435        }
1436
1437        static MemberInfo FindPropertyOrField(Type type, string memberName, bool staticAccess)
1438        {
1439            BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly |
1440                (staticAccess ? BindingFlags.Static : BindingFlags.Instance);
1441            foreach (Type t in SelfAndBaseTypes(type))
1442            {
1443                MemberInfo[] members = t.FindMembers(MemberTypes.Property | MemberTypes.Field,
1444                    flags, Type.FilterNameIgnoreCase, memberName);
1445                if (members.Length != 0)
1446                    return members[0];
1447            }
1448            return null;
1449        }
1450
1451        int FindMethod(Type type, string methodName, bool staticAccess, Expression[] args, out MethodBase method)
1452        {
1453            BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly |
1454                (staticAccess ? BindingFlags.Static : BindingFlags.Instance);
1455            foreach (Type t in SelfAndBaseTypes(type))
1456            {
1457                MemberInfo[] members = t.FindMembers(MemberTypes.Method,
1458                    flags, Type.FilterNameIgnoreCase, methodName);
1459                int count = FindBestMethod(members.Cast<MethodBase>(), args, out method);
1460                if (count != 0)
1461                    return count;
1462            }
1463            method = null;
1464            return 0;
1465        }
1466
1467        int FindIndexer(Type type, Expression[] args, out MethodBase method)
1468        {
1469            foreach (Type t in SelfAndBaseTypes(type))
1470            {
1471                MemberInfo[] members = t.GetDefaultMembers();
1472                if (members.Length != 0)
1473                {
1474                    IEnumerable<MethodBase> methods = members.
1475                        OfType<PropertyInfo>().
1476                        Select(p => (MethodBase)p.GetGetMethod()).
1477                        Where(m => m != null);
1478                    int count = FindBestMethod(methods, args, out method);
1479                    if (count != 0)
1480                        return count;
1481                }
1482            }
1483            method = null;
1484            return 0;
1485        }
1486
1487        static IEnumerable<Type> SelfAndBaseTypes(Type type)
1488        {
1489            if (type.IsInterface)
1490            {
1491                List<Type> types = new List<Type>();
1492                AddInterface(types, type);
1493                return types;
1494            }
1495            return SelfAndBaseClasses(type);
1496        }
1497
1498        static IEnumerable<Type> SelfAndBaseClasses(Type type)
1499        {
1500            while (type != null)
1501            {
1502                yield return type;
1503                type = type.BaseType;
1504            }
1505        }
1506
1507        static void AddInterface(List<Type> types, Type type)
1508        {
1509            if (!types.Contains(type))
1510            {
1511                types.Add(type);
1512                foreach (Type t in type.GetInterfaces())
1513                    AddInterface(types, t);
1514            }
1515        }
1516
1517        class MethodData
1518        {
1519            public MethodBase MethodBase;
1520            public ParameterInfo[] Parameters;
1521            public Expression[] Args;
1522        }
1523
1524        int FindBestMethod(IEnumerable<MethodBase> methods, Expression[] args, out MethodBase method)
1525        {
1526            MethodData[] applicable = methods.
1527                Select(m => new MethodData
1528                {
1529                    MethodBase = m,
1530                    Parameters = m.GetParameters()
1531                }).
1532                Where(m => IsApplicable(m, args)).
1533                ToArray();
1534            if (applicable.Length > 1)
1535            {
1536                applicable = applicable.
1537                    Where(m => applicable.All(n => m == n || IsBetterThan(args, m, n))).
1538                    ToArray();
1539            }
1540            if (applicable.Length == 1)
1541            {
1542                MethodData md = applicable[0];
1543                for (int i = 0; i < args.Length; i++)
1544                    args[i] = md.Args[i];
1545                method = md.MethodBase;
1546            }
1547            else
1548            {
1549                method = null;
1550            }
1551            return applicable.Length;
1552        }
1553
1554        bool IsApplicable(MethodData method, Expression[] args)
1555        {
1556            if (method.Parameters.Length != args.Length)
1557                return false;
1558            Expression[] promotedArgs = new Expression[args.Length];
1559            for (int i = 0; i < args.Length; i++)
1560            {
1561                ParameterInfo pi = method.Parameters[i];
1562                if (pi.IsOut)
1563                    return false;
1564                Expression promoted = PromoteExpression(args[i], pi.ParameterType, false);
1565                if (promoted == null)
1566                    return false;
1567                promotedArgs[i] = promoted;
1568            }
1569            method.Args = promotedArgs;
1570            return true;
1571        }
1572
1573        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", Justification = "Legacy code.")]
1574        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Legacy code.")]
1575        Expression PromoteExpression(Expression expr, Type type, bool exact)
1576        {
1577            if (expr.Type == type)
1578                return expr;
1579            if (expr is ConstantExpression)
1580            {
1581                ConstantExpression ce = (ConstantExpression)expr;
1582                if (ce == nullLiteral)
1583                {
1584                    if (!type.IsValueType || IsNullableType(type))
1585                        return Expression.Constant(null, type);
1586                }
1587                else
1588                {
1589                    string text;
1590                    if (literals.TryGetValue(ce, out text))
1591                    {
1592                        Type target = GetNonNullableType(type);
1593                        Object value = null;
1594                        switch (Type.GetTypeCode(ce.Type))
1595                        {
1596                            case TypeCode.Int32:
1597                            case TypeCode.UInt32:
1598                            case TypeCode.Int64:
1599                            case TypeCode.UInt64:
1600                                if (target.IsEnum)
1601                                {
1602                                    // promoting from a number to an enum
1603                                    value = Enum.Parse(target, text);
1604                                }
1605                                else if (target == typeof(char))
1606                                {
1607                                    // promote from a number to a char
1608                                    value = Convert.ToChar(ce.Value, CultureInfo.InvariantCulture);
1609                                }
1610                                else
1611                                {
1612                                    value = ParseNumber(text, target);
1613                                }
1614                                break;
1615                            case TypeCode.Double:
1616                                if (target == typeof(decimal))
1617                                    value = ParseNumber(text, target);
1618                                break;
1619                            case TypeCode.String:
1620                                value = ParseEnum(text, target);
1621                                break;
1622                        }
1623                        if (value != null)
1624                            return Expression.Constant(value, type);
1625                    }
1626                }
1627            }
1628            if (IsCompatibleWith(expr.Type, type))
1629            {
1630                if (type.IsValueType || exact)
1631                    return Expression.Convert(expr, type);
1632                return expr;
1633            }
1634            return null;
1635        }
1636
1637        static object ParseNumber(string text, Type type)
1638        {
1639            switch (Type.GetTypeCode(GetNonNullableType(type)))
1640            {
1641                case TypeCode.SByte:
1642                    sbyte sb;
1643                    if (sbyte.TryParse(text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out sb))
1644                        return sb;
1645                    break;
1646                case TypeCode.Byte:
1647                    byte b;
1648                    if (byte.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out b))
1649                        return b;
1650                    break;
1651                case TypeCode.Int16:
1652                    short s;
1653                    if (short.TryParse(text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out s))
1654                        return s;
1655                    break;
1656                case TypeCode.UInt16:
1657                    ushort us;
1658                    if (ushort.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out us))
1659                        return us;
1660                    break;
1661                case TypeCode.Int32:
1662                    int i;
1663                    if (int.TryParse(text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out i))
1664                        return i;
1665                    break;
1666                case TypeCode.UInt32:
1667                    uint ui;
1668                    if (uint.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out ui))
1669                        return ui;
1670                    break;
1671                case TypeCode.Int64:
1672                    long l;
1673                    if (long.TryParse(text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out l))
1674                        return l;
1675                    break;
1676                case TypeCode.UInt64:
1677                    ulong ul;
1678                    if (ulong.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out ul))
1679                        return ul;
1680                    break;
1681                case TypeCode.Single:
1682                    float f;
1683                    if (float.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out f))
1684                        return f;
1685                    break;
1686                case TypeCode.Double:
1687                    double d;
1688                    if (double.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out d))
1689                        return d;
1690                    break;
1691                case TypeCode.Decimal:
1692                    decimal e;
1693                    if (decimal.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out e))
1694                        return e;
1695                    break;
1696            }
1697            return null;
1698        }
1699
1700        static object ParseEnum(string name, Type type)
1701        {
1702            if (type.IsEnum)
1703            {
1704                MemberInfo[] memberInfos = type.FindMembers(MemberTypes.Field,
1705                    BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static,
1706                    Type.FilterNameIgnoreCase, name);
1707                if (memberInfos.Length != 0)
1708                    return ((FieldInfo)memberInfos[0]).GetValue(null);
1709            }
1710            return null;
1711        }
1712
1713        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Legacy code.")]
1714        static bool IsCompatibleWith(Type source, Type target)
1715        {
1716            if (source == target)
1717                return true;
1718            if (!target.IsValueType)
1719                return target.IsAssignableFrom(source);
1720            Type st = GetNonNullableType(source);
1721            Type tt = GetNonNullableType(target);
1722            if (st != source && tt == target)
1723                return false;
1724            TypeCode sc = st.IsEnum ? TypeCode.Object : Type.GetTypeCode(st);
1725            TypeCode tc = tt.IsEnum ? TypeCode.Object : Type.GetTypeCode(tt);
1726            switch (sc)
1727            {
1728                case TypeCode.SByte:
1729                    switch (tc)
1730                    {
1731                        case TypeCode.SByte:
1732                        case TypeCode.Int16:
1733                        case TypeCode.Int32:
1734                        case TypeCode.Int64:
1735                        case TypeCode.Single:
1736                        case TypeCode.Double:
1737                        case TypeCode.Decimal:
1738                            return true;
1739                    }
1740                    break;
1741                case TypeCode.Byte:
1742                    switch (tc)
1743                    {
1744                        case TypeCode.Byte:
1745                        case TypeCode.Int16:
1746                        case TypeCode.UInt16:
1747                        case TypeCode.Int32:
1748                        case TypeCode.UInt32:
1749                        case TypeCode.Int64:
1750                        case TypeCode.UInt64:
1751                        case TypeCode.Single:
1752                        case TypeCode.Double:
1753                        case TypeCode.Decimal:
1754                            return true;
1755                    }
1756                    break;
1757                case TypeCode.Int16:
1758                    switch (tc)
1759                    {
1760                        case TypeCode.Int16:
1761                        case TypeCode.Int32:
1762                        case TypeCode.Int64:
1763                        case TypeCode.Single:
1764                        case TypeCode.Double:
1765                        case TypeCode.Decimal:
1766                            return true;
1767                    }
1768                    break;
1769                case TypeCode.UInt16:
1770                    switch (tc)
1771                    {
1772                        case TypeCode.UInt16:
1773                        case TypeCode.Int32:
1774                        case TypeCode.UInt32:
1775                        case TypeCode.Int64:
1776                        case TypeCode.UInt64:
1777                        case TypeCode.Single:
1778                        case TypeCode.Double:
1779                        case TypeCode.Decimal:
1780                            return true;
1781                    }
1782                    break;
1783                case TypeCode.Int32:
1784                    switch (tc)
1785                    {
1786                        case TypeCode.Int32:
1787                        case TypeCode.Int64:
1788                        case TypeCode.Single:
1789                        case TypeCode.Double:
1790                        case TypeCode.Decimal:
1791                            return true;
1792                    }
1793                    break;
1794                case TypeCode.UInt32:
1795                    switch (tc)
1796                    {
1797                        case TypeCode.UInt32:
1798                        case TypeCode.Int64:
1799                        case TypeCode.UInt64:
1800                        case TypeCode.Single:
1801                        case TypeCode.Double:
1802                        case TypeCode.Decimal:
1803                            return true;
1804                    }
1805                    break;
1806                case TypeCode.Int64:
1807                    switch (tc)
1808                    {
1809                        case TypeCode.Int64:
1810                        case TypeCode.Single:
1811                        case TypeCode.Double:
1812                        case TypeCode.Decimal:
1813                            return true;
1814                    }
1815                    break;
1816                case TypeCode.UInt64:
1817                    switch (tc)
1818                    {
1819                        case TypeCode.UInt64:
1820                        case TypeCode.Single:
1821                        case TypeCode.Double:
1822                        case TypeCode.Decimal:
1823                            return true;
1824                    }
1825                    break;
1826                case TypeCode.Single:
1827                    switch (tc)
1828                    {
1829                        case TypeCode.Single:
1830                        case TypeCode.Double:
1831                            return true;
1832                    }
1833                    break;
1834                default:
1835                    if (st == tt)
1836                        return true;
1837                    break;
1838            }
1839            return false;
1840        }
1841
1842        static bool IsBetterThan(Expression[] args, MethodData m1, MethodData m2)
1843        {
1844            bool better = false;
1845            for (int i = 0; i < args.Length; i++)
1846            {
1847                int c = CompareConversions(args[i].Type,
1848                    m1.Parameters[i].ParameterType,
1849                    m2.Parameters[i].ParameterType);
1850                if (c < 0)
1851                    return false;
1852                if (c > 0)
1853                    better = true;
1854            }
1855            return better;
1856        }
1857
1858        // Return 1 if s -> t1 is a better conversion than s -> t2
1859        // Return -1 if s -> t2 is a better conversion than s -> t1
1860        // Return 0 if neither conversion is better
1861        static int CompareConversions(Type s, Type t1, Type t2)
1862        {
1863            if (t1 == t2)
1864                return 0;
1865            if (s == t1)
1866                return 1;
1867            if (s == t2)
1868                return -1;
1869            bool t1t2 = IsCompatibleWith(t1, t2);
1870            bool t2t1 = IsCompatibleWith(t2, t1);
1871            if (t1t2 && !t2t1)
1872                return 1;
1873            if (t2t1 && !t1t2)
1874                return -1;
1875            if (IsSignedIntegralType(t1) && IsUnsignedIntegralType(t2))
1876                return 1;
1877            if (IsSignedIntegralType(t2) && IsUnsignedIntegralType(t1))
1878                return -1;
1879            return 0;
1880        }
1881
1882        static Expression GenerateEqual(Expression left, Expression right)
1883        {
1884            return Expression.Equal(left, right);
1885        }
1886
1887        static Expression GenerateNotEqual(Expression left, Expression right)
1888        {
1889            return Expression.NotEqual(left, right);
1890        }
1891
1892        static Expression GenerateGreaterThan(Expression left, Expression right)
1893        {
1894            if (left.Type == typeof(string))
1895            {
1896                return Expression.GreaterThan(
1897                    GenerateStaticMethodCall("Compare", left, right),
1898                    Expression.Constant(0)
1899                );
1900            }
1901            return Expression.GreaterThan(left, right);
1902        }
1903
1904        static Expression GenerateGreaterThanEqual(Expression left, Expression right)
1905        {
1906            if (left.Type == typeof(string))
1907            {
1908                return Expression.GreaterThanOrEqual(
1909                    GenerateStaticMethodCall("Compare", left, right),
1910                    Expression.Constant(0)
1911                );
1912            }
1913            return Expression.GreaterThanOrEqual(left, right);
1914        }
1915
1916        static Expression GenerateLessThan(Expression left, Expression right)
1917        {
1918            if (left.Type == typeof(string))
1919            {
1920                return Expression.LessThan(
1921                    GenerateStaticMethodCall("Compare", left, right),
1922                    Expression.Constant(0)
1923                );
1924            }
1925            return Expression.LessThan(left, right);
1926        }
1927
1928        static Expression GenerateLessThanEqual(Expression left, Expression right)
1929        {
1930            if (left.Type == typeof(string))
1931            {
1932                return Expression.LessThanOrEqual(
1933                    GenerateStaticMethodCall("Compare", left, right),
1934                    Expression.Constant(0)
1935                );
1936            }
1937            return Expression.LessThanOrEqual(left, right);
1938        }
1939
1940        static Expression GenerateAdd(Expression left, Expression right)
1941        {
1942            if (left.Type == typeof(string) && right.Type == typeof(string))
1943            {
1944                return GenerateStaticMethodCall("Concat", left, right);
1945            }
1946            return Expression.Add(left, right);
1947        }
1948
1949        static Expression GenerateSubtract(Expression left, Expression right)
1950        {
1951            return Expression.Subtract(left, right);
1952        }
1953
1954        static Expression GenerateStringConcat(Expression left, Expression right)
1955        {
1956            if (left.Type.IsValueType)
1957                left = Expression.Convert(left, typeof(object));
1958            if (right.Type.IsValueType)
1959                right = Expression.Convert(right, typeof(object));
1960            return Expression.Call(
1961                null,
1962                typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }),
1963                new[] { left, right });
1964        }
1965
1966        static MethodInfo GetStaticMethod(string methodName, Expression left, Expression right)
1967        {
1968            return left.Type.GetMethod(methodName, new[] { left.Type, right.Type });
1969        }
1970
1971        static Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right)
1972        {
1973            return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right });
1974        }
1975
1976        void SetTextPos(int pos)
1977        {
1978            textPos = pos;
1979            ch = textPos < textLen ? text[textPos] : '\0';
1980        }
1981
1982        void NextChar()
1983        {
1984            if (textPos < textLen)
1985                textPos++;
1986            ch = textPos < textLen ? text[textPos] : '\0';
1987        }
1988
1989        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Legacy code.")]
1990        void NextToken()
1991        {
1992            while (Char.IsWhiteSpace(ch))
1993                NextChar();
1994            TokenId t;
1995            int tokenPos = textPos;
1996            switch (ch)
1997            {
1998                case '%':
1999                    NextChar();
2000                    t = TokenId.Percent;
2001                    break;
2002                case '&':
2003                    NextChar();
2004                    if (ch == '&')
2005                    {
2006                        NextChar();
2007                        t = TokenId.DoubleAmphersand;
2008                    }
2009                    else
2010                    {
2011                        t = TokenId.Amphersand;
2012                    }
2013                    break;
2014                case '(':
2015                    NextChar();
2016                    t = TokenId.OpenParen;
2017                    break;
2018                case ')':
2019                    NextChar();
2020                    t = TokenId.CloseParen;
2021                    break;
2022                case ',':
2023                    NextChar();
2024                    t = TokenId.Comma;
2025                    break;
2026                case '/':
2027                    NextChar();
2028                    t = TokenId.Dot;
2029                    break;
2030                case ':':
2031                    NextChar();
2032                    t = TokenId.Colon;
2033                    break;
2034                case '?':
2035                    NextChar();
2036                    t = TokenId.Question;
2037                    break;
2038                case '[':
2039                    NextChar();
2040                    t = TokenId.OpenBracket;
2041                    break;
2042                case ']':
2043                    NextChar();
2044                    t = TokenId.CloseBracket;
2045                    break;
2046                case '|':
2047                    NextChar();
2048                    if (ch == '|')
2049                    {
2050                        NextChar();
2051                        t = TokenId.DoubleBar;
2052                    }
2053                    else
2054                    {
2055                        t = TokenId.Bar;
2056                    }
2057                    break;
2058                case '"':
2059                case '\'':
2060                    char quote = ch;
2061                    do
2062                    {
2063                        NextChar();
2064                        while (textPos < textLen && ch != quote)
2065                        {
2066                            if (ch == '\\')
2067                            {
2068                                NextChar();
2069                            }
2070
2071                            NextChar();
2072                        }
2073
2074                        if (textPos == textLen)
2075                            throw ParseError(textPos, SR.UnterminatedStringLiteral);
2076                        NextChar();
2077                    } while (ch == quote);
2078                    t = TokenId.StringLiteral;
2079                    break;
2080                default:
2081                    if (IsIdentifierStart(ch) || ch == '@' || ch == '_')
2082                    {
2083                        do
2084                        {
2085                            NextChar();
2086                        } while (IsIdentifierPart(ch) || ch == '_');
2087                        t = TokenId.Identifier;
2088                        break;
2089                    }
2090                    if (Char.IsDigit(ch))
2091                    {
2092                        t = TokenId.IntegerLiteral;
2093                        do
2094                        {
2095                            NextChar();
2096                        } while (Char.IsDigit(ch));
2097                        if (ch == '.')
2098                        {
2099                            t = TokenId.RealLiteral;
2100                            NextChar();
2101                            ValidateDigit();
2102                            do
2103                            {
2104                                NextChar();
2105                            } while (Char.IsDigit(ch));
2106                        }
2107                        if (ch == 'E' || ch == 'e')
2108                        {
2109                            t = TokenId.RealLiteral;
2110                            NextChar();
2111                            if (ch == '+' || ch == '-')
2112                                NextChar();
2113                            ValidateDigit();
2114                            do
2115                            {
2116                                NextChar();
2117                            } while (Char.IsDigit(ch));
2118                        }
2119                        if (ch == 'F' || ch == 'f' || ch == 'M' || ch == 'm' || ch == 'D' || ch == 'd')
2120                        {
2121                            t = TokenId.RealLiteral;
2122                            NextChar();
2123                        }
2124                        break;
2125                    }
2126                    if (textPos == textLen)
2127                    {
2128                        t = TokenId.End;
2129                        break;
2130                    }
2131                    throw ParseError(textPos, SR.InvalidCharacter(ch));
2132            }
2133            token.id = t;
2134            token.text = text.Substring(tokenPos, textPos - tokenPos);
2135            token.pos = tokenPos;
2136
2137            token.id = ReclassifyToken(token);
2138        }
2139
2140        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", Justification = "Legacy code.")]
2141        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Legacy code.")]
2142        TokenId ReclassifyToken(Token token)
2143        {
2144            if (token.id == TokenId.Identifier)
2145            {
2146                if (token.text == "add")
2147                {
2148                    return TokenId.Plus;
2149                }
2150                else if (token.text == "and")
2151                {
2152                    return TokenId.DoubleAmphersand;
2153                }
2154                else if (token.text == "div")
2155                {
2156                    return TokenId.Slash;
2157                }
2158                else if (token.text == "sub")
2159                {
2160                    return TokenId.Minus;
2161                }
2162                else if (token.text == "mul")
2163                {
2164                    return TokenId.Asterisk;
2165                }
2166                else if (token.text == "mod")
2167                {
2168                    return TokenId.Percent;
2169                }
2170                else if (token.text == "ne")
2171                {
2172                    return TokenId.ExclamationEqual;
2173                }
2174                else if (token.text == "not")
2175                {
2176                    return TokenId.Exclamation;
2177                }
2178                else if (token.text == "le")
2179                {
2180                    return TokenId.LessThanEqual;
2181                }
2182                else if (token.text == "lt")
2183                {
2184                    return TokenId.LessThan;
2185                }
2186                else if (token.text == "eq")
2187                {
2188                    return TokenId.DoubleEqual;
2189                }
2190                else if (token.text == "eq")
2191                {
2192                    return TokenId.DoubleEqual;
2193                }
2194                else if (token.text == "ge")
2195                {
2196                    return TokenId.GreaterThanEqual;
2197                }
2198                else if (token.text == "gt")
2199                {
2200                    return TokenId.GreaterThan;
2201                }
2202            }
2203
2204            return token.id;
2205        }
2206
2207        static bool IsIdentifierStart(char ch)
2208        {
2209            const int mask =
2210                1 << (int)UnicodeCategory.UppercaseLetter |
2211                1 << (int)UnicodeCategory.LowercaseLetter |
2212                1 << (int)UnicodeCategory.TitlecaseLetter |
2213                1 << (int)UnicodeCategory.ModifierLetter |
2214                1 << (int)UnicodeCategory.OtherLetter |
2215                1 << (int)UnicodeCategory.LetterNumber;
2216            return (1 << (int)Char.GetUnicodeCategory(ch) & mask) != 0;
2217        }
2218
2219        static bool IsIdentifierPart(char ch)
2220        {
2221            const int mask =
2222                1 << (int)UnicodeCategory.UppercaseLetter |
2223                1 << (int)UnicodeCategory.LowercaseLetter |
2224                1 << (int)UnicodeCategory.TitlecaseLetter |
2225                1 << (int)UnicodeCategory.ModifierLetter |
2226                1 << (int)UnicodeCategory.OtherLetter |
2227                1 << (int)UnicodeCategory.LetterNumber |
2228                1 << (int)UnicodeCategory.DecimalDigitNumber |
2229                1 << (int)UnicodeCategory.ConnectorPunctuation |
2230                1 << (int)UnicodeCategory.NonSpacingMark |
2231                1 << (int)UnicodeCategory.SpacingCombiningMark |
2232                1 << (int)UnicodeCategory.Format;
2233            return (1 << (int)Char.GetUnicodeCategory(ch) & mask) != 0;
2234        }
2235
2236        bool TokenIdentifierIs(string id)
2237        {
2238            return token.id == TokenId.Identifier && String.Equals(id, token.text, StringComparison.OrdinalIgnoreCase);
2239        }
2240
2241        string GetIdentifier()
2242        {
2243            ValidateToken(TokenId.Identifier, SR.IdentifierExpected);
2244            string id = token.text;
2245            if (id.Length > 1 && id[0] == '@')
2246                id = id.Substring(1);
2247            return id;
2248        }
2249
2250        void ValidateDigit()
2251        {
2252            if (!Char.IsDigit(ch))
2253                throw ParseError(textPos, SR.DigitExpected);
2254        }
2255
2256        void ValidateToken(TokenId t, string errorMessage)
2257        {
2258            if (token.id != t)
2259                throw ParseError(errorMessage);
2260        }
2261
2262        void ValidateToken(TokenId t)
2263        {
2264            if (token.id != t)
2265                throw ParseError(SR.SyntaxError);
2266        }
2267
2268        Exception ParseError(string format, params object[] args)
2269        {
2270            return ParseError(token.pos, format, args);
2271        }
2272
2273        static Exception ParseError(int pos, string format, params object[] args)
2274        {
2275            return new ParseException(string.Format(CultureInfo.CurrentCulture, format, args), pos);
2276        }
2277
2278        static Dictionary<string, object> CreateKeywords()
2279        {
2280            Dictionary<string, object> d = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
2281            d.Add("true", trueLiteral);
2282            d.Add("false", falseLiteral);
2283            d.Add("null", nullLiteral);
2284            d.Add(keywordIt, keywordIt);
2285            d.Add(keywordIif, keywordIif);
2286            foreach (Type type in predefinedTypes)
2287                d.Add(type.Name, type);
2288            return d;
2289        }
2290    }
2291
2292}