context: //Copyright (C) Microsoft Corporation.  All rights reserved.
  
  using System;
  using System.Collections.Generic;
  using System.Text;
  using System.Linq.Expressions;
  using System.Reflection;
  using System.Reflection.Emit;
  using System.Threading;
  
  namespace System.Linq.Dynamic
  {
      public static class DynamicQueryable
      {
          public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values) {
              return (IQueryable<T>)Where((IQueryable)source, predicate, values);
          }
  
          public static IQueryable Where(this IQueryable source, string predicate, params object[] values) {
              if (source == null) throw new ArgumentNullException("source");
              if (predicate == null) throw new ArgumentNullException("predicate");
              LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values);
              return source.Provider.CreateQuery(
                  Expression.Call(
                      typeof(Queryable), "Where",
                      new Type[] { source.ElementType },
                      source.Expression, Expression.Quote(lambda)));
          }
  
          public static IQueryable Select(this IQueryable source, string selector, params object[] values) {
              if (source == null) throw new ArgumentNullException("source");
              if (selector == null) throw new ArgumentNullException("selector");
              LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, null, selector, values);
              return source.Provider.CreateQuery(
                  Expression.Call(
                      typeof(Queryable), "Select",
                      new Type[] { source.ElementType, lambda.Body.Type },
                      source.Expression, Expression.Quote(lambda)));
          }
  
          public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) {
              return (IQueryable<T>)OrderBy((IQueryable)source, ordering, values);
          }
  
          public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) {
              if (source == null) throw new ArgumentNullException("source");
              if (ordering == null) throw new ArgumentNullException("ordering");
              ParameterExpression[] parameters = new ParameterExpression[] {
                  Expression.Parameter(source.ElementType, "") };
              ExpressionParser parser = new ExpressionParser(parameters, ordering, values);
              IEnumerable<DynamicOrdering> orderings = parser.ParseOrdering();
              Expression queryExpr = source.Expression;
              string methodAsc = "OrderBy";
              string methodDesc = "OrderByDescending";
              foreach (DynamicOrdering o in orderings) {
                  queryExpr = Expression.Call(
                      typeof(Queryable), o.Ascending ? methodAsc : methodDesc,
                      new Type[] { source.ElementType, o.Selector.Type },
                      queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters)));
                  methodAsc = "ThenBy";
                  methodDesc = "ThenByDescending";
              }
              return source.Provider.CreateQuery(queryExpr);
          }
  
          public static IQueryable Take(this IQueryable source, int count) {
              if (source == null) throw new ArgumentNullException("source");
              return source.Provider.CreateQuery(
                  Expression.Call(
                      typeof(Queryable), "Take",
                      new Type[] { source.ElementType },
                      source.Expression, Expression.Constant(count)));
          }
  
          public static IQueryable Skip(this IQueryable source, int count) {
              if (source == null) throw new ArgumentNullException("source");
              return source.Provider.CreateQuery(
                  Expression.Call(
                      typeof(Queryable), "Skip",
                      new Type[] { source.ElementType },
                      source.Expression, Expression.Constant(count)));
          }
  
          public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values) {
              if (source == null) throw new ArgumentNullException("source");
              if (keySelector == null) throw new ArgumentNullException("keySelector");
              if (elementSelector == null) throw new ArgumentNullException("elementSelector");
              LambdaExpression keyLambda = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, values);
              LambdaExpression elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, values);
              return source.Provider.CreateQuery(
                  Expression.Call(
                      typeof(Queryable), "GroupBy",
                      new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type },
                      source.Expression, Expression.Quote(keyLambda), Expression.Quote(elementLambda)));
          }
  
          public static bool Any(this IQueryable source) {
              if (source == null) throw new ArgumentNullException("source");
              return (bool)source.Provider.Execute(
                  Expression.Call(
                      typeof(Queryable), "Any",
                      new Type[] { source.ElementType }, source.Expression));
          }
  
          public static int Count(this IQueryable source) {
              if (source == null) throw new ArgumentNullException("source");
              return (int)source.Provider.Execute(
                  Expression.Call(
                      typeof(Queryable), "Count",
                      new Type[] { source.ElementType }, source.Expression));
          }
      }
  
      public abstract class DynamicClass
      {
          public override string ToString() {
              PropertyInfo[] props = this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
              StringBuilder sb = new StringBuilder();
              sb.Append("{");
              for (int i = 0; i < props.Length; i++) {
                  if (i > 0) sb.Append(", ");
                  sb.Append(props[i].Name);
                  sb.Append("=");
                  sb.Append(props[i].GetValue(this, null));
              }
              sb.Append("}");
              return sb.ToString();
          }
      }
  
      public class DynamicProperty
      {
          string name;
          Type type;
  
          public DynamicProperty(string name, Type type) {
              if (name == null) throw new ArgumentNullException("name");
              if (type == null) throw new ArgumentNullException("type");
              this.name = name;
              this.type = type;
          }
  
          public string Name {
              get { return name; }
          }
  
          public Type Type {
              get { return type; }
          }
      }
  
      public static class DynamicExpression
      {
          public static Expression Parse(Type resultType, string expression, params object[] values) {
              ExpressionParser parser = new ExpressionParser(null, expression, values);
              return parser.Parse(resultType);
          }
  
          public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, params object[] values) {
              return ParseLambda(new ParameterExpression[] { Expression.Parameter(itType, "") }, resultType, expression, values);
          }
  
          public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, params object[] values) {
              ExpressionParser parser = new ExpressionParser(parameters, expression, values);
              return Expression.Lambda(parser.Parse(resultType), parameters);
          }
  
          public static Expression<Func<T, S>> ParseLambda<T, S>(string expression, params object[] values) {
              return (Expression<Func<T, S>>)ParseLambda(typeof(T), typeof(S), expression, values);
          }
  
          public static Type CreateClass(params DynamicProperty[] properties) {
              return ClassFactory.Instance.GetDynamicClass(properties);
          }
  
          public static Type CreateClass(IEnumerable<DynamicProperty> properties) {
              return ClassFactory.Instance.GetDynamicClass(properties);
          }
      }
  
      internal class DynamicOrdering
      {
          public Expression Selector;
          public bool Ascending;
      }
  
      internal class Signature : IEquatable<Signature>
      {
          public DynamicProperty[] properties;
          public int hashCode;
  
          public Signature(IEnumerable<DynamicProperty> properties) {
              this.properties = properties.ToArray();
              hashCode = 0;
              foreach (DynamicProperty p in properties) {
                  hashCode ^= p.Name.GetHashCode() ^ p.Type.GetHashCode();
              }
          }
  
          public override int GetHashCode() {
              return hashCode;
          }
  
          public override bool Equals(object obj) {
              return obj is Signature ? Equals((Signature)obj) : false;
          }
  
          public bool Equals(Signature other) {
              if (properties.Length != other.properties.Length) return false;
              for (int i = 0; i < properties.Length; i++) {
                  if (properties[i].Name != other.properties[i].Name ||
                      properties[i].Type != other.properties[i].Type) return false;
              }
              return true;
          }
      }
  
      internal class ClassFactory
      {
          public static readonly ClassFactory Instance = new ClassFactory();
  
          static ClassFactory() { }  // Trigger lazy initialization of static fields
  
          ModuleBuilder module;
          Dictionary<Signature, Type> classes;
          int classCount;
          ReaderWriterLock rwLock;
  
          private ClassFactory() {
              AssemblyName name = new AssemblyName("DynamicClasses");
              AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
  #if ENABLE_LINQ_PARTIAL_TRUST
              new ReflectionPermission(PermissionState.Unrestricted).Assert();
  #endif
              try {
                  module = assembly.DefineDynamicModule("Module");
              }
              finally {
  #if ENABLE_LINQ_PARTIAL_TRUST
                  PermissionSet.RevertAssert();
  #endif
              }
              classes = new Dictionary<Signature, Type>();
              rwLock = new ReaderWriterLock();
          }
  
          public Type GetDynamicClass(IEnumerable<DynamicProperty> properties) {
              rwLock.AcquireReaderLock(Timeout.Infinite);
              try {
                  Signature signature = new Signature(properties);
                  Type type;
                  if (!classes.TryGetValue(signature, out type)) {
                      type = CreateDynamicClass(signature.properties);
                      classes.Add(signature, type);
                  }
                  return type;
              }
              finally {
                  rwLock.ReleaseReaderLock();
              }
          }
  
          Type CreateDynamicClass(DynamicProperty[] properties) {
              LockCookie cookie = rwLock.UpgradeToWriterLock(Timeout.Infinite);
              try {
                  string typeName = "DynamicClass" + (classCount + 1);
  #if ENABLE_LINQ_PARTIAL_TRUST
                  new ReflectionPermission(PermissionState.Unrestricted).Assert();
  #endif
                  try {
                      TypeBuilder tb = this.module.DefineType(typeName, TypeAttributes.Class |
                          TypeAttributes.Public, typeof(DynamicClass));
                      FieldInfo[] fields = GenerateProperties(tb, properties);
                      GenerateEquals(tb, fields);
                      GenerateGetHashCode(tb, fields);
                      Type result = tb.CreateType();
                      classCount++;
                      return result;
                  }
                  finally {
  #if ENABLE_LINQ_PARTIAL_TRUST
                      PermissionSet.RevertAssert();
  #endif
                  }
              }
              finally {
                  rwLock.DowngradeFromWriterLock(ref cookie);
              }
          }
  
          FieldInfo[] GenerateProperties(TypeBuilder tb, DynamicProperty[] properties) {
              FieldInfo[] fields = new FieldBuilder[properties.Length];
              for (int i = 0; i < properties.Length; i++) {
                  DynamicProperty dp = properties[i];
                  FieldBuilder fb = tb.DefineField("_" + dp.Name, dp.Type, FieldAttributes.Private);
                  PropertyBuilder pb = tb.DefineProperty(dp.Name, PropertyAttributes.HasDefault, dp.Type, null);
                  MethodBuilder mbGet = tb.DefineMethod("get_" + dp.Name,
                      MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                      dp.Type, Type.EmptyTypes);
                  ILGenerator genGet = mbGet.GetILGenerator();
                  genGet.Emit(OpCodes.Ldarg_0);
                  genGet.Emit(OpCodes.Ldfld, fb);
                  genGet.Emit(OpCodes.Ret);
                  MethodBuilder mbSet = tb.DefineMethod("set_" + dp.Name,
                      MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                      null, new Type[] { dp.Type });
                  ILGenerator genSet = mbSet.GetILGenerator();
                  genSet.Emit(OpCodes.Ldarg_0);
                  genSet.Emit(OpCodes.Ldarg_1);
                  genSet.Emit(OpCodes.Stfld, fb);
                  genSet.Emit(OpCodes.Ret);
                  pb.SetGetMethod(mbGet);
                  pb.SetSetMethod(mbSet);
                  fields[i] = fb;
              }
              return fields;
          }
  
          void GenerateEquals(TypeBuilder tb, FieldInfo[] fields) {
              MethodBuilder mb = tb.DefineMethod("Equals",
                  MethodAttributes.Public | MethodAttributes.ReuseSlot |
                  MethodAttributes.Virtual | MethodAttributes.HideBySig,
                  typeof(bool), new Type[] { typeof(object) });
              ILGenerator gen = mb.GetILGenerator();
              LocalBuilder other = gen.DeclareLocal(tb);
              Label next = gen.DefineLabel();
              gen.Emit(OpCodes.Ldarg_1);
              gen.Emit(OpCodes.Isinst, tb);
              gen.Emit(OpCodes.Stloc, other);
              gen.Emit(OpCodes.Ldloc, other);
              gen.Emit(OpCodes.Brtrue_S, next);
              gen.Emit(OpCodes.Ldc_I4_0);
              gen.Emit(OpCodes.Ret);
              gen.MarkLabel(next);
              foreach (FieldInfo field in fields) {
                  Type ft = field.FieldType;
                  Type ct = typeof(EqualityComparer<>).MakeGenericType(ft);
                  next = gen.DefineLabel();
                  gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null);
                  gen.Emit(OpCodes.Ldarg_0);
                  gen.Emit(OpCodes.Ldfld, field);
                  gen.Emit(OpCodes.Ldloc, other);
                  gen.Emit(OpCodes.Ldfld, field);
                  gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("Equals", new Type[] { ft, ft }), null);
                  gen.Emit(OpCodes.Brtrue_S, next);
                  gen.Emit(OpCodes.Ldc_I4_0);
                  gen.Emit(OpCodes.Ret);
                  gen.MarkLabel(next);
              }
              gen.Emit(OpCodes.Ldc_I4_1);
              gen.Emit(OpCodes.Ret);
          }
  
          void GenerateGetHashCode(TypeBuilder tb, FieldInfo[] fields) {
              MethodBuilder mb = tb.DefineMethod("GetHashCode",
                  MethodAttributes.Public | MethodAttributes.ReuseSlot |
                  MethodAttributes.Virtual | MethodAttributes.HideBySig,
                  typeof(int), Type.EmptyTypes);
              ILGenerator gen = mb.GetILGenerator();
              gen.Emit(OpCodes.Ldc_I4_0);
              foreach (FieldInfo field in fields) {
                  Type ft = field.FieldType;
                  Type ct = typeof(EqualityComparer<>).MakeGenericType(ft);
                  gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null);
                  gen.Emit(OpCodes.Ldarg_0);
                  gen.Emit(OpCodes.Ldfld, field);
                  gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("GetHashCode", new Type[] { ft }), null);
                  gen.Emit(OpCodes.Xor);
              }
              gen.Emit(OpCodes.Ret);
          }
      }
  
      public sealed class ParseException : Exception
      {
          int position;
  
          public ParseException(string message, int position)
              : base(message) {
              this.position = position;
          }
  
          public int Position {
              get { return position; }
          }
  
          public override string ToString() {
              return string.Format(Res.ParseExceptionFormat, Message, position);
          }
      }
  
      internal class ExpressionParser
      {
          struct Token
          {
              public TokenId id;
              public string text;
              public int pos;
          }
  
          enum TokenId
          {
              Unknown,
              End,
              Identifier,
              StringLiteral,
              IntegerLiteral,
              RealLiteral,
              Exclamation,
              Percent,
              Amphersand,
              OpenParen,
              CloseParen,
              Asterisk,
              Plus,
              Comma,
              Minus,
              Dot,
              Slash,
              Colon,
              LessThan,
              Equal,
              GreaterThan,
              Question,
              OpenBracket,
              CloseBracket,
              Bar,
              ExclamationEqual,
              DoubleAmphersand,
              LessThanEqual,
              LessGreater,
              DoubleEqual,
              GreaterThanEqual,
              DoubleBar
          }
  
          interface ILogicalSignatures
          {
              void F(bool x, bool y);
              void F(bool? x, bool? y);
          }
  
          interface IArithmeticSignatures
          {
              void F(int x, int y);
              void F(uint x, uint y);
              void F(long x, long y);
              void F(ulong x, ulong y);
              void F(float x, float y);
              void F(double x, double y);
              void F(decimal x, decimal y);
              void F(int? x, int? y);
              void F(uint? x, uint? y);
              void F(long? x, long? y);
              void F(ulong? x, ulong? y);
              void F(float? x, float? y);
              void F(double? x, double? y);
              void F(decimal? x, decimal? y);
          }
  
          interface IRelationalSignatures : IArithmeticSignatures
          {
              void F(string x, string y);
              void F(char x, char y);
              void F(DateTime x, DateTime y);
              void F(TimeSpan x, TimeSpan y);
              void F(char? x, char? y);
              void F(DateTime? x, DateTime? y);
              void F(TimeSpan? x, TimeSpan? y);
          }
  
          interface IEqualitySignatures : IRelationalSignatures
          {
              void F(bool x, bool y);
              void F(bool? x, bool? y);
          }
  
          interface IAddSignatures : IArithmeticSignatures
          {
              void F(DateTime x, TimeSpan y);
              void F(TimeSpan x, TimeSpan y);
              void F(DateTime? x, TimeSpan? y);
              void F(TimeSpan? x, TimeSpan? y);
          }
  
          interface ISubtractSignatures : IAddSignatures
          {
              void F(DateTime x, DateTime y);
              void F(DateTime? x, DateTime? y);
          }
  
          interface INegationSignatures
          {
              void F(int x);
              void F(long x);
              void F(float x);
              void F(double x);
              void F(decimal x);
              void F(int? x);
              void F(long? x);
              void F(float? x);
              void F(double? x);
              void F(decimal? x);
          }
  
          interface INotSignatures
          {
              void F(bool x);
              void F(bool? x);
          }
  
          interface IEnumerableSignatures
          {
              void Where(bool predicate);
              void Any();
              void Any(bool predicate);
              void All(bool predicate);
              void Count();
              void Count(bool predicate);
              void Min(object selector);
              void Max(object selector);
              void Sum(int selector);
              void Sum(int? selector);
              void Sum(long selector);
              void Sum(long? selector);
              void Sum(float selector);
              void Sum(float? selector);
              void Sum(double selector);
              void Sum(double? selector);
              void Sum(decimal selector);
              void Sum(decimal? selector);
              void Average(int selector);
              void Average(int? selector);
              void Average(long selector);
              void Average(long? selector);
              void Average(float selector);
              void Average(float? selector);
              void Average(double selector);
              void Average(double? selector);
              void Average(decimal selector);
              void Average(decimal? selector);
          }
  
          static readonly Type[] predefinedTypes = {
              typeof(Object),
              typeof(Boolean),
              typeof(Char),
              typeof(String),
              typeof(SByte),
              typeof(Byte),
              typeof(Int16),
              typeof(UInt16),
              typeof(Int32),
              typeof(UInt32),
              typeof(Int64),
              typeof(UInt64),
              typeof(Single),
              typeof(Double),
              typeof(Decimal),
              typeof(DateTime),
              typeof(TimeSpan),
              typeof(Guid),
              typeof(Math),
              typeof(Convert)
          };
  
          static readonly Expression trueLiteral = Expression.Constant(true);
          static readonly Expression falseLiteral = Expression.Constant(false);
          static readonly Expression nullLiteral = Expression.Constant(null);
  
          static readonly string keywordIt = "it";
          static readonly string keywordIif = "iif";
          static readonly string keywordNew = "new";
  
          static Dictionary<string, object> keywords;
  
          Dictionary<string, object> symbols;
          IDictionary<string, object> externals;
          Dictionary<Expression, string> literals;
          ParameterExpression it;
          string text;
          int textPos;
          int textLen;
          char ch;
          Token token;
  
          public ExpressionParser(ParameterExpression[] parameters, string expression, object[] values) {
              if (expression == null) throw new ArgumentNullException("expression");
              if (keywords == null) keywords = CreateKeywords();
              symbols = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
              literals = new Dictionary<Expression, string>();
              if (parameters != null) ProcessParameters(parameters);
              if (values != null) ProcessValues(values);
              text = expression;
              textLen = text.Length;
              SetTextPos(0);
              NextToken();
          }
  
          void ProcessParameters(ParameterExpression[] parameters) {
              foreach (ParameterExpression pe in parameters)
                  if (!String.IsNullOrEmpty(pe.Name))
                      AddSymbol(pe.Name, pe);
              if (parameters.Length == 1 && String.IsNullOrEmpty(parameters[0].Name))
                  it = parameters[0];
          }
  
          void ProcessValues(object[] values) {
              for (int i = 0; i < values.Length; i++) {
                  object value = values[i];
                  if (i == values.Length - 1 && value is IDictionary<string, object>) {
                      externals = (IDictionary<string, object>)value;
                  }
                  else {
                      AddSymbol("@" + i.ToString(System.Globalization.CultureInfo.InvariantCulture), value);
                  }
              }
          }
  
          void AddSymbol(string name, object value) {
              if (symbols.ContainsKey(name))
                  throw ParseError(Res.DuplicateIdentifier, name);
              symbols.Add(name, value);
          }
  
          public Expression Parse(Type resultType) {
              int exprPos = token.pos;
              Expression expr = ParseExpression();
              if (resultType != null)
                  if ((expr = PromoteExpression(expr, resultType, true)) == null)
                      throw ParseError(exprPos, Res.ExpressionTypeMismatch, GetTypeName(resultType));
              ValidateToken(TokenId.End, Res.SyntaxError);
              return expr;
          }
  
  #pragma warning disable 0219
          public IEnumerable<DynamicOrdering> ParseOrdering() {
              List<DynamicOrdering> orderings = new List<DynamicOrdering>();
              while (true) {
                  Expression expr = ParseExpression();
                  bool ascending = true;
                  if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending")) {
                      NextToken();
                  }
                  else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending")) {
                      NextToken();
                      ascending = false;
                  }
                  orderings.Add(new DynamicOrdering { Selector = expr, Ascending = ascending });
                  if (token.id != TokenId.Comma) break;
                  NextToken();
              }
              ValidateToken(TokenId.End, Res.SyntaxError);
              return orderings;
          }
  #pragma warning restore 0219
  
          // ?: operator
          Expression ParseExpression() {
              int errorPos = token.pos;
              Expression expr = ParseLogicalOr();
              if (token.id == TokenId.Question) {
                  NextToken();
                  Expression expr1 = ParseExpression();
                  ValidateToken(TokenId.Colon, Res.ColonExpected);
                  NextToken();
                  Expression expr2 = ParseExpression();
                  expr = GenerateConditional(expr, expr1, expr2, errorPos);
              }
              return expr;
          }
  
          // ||, or operator
          Expression ParseLogicalOr() {
              Expression left = ParseLogicalAnd();
              while (token.id == TokenId.DoubleBar || TokenIdentifierIs("or")) {
                  Token op = token;
                  NextToken();
                  Expression right = ParseLogicalAnd();
                  CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos);
                  left = Expression.OrElse(left, right);
              }
              return left;
          }
  
          // &&, and operator
          Expression ParseLogicalAnd() {
              Expression left = ParseComparison();
              while (token.id == TokenId.DoubleAmphersand || TokenIdentifierIs("and")) {
                  Token op = token;
                  NextToken();
                  Expression right = ParseComparison();
                  CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos);
                  left = Expression.AndAlso(left, right);
              }
              return left;
          }
  
          // =, ==, !=, <>, >, >=, <, <= operators
          Expression ParseComparison() {
              Expression left = ParseAdditive();
              while (token.id == TokenId.Equal || token.id == TokenId.DoubleEqual ||
                  token.id == TokenId.ExclamationEqual || token.id == TokenId.LessGreater ||
                  token.id == TokenId.GreaterThan || token.id == TokenId.GreaterThanEqual ||
                  token.id == TokenId.LessThan || token.id == TokenId.LessThanEqual) {
                  Token op = token;
                  NextToken();
                  Expression right = ParseAdditive();
                  bool isEquality = op.id == TokenId.Equal || op.id == TokenId.DoubleEqual ||
                      op.id == TokenId.ExclamationEqual || op.id == TokenId.LessGreater;
                  if (isEquality && !left.Type.IsValueType && !right.Type.IsValueType) {
                      if (left.Type != right.Type) {
                          if (left.Type.IsAssignableFrom(right.Type)) {
                              right = Expression.Convert(right, left.Type);
                          }
                          else if (right.Type.IsAssignableFrom(left.Type)) {
                              left = Expression.Convert(left, right.Type);
                          }
                          else {
                              throw IncompatibleOperandsError(op.text, left, right, op.pos);
                          }
                      }
                  }
                  else if (IsEnumType(left.Type) || IsEnumType(right.Type)) {
                      if (left.Type != right.Type) {
                          Expression e;
                          if ((e = PromoteExpression(right, left.Type, true)) != null) {
                              right = e;
                          }
                          else if ((e = PromoteExpression(left, right.Type, true)) != null) {
                              left = e;
                          }
                          else {
                              throw IncompatibleOperandsError(op.text, left, right, op.pos);
                          }
                      }
                  }
                  else {
                      CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures),
                          op.text, ref left, ref right, op.pos);
                  }
                  switch (op.id) {
                      case TokenId.Equal:
                      case TokenId.DoubleEqual:
                          left = GenerateEqual(left, right);
                          break;
                      case TokenId.ExclamationEqual:
                      case TokenId.LessGreater:
                          left = GenerateNotEqual(left, right);
                          break;
                      case TokenId.GreaterThan:
  
gt:                         left = GenerateGreaterThan(left, right);