Files
Umbraco-CMS/umbraco.MacroEngines.Juno/RazorDynamicNode/DynamicQueryable.cs
agrath@gmail.com cbe94ae6aa Refactored ExtensionMethod search code from DynamicNodeList (searches for extension methods e.g. .Random()) to tidy it up
Moved Extension Method searching code to seperate static class
Moved all the defined extension methods for DynamicNode into the same ExtensionMethods.cs file
Changed the way that .ContainsAny within a where is evaluated to use the ExtensionMethodFinder class - should allow extra search methods to be defined
2011-02-26 16:15:45 -13:00

2800 lines
115 KiB
C#

//Copyright (C) Microsoft Corporation. All rights reserved.
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using System.Dynamic;
using umbraco.MacroEngines;
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);
if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicNode))
{
//source list is DynamicNode and the lambda returns a Func<object>
IQueryable<DynamicNode> typedSource = source as IQueryable<DynamicNode>;
Func<DynamicNode, object> func = (Func<DynamicNode, object>)lambda.Compile();
return typedSource.Where(delegate(DynamicNode node)
{
object value = -1;
//value = func(node);
//I can't figure out why this is double func<>'d
try
{
var firstFuncResult = func(node);
if (firstFuncResult is Func<DynamicNode, object>)
{
value = (firstFuncResult as Func<DynamicNode, object>)(node);
}
if (firstFuncResult is Func<DynamicNode, bool>)
{
value = (firstFuncResult as Func<DynamicNode, bool>)(node);
}
if (firstFuncResult is bool)
{
return (bool)firstFuncResult;
}
if (value is bool)
{
return (bool)value;
}
return false;
}
catch (Exception)
{
return false;
}
}).AsQueryable();
}
else
{
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");
IQueryable<DynamicNode> typedSource = source as IQueryable<DynamicNode>;
if (!ordering.Contains(","))
{
bool descending = false;
if (ordering.IndexOf(" descending", StringComparison.CurrentCultureIgnoreCase) >= 0)
{
ordering = ordering.Replace(" descending", "");
descending = true;
}
if (ordering.IndexOf(" desc", StringComparison.CurrentCultureIgnoreCase) >= 0)
{
ordering = ordering.Replace(" desc", "");
descending = true;
}
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), ordering, values);
if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicNode))
{
//source list is DynamicNode and the lambda returns a Func<object>
Func<DynamicNode, object> func = (Func<DynamicNode, object>)lambda.Compile();
if (!descending)
{
return typedSource.OrderBy(delegate(DynamicNode node)
{
object value = -1;
var firstFuncResult = func(node);
if (firstFuncResult is Func<DynamicNode, object>)
{
value = (firstFuncResult as Func<DynamicNode, object>)(node);
}
return value;
}).AsQueryable();
}
else
{
return typedSource.OrderByDescending(delegate(DynamicNode node)
{
object value = -1;
var firstFuncResult = func(node);
if (firstFuncResult is Func<DynamicNode, object>)
{
value = (firstFuncResult as Func<DynamicNode, object>)(node);
}
return value;
}).AsQueryable();
}
}
}
bool isDynamicNodeList = false;
if (typedSource != null)
{
isDynamicNodeList = true;
}
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)
{
if (!isDynamicNodeList)
{
queryExpr = Expression.Call(
typeof(Queryable), o.Ascending ? methodAsc : methodDesc,
new Type[] { source.ElementType, o.Selector.Type },
queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters)));
}
else
{
//reroute each stacked Expression.Call into our own methods that know how to deal
//with DynamicNode
queryExpr = Expression.Call(
typeof(DynamicNodeListOrdering),
o.Ascending ? methodAsc : methodDesc,
null,
queryExpr,
Expression.Quote(Expression.Lambda(o.Selector, parameters))
);
}
methodAsc = "ThenBy";
methodDesc = "ThenByDescending";
}
if (isDynamicNodeList)
{
return typedSource.Provider.CreateQuery(queryExpr);
}
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 = HandleDynamicNodeLambdas(ExpressionType.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 = HandleDynamicNodeLambdas(ExpressionType.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 if (left is LambdaExpression || right is LambdaExpression)
{
//do nothing here (but further down we'll handle the lambdaexpression)
}
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 = HandleDynamicNodeLambdas(ExpressionType.Equal, left, right);
break;
case TokenId.ExclamationEqual:
case TokenId.LessGreater:
left = HandleDynamicNodeLambdas(ExpressionType.NotEqual, left, right);
break;
case TokenId.GreaterThan:
left = HandleDynamicNodeLambdas(ExpressionType.GreaterThan, left, right);
break;
case TokenId.GreaterThanEqual:
left = HandleDynamicNodeLambdas(ExpressionType.GreaterThanOrEqual, left, right);
break;
case TokenId.LessThan:
left = HandleDynamicNodeLambdas(ExpressionType.LessThan, left, right);
break;
case TokenId.LessThanEqual:
left = HandleDynamicNodeLambdas(ExpressionType.LessThanOrEqual, left, right);
break;
}
}
return left;
}
// +, -, & operators
Expression ParseAdditive()
{
Expression left = ParseMultiplicative();
while (token.id == TokenId.Plus || token.id == TokenId.Minus ||
token.id == TokenId.Amphersand)
{
Token op = token;
NextToken();
Expression right = ParseMultiplicative();
switch (op.id)
{
case TokenId.Plus:
if (left.Type == typeof(string) || right.Type == typeof(string))
goto case TokenId.Amphersand;
CheckAndPromoteOperands(typeof(IAddSignatures), op.text, ref left, ref right, op.pos);
left = GenerateAdd(left, right);
break;
case TokenId.Minus:
CheckAndPromoteOperands(typeof(ISubtractSignatures), op.text, ref left, ref right, op.pos);
left = GenerateSubtract(left, right);
break;
case TokenId.Amphersand:
left = GenerateStringConcat(left, right);
break;
}
}
return left;
}
// *, /, %, mod operators
Expression ParseMultiplicative()
{
Expression left = ParseUnary();
while (token.id == TokenId.Asterisk || token.id == TokenId.Slash ||
token.id == TokenId.Percent || TokenIdentifierIs("mod"))
{
Token op = token;
NextToken();
Expression right = ParseUnary();
CheckAndPromoteOperands(typeof(IArithmeticSignatures), op.text, ref left, ref right, op.pos);
switch (op.id)
{
case TokenId.Asterisk:
left = Expression.Multiply(left, right);
break;
case TokenId.Slash:
left = Expression.Divide(left, right);
break;
case TokenId.Percent:
case TokenId.Identifier:
left = HandleDynamicNodeLambdas(ExpressionType.Modulo, left, right);
break;
}
}
return left;
}
// -, !, not unary operators
Expression ParseUnary()
{
if (token.id == TokenId.Minus || token.id == TokenId.Exclamation ||
TokenIdentifierIs("not"))
{
Token op = token;
NextToken();
if (op.id == TokenId.Minus && (token.id == TokenId.IntegerLiteral ||
token.id == TokenId.RealLiteral))
{
token.text = "-" + token.text;
token.pos = op.pos;
return ParsePrimary();
}
Expression expr = ParseUnary();
if (op.id == TokenId.Minus)
{
CheckAndPromoteOperand(typeof(INegationSignatures), op.text, ref expr, op.pos);
expr = Expression.Negate(expr);
}
else
{
CheckAndPromoteOperand(typeof(INotSignatures), op.text, ref expr, op.pos);
expr = Expression.Not(expr);
}
return expr;
}
return ParsePrimary();
}
Expression ParsePrimary()
{
Expression expr = ParsePrimaryStart();
while (true)
{
if (token.id == TokenId.Dot)
{
NextToken();
expr = ParseMemberAccess(null, expr);
}
else if (token.id == TokenId.OpenBracket)
{
expr = ParseElementAccess(expr);
}
else
{
break;
}
}
return expr;
}
Expression ParsePrimaryStart()
{
switch (token.id)
{
case TokenId.Identifier:
return ParseIdentifier();
case TokenId.StringLiteral:
return ParseStringLiteral();
case TokenId.IntegerLiteral:
return ParseIntegerLiteral();
case TokenId.RealLiteral:
return ParseRealLiteral();
case TokenId.OpenParen:
return ParseParenExpression();
default:
throw ParseError(Res.ExpressionExpected);
}
}
Expression ParseStringLiteral()
{
ValidateToken(TokenId.StringLiteral);
char quote = token.text[0];
string s = token.text.Substring(1, token.text.Length - 2);
int start = 0;
while (true)
{
int i = s.IndexOf(quote, start);
if (i < 0) break;
s = s.Remove(i, 1);
start = i + 1;
}
if (quote == '\'')
{
if (s.Length != 1)
throw ParseError(Res.InvalidCharacterLiteral);
NextToken();
return CreateLiteral(s[0], s);
}
NextToken();
return CreateLiteral(s, s);
}
Expression ParseIntegerLiteral()
{
ValidateToken(TokenId.IntegerLiteral);
string text = token.text;
if (text[0] != '-')
{
ulong value;
if (!UInt64.TryParse(text, out value))
throw ParseError(Res.InvalidIntegerLiteral, text);
NextToken();
if (value <= (ulong)Int32.MaxValue) return CreateLiteral((int)value, text);
if (value <= (ulong)UInt32.MaxValue) return CreateLiteral((uint)value, text);
if (value <= (ulong)Int64.MaxValue) return CreateLiteral((long)value, text);
return CreateLiteral(value, text);
}
else
{
long value;
if (!Int64.TryParse(text, out value))
throw ParseError(Res.InvalidIntegerLiteral, text);
NextToken();
if (value >= Int32.MinValue && value <= Int32.MaxValue)
return CreateLiteral((int)value, text);
return CreateLiteral(value, text);
}
}
Expression ParseRealLiteral()
{
ValidateToken(TokenId.RealLiteral);
string text = token.text;
object value = null;
char last = text[text.Length - 1];
if (last == 'F' || last == 'f')
{
float f;
if (Single.TryParse(text.Substring(0, text.Length - 1), out f)) value = f;
}
else
{
double d;
if (Double.TryParse(text, out d)) value = d;
}
if (value == null) throw ParseError(Res.InvalidRealLiteral, text);
NextToken();
return CreateLiteral(value, text);
}
Expression CreateLiteral(object value, string text)
{
ConstantExpression expr = Expression.Constant(value);
literals.Add(expr, text);
return expr;
}
Expression ParseParenExpression()
{
ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
NextToken();
Expression e = ParseExpression();
ValidateToken(TokenId.CloseParen, Res.CloseParenOrOperatorExpected);
NextToken();
return e;
}
Expression ParseIdentifier()
{
ValidateToken(TokenId.Identifier);
object value;
if (keywords.TryGetValue(token.text, out value))
{
if (value is Type) return ParseTypeAccess((Type)value);
if (value == (object)keywordIt) return ParseIt();
if (value == (object)keywordIif) return ParseIif();
if (value == (object)keywordNew) return ParseNew();
NextToken();
return (Expression)value;
}
if (symbols.TryGetValue(token.text, out value) ||
externals != null && externals.TryGetValue(token.text, out value))
{
Expression expr = value as Expression;
if (expr == null)
{
expr = Expression.Constant(value);
}
else
{
LambdaExpression lambda = expr as LambdaExpression;
if (lambda != null) return ParseLambdaInvocation(lambda);
}
NextToken();
return expr;
}
if (it != null) return ParseMemberAccess(null, it);
throw ParseError(Res.UnknownIdentifier, token.text);
}
Expression ParseIt()
{
if (it == null)
throw ParseError(Res.NoItInScope);
NextToken();
return it;
}
Expression ParseIif()
{
int errorPos = token.pos;
NextToken();
Expression[] args = ParseArgumentList();
if (args.Length != 3)
throw ParseError(errorPos, Res.IifRequiresThreeArgs);
return GenerateConditional(args[0], args[1], args[2], errorPos);
}
Expression GenerateConditional(Expression test, Expression expr1, Expression expr2, int errorPos)
{
if (test.Type != typeof(bool))
throw ParseError(errorPos, Res.FirstExprMustBeBool);
if (expr1.Type != expr2.Type)
{
Expression expr1as2 = expr2 != nullLiteral ? PromoteExpression(expr1, expr2.Type, true) : null;
Expression expr2as1 = expr1 != nullLiteral ? PromoteExpression(expr2, expr1.Type, true) : null;
if (expr1as2 != null && expr2as1 == null)
{
expr1 = expr1as2;
}
else if (expr2as1 != null && expr1as2 == null)
{
expr2 = expr2as1;
}
else
{
string type1 = expr1 != nullLiteral ? expr1.Type.Name : "null";
string type2 = expr2 != nullLiteral ? expr2.Type.Name : "null";
if (expr1as2 != null && expr2as1 != null)
throw ParseError(errorPos, Res.BothTypesConvertToOther, type1, type2);
throw ParseError(errorPos, Res.NeitherTypeConvertsToOther, type1, type2);
}
}
return Expression.Condition(test, expr1, expr2);
}
Expression ParseNew()
{
NextToken();
ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
NextToken();
List<DynamicProperty> properties = new List<DynamicProperty>();
List<Expression> expressions = new List<Expression>();
while (true)
{
int exprPos = token.pos;
Expression expr = ParseExpression();
string propName;
if (TokenIdentifierIs("as"))
{
NextToken();
propName = GetIdentifier();
NextToken();
}
else
{
MemberExpression me = expr as MemberExpression;
if (me == null) throw ParseError(exprPos, Res.MissingAsClause);
propName = me.Member.Name;
}
expressions.Add(expr);
properties.Add(new DynamicProperty(propName, expr.Type));
if (token.id != TokenId.Comma) break;
NextToken();
}
ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);
NextToken();
Type type = DynamicExpression.CreateClass(properties);
MemberBinding[] bindings = new MemberBinding[properties.Count];
for (int i = 0; i < bindings.Length; i++)
bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);
return Expression.MemberInit(Expression.New(type), bindings);
}
Expression ParseLambdaInvocation(LambdaExpression lambda)
{
int errorPos = token.pos;
NextToken();
Expression[] args = ParseArgumentList();
MethodBase method;
if (FindMethod(lambda.Type, "Invoke", false, args, out method) != 1)
throw ParseError(errorPos, Res.ArgsIncompatibleWithLambda);
return Expression.Invoke(lambda, args);
}
Expression ParseTypeAccess(Type type)
{
int errorPos = token.pos;
NextToken();
if (token.id == TokenId.Question)
{
if (!type.IsValueType || IsNullableType(type))
throw ParseError(errorPos, Res.TypeHasNoNullableForm, GetTypeName(type));
type = typeof(Nullable<>).MakeGenericType(type);
NextToken();
}
if (token.id == TokenId.OpenParen)
{
Expression[] args = ParseArgumentList();
MethodBase method;
switch (FindBestMethod(type.GetConstructors(), args, out method))
{
case 0:
if (args.Length == 1)
return GenerateConversion(args[0], type, errorPos);
throw ParseError(errorPos, Res.NoMatchingConstructor, GetTypeName(type));
case 1:
return Expression.New((ConstructorInfo)method, args);
default:
throw ParseError(errorPos, Res.AmbiguousConstructorInvocation, GetTypeName(type));
}
}
ValidateToken(TokenId.Dot, Res.DotOrOpenParenExpected);
NextToken();
return ParseMemberAccess(type, null);
}
Expression GenerateConversion(Expression expr, Type type, int errorPos)
{
Type exprType = expr.Type;
if (exprType == type) return expr;
if (exprType.IsValueType && type.IsValueType)
{
if ((IsNullableType(exprType) || IsNullableType(type)) &&
GetNonNullableType(exprType) == GetNonNullableType(type))
return Expression.Convert(expr, type);
if ((IsNumericType(exprType) || IsEnumType(exprType)) &&
(IsNumericType(type)) || IsEnumType(type))
return Expression.ConvertChecked(expr, type);
}
if (exprType.IsAssignableFrom(type) || type.IsAssignableFrom(exprType) ||
exprType.IsInterface || type.IsInterface)
return Expression.Convert(expr, type);
throw ParseError(errorPos, Res.CannotConvertValue,
GetTypeName(exprType), GetTypeName(type));
}
Expression ParseMemberAccess(Type type, Expression instance)
{
if (instance != null) type = instance.Type;
int errorPos = token.pos;
string id = GetIdentifier();
NextToken();
if (token.id == TokenId.OpenParen)
{
if (instance != null && type != typeof(string))
{
Type enumerableType = FindGenericType(typeof(IEnumerable<>), type);
if (enumerableType != null)
{
Type elementType = enumerableType.GetGenericArguments()[0];
return ParseAggregate(instance, elementType, id, errorPos);
}
}
Expression[] args = ParseArgumentList();
MethodBase mb;
LambdaExpression instanceAsString = null;
ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicNode), "instance");
if (type.IsGenericType && type != typeof(string))
{
var typeArgs = type.GetGenericArguments();
if (typeArgs[0] == typeof(DynamicNode))
{
if (instance != null && instance is LambdaExpression)
{
if (typeArgs[1] == typeof(object))
{
instanceAsString = StringFormat(instance as LambdaExpression, instanceExpression);
type = typeof(string);
}
if (typeArgs[1] == typeof(string))
{
instanceAsString = instance as LambdaExpression;
type = typeof(string);
}
}
}
}
switch (FindMethod(type, id, instance == null, args, out mb))
{
case 0:
//not found
if (type == typeof(string) && instanceAsString != null)
{
Expression[] newArgs = (new List<Expression>() { Expression.Invoke(instanceAsString, instanceExpression) }).Concat(args).ToArray();
mb = ExtensionMethodFinder.FindExtensionMethod(typeof(string), newArgs, id);
if (mb != null)
{
return CallMethodOnDynamicNode(instance, newArgs, instanceAsString, instanceExpression, (MethodInfo)mb, true);
}
}
throw ParseError(errorPos, Res.NoApplicableMethod,
id, GetTypeName(type));
case 1:
MethodInfo method = (MethodInfo)mb;
if (!IsPredefinedType(method.DeclaringType))
throw ParseError(errorPos, Res.MethodsAreInaccessible, GetTypeName(method.DeclaringType));
if (method.ReturnType == typeof(void))
throw ParseError(errorPos, Res.MethodIsVoid,
id, GetTypeName(method.DeclaringType));
if (instanceAsString != null)
{
return CallMethodOnDynamicNode(instance, args, instanceAsString, instanceExpression, method, false);
}
return Expression.Call(instance, (MethodInfo)method, args);
default:
throw ParseError(errorPos, Res.AmbiguousMethodInvocation,
id, GetTypeName(type));
}
}
else
{
//hack to bypass property existance checks
//Gareth Evans, twitter @agrath
if (typeof(DynamicObject).IsAssignableFrom(type))
{
//We are going to generate a dynamic method by hand coding the expression tree
//this will invoke TryGetMember (but wrapped in an expression tree)
//so that when it's evaluated, DynamicNode should be supported
ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicNode), "instance");
ParameterExpression result = Expression.Parameter(typeof(object), "result");
ParameterExpression binder = Expression.Variable(typeof(DynamicQueryableGetMemberBinder), "binder");
ParameterExpression ignoreCase = Expression.Variable(typeof(bool), "ignoreCase");
ConstructorInfo getMemberBinderConstructor = typeof(DynamicQueryableGetMemberBinder).GetConstructor(new Type[] { typeof(string), typeof(bool) });
LabelTarget blockReturnLabel = Expression.Label(typeof(object));
MethodInfo method = typeof(DynamicNode).GetMethod("TryGetMember");
BlockExpression block = Expression.Block(
typeof(object),
new[] { ignoreCase, binder, result },
Expression.Assign(ignoreCase, Expression.Constant(false, typeof(bool))),
Expression.Assign(binder, Expression.New(getMemberBinderConstructor, Expression.Constant(id, typeof(string)), ignoreCase)),
Expression.Assign(result, Expression.Constant(null)),
Expression.IfThen(Expression.NotEqual(Expression.Constant(null), instanceExpression),
Expression.Call(instanceExpression, method, binder, result)),
Expression.Return(blockReturnLabel, result),
Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object)))
);
LambdaExpression lax = Expression.Lambda<Func<DynamicNode, object>>(block, instanceExpression);
return lax;
}
//Looks for a member on the type, but above, we're rerouting that into TryGetMember
MemberInfo member = FindPropertyOrField(type, id, instance == null);
if (member == null)
throw ParseError(errorPos, Res.UnknownPropertyOrField,
id, GetTypeName(type));
return member is PropertyInfo ?
Expression.Property(instance, (PropertyInfo)member) :
Expression.Field(instance, (FieldInfo)member);
}
}
private static Expression CallMethodOnDynamicNode(Expression instance, Expression[] args, LambdaExpression instanceAsString, ParameterExpression instanceExpression, MethodInfo method, bool isStatic)
{
ConstantExpression defaultReturnValue = Expression.Constant(null, typeof(object));
Type methodReturnType = method.ReturnType;
switch (methodReturnType.Name)
{
case "String":
defaultReturnValue = Expression.Constant(null, typeof(string));
break;
case "Int32":
defaultReturnValue = Expression.Constant(0, typeof(int));
break;
case "Boolean":
defaultReturnValue = Expression.Constant(false, typeof(bool));
break;
}
ParameterExpression result = Expression.Parameter(method.ReturnType, "result");
LabelTarget blockReturnLabel = Expression.Label(method.ReturnType);
BlockExpression block = Expression.Block(
method.ReturnType,
new[] { result },
Expression.Assign(result,
Expression.Call(
isStatic ? null : Expression.Invoke(instanceAsString, instanceExpression),
method,
args)
),
Expression.Return(blockReturnLabel, result),
Expression.Label(blockReturnLabel, defaultReturnValue)
);
switch (methodReturnType.Name)
{
case "String":
return Expression.Lambda<Func<DynamicNode, string>>(block, instanceExpression);
case "Int32":
return Expression.Lambda<Func<DynamicNode, int>>(block, instanceExpression);
case "Boolean":
return Expression.Lambda<Func<DynamicNode, bool>>(block, instanceExpression);
}
return Expression.Call(instance, (MethodInfo)method, args);
}
static Type FindGenericType(Type generic, Type type)
{
while (type != null && type != typeof(object))
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == generic) return type;
if (generic.IsInterface)
{
foreach (Type intfType in type.GetInterfaces())
{
Type found = FindGenericType(generic, intfType);
if (found != null) return found;
}
}
type = type.BaseType;
}
return null;
}
LambdaExpression StringFormat(LambdaExpression lax, ParameterExpression instanceExpression)
{
ParameterExpression cresult = Expression.Parameter(typeof(string), "cresult");
ParameterExpression temp = Expression.Parameter(typeof(object), "temp");
ParameterExpression stemp = Expression.Parameter(typeof(string), "string");
LabelTarget cblockReturnLabel = Expression.Label(typeof(string));
MethodInfo stringFormat = typeof(string).GetMethod("Format", new Type[] { typeof(string), typeof(object) });
BlockExpression cblock = Expression.Block(
typeof(string),
new[] { cresult, temp },
Expression.Assign(temp, Expression.Invoke(lax, instanceExpression)),
Expression.Assign(cresult, Expression.Call(stringFormat, Expression.Constant("{0}"), temp)),
Expression.Return(cblockReturnLabel, cresult),
Expression.Label(cblockReturnLabel, Expression.Constant(null, typeof(string))));
LambdaExpression lax2 = Expression.Lambda<Func<DynamicNode, string>>(cblock, instanceExpression);
var expression = Expression.Lambda<Func<DynamicNode, string>>(cblock, instanceExpression);
return expression;
}
Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos)
{
ParameterExpression outerIt = it;
ParameterExpression innerIt = Expression.Parameter(elementType, "");
it = innerIt;
Expression[] args = ParseArgumentList();
it = outerIt;
MethodBase signature;
if (FindMethod(typeof(IEnumerableSignatures), methodName, false, args, out signature) != 1)
throw ParseError(errorPos, Res.NoApplicableAggregate, methodName);
Type[] typeArgs;
if (signature.Name == "Min" || signature.Name == "Max")
{
typeArgs = new Type[] { elementType, args[0].Type };
}
else
{
typeArgs = new Type[] { elementType };
}
if (args.Length == 0)
{
args = new Expression[] { instance };
}
else
{
args = new Expression[] { instance, Expression.Lambda(args[0], innerIt) };
}
return Expression.Call(typeof(Enumerable), signature.Name, typeArgs, args);
}
Expression[] ParseArgumentList()
{
ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
NextToken();
Expression[] args = token.id != TokenId.CloseParen ? ParseArguments() : new Expression[0];
ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);
NextToken();
return args;
}
Expression[] ParseArguments()
{
List<Expression> argList = new List<Expression>();
while (true)
{
argList.Add(ParseExpression());
if (token.id != TokenId.Comma) break;
NextToken();
}
return argList.ToArray();
}
Expression ParseElementAccess(Expression expr)
{
int errorPos = token.pos;
ValidateToken(TokenId.OpenBracket, Res.OpenParenExpected);
NextToken();
Expression[] args = ParseArguments();
ValidateToken(TokenId.CloseBracket, Res.CloseBracketOrCommaExpected);
NextToken();
if (expr.Type.IsArray)
{
if (expr.Type.GetArrayRank() != 1 || args.Length != 1)
throw ParseError(errorPos, Res.CannotIndexMultiDimArray);
Expression index = PromoteExpression(args[0], typeof(int), true);
if (index == null)
throw ParseError(errorPos, Res.InvalidIndex);
return Expression.ArrayIndex(expr, index);
}
else
{
MethodBase mb;
switch (FindIndexer(expr.Type, args, out mb))
{
case 0:
throw ParseError(errorPos, Res.NoApplicableIndexer,
GetTypeName(expr.Type));
case 1:
return Expression.Call(expr, (MethodInfo)mb, args);
default:
throw ParseError(errorPos, Res.AmbiguousIndexerInvocation,
GetTypeName(expr.Type));
}
}
}
static bool IsPredefinedType(Type type)
{
foreach (Type t in predefinedTypes) if (t == type) return true;
return false;
}
static bool IsNullableType(Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
}
static Type GetNonNullableType(Type type)
{
return IsNullableType(type) ? type.GetGenericArguments()[0] : type;
}
static string GetTypeName(Type type)
{
Type baseType = GetNonNullableType(type);
string s = baseType.Name;
if (type != baseType) s += '?';
return s;
}
static bool IsNumericType(Type type)
{
return GetNumericTypeKind(type) != 0;
}
static bool IsSignedIntegralType(Type type)
{
return GetNumericTypeKind(type) == 2;
}
static bool IsUnsignedIntegralType(Type type)
{
return GetNumericTypeKind(type) == 3;
}
static int GetNumericTypeKind(Type type)
{
type = GetNonNullableType(type);
if (type.IsEnum) return 0;
switch (Type.GetTypeCode(type))
{
case TypeCode.Char:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return 1;
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
return 2;
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return 3;
default:
return 0;
}
}
static bool IsEnumType(Type type)
{
return GetNonNullableType(type).IsEnum;
}
void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos)
{
Expression[] args = new Expression[] { expr };
MethodBase method;
if (FindMethod(signatures, "F", false, args, out method) != 1)
throw ParseError(errorPos, Res.IncompatibleOperand,
opName, GetTypeName(args[0].Type));
expr = args[0];
}
void CheckAndPromoteOperands(Type signatures, string opName, ref Expression left, ref Expression right, int errorPos)
{
Expression[] args = new Expression[] { left, right };
MethodBase method;
if (FindMethod(signatures, "F", false, args, out method) != 1)
throw IncompatibleOperandsError(opName, left, right, errorPos);
left = args[0];
right = args[1];
}
Exception IncompatibleOperandsError(string opName, Expression left, Expression right, int pos)
{
return ParseError(pos, Res.IncompatibleOperands,
opName, GetTypeName(left.Type), GetTypeName(right.Type));
}
MemberInfo FindPropertyOrField(Type type, string memberName, bool staticAccess)
{
BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly |
(staticAccess ? BindingFlags.Static : BindingFlags.Instance);
foreach (Type t in SelfAndBaseTypes(type))
{
MemberInfo[] members = t.FindMembers(MemberTypes.Property | MemberTypes.Field,
flags, Type.FilterNameIgnoreCase, memberName);
if (members.Length != 0) return members[0];
}
return null;
}
int FindMethod(Type type, string methodName, bool staticAccess, Expression[] args, out MethodBase method)
{
BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly |
(staticAccess ? BindingFlags.Static : BindingFlags.Instance);
foreach (Type t in SelfAndBaseTypes(type))
{
MemberInfo[] members = t.FindMembers(MemberTypes.Method,
flags, Type.FilterNameIgnoreCase, methodName);
int count = FindBestMethod(members.Cast<MethodBase>(), args, out method);
if (count != 0) return count;
}
method = null;
return 0;
}
int FindIndexer(Type type, Expression[] args, out MethodBase method)
{
foreach (Type t in SelfAndBaseTypes(type))
{
MemberInfo[] members = t.GetDefaultMembers();
if (members.Length != 0)
{
IEnumerable<MethodBase> methods = members.
OfType<PropertyInfo>().
Select(p => (MethodBase)p.GetGetMethod()).
Where(m => m != null);
int count = FindBestMethod(methods, args, out method);
if (count != 0) return count;
}
}
method = null;
return 0;
}
static IEnumerable<Type> SelfAndBaseTypes(Type type)
{
if (type.IsInterface)
{
List<Type> types = new List<Type>();
AddInterface(types, type);
return types;
}
return SelfAndBaseClasses(type);
}
static IEnumerable<Type> SelfAndBaseClasses(Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
static void AddInterface(List<Type> types, Type type)
{
if (!types.Contains(type))
{
types.Add(type);
foreach (Type t in type.GetInterfaces()) AddInterface(types, t);
}
}
class MethodData
{
public MethodBase MethodBase;
public ParameterInfo[] Parameters;
public Expression[] Args;
}
int FindBestMethod(IEnumerable<MethodBase> methods, Expression[] args, out MethodBase method)
{
MethodData[] applicable = methods.
Select(m => new MethodData { MethodBase = m, Parameters = m.GetParameters() }).
Where(m => IsApplicable(m, args)).
ToArray();
if (applicable.Length > 1)
{
applicable = applicable.
Where(m => applicable.All(n => m == n || IsBetterThan(args, m, n))).
ToArray();
}
if (applicable.Length == 1)
{
MethodData md = applicable[0];
for (int i = 0; i < args.Length; i++) args[i] = md.Args[i];
method = md.MethodBase;
}
else
{
method = null;
}
return applicable.Length;
}
bool IsApplicable(MethodData method, Expression[] args)
{
if (method.Parameters.Length != args.Length) return false;
Expression[] promotedArgs = new Expression[args.Length];
for (int i = 0; i < args.Length; i++)
{
ParameterInfo pi = method.Parameters[i];
if (pi.IsOut) return false;
Expression promoted = PromoteExpression(args[i], pi.ParameterType, false);
if (promoted == null) return false;
promotedArgs[i] = promoted;
}
method.Args = promotedArgs;
return true;
}
Expression PromoteExpression(Expression expr, Type type, bool exact)
{
//if the type of the expression is the correct target type, just return it here
if (expr.Type == type) return expr;
//if the type of the expression is a func<DynamicNode, object> - invokable returning object,
//we are going to return it here, because we can get the real value when we actually have the instance
//if (typeof(Func<DynamicNode, object>).IsAssignableFrom(expr.Type)) return expr;
if (expr is LambdaExpression && ((LambdaExpression)expr).Parameters.Count > 0 && ((LambdaExpression)expr).Parameters[0].Type == typeof(DynamicNode))
{
return expr;
}
if (expr is ConstantExpression)
{
ConstantExpression ce = (ConstantExpression)expr;
if (ce == nullLiteral)
{
if (!type.IsValueType || IsNullableType(type))
return Expression.Constant(null, type);
}
else
{
string text;
if (literals.TryGetValue(ce, out text))
{
Type target = GetNonNullableType(type);
Object value = null;
switch (Type.GetTypeCode(ce.Type))
{
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
value = ParseNumber(text, target);
break;
case TypeCode.Double:
if (target == typeof(decimal)) value = ParseNumber(text, target);
break;
case TypeCode.String:
value = ParseEnum(text, target);
break;
}
if (value != null)
return Expression.Constant(value, type);
}
}
}
if (IsCompatibleWith(expr.Type, type))
{
if (type.IsValueType || exact) return Expression.Convert(expr, type);
return expr;
}
return null;
}
static object ParseNumber(string text, Type type)
{
switch (Type.GetTypeCode(GetNonNullableType(type)))
{
case TypeCode.SByte:
sbyte sb;
if (sbyte.TryParse(text, out sb)) return sb;
break;
case TypeCode.Byte:
byte b;
if (byte.TryParse(text, out b)) return b;
break;
case TypeCode.Int16:
short s;
if (short.TryParse(text, out s)) return s;
break;
case TypeCode.UInt16:
ushort us;
if (ushort.TryParse(text, out us)) return us;
break;
case TypeCode.Int32:
int i;
if (int.TryParse(text, out i)) return i;
break;
case TypeCode.UInt32:
uint ui;
if (uint.TryParse(text, out ui)) return ui;
break;
case TypeCode.Int64:
long l;
if (long.TryParse(text, out l)) return l;
break;
case TypeCode.UInt64:
ulong ul;
if (ulong.TryParse(text, out ul)) return ul;
break;
case TypeCode.Single:
float f;
if (float.TryParse(text, out f)) return f;
break;
case TypeCode.Double:
double d;
if (double.TryParse(text, out d)) return d;
break;
case TypeCode.Decimal:
decimal e;
if (decimal.TryParse(text, out e)) return e;
break;
}
return null;
}
static object ParseEnum(string name, Type type)
{
if (type.IsEnum)
{
MemberInfo[] memberInfos = type.FindMembers(MemberTypes.Field,
BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static,
Type.FilterNameIgnoreCase, name);
if (memberInfos.Length != 0) return ((FieldInfo)memberInfos[0]).GetValue(null);
}
return null;
}
static bool IsCompatibleWith(Type source, Type target)
{
if (source == target) return true;
if (!target.IsValueType) return target.IsAssignableFrom(source);
Type st = GetNonNullableType(source);
Type tt = GetNonNullableType(target);
if (st != source && tt == target) return false;
TypeCode sc = st.IsEnum ? TypeCode.Object : Type.GetTypeCode(st);
TypeCode tc = tt.IsEnum ? TypeCode.Object : Type.GetTypeCode(tt);
switch (sc)
{
case TypeCode.SByte:
switch (tc)
{
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.Byte:
switch (tc)
{
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.Int16:
switch (tc)
{
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.UInt16:
switch (tc)
{
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.Int32:
switch (tc)
{
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.UInt32:
switch (tc)
{
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.Int64:
switch (tc)
{
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.UInt64:
switch (tc)
{
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.Single:
switch (tc)
{
case TypeCode.Single:
case TypeCode.Double:
return true;
}
break;
default:
if (st == tt) return true;
break;
}
return false;
}
static bool IsBetterThan(Expression[] args, MethodData m1, MethodData m2)
{
bool better = false;
for (int i = 0; i < args.Length; i++)
{
int c = CompareConversions(args[i].Type,
m1.Parameters[i].ParameterType,
m2.Parameters[i].ParameterType);
if (c < 0) return false;
if (c > 0) better = true;
}
return better;
}
// Return 1 if s -> t1 is a better conversion than s -> t2
// Return -1 if s -> t2 is a better conversion than s -> t1
// Return 0 if neither conversion is better
static int CompareConversions(Type s, Type t1, Type t2)
{
if (t1 == t2) return 0;
if (s == t1) return 1;
if (s == t2) return -1;
bool t1t2 = IsCompatibleWith(t1, t2);
bool t2t1 = IsCompatibleWith(t2, t1);
if (t1t2 && !t2t1) return 1;
if (t2t1 && !t1t2) return -1;
if (IsSignedIntegralType(t1) && IsUnsignedIntegralType(t2)) return 1;
if (IsSignedIntegralType(t2) && IsUnsignedIntegralType(t1)) return -1;
return 0;
}
Expression GenerateEqual(Expression left, Expression right)
{
return HandleDynamicNodeLambdas(ExpressionType.Equal, left, right);
}
private static Expression HandleDynamicNodeLambdas(ExpressionType expressionType, Expression left, Expression right)
{
bool leftIsLambda = false, rightIsLambda = false;
Expression innerLeft = null;
Expression innerRight = null;
UnaryExpression unboxedLeft = null, unboxedRight = null;
ParameterExpression[] parameters = null;
if (left is LambdaExpression && (left as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicNode))
{
leftIsLambda = true;
}
if (right is LambdaExpression && (right as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicNode))
{
rightIsLambda = true;
}
if (leftIsLambda && !rightIsLambda)
{
parameters = new ParameterExpression[(left as LambdaExpression).Parameters.Count];
(left as LambdaExpression).Parameters.CopyTo(parameters, 0);
if (right is ConstantExpression)
{
//left lambda, right constant
var invokedExpr = Expression.Invoke(left, (left as LambdaExpression).Parameters.Cast<Expression>());
innerLeft = Expression.Convert(invokedExpr, (right as ConstantExpression).Type);
}
if (leftIsLambda && !rightIsLambda && right is MemberExpression)
{
var invokedExpr = Expression.Invoke(left, (left as LambdaExpression).Parameters.Cast<Expression>());
innerLeft = Expression.Convert(invokedExpr, (right as MemberExpression).Type);
}
}
if (rightIsLambda && !leftIsLambda)
{
parameters = new ParameterExpression[(right as LambdaExpression).Parameters.Count];
(right as LambdaExpression).Parameters.CopyTo(parameters, 0);
if (left is ConstantExpression)
{
//right lambda, left constant
var invokedExpr = Expression.Invoke(right, (right as LambdaExpression).Parameters.Cast<Expression>());
innerRight = Expression.Convert(invokedExpr, (left as ConstantExpression).Type);
}
if (right is MemberExpression)
{
var invokedExpr = Expression.Invoke(right, (right as LambdaExpression).Parameters.Cast<Expression>());
innerRight = Expression.Convert(invokedExpr, (left as MemberExpression).Type);
}
}
bool sequenceEqual = false;
if (leftIsLambda && rightIsLambda)
{
{
Type leftType = ((LambdaExpression)left).Type;
Type rightType = ((LambdaExpression)right).Type;
Type[] leftTypeGenericArguments = leftType.GetGenericArguments();
Type[] rightTypeGenericArguments = rightType.GetGenericArguments();
if (leftTypeGenericArguments.SequenceEqual(rightTypeGenericArguments))
{
sequenceEqual = true;
if (leftTypeGenericArguments.Length == 2)
{
Type TOut = leftTypeGenericArguments[1];
if (expressionType == ExpressionType.AndAlso)
{
return PredicateBuilder.And<DynamicNode>(left as Expression<Func<DynamicNode, bool>>, right as Expression<Func<DynamicNode, bool>>);
}
if (expressionType == ExpressionType.OrElse)
{
return PredicateBuilder.Or<DynamicNode>(left as Expression<Func<DynamicNode, bool>>, right as Expression<Func<DynamicNode, bool>>);
}
}
}
else
{
if (leftTypeGenericArguments.Length == 2)
{
//sequence not equal - could be Func<DynamicNode,object> && Func<DynamicNode,bool>
if (leftTypeGenericArguments.First() == rightTypeGenericArguments.First())
{
bool leftIsObject = leftTypeGenericArguments.ElementAt(1) == typeof(object);
bool rightIsObject = rightTypeGenericArguments.ElementAt(1) == typeof(object);
//if one is an object but not the other
if (leftIsObject ^ rightIsObject)
{
if (leftIsObject)
{
//left side is object
if (innerLeft == null)
{
parameters = new ParameterExpression[(left as LambdaExpression).Parameters.Count];
(left as LambdaExpression).Parameters.CopyTo(parameters, 0);
innerLeft = Expression.Invoke(left, parameters);
}
unboxedLeft = Expression.Unbox(innerLeft, rightTypeGenericArguments.ElementAt(1));
//left is invoked and unboxed to right's TOut, right was not boxed
if (expressionType == ExpressionType.AndAlso)
{
return PredicateBuilder.And<DynamicNode>(right as Expression<Func<DynamicNode, bool>>, Expression.Lambda<Func<DynamicNode, bool>>(unboxedLeft, parameters) as Expression<Func<DynamicNode, bool>>);
}
if (expressionType == ExpressionType.OrElse)
{
return PredicateBuilder.And<DynamicNode>(right as Expression<Func<DynamicNode, bool>>, Expression.Lambda<Func<DynamicNode, bool>>(unboxedLeft, parameters) as Expression<Func<DynamicNode, bool>>);
}
}
else
{
//right side is object
if (innerRight == null)
{
parameters = new ParameterExpression[(right as LambdaExpression).Parameters.Count];
(right as LambdaExpression).Parameters.CopyTo(parameters, 0);
innerRight = Expression.Invoke(right, parameters);
}
unboxedRight = Expression.Unbox(innerRight, leftTypeGenericArguments.ElementAt(1));
//right is invoked and unboxed to left's TOut, left was not boxed
if (expressionType == ExpressionType.AndAlso)
{
return PredicateBuilder.And<DynamicNode>(left as Expression<Func<DynamicNode, bool>>, Expression.Lambda<Func<DynamicNode, bool>>(unboxedRight, parameters) as Expression<Func<DynamicNode, bool>>);
}
if (expressionType == ExpressionType.OrElse)
{
return PredicateBuilder.And<DynamicNode>(left as Expression<Func<DynamicNode, bool>>, Expression.Lambda<Func<DynamicNode, bool>>(unboxedRight, parameters) as Expression<Func<DynamicNode, bool>>);
}
}
}
}
}
}
}
}
if (leftIsLambda && innerLeft == null)
{
//left is a lambda, but the right was an unhandled expression type
//!ConstantExpression, !MemberExpression
//make sure the left gets invoked
if (parameters == null)
{
parameters = new ParameterExpression[(left as LambdaExpression).Parameters.Count];
(left as LambdaExpression).Parameters.CopyTo(parameters, 0);
}
innerLeft = Expression.Invoke(left, parameters);
}
if (rightIsLambda && innerRight == null)
{
//right is a lambda, but the left was an unhandled expression type
//!ConstantExpression, !MemberExpression
//make sure the right gets invoked
if (parameters == null)
{
parameters = new ParameterExpression[(right as LambdaExpression).Parameters.Count];
(right as LambdaExpression).Parameters.CopyTo(parameters, 0);
}
innerRight = Expression.Invoke(right, parameters);
}
if (leftIsLambda && !rightIsLambda && innerLeft != null && !(innerLeft is UnaryExpression) && innerLeft.Type is object)
{
//innerLeft is an invoke
unboxedLeft = Expression.Unbox(innerLeft, right.Type);
}
if (rightIsLambda && !leftIsLambda && innerRight != null && !(innerRight is UnaryExpression) && innerRight.Type is object)
{
//innerRight is an invoke
unboxedRight = Expression.Unbox(innerRight, left.Type);
}
BinaryExpression binaryExpression = null;
var finalLeft = unboxedLeft ?? innerLeft ?? left;
var finalRight = unboxedRight ?? innerRight ?? right;
switch (expressionType)
{
case ExpressionType.Equal:
binaryExpression = Expression.Equal(finalLeft, finalRight);
break;
case ExpressionType.NotEqual:
binaryExpression = Expression.NotEqual(finalLeft, finalRight);
break;
case ExpressionType.GreaterThan:
binaryExpression = Expression.GreaterThan(finalLeft, finalRight);
break;
case ExpressionType.LessThan:
binaryExpression = Expression.LessThan(finalLeft, finalRight);
break;
case ExpressionType.GreaterThanOrEqual:
binaryExpression = Expression.GreaterThanOrEqual(finalLeft, finalRight);
break;
case ExpressionType.LessThanOrEqual:
binaryExpression = Expression.LessThanOrEqual(finalLeft, finalRight);
break;
case ExpressionType.Modulo:
binaryExpression = Expression.Modulo(finalLeft, finalRight);
return (Expression.Lambda<Func<DynamicNode, int>>(binaryExpression, parameters));
case ExpressionType.AndAlso:
if (leftIsLambda && rightIsLambda && sequenceEqual)
{
return Expression.Equal(left, right);
}
else
{
return (Expression.Lambda<Func<DynamicNode, DynamicNode, Boolean>>(Expression.AndAlso(finalLeft, finalRight), parameters));
}
case ExpressionType.OrElse:
if (leftIsLambda && rightIsLambda && sequenceEqual)
{
return Expression.Equal(left, right);
}
else
{
return (Expression.Lambda<Func<DynamicNode, Boolean>>(Expression.OrElse(finalLeft, finalRight), parameters));
}
default:
return Expression.Equal(left, right);
}
var body = Expression.Condition(Expression.TypeEqual(innerLeft, right.Type), binaryExpression, Expression.Constant(false));
return Expression.Lambda<Func<DynamicNode, bool>>(body, parameters);
}
Expression GenerateNotEqual(Expression left, Expression right)
{
return HandleDynamicNodeLambdas(ExpressionType.NotEqual, left, right);
}
Expression GenerateGreaterThan(Expression left, Expression right)
{
if (left.Type == typeof(string))
{
return Expression.GreaterThan(
GenerateStaticMethodCall("Compare", left, right),
Expression.Constant(0)
);
}
return HandleDynamicNodeLambdas(ExpressionType.GreaterThan, left, right);
}
Expression GenerateGreaterThanEqual(Expression left, Expression right)
{
if (left.Type == typeof(string))
{
return Expression.GreaterThanOrEqual(
GenerateStaticMethodCall("Compare", left, right),
Expression.Constant(0)
);
}
return HandleDynamicNodeLambdas(ExpressionType.GreaterThanOrEqual, left, right);
}
Expression GenerateLessThan(Expression left, Expression right)
{
if (left.Type == typeof(string))
{
return Expression.LessThan(
GenerateStaticMethodCall("Compare", left, right),
Expression.Constant(0)
);
}
return HandleDynamicNodeLambdas(ExpressionType.LessThan, left, right);
}
Expression GenerateLessThanEqual(Expression left, Expression right)
{
if (left.Type == typeof(string))
{
return Expression.LessThanOrEqual(
GenerateStaticMethodCall("Compare", left, right),
Expression.Constant(0)
);
}
return HandleDynamicNodeLambdas(ExpressionType.LessThanOrEqual, left, right);
}
Expression GenerateAdd(Expression left, Expression right)
{
if (left.Type == typeof(string) && right.Type == typeof(string))
{
return GenerateStaticMethodCall("Concat", left, right);
}
return Expression.Add(left, right);
}
Expression GenerateSubtract(Expression left, Expression right)
{
return Expression.Subtract(left, right);
}
Expression GenerateStringConcat(Expression left, Expression right)
{
return Expression.Call(
null,
typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }),
new[] { left, right });
}
MethodInfo GetStaticMethod(string methodName, Expression left, Expression right)
{
return left.Type.GetMethod(methodName, new[] { left.Type, right.Type });
}
Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right)
{
return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right });
}
void SetTextPos(int pos)
{
textPos = pos;
ch = textPos < textLen ? text[textPos] : '\0';
}
void NextChar()
{
if (textPos < textLen) textPos++;
ch = textPos < textLen ? text[textPos] : '\0';
}
void NextToken()
{
while (Char.IsWhiteSpace(ch)) NextChar();
TokenId t;
int tokenPos = textPos;
switch (ch)
{
case '!':
NextChar();
if (ch == '=')
{
NextChar();
t = TokenId.ExclamationEqual;
}
else
{
t = TokenId.Exclamation;
}
break;
case '%':
NextChar();
t = TokenId.Percent;
break;
case '&':
NextChar();
if (ch == '&')
{
NextChar();
t = TokenId.DoubleAmphersand;
}
else
{
t = TokenId.Amphersand;
}
break;
case '(':
NextChar();
t = TokenId.OpenParen;
break;
case ')':
NextChar();
t = TokenId.CloseParen;
break;
case '*':
NextChar();
t = TokenId.Asterisk;
break;
case '+':
NextChar();
t = TokenId.Plus;
break;
case ',':
NextChar();
t = TokenId.Comma;
break;
case '-':
NextChar();
t = TokenId.Minus;
break;
case '.':
NextChar();
t = TokenId.Dot;
break;
case '/':
NextChar();
t = TokenId.Slash;
break;
case ':':
NextChar();
t = TokenId.Colon;
break;
case '<':
NextChar();
if (ch == '=')
{
NextChar();
t = TokenId.LessThanEqual;
}
else if (ch == '>')
{
NextChar();
t = TokenId.LessGreater;
}
else
{
t = TokenId.LessThan;
}
break;
case '=':
NextChar();
if (ch == '=')
{
NextChar();
t = TokenId.DoubleEqual;
}
else
{
t = TokenId.Equal;
}
break;
case '>':
NextChar();
if (ch == '=')
{
NextChar();
t = TokenId.GreaterThanEqual;
}
else
{
t = TokenId.GreaterThan;
}
break;
case '?':
NextChar();
t = TokenId.Question;
break;
case '[':
NextChar();
t = TokenId.OpenBracket;
break;
case ']':
NextChar();
t = TokenId.CloseBracket;
break;
case '|':
NextChar();
if (ch == '|')
{
NextChar();
t = TokenId.DoubleBar;
}
else
{
t = TokenId.Bar;
}
break;
case '"':
case '\'':
char quote = ch;
do
{
NextChar();
while (textPos < textLen && ch != quote) NextChar();
if (textPos == textLen)
throw ParseError(textPos, Res.UnterminatedStringLiteral);
NextChar();
} while (ch == quote);
t = TokenId.StringLiteral;
break;
default:
if (Char.IsLetter(ch) || ch == '@' || ch == '_')
{
do
{
NextChar();
} while (Char.IsLetterOrDigit(ch) || ch == '_');
t = TokenId.Identifier;
break;
}
if (Char.IsDigit(ch))
{
t = TokenId.IntegerLiteral;
do
{
NextChar();
} while (Char.IsDigit(ch));
if (ch == '.')
{
t = TokenId.RealLiteral;
NextChar();
ValidateDigit();
do
{
NextChar();
} while (Char.IsDigit(ch));
}
if (ch == 'E' || ch == 'e')
{
t = TokenId.RealLiteral;
NextChar();
if (ch == '+' || ch == '-') NextChar();
ValidateDigit();
do
{
NextChar();
} while (Char.IsDigit(ch));
}
if (ch == 'F' || ch == 'f') NextChar();
break;
}
if (textPos == textLen)
{
t = TokenId.End;
break;
}
throw ParseError(textPos, Res.InvalidCharacter, ch);
}
token.id = t;
token.text = text.Substring(tokenPos, textPos - tokenPos);
token.pos = tokenPos;
}
bool TokenIdentifierIs(string id)
{
return token.id == TokenId.Identifier && String.Equals(id, token.text, StringComparison.OrdinalIgnoreCase);
}
string GetIdentifier()
{
ValidateToken(TokenId.Identifier, Res.IdentifierExpected);
string id = token.text;
if (id.Length > 1 && id[0] == '@') id = id.Substring(1);
return id;
}
void ValidateDigit()
{
if (!Char.IsDigit(ch)) throw ParseError(textPos, Res.DigitExpected);
}
void ValidateToken(TokenId t, string errorMessage)
{
if (token.id != t) throw ParseError(errorMessage);
}
void ValidateToken(TokenId t)
{
if (token.id != t) throw ParseError(Res.SyntaxError);
}
Exception ParseError(string format, params object[] args)
{
return ParseError(token.pos, format, args);
}
Exception ParseError(int pos, string format, params object[] args)
{
return new ParseException(string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args), pos);
}
static Dictionary<string, object> CreateKeywords()
{
Dictionary<string, object> d = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
d.Add("true", trueLiteral);
d.Add("false", falseLiteral);
d.Add("null", nullLiteral);
d.Add(keywordIt, keywordIt);
d.Add(keywordIif, keywordIif);
d.Add(keywordNew, keywordNew);
foreach (Type type in predefinedTypes) d.Add(type.Name, type);
return d;
}
}
static class Res
{
public const string DuplicateIdentifier = "The identifier '{0}' was defined more than once";
public const string ExpressionTypeMismatch = "Expression of type '{0}' expected";
public const string ExpressionExpected = "Expression expected";
public const string InvalidCharacterLiteral = "Character literal must contain exactly one character";
public const string InvalidIntegerLiteral = "Invalid integer literal '{0}'";
public const string InvalidRealLiteral = "Invalid real literal '{0}'";
public const string UnknownIdentifier = "Unknown identifier '{0}'";
public const string NoItInScope = "No 'it' is in scope";
public const string IifRequiresThreeArgs = "The 'iif' function requires three arguments";
public const string FirstExprMustBeBool = "The first expression must be of type 'Boolean'";
public const string BothTypesConvertToOther = "Both of the types '{0}' and '{1}' convert to the other";
public const string NeitherTypeConvertsToOther = "Neither of the types '{0}' and '{1}' converts to the other";
public const string MissingAsClause = "Expression is missing an 'as' clause";
public const string ArgsIncompatibleWithLambda = "Argument list incompatible with lambda expression";
public const string TypeHasNoNullableForm = "Type '{0}' has no nullable form";
public const string NoMatchingConstructor = "No matching constructor in type '{0}'";
public const string AmbiguousConstructorInvocation = "Ambiguous invocation of '{0}' constructor";
public const string CannotConvertValue = "A value of type '{0}' cannot be converted to type '{1}'";
public const string NoApplicableMethod = "No applicable method '{0}' exists in type '{1}'";
public const string MethodsAreInaccessible = "Methods on type '{0}' are not accessible";
public const string MethodIsVoid = "Method '{0}' in type '{1}' does not return a value";
public const string AmbiguousMethodInvocation = "Ambiguous invocation of method '{0}' in type '{1}'";
public const string UnknownPropertyOrField = "No property or field '{0}' exists in type '{1}'";
public const string NoApplicableAggregate = "No applicable aggregate method '{0}' exists";
public const string CannotIndexMultiDimArray = "Indexing of multi-dimensional arrays is not supported";
public const string InvalidIndex = "Array index must be an integer expression";
public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}'";
public const string AmbiguousIndexerInvocation = "Ambiguous invocation of indexer in type '{0}'";
public const string IncompatibleOperand = "Operator '{0}' incompatible with operand type '{1}'";
public const string IncompatibleOperands = "Operator '{0}' incompatible with operand types '{1}' and '{2}'";
public const string UnterminatedStringLiteral = "Unterminated string literal";
public const string InvalidCharacter = "Syntax error '{0}'";
public const string DigitExpected = "Digit expected";
public const string SyntaxError = "Syntax error";
public const string TokenExpected = "{0} expected";
public const string ParseExceptionFormat = "{0} (at index {1})";
public const string ColonExpected = "':' expected";
public const string OpenParenExpected = "'(' expected";
public const string CloseParenOrOperatorExpected = "')' or operator expected";
public const string CloseParenOrCommaExpected = "')' or ',' expected";
public const string DotOrOpenParenExpected = "'.' or '(' expected";
public const string OpenBracketExpected = "'[' expected";
public const string CloseBracketOrCommaExpected = "']' or ',' expected";
public const string IdentifierExpected = "Identifier expected";
}
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
}
}