diff --git a/src/System.Linq.Dynamic.Core/Compatibility/Nullable/NotNullWhenAttribute.cs b/src/System.Linq.Dynamic.Core/Compatibility/Nullable/NotNullWhenAttribute.cs
new file mode 100644
index 00000000..676ef898
--- /dev/null
+++ b/src/System.Linq.Dynamic.Core/Compatibility/Nullable/NotNullWhenAttribute.cs
@@ -0,0 +1,56 @@
+#region License
+// MIT License
+//
+// Copyright (c) Manuel Römer
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+#endregion
+
+#if NETSTANDARD1_3_OR_GREATER || NET35 || NET40 || NET45 || NET452 || NET46 || NETCOREAPP2_1 || UAP10_0
+
+// ReSharper disable once CheckNamespace
+namespace System.Diagnostics.CodeAnalysis;
+
+///
+/// Specifies that when a method returns ,
+/// the parameter will not be even if the corresponding type allows it.
+///
+[AttributeUsage(AttributeTargets.Parameter)]
+[DebuggerNonUserCode]
+internal sealed class NotNullWhenAttribute : Attribute
+{
+ ///
+ /// Gets the return value condition.
+ /// If the method returns this value, the associated parameter will not be .
+ ///
+ public bool ReturnValue { get; }
+
+ ///
+ /// Initializes the attribute with the specified return value condition.
+ ///
+ ///
+ /// The return value condition.
+ /// If the method returns this value, the associated parameter will not be .
+ ///
+ public NotNullWhenAttribute(bool returnValue)
+ {
+ ReturnValue = returnValue;
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/ConstantExpressionWrapper.cs b/src/System.Linq.Dynamic.Core/Parser/ConstantExpressionWrapper.cs
index 7b3e403b..201afbf6 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ConstantExpressionWrapper.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ConstantExpressionWrapper.cs
@@ -1,199 +1,213 @@
-using System.Linq.Expressions;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq.Expressions;
+#if UAP10_0 || NETSTANDARD1_3
using System.Reflection;
+#endif
-namespace System.Linq.Dynamic.Core.Parser
+namespace System.Linq.Dynamic.Core.Parser;
+
+///
+/// Based on gblog by graeme-hill. https://github.com/graeme-hill/gblog/blob/master/source_content/articles/2014.139_entity-framework-dynamic-queries-and-parameterization.mkd
+///
+internal class ConstantExpressionWrapper : IConstantExpressionWrapper
{
- ///
- /// Based on gblog by graeme-hill. https://github.com/graeme-hill/gblog/blob/master/source_content/articles/2014.139_entity-framework-dynamic-queries-and-parameterization.mkd
- ///
- internal class ConstantExpressionWrapper : IConstantExpressionWrapper
+ public void Wrap(ref Expression expression)
{
- public void Wrap(ref Expression expression)
+ if (expression is ConstantExpression constantExpression)
{
- if (expression is ConstantExpression constantExpression)
- {
- if (constantExpression.Type == typeof(bool))
- {
- expression = WrappedConstant((bool)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(bool?))
- {
- expression = WrappedConstant((bool?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(char))
- {
- expression = WrappedConstant((char)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(char?))
- {
- expression = WrappedConstant((char?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(byte))
- {
- expression = WrappedConstant((byte)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(byte?))
- {
- expression = WrappedConstant((byte?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(sbyte))
- {
- expression = WrappedConstant((sbyte)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(string))
- {
- expression = WrappedConstant((string)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(float))
- {
- expression = WrappedConstant((float)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(float?))
- {
- expression = WrappedConstant((float?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(decimal))
- {
- expression = WrappedConstant((decimal)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(decimal?))
- {
- expression = WrappedConstant((decimal?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(double))
- {
- expression = WrappedConstant((double)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(double?))
- {
- expression = WrappedConstant((double?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(long))
- {
- expression = WrappedConstant((long)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(long?))
- {
- expression = WrappedConstant((long?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(ulong))
- {
- expression = WrappedConstant((ulong)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(ulong?))
- {
- expression = WrappedConstant((ulong?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(int))
- {
- expression = WrappedConstant((int)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(int?))
- {
- expression = WrappedConstant((int?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(uint))
- {
- expression = WrappedConstant((uint)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(uint?))
- {
- expression = WrappedConstant((uint?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(short))
- {
- expression = WrappedConstant((short)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(short?))
- {
- expression = WrappedConstant((short?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(ushort))
- {
- expression = WrappedConstant((ushort)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(ushort?))
- {
- expression = WrappedConstant((ushort?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(Guid))
- {
- expression = WrappedConstant((Guid)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(Guid?))
- {
- expression = WrappedConstant((Guid?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(DateTime))
- {
- expression = WrappedConstant((DateTime)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(DateTime?))
- {
- expression = WrappedConstant((DateTime?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(DateTimeOffset))
- {
- expression = WrappedConstant((DateTimeOffset)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(DateTimeOffset?))
- {
- expression = WrappedConstant((DateTimeOffset?)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(TimeSpan))
- {
- expression = WrappedConstant((TimeSpan)constantExpression.Value);
- }
- else if (constantExpression.Type == typeof(TimeSpan?))
- {
- expression = WrappedConstant((TimeSpan?)constantExpression.Value);
- }
-
- return;
+ if (constantExpression.Type == typeof(bool))
+ {
+ expression = Wrap((bool)constantExpression.Value);
}
-
- if (expression is NewExpression newExpression)
- {
- if (newExpression.Type == typeof(Guid))
- {
- expression = WrappedConstant(Expression.Lambda>(newExpression).Compile()());
- }
- else if (newExpression.Type == typeof(Guid?))
- {
- expression = WrappedConstant(Expression.Lambda>(newExpression).Compile()());
- }
- else if (newExpression.Type == typeof(DateTime))
- {
- expression = WrappedConstant(Expression.Lambda>(newExpression).Compile()());
- }
- else if (newExpression.Type == typeof(DateTime?))
- {
- expression = WrappedConstant(Expression.Lambda>(newExpression).Compile()());
- }
- else if (newExpression.Type == typeof(DateTimeOffset))
- {
- expression = WrappedConstant(Expression.Lambda>(newExpression).Compile()());
- }
- else if (newExpression.Type == typeof(DateTimeOffset?))
- {
- expression = WrappedConstant(Expression.Lambda>(newExpression).Compile()());
- }
- else if (newExpression.Type == typeof(TimeSpan))
- {
- expression = WrappedConstant(Expression.Lambda>(newExpression).Compile()());
- }
- else if (newExpression.Type == typeof(TimeSpan?))
- {
- expression = WrappedConstant(Expression.Lambda>(newExpression).Compile()());
- }
+ else if (constantExpression.Type == typeof(bool?))
+ {
+ expression = Wrap((bool?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(char))
+ {
+ expression = Wrap((char)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(char?))
+ {
+ expression = Wrap((char?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(byte))
+ {
+ expression = Wrap((byte)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(byte?))
+ {
+ expression = Wrap((byte?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(sbyte))
+ {
+ expression = Wrap((sbyte)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(string))
+ {
+ expression = Wrap((string)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(float))
+ {
+ expression = Wrap((float)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(float?))
+ {
+ expression = Wrap((float?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(decimal))
+ {
+ expression = Wrap((decimal)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(decimal?))
+ {
+ expression = Wrap((decimal?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(double))
+ {
+ expression = Wrap((double)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(double?))
+ {
+ expression = Wrap((double?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(long))
+ {
+ expression = Wrap((long)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(long?))
+ {
+ expression = Wrap((long?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(ulong))
+ {
+ expression = Wrap((ulong)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(ulong?))
+ {
+ expression = Wrap((ulong?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(int))
+ {
+ expression = Wrap((int)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(int?))
+ {
+ expression = Wrap((int?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(uint))
+ {
+ expression = Wrap((uint)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(uint?))
+ {
+ expression = Wrap((uint?)constantExpression.Value);
}
+ else if (constantExpression.Type == typeof(short))
+ {
+ expression = Wrap((short)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(short?))
+ {
+ expression = Wrap((short?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(ushort))
+ {
+ expression = Wrap((ushort)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(ushort?))
+ {
+ expression = Wrap((ushort?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(Guid))
+ {
+ expression = Wrap((Guid)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(Guid?))
+ {
+ expression = Wrap((Guid?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(DateTime))
+ {
+ expression = Wrap((DateTime)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(DateTime?))
+ {
+ expression = Wrap((DateTime?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(DateTimeOffset))
+ {
+ expression = Wrap((DateTimeOffset)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(DateTimeOffset?))
+ {
+ expression = Wrap((DateTimeOffset?)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(TimeSpan))
+ {
+ expression = Wrap((TimeSpan)constantExpression.Value);
+ }
+ else if (constantExpression.Type == typeof(TimeSpan?))
+ {
+ expression = Wrap((TimeSpan?)constantExpression.Value);
+ }
+
+ return;
}
- private static MemberExpression WrappedConstant(TValue value)
+ if (expression is NewExpression newExpression)
{
- var wrapper = new WrappedValue(value);
+ if (newExpression.Type == typeof(Guid))
+ {
+ expression = Wrap(Expression.Lambda>(newExpression).Compile()());
+ }
+ else if (newExpression.Type == typeof(Guid?))
+ {
+ expression = Wrap(Expression.Lambda>(newExpression).Compile()());
+ }
+ else if (newExpression.Type == typeof(DateTime))
+ {
+ expression = Wrap(Expression.Lambda>(newExpression).Compile()());
+ }
+ else if (newExpression.Type == typeof(DateTime?))
+ {
+ expression = Wrap(Expression.Lambda>(newExpression).Compile()());
+ }
+ else if (newExpression.Type == typeof(DateTimeOffset))
+ {
+ expression = Wrap(Expression.Lambda>(newExpression).Compile()());
+ }
+ else if (newExpression.Type == typeof(DateTimeOffset?))
+ {
+ expression = Wrap(Expression.Lambda>(newExpression).Compile()());
+ }
+ else if (newExpression.Type == typeof(TimeSpan))
+ {
+ expression = Wrap(Expression.Lambda>(newExpression).Compile()());
+ }
+ else if (newExpression.Type == typeof(TimeSpan?))
+ {
+ expression = Wrap(Expression.Lambda>(newExpression).Compile()());
+ }
+ }
+ }
- return Expression.Property(Expression.Constant(wrapper), typeof(WrappedValue).GetProperty("Value")!);
+ public bool TryUnwrap(MemberExpression? expression, [NotNullWhen(true)] out TValue? value)
+ {
+ if (expression?.Expression is ConstantExpression { Value: WrappedValue wrapper })
+ {
+ value = wrapper.Value!;
+ return true;
}
+
+ value = default;
+ return false;
+ }
+
+ private static MemberExpression Wrap(TValue value)
+ {
+ var wrapper = new WrappedValue(value);
+
+ return Expression.Property(Expression.Constant(wrapper), typeof(WrappedValue).GetProperty("Value")!);
}
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs
index 2a8dc419..5900e037 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs
@@ -1,209 +1,221 @@
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq.Dynamic.Core.Validation;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
-namespace System.Linq.Dynamic.Core.Parser
+namespace System.Linq.Dynamic.Core.Parser;
+
+internal class ExpressionHelper : IExpressionHelper
{
- internal class ExpressionHelper : IExpressionHelper
+ private readonly IConstantExpressionWrapper _constantExpressionWrapper = new ConstantExpressionWrapper();
+ private readonly ParsingConfig _parsingConfig;
+
+ internal ExpressionHelper(ParsingConfig parsingConfig)
{
- private readonly IConstantExpressionWrapper _constantExpressionWrapper = new ConstantExpressionWrapper();
- private readonly ParsingConfig _parsingConfig;
+ _parsingConfig = Check.NotNull(parsingConfig);
+ }
- internal ExpressionHelper(ParsingConfig parsingConfig)
+ public void WrapConstantExpression(ref Expression argument)
+ {
+ if (_parsingConfig.UseParameterizedNamesInDynamicQuery)
{
- _parsingConfig = Check.NotNull(parsingConfig);
+ _constantExpressionWrapper.Wrap(ref argument);
}
+ }
- public void WrapConstantExpression(ref Expression argument)
+ public bool TryUnwrapConstantExpression(Expression? expression, [NotNullWhen(true)] out TValue? value)
+ {
+ if (_parsingConfig.UseParameterizedNamesInDynamicQuery && _constantExpressionWrapper.TryUnwrap(expression as MemberExpression, out value))
{
- if (_parsingConfig.UseParameterizedNamesInDynamicQuery)
- {
- _constantExpressionWrapper.Wrap(ref argument);
- }
+ return true;
}
- public void ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref Expression left, ref Expression right)
- {
- if (left.Type == right.Type)
- {
- return;
- }
+ value = default;
+ return false;
+ }
- if (left.Type == typeof(ulong) || right.Type == typeof(ulong))
- {
- right = right.Type != typeof(ulong) ? Expression.Convert(right, typeof(ulong)) : right;
- left = left.Type != typeof(ulong) ? Expression.Convert(left, typeof(ulong)) : left;
- }
- else if (left.Type == typeof(long) || right.Type == typeof(long))
- {
- right = right.Type != typeof(long) ? Expression.Convert(right, typeof(long)) : right;
- left = left.Type != typeof(long) ? Expression.Convert(left, typeof(long)) : left;
- }
- else if (left.Type == typeof(uint) || right.Type == typeof(uint))
- {
- right = right.Type != typeof(uint) ? Expression.Convert(right, typeof(uint)) : right;
- left = left.Type != typeof(uint) ? Expression.Convert(left, typeof(uint)) : left;
- }
- else if (left.Type == typeof(int) || right.Type == typeof(int))
- {
- right = right.Type != typeof(int) ? Expression.Convert(right, typeof(int)) : right;
- left = left.Type != typeof(int) ? Expression.Convert(left, typeof(int)) : left;
- }
- else if (left.Type == typeof(ushort) || right.Type == typeof(ushort))
- {
- right = right.Type != typeof(ushort) ? Expression.Convert(right, typeof(ushort)) : right;
- left = left.Type != typeof(ushort) ? Expression.Convert(left, typeof(ushort)) : left;
- }
- else if (left.Type == typeof(short) || right.Type == typeof(short))
- {
- right = right.Type != typeof(short) ? Expression.Convert(right, typeof(short)) : right;
- left = left.Type != typeof(short) ? Expression.Convert(left, typeof(short)) : left;
- }
- else if (left.Type == typeof(byte) || right.Type == typeof(byte))
- {
- right = right.Type != typeof(byte) ? Expression.Convert(right, typeof(byte)) : right;
- left = left.Type != typeof(byte) ? Expression.Convert(left, typeof(byte)) : left;
- }
+ public void ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref Expression left, ref Expression right)
+ {
+ if (left.Type == right.Type)
+ {
+ return;
}
- public Expression GenerateAdd(Expression left, Expression right)
+ if (left.Type == typeof(ulong) || right.Type == typeof(ulong))
{
- return Expression.Add(left, right);
+ right = right.Type != typeof(ulong) ? Expression.Convert(right, typeof(ulong)) : right;
+ left = left.Type != typeof(ulong) ? Expression.Convert(left, typeof(ulong)) : left;
}
-
- public Expression GenerateStringConcat(Expression left, Expression right)
+ else if (left.Type == typeof(long) || right.Type == typeof(long))
{
- return GenerateStaticMethodCall("Concat", left, right);
+ right = right.Type != typeof(long) ? Expression.Convert(right, typeof(long)) : right;
+ left = left.Type != typeof(long) ? Expression.Convert(left, typeof(long)) : left;
}
-
- public Expression GenerateSubtract(Expression left, Expression right)
+ else if (left.Type == typeof(uint) || right.Type == typeof(uint))
{
- return Expression.Subtract(left, right);
+ right = right.Type != typeof(uint) ? Expression.Convert(right, typeof(uint)) : right;
+ left = left.Type != typeof(uint) ? Expression.Convert(left, typeof(uint)) : left;
}
-
- public Expression GenerateEqual(Expression left, Expression right)
+ else if (left.Type == typeof(int) || right.Type == typeof(int))
+ {
+ right = right.Type != typeof(int) ? Expression.Convert(right, typeof(int)) : right;
+ left = left.Type != typeof(int) ? Expression.Convert(left, typeof(int)) : left;
+ }
+ else if (left.Type == typeof(ushort) || right.Type == typeof(ushort))
+ {
+ right = right.Type != typeof(ushort) ? Expression.Convert(right, typeof(ushort)) : right;
+ left = left.Type != typeof(ushort) ? Expression.Convert(left, typeof(ushort)) : left;
+ }
+ else if (left.Type == typeof(short) || right.Type == typeof(short))
{
- OptimizeForEqualityIfPossible(ref left, ref right);
+ right = right.Type != typeof(short) ? Expression.Convert(right, typeof(short)) : right;
+ left = left.Type != typeof(short) ? Expression.Convert(left, typeof(short)) : left;
+ }
+ else if (left.Type == typeof(byte) || right.Type == typeof(byte))
+ {
+ right = right.Type != typeof(byte) ? Expression.Convert(right, typeof(byte)) : right;
+ left = left.Type != typeof(byte) ? Expression.Convert(left, typeof(byte)) : left;
+ }
+ }
- WrapConstantExpressions(ref left, ref right);
+ public Expression GenerateAdd(Expression left, Expression right)
+ {
+ return Expression.Add(left, right);
+ }
- return Expression.Equal(left, right);
- }
+ public Expression GenerateStringConcat(Expression left, Expression right)
+ {
+ return GenerateStaticMethodCall("Concat", left, right);
+ }
- public Expression GenerateNotEqual(Expression left, Expression right)
- {
- OptimizeForEqualityIfPossible(ref left, ref right);
+ public Expression GenerateSubtract(Expression left, Expression right)
+ {
+ return Expression.Subtract(left, right);
+ }
- WrapConstantExpressions(ref left, ref right);
+ public Expression GenerateEqual(Expression left, Expression right)
+ {
+ OptimizeForEqualityIfPossible(ref left, ref right);
- return Expression.NotEqual(left, right);
- }
+ WrapConstantExpressions(ref left, ref right);
- public Expression GenerateGreaterThan(Expression left, Expression right)
- {
- if (left.Type == typeof(string))
- {
- return Expression.GreaterThan(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
- }
+ return Expression.Equal(left, right);
+ }
- if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
- {
- var leftPart = left.Type.GetTypeInfo().IsEnum ? Expression.Convert(left, Enum.GetUnderlyingType(left.Type)) : left;
- var rightPart = right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right;
- return Expression.GreaterThan(leftPart, rightPart);
- }
+ public Expression GenerateNotEqual(Expression left, Expression right)
+ {
+ OptimizeForEqualityIfPossible(ref left, ref right);
+
+ WrapConstantExpressions(ref left, ref right);
- WrapConstantExpressions(ref left, ref right);
+ return Expression.NotEqual(left, right);
+ }
- return Expression.GreaterThan(left, right);
+ public Expression GenerateGreaterThan(Expression left, Expression right)
+ {
+ if (left.Type == typeof(string))
+ {
+ return Expression.GreaterThan(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
}
- public Expression GenerateGreaterThanEqual(Expression left, Expression right)
+ if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
{
- if (left.Type == typeof(string))
- {
- return Expression.GreaterThanOrEqual(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
- }
+ var leftPart = left.Type.GetTypeInfo().IsEnum ? Expression.Convert(left, Enum.GetUnderlyingType(left.Type)) : left;
+ var rightPart = right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right;
+ return Expression.GreaterThan(leftPart, rightPart);
+ }
- if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
- {
- return Expression.GreaterThanOrEqual(left.Type.GetTypeInfo().IsEnum ? Expression.Convert(left, Enum.GetUnderlyingType(left.Type)) : left,
- right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right);
- }
+ WrapConstantExpressions(ref left, ref right);
- WrapConstantExpressions(ref left, ref right);
+ return Expression.GreaterThan(left, right);
+ }
- return Expression.GreaterThanOrEqual(left, right);
+ public Expression GenerateGreaterThanEqual(Expression left, Expression right)
+ {
+ if (left.Type == typeof(string))
+ {
+ return Expression.GreaterThanOrEqual(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
}
- public Expression GenerateLessThan(Expression left, Expression right)
+ if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
{
- if (left.Type == typeof(string))
- {
- return Expression.LessThan(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
- }
+ return Expression.GreaterThanOrEqual(left.Type.GetTypeInfo().IsEnum ? Expression.Convert(left, Enum.GetUnderlyingType(left.Type)) : left,
+ right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right);
+ }
- if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
- {
- return Expression.LessThan(left.Type.GetTypeInfo().IsEnum ? Expression.Convert(left, Enum.GetUnderlyingType(left.Type)) : left,
- right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right);
- }
+ WrapConstantExpressions(ref left, ref right);
- WrapConstantExpressions(ref left, ref right);
+ return Expression.GreaterThanOrEqual(left, right);
+ }
- return Expression.LessThan(left, right);
+ public Expression GenerateLessThan(Expression left, Expression right)
+ {
+ if (left.Type == typeof(string))
+ {
+ return Expression.LessThan(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
}
- public Expression GenerateLessThanEqual(Expression left, Expression right)
+ if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
{
- if (left.Type == typeof(string))
- {
- return Expression.LessThanOrEqual(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
- }
+ return Expression.LessThan(left.Type.GetTypeInfo().IsEnum ? Expression.Convert(left, Enum.GetUnderlyingType(left.Type)) : left,
+ right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right);
+ }
- if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
- {
- return Expression.LessThanOrEqual(left.Type.GetTypeInfo().IsEnum ? Expression.Convert(left, Enum.GetUnderlyingType(left.Type)) : left,
- right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right);
- }
+ WrapConstantExpressions(ref left, ref right);
- WrapConstantExpressions(ref left, ref right);
+ return Expression.LessThan(left, right);
+ }
- return Expression.LessThanOrEqual(left, right);
+ public Expression GenerateLessThanEqual(Expression left, Expression right)
+ {
+ if (left.Type == typeof(string))
+ {
+ return Expression.LessThanOrEqual(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
}
- public void OptimizeForEqualityIfPossible(ref Expression left, ref Expression right)
+ if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
{
- // The goal here is to provide the way to convert some types from the string form in a way that is compatible with Linq to Entities.
- // The Expression.Call(typeof(Guid).GetMethod("Parse"), right); does the job only for Linq to Object but Linq to Entities.
- Type leftType = left.Type;
- Type rightType = right.Type;
+ return Expression.LessThanOrEqual(left.Type.GetTypeInfo().IsEnum ? Expression.Convert(left, Enum.GetUnderlyingType(left.Type)) : left,
+ right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right);
+ }
- if (rightType == typeof(string) && right.NodeType == ExpressionType.Constant)
- {
- right = OptimizeStringForEqualityIfPossible((string?)((ConstantExpression)right).Value, leftType) ?? right;
- }
+ WrapConstantExpressions(ref left, ref right);
- if (leftType == typeof(string) && left.NodeType == ExpressionType.Constant)
- {
- left = OptimizeStringForEqualityIfPossible((string?)((ConstantExpression)left).Value, rightType) ?? left;
- }
+ return Expression.LessThanOrEqual(left, right);
+ }
+
+ public void OptimizeForEqualityIfPossible(ref Expression left, ref Expression right)
+ {
+ // The goal here is to provide the way to convert some types from the string form in a way that is compatible with Linq to Entities.
+ // The Expression.Call(typeof(Guid).GetMethod("Parse"), right); does the job only for Linq to Object but Linq to Entities.
+ Type leftType = left.Type;
+ Type rightType = right.Type;
+
+ if (rightType == typeof(string) && right.NodeType == ExpressionType.Constant)
+ {
+ right = OptimizeStringForEqualityIfPossible((string?)((ConstantExpression)right).Value, leftType) ?? right;
}
- public Expression? OptimizeStringForEqualityIfPossible(string? text, Type type)
+ if (leftType == typeof(string) && left.NodeType == ExpressionType.Constant)
{
- if (type == typeof(DateTime) && DateTime.TryParse(text, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateTime))
- {
- return Expression.Constant(dateTime, typeof(DateTime));
- }
+ left = OptimizeStringForEqualityIfPossible((string?)((ConstantExpression)left).Value, rightType) ?? left;
+ }
+ }
+
+ public Expression? OptimizeStringForEqualityIfPossible(string? text, Type type)
+ {
+ if (type == typeof(DateTime) && DateTime.TryParse(text, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateTime))
+ {
+ return Expression.Constant(dateTime, typeof(DateTime));
+ }
#if !NET35
- if (type == typeof(Guid) && Guid.TryParse(text, out Guid guid))
- {
- return Expression.Constant(guid, typeof(Guid));
- }
+ if (type == typeof(Guid) && Guid.TryParse(text, out Guid guid))
+ {
+ return Expression.Constant(guid, typeof(Guid));
+ }
#else
try
{
@@ -214,191 +226,190 @@ public void OptimizeForEqualityIfPossible(ref Expression left, ref Expression ri
// Doing it in old fashion way when no TryParse interface was provided by .NET
}
#endif
- return null;
- }
+ return null;
+ }
- public bool MemberExpressionIsDynamic(Expression expression)
- {
+ public bool MemberExpressionIsDynamic(Expression expression)
+ {
#if NET35
return false;
#else
- return expression is MemberExpression memberExpression && memberExpression.Member.GetCustomAttribute() != null;
+ return expression is MemberExpression memberExpression && memberExpression.Member.GetCustomAttribute() != null;
#endif
- }
+ }
- public Expression ConvertToExpandoObjectAndCreateDynamicExpression(Expression expression, Type type, string propertyName)
- {
+ public Expression ConvertToExpandoObjectAndCreateDynamicExpression(Expression expression, Type type, string propertyName)
+ {
#if !NET35 && !UAP10_0 && !NETSTANDARD1_3
- return Expression.Dynamic(new DynamicGetMemberBinder(propertyName, _parsingConfig), type, expression);
+ return Expression.Dynamic(new DynamicGetMemberBinder(propertyName, _parsingConfig), type, expression);
#else
throw new NotSupportedException(Res.DynamicExpandoObjectIsNotSupported);
#endif
- }
+ }
- private MethodInfo GetStaticMethod(string methodName, Expression left, Expression right)
+ private MethodInfo GetStaticMethod(string methodName, Expression left, Expression right)
+ {
+ var methodInfo = left.Type.GetMethod(methodName, new[] { left.Type, right.Type });
+ if (methodInfo == null)
{
- var methodInfo = left.Type.GetMethod(methodName, new[] { left.Type, right.Type });
- if (methodInfo == null)
- {
- methodInfo = right.Type.GetMethod(methodName, new[] { left.Type, right.Type })!;
- }
-
- return methodInfo;
+ methodInfo = right.Type.GetMethod(methodName, new[] { left.Type, right.Type })!;
}
- private Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right)
- {
- return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right });
- }
+ return methodInfo;
+ }
- private void WrapConstantExpressions(ref Expression left, ref Expression right)
- {
- if (_parsingConfig.UseParameterizedNamesInDynamicQuery)
- {
- _constantExpressionWrapper.Wrap(ref left);
- _constantExpressionWrapper.Wrap(ref right);
- }
- }
+ private Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right)
+ {
+ return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right });
+ }
- public bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, bool addSelf, out Expression generatedExpression)
+ private void WrapConstantExpressions(ref Expression left, ref Expression right)
+ {
+ if (_parsingConfig.UseParameterizedNamesInDynamicQuery)
{
- var expressions = CollectExpressions(addSelf, sourceExpression);
-
- if (expressions.Count == 1 && !(expressions[0] is MethodCallExpression))
- {
- generatedExpression = sourceExpression;
- return false;
- }
+ _constantExpressionWrapper.Wrap(ref left);
+ _constantExpressionWrapper.Wrap(ref right);
+ }
+ }
- // Reverse the list
- expressions.Reverse();
+ public bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, bool addSelf, out Expression generatedExpression)
+ {
+ var expressions = CollectExpressions(addSelf, sourceExpression);
- // Convert all expressions into '!= null' expressions (only if the type can be null)
- var binaryExpressions = expressions
- .Where(expression => TypeHelper.TypeCanBeNull(expression.Type))
- .Select(expression => Expression.NotEqual(expression, Expression.Constant(null)))
- .ToArray();
+ if (expressions.Count == 1 && !(expressions[0] is MethodCallExpression))
+ {
+ generatedExpression = sourceExpression;
+ return false;
+ }
- // Convert all binary expressions into `AndAlso(...)`
- generatedExpression = binaryExpressions[0];
- for (int i = 1; i < binaryExpressions.Length; i++)
- {
- generatedExpression = Expression.AndAlso(generatedExpression, binaryExpressions[i]);
- }
+ // Reverse the list
+ expressions.Reverse();
- return true;
- }
+ // Convert all expressions into '!= null' expressions (only if the type can be null)
+ var binaryExpressions = expressions
+ .Where(expression => TypeHelper.TypeCanBeNull(expression.Type))
+ .Select(expression => Expression.NotEqual(expression, Expression.Constant(null)))
+ .ToArray();
- public bool ExpressionQualifiesForNullPropagation(Expression? expression)
+ // Convert all binary expressions into `AndAlso(...)`
+ generatedExpression = binaryExpressions[0];
+ for (int i = 1; i < binaryExpressions.Length; i++)
{
- return
- expression is MemberExpression ||
- expression is ParameterExpression ||
- expression is MethodCallExpression ||
- expression is UnaryExpression;
+ generatedExpression = Expression.AndAlso(generatedExpression, binaryExpressions[i]);
}
- public Expression GenerateDefaultExpression(Type type)
- {
+ return true;
+ }
+
+ public bool ExpressionQualifiesForNullPropagation(Expression? expression)
+ {
+ return
+ expression is MemberExpression ||
+ expression is ParameterExpression ||
+ expression is MethodCallExpression ||
+ expression is UnaryExpression;
+ }
+
+ public Expression GenerateDefaultExpression(Type type)
+ {
#if NET35
return Expression.Constant(Activator.CreateInstance(type));
#else
- return Expression.Default(type);
+ return Expression.Default(type);
#endif
+ }
+
+ private Expression? GetMemberExpression(Expression? expression)
+ {
+ if (ExpressionQualifiesForNullPropagation(expression))
+ {
+ return expression;
}
- private Expression? GetMemberExpression(Expression? expression)
+ if (expression is LambdaExpression lambdaExpression)
{
- if (ExpressionQualifiesForNullPropagation(expression))
+ if (lambdaExpression.Body is MemberExpression bodyAsMemberExpression)
{
- return expression;
+ return bodyAsMemberExpression;
}
- if (expression is LambdaExpression lambdaExpression)
+ if (lambdaExpression.Body is UnaryExpression bodyAsUnaryExpression)
{
- if (lambdaExpression.Body is MemberExpression bodyAsMemberExpression)
- {
- return bodyAsMemberExpression;
- }
-
- if (lambdaExpression.Body is UnaryExpression bodyAsUnaryExpression)
- {
- return bodyAsUnaryExpression.Operand;
- }
+ return bodyAsUnaryExpression.Operand;
}
-
- return null;
}
- private List CollectExpressions(bool addSelf, Expression sourceExpression)
- {
- Expression? expression = GetMemberExpression(sourceExpression);
+ return null;
+ }
- var list = new List();
+ private List CollectExpressions(bool addSelf, Expression sourceExpression)
+ {
+ Expression? expression = GetMemberExpression(sourceExpression);
- if (addSelf)
- {
- switch (expression)
- {
- case MemberExpression _:
- list.Add(sourceExpression);
- break;
-
- // ReSharper disable once RedundantEmptySwitchSection
- default:
- break;
- }
- }
+ var list = new List();
- bool expressionRecognized;
- do
+ if (addSelf)
+ {
+ switch (expression)
{
- switch (expression)
- {
- case MemberExpression memberExpression:
- expression = GetMemberExpression(memberExpression.Expression);
- expressionRecognized = expression != null;
- break;
-
- case MethodCallExpression methodCallExpression:
- expression = GetMethodCallExpression(methodCallExpression);
- expressionRecognized = expression != null;
- break;
-
- case UnaryExpression unaryExpression:
- expression = GetUnaryExpression(unaryExpression);
- expressionRecognized = expression != null;
- break;
-
- default:
- expressionRecognized = false;
- break;
- }
-
- if (expressionRecognized && ExpressionQualifiesForNullPropagation(expression))
- {
- list.Add(expression!);
- }
- } while (expressionRecognized);
-
- return list;
+ case MemberExpression _:
+ list.Add(sourceExpression);
+ break;
+
+ // ReSharper disable once RedundantEmptySwitchSection
+ default:
+ break;
+ }
}
- private static Expression? GetMethodCallExpression(MethodCallExpression methodCallExpression)
+ bool expressionRecognized;
+ do
{
- if (methodCallExpression.Object != null)
+ switch (expression)
+ {
+ case MemberExpression memberExpression:
+ expression = GetMemberExpression(memberExpression.Expression);
+ expressionRecognized = expression != null;
+ break;
+
+ case MethodCallExpression methodCallExpression:
+ expression = GetMethodCallExpression(methodCallExpression);
+ expressionRecognized = expression != null;
+ break;
+
+ case UnaryExpression unaryExpression:
+ expression = GetUnaryExpression(unaryExpression);
+ expressionRecognized = expression != null;
+ break;
+
+ default:
+ expressionRecognized = false;
+ break;
+ }
+
+ if (expressionRecognized && ExpressionQualifiesForNullPropagation(expression))
{
- // Something like: "np(FooValue.Zero().Length)"
- return methodCallExpression.Object;
+ list.Add(expression!);
}
+ } while (expressionRecognized);
- // Something like: "np(MyClasses.FirstOrDefault())"
- return methodCallExpression.Arguments.FirstOrDefault();
- }
+ return list;
+ }
- private static Expression? GetUnaryExpression(UnaryExpression? unaryExpression)
+ private static Expression? GetMethodCallExpression(MethodCallExpression methodCallExpression)
+ {
+ if (methodCallExpression.Object != null)
{
- return unaryExpression?.Operand;
+ // Something like: "np(FooValue.Zero().Length)"
+ return methodCallExpression.Object;
}
+
+ // Something like: "np(MyClasses.FirstOrDefault())"
+ return methodCallExpression.Arguments.FirstOrDefault();
+ }
+
+ private static Expression? GetUnaryExpression(UnaryExpression? unaryExpression)
+ {
+ return unaryExpression?.Operand;
}
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
index dad76ebd..cb840f6a 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
@@ -12,2202 +12,2209 @@
using System.Reflection;
using AnyOfTypes;
-namespace System.Linq.Dynamic.Core.Parser
+namespace System.Linq.Dynamic.Core.Parser;
+
+///
+/// ExpressionParser
+///
+public class ExpressionParser
{
+ private static readonly string methodOrderBy = nameof(Queryable.OrderBy);
+ private static readonly string methodOrderByDescending = nameof(Queryable.OrderByDescending);
+ private static readonly string methodThenBy = nameof(Queryable.ThenBy);
+ private static readonly string methodThenByDescending = nameof(Queryable.ThenByDescending);
+
+ private readonly ParsingConfig _parsingConfig;
+ private readonly MethodFinder _methodFinder;
+ private readonly IKeywordsHelper _keywordsHelper;
+ private readonly TextParser _textParser;
+ private readonly NumberParser _numberParser;
+ private readonly IExpressionHelper _expressionHelper;
+ private readonly ITypeFinder _typeFinder;
+ private readonly ITypeConverterFactory _typeConverterFactory;
+ private readonly Dictionary _internals = new();
+ private readonly Dictionary _symbols;
+
+ private IDictionary? _externals;
+ private ParameterExpression? _it;
+ private ParameterExpression? _parent;
+ private ParameterExpression? _root;
+ private Type? _resultType;
+ private bool _createParameterCtor;
+
///
- /// ExpressionParser
+ /// Gets name for the `it` field. By default this is set to the KeyWord value "it".
///
- public class ExpressionParser
- {
- private static readonly string methodOrderBy = nameof(Queryable.OrderBy);
- private static readonly string methodOrderByDescending = nameof(Queryable.OrderByDescending);
- private static readonly string methodThenBy = nameof(Queryable.ThenBy);
- private static readonly string methodThenByDescending = nameof(Queryable.ThenByDescending);
+ public string ItName { get; private set; } = KeywordsHelper.KEYWORD_IT;
- private readonly ParsingConfig _parsingConfig;
- private readonly MethodFinder _methodFinder;
- private readonly IKeywordsHelper _keywordsHelper;
- private readonly TextParser _textParser;
- private readonly NumberParser _numberParser;
- private readonly IExpressionHelper _expressionHelper;
- private readonly ITypeFinder _typeFinder;
- private readonly ITypeConverterFactory _typeConverterFactory;
- private readonly Dictionary _internals = new();
- private readonly Dictionary _symbols;
+ ///
+ /// There was a problem when an expression contained multiple lambdas where
+ /// the ItName was not cleared and freed for the next lambda. This variable
+ /// stores the ItName of the last parsed lambda.
+ /// Not used internally by ExpressionParser, but used to preserve compatiblity of parsingConfig.RenameParameterExpression
+ /// which was designed to only work with mono-lambda expressions.
+ ///
+ public string LastLambdaItName { get; private set; } = KeywordsHelper.KEYWORD_IT;
- private IDictionary? _externals;
- private ParameterExpression? _it;
- private ParameterExpression? _parent;
- private ParameterExpression? _root;
- private Type? _resultType;
- private bool _createParameterCtor;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The parameters.
+ /// The expression.
+ /// The values.
+ /// The parsing configuration.
+ public ExpressionParser(ParameterExpression[]? parameters, string expression, object?[]? values, ParsingConfig? parsingConfig)
+ {
+ Check.NotEmpty(expression, nameof(expression));
- ///
- /// Gets name for the `it` field. By default this is set to the KeyWord value "it".
- ///
- public string ItName { get; private set; } = KeywordsHelper.KEYWORD_IT;
+ _symbols = new Dictionary(parsingConfig is { IsCaseSensitive: true } ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase);
+ _parsingConfig = parsingConfig ?? ParsingConfig.Default;
- ///
- /// There was a problem when an expression contained multiple lambdas where
- /// the ItName was not cleared and freed for the next lambda. This variable
- /// stores the ItName of the last parsed lambda.
- /// Not used internally by ExpressionParser, but used to preserve compatiblity of parsingConfig.RenameParameterExpression
- /// which was designed to only work with mono-lambda expressions.
- ///
- public string LastLambdaItName { get; private set; } = KeywordsHelper.KEYWORD_IT;
+ _keywordsHelper = new KeywordsHelper(_parsingConfig);
+ _textParser = new TextParser(_parsingConfig, expression);
+ _numberParser = new NumberParser(parsingConfig);
+ _methodFinder = new MethodFinder(_parsingConfig);
+ _expressionHelper = new ExpressionHelper(_parsingConfig);
+ _typeFinder = new TypeFinder(_parsingConfig, _keywordsHelper);
+ _typeConverterFactory = new TypeConverterFactory(_parsingConfig);
- ///
- /// Initializes a new instance of the class.
- ///
- /// The parameters.
- /// The expression.
- /// The values.
- /// The parsing configuration.
- public ExpressionParser(ParameterExpression[]? parameters, string expression, object?[]? values, ParsingConfig? parsingConfig)
+ if (parameters != null)
{
- Check.NotEmpty(expression, nameof(expression));
+ ProcessParameters(parameters);
+ }
- _symbols = new Dictionary(parsingConfig is { IsCaseSensitive: true } ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase);
- _parsingConfig = parsingConfig ?? ParsingConfig.Default;
+ if (values != null)
+ {
+ ProcessValues(values);
+ }
+ }
- _keywordsHelper = new KeywordsHelper(_parsingConfig);
- _textParser = new TextParser(_parsingConfig, expression);
- _numberParser = new NumberParser(parsingConfig);
- _methodFinder = new MethodFinder(_parsingConfig);
- _expressionHelper = new ExpressionHelper(_parsingConfig);
- _typeFinder = new TypeFinder(_parsingConfig, _keywordsHelper);
- _typeConverterFactory = new TypeConverterFactory(_parsingConfig);
+ private void ProcessParameters(ParameterExpression[] parameters)
+ {
+ foreach (ParameterExpression pe in parameters.Where(p => !string.IsNullOrEmpty(p.Name)))
+ {
+ AddSymbol(pe.Name, pe);
+ }
- if (parameters != null)
- {
- ProcessParameters(parameters);
- }
+ // If there is only 1 ParameterExpression, do also allow access using 'it'
+ if (parameters.Length == 1)
+ {
+ _parent = _it;
+ _it = parameters[0];
- if (values != null)
+ if (_root == null)
{
- ProcessValues(values);
+ _root = _it;
}
}
+ }
- private void ProcessParameters(ParameterExpression[] parameters)
+ private void ProcessValues(object?[] values)
+ {
+ for (int i = 0; i < values.Length; i++)
{
- foreach (ParameterExpression pe in parameters.Where(p => !string.IsNullOrEmpty(p.Name)))
- {
- AddSymbol(pe.Name, pe);
- }
+ object? value = values[i];
+ IDictionary? externals;
- // If there is only 1 ParameterExpression, do also allow access using 'it'
- if (parameters.Length == 1)
+ if (i == values.Length - 1 && (externals = value as IDictionary) != null)
{
- _parent = _it;
- _it = parameters[0];
-
- if (_root == null)
- {
- _root = _it;
- }
+ _externals = externals;
}
- }
-
- private void ProcessValues(object?[] values)
- {
- for (int i = 0; i < values.Length; i++)
+ else
{
- object? value = values[i];
- IDictionary? externals;
-
- if (i == values.Length - 1 && (externals = value as IDictionary) != null)
- {
- _externals = externals;
- }
- else
- {
- AddSymbol("@" + i.ToString(CultureInfo.InvariantCulture), value);
- }
+ AddSymbol("@" + i.ToString(CultureInfo.InvariantCulture), value);
}
}
+ }
- private void AddSymbol(string name, object? value)
+ private void AddSymbol(string name, object? value)
+ {
+ if (_symbols.ContainsKey(name))
{
- if (_symbols.ContainsKey(name))
- {
- throw ParseError(Res.DuplicateIdentifier, name);
- }
-
- _symbols.Add(name, value);
+ throw ParseError(Res.DuplicateIdentifier, name);
}
- ///
- /// Uses the TextParser to parse the string into the specified result type.
- ///
- /// Type of the result.
- /// if set to true [create parameter ctor].
- /// Expression
- public Expression Parse(Type? resultType, bool createParameterCtor = true)
- {
- _resultType = resultType;
- _createParameterCtor = createParameterCtor;
+ _symbols.Add(name, value);
+ }
- int exprPos = _textParser.CurrentToken.Pos;
- Expression? expr = ParseConditionalOperator();
+ ///
+ /// Uses the TextParser to parse the string into the specified result type.
+ ///
+ /// Type of the result.
+ /// if set to true [create parameter ctor].
+ /// Expression
+ public Expression Parse(Type? resultType, bool createParameterCtor = true)
+ {
+ _resultType = resultType;
+ _createParameterCtor = createParameterCtor;
- if (resultType != null)
+ int exprPos = _textParser.CurrentToken.Pos;
+ Expression? expr = ParseConditionalOperator();
+
+ if (resultType != null)
+ {
+ if ((expr = _parsingConfig.ExpressionPromoter.Promote(expr, resultType, true, false)) == null)
{
- if ((expr = _parsingConfig.ExpressionPromoter.Promote(expr, resultType, true, false)) == null)
- {
- throw ParseError(exprPos, Res.ExpressionTypeMismatch, TypeHelper.GetTypeName(resultType));
- }
+ throw ParseError(exprPos, Res.ExpressionTypeMismatch, TypeHelper.GetTypeName(resultType));
}
+ }
- _textParser.ValidateToken(TokenId.End, Res.SyntaxError);
+ _textParser.ValidateToken(TokenId.End, Res.SyntaxError);
- return expr;
- }
+ return expr;
+ }
#pragma warning disable 0219
- internal IList ParseOrdering(bool forceThenBy = false)
+ internal IList ParseOrdering(bool forceThenBy = false)
+ {
+ var orderings = new List();
+ while (true)
{
- var orderings = new List();
- while (true)
+ Expression expr = ParseConditionalOperator();
+ bool ascending = true;
+ if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending"))
{
- Expression expr = ParseConditionalOperator();
- bool ascending = true;
- if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending"))
- {
- _textParser.NextToken();
- }
- else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending"))
- {
- _textParser.NextToken();
- ascending = false;
- }
-
- string methodName;
- if (forceThenBy || orderings.Count > 0)
- {
- methodName = ascending ? methodThenBy : methodThenByDescending;
- }
- else
- {
- methodName = ascending ? methodOrderBy : methodOrderByDescending;
- }
+ _textParser.NextToken();
+ }
+ else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending"))
+ {
+ _textParser.NextToken();
+ ascending = false;
+ }
- orderings.Add(new DynamicOrdering { Selector = expr, Ascending = ascending, MethodName = methodName });
+ string methodName;
+ if (forceThenBy || orderings.Count > 0)
+ {
+ methodName = ascending ? methodThenBy : methodThenByDescending;
+ }
+ else
+ {
+ methodName = ascending ? methodOrderBy : methodOrderByDescending;
+ }
- if (_textParser.CurrentToken.Id != TokenId.Comma)
- {
- break;
- }
+ orderings.Add(new DynamicOrdering { Selector = expr, Ascending = ascending, MethodName = methodName });
- _textParser.NextToken();
+ if (_textParser.CurrentToken.Id != TokenId.Comma)
+ {
+ break;
}
- _textParser.ValidateToken(TokenId.End, Res.SyntaxError);
- return orderings;
+ _textParser.NextToken();
}
+
+ _textParser.ValidateToken(TokenId.End, Res.SyntaxError);
+ return orderings;
+ }
#pragma warning restore 0219
- // ?: operator
- private Expression ParseConditionalOperator()
+ // ?: operator
+ private Expression ParseConditionalOperator()
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ Expression expr = ParseNullCoalescingOperator();
+ if (_textParser.CurrentToken.Id == TokenId.Question)
{
- int errorPos = _textParser.CurrentToken.Pos;
- Expression expr = ParseNullCoalescingOperator();
- if (_textParser.CurrentToken.Id == TokenId.Question)
- {
- _textParser.NextToken();
- Expression expr1 = ParseConditionalOperator();
- _textParser.ValidateToken(TokenId.Colon, Res.ColonExpected);
- _textParser.NextToken();
- Expression expr2 = ParseConditionalOperator();
- expr = GenerateConditional(expr, expr1, expr2, false, errorPos);
- }
- return expr;
+ _textParser.NextToken();
+ Expression expr1 = ParseConditionalOperator();
+ _textParser.ValidateToken(TokenId.Colon, Res.ColonExpected);
+ _textParser.NextToken();
+ Expression expr2 = ParseConditionalOperator();
+ expr = GenerateConditional(expr, expr1, expr2, false, errorPos);
}
+ return expr;
+ }
- // ?? (null-coalescing) operator
- private Expression ParseNullCoalescingOperator()
+ // ?? (null-coalescing) operator
+ private Expression ParseNullCoalescingOperator()
+ {
+ Expression expr = ParseLambdaOperator();
+ if (_textParser.CurrentToken.Id == TokenId.NullCoalescing)
{
- Expression expr = ParseLambdaOperator();
- if (_textParser.CurrentToken.Id == TokenId.NullCoalescing)
- {
- _textParser.NextToken();
- Expression right = ParseConditionalOperator();
- expr = Expression.Coalesce(expr, right);
- }
- return expr;
+ _textParser.NextToken();
+ Expression right = ParseConditionalOperator();
+ expr = Expression.Coalesce(expr, right);
}
+ return expr;
+ }
- // => operator - Added Support for projection operator
- private Expression ParseLambdaOperator()
+ // => operator - Added Support for projection operator
+ private Expression ParseLambdaOperator()
+ {
+ Expression expr = ParseOrOperator();
+ if (_textParser.CurrentToken.Id == TokenId.Lambda && _it?.Type == expr.Type)
{
- Expression expr = ParseOrOperator();
- if (_textParser.CurrentToken.Id == TokenId.Lambda && _it?.Type == expr.Type)
+ _textParser.NextToken();
+ if (_textParser.CurrentToken.Id == TokenId.Identifier || _textParser.CurrentToken.Id == TokenId.OpenParen)
{
- _textParser.NextToken();
- if (_textParser.CurrentToken.Id == TokenId.Identifier || _textParser.CurrentToken.Id == TokenId.OpenParen)
- {
- var right = ParseConditionalOperator();
- return Expression.Lambda(right, new[] { (ParameterExpression)expr });
- }
- _textParser.ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
+ var right = ParseConditionalOperator();
+ return Expression.Lambda(right, new[] { (ParameterExpression)expr });
}
- return expr;
+ _textParser.ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
}
+ return expr;
+ }
- // Or operator
- // - ||
- // - Or
- // - OrElse
- private Expression ParseOrOperator()
+ // Or operator
+ // - ||
+ // - Or
+ // - OrElse
+ private Expression ParseOrOperator()
+ {
+ Expression left = ParseAndOperator();
+ while (_textParser.CurrentToken.Id == TokenId.DoubleBar)
{
- Expression left = ParseAndOperator();
- while (_textParser.CurrentToken.Id == TokenId.DoubleBar)
- {
- Token op = _textParser.CurrentToken;
- _textParser.NextToken();
- Expression right = ParseAndOperator();
- CheckAndPromoteOperands(typeof(ILogicalSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
- left = Expression.OrElse(left, right);
- }
- return left;
+ Token op = _textParser.CurrentToken;
+ _textParser.NextToken();
+ Expression right = ParseAndOperator();
+ CheckAndPromoteOperands(typeof(ILogicalSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
+ left = Expression.OrElse(left, right);
}
+ return left;
+ }
- // And operator
- // - &&
- // - And
- // - AndAlso
- private Expression ParseAndOperator()
+ // And operator
+ // - &&
+ // - And
+ // - AndAlso
+ private Expression ParseAndOperator()
+ {
+ Expression left = ParseIn();
+ while (_textParser.CurrentToken.Id == TokenId.DoubleAmpersand)
{
- Expression left = ParseIn();
- while (_textParser.CurrentToken.Id == TokenId.DoubleAmpersand)
- {
- Token op = _textParser.CurrentToken;
- _textParser.NextToken();
- Expression right = ParseIn();
- CheckAndPromoteOperands(typeof(ILogicalSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
- left = Expression.AndAlso(left, right);
- }
- return left;
+ Token op = _textParser.CurrentToken;
+ _textParser.NextToken();
+ Expression right = ParseIn();
+ CheckAndPromoteOperands(typeof(ILogicalSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
+ left = Expression.AndAlso(left, right);
}
+ return left;
+ }
+
+ // in operator for literals - example: "x in (1,2,3,4)"
+ // in operator to mimic contains - example: "x in @0", compare to @0.Contains(x)
+ // Adapted from ticket submitted by github user mlewis9548
+ private Expression ParseIn()
+ {
+ Expression left = ParseLogicalAndOrOperator();
+ Expression accumulate = left;
- // in operator for literals - example: "x in (1,2,3,4)"
- // in operator to mimic contains - example: "x in @0", compare to @0.Contains(x)
- // Adapted from ticket submitted by github user mlewis9548
- private Expression ParseIn()
+ while (TokenIdentifierIs("in"))
{
- Expression left = ParseLogicalAndOrOperator();
- Expression accumulate = left;
+ var op = _textParser.CurrentToken;
- while (TokenIdentifierIs("in"))
+ _textParser.NextToken();
+ if (_textParser.CurrentToken.Id == TokenId.OpenParen) // literals (or other inline list)
{
- var op = _textParser.CurrentToken;
-
- _textParser.NextToken();
- if (_textParser.CurrentToken.Id == TokenId.OpenParen) // literals (or other inline list)
+ while (_textParser.CurrentToken.Id != TokenId.CloseParen)
{
- while (_textParser.CurrentToken.Id != TokenId.CloseParen)
- {
- _textParser.NextToken();
-
- // we need to parse unary expressions because otherwise 'in' clause will fail in use cases like 'in (-1, -1)' or 'in (!true)'
- Expression right = ParseUnary();
-
- // if the identifier is an Enum, try to convert the right-side also to an Enum.
- if (left.Type.GetTypeInfo().IsEnum && right is ConstantExpression constantExpression)
- {
- right = ParseEnumToConstantExpression(op.Pos, left.Type, constantExpression);
- }
-
- // else, check for direct type match
- else if (left.Type != right.Type)
- {
- CheckAndPromoteOperands(typeof(IEqualitySignatures), TokenId.DoubleEqual, "==", ref left, ref right, op.Pos);
- }
+ _textParser.NextToken();
- if (accumulate.Type != typeof(bool))
- {
- accumulate = _expressionHelper.GenerateEqual(left, right);
- }
- else
- {
- accumulate = Expression.OrElse(accumulate, _expressionHelper.GenerateEqual(left, right));
- }
+ // we need to parse unary expressions because otherwise 'in' clause will fail in use cases like 'in (-1, -1)' or 'in (!true)'
+ Expression right = ParseUnary();
- if (_textParser.CurrentToken.Id == TokenId.End)
- {
- throw ParseError(op.Pos, Res.CloseParenOrCommaExpected);
- }
+ // if the identifier is an Enum, try to convert the right-side also to an Enum.
+ if (left.Type.GetTypeInfo().IsEnum && right is ConstantExpression constantExpression)
+ {
+ right = ParseEnumToConstantExpression(op.Pos, left.Type, constantExpression);
}
- // Since this started with an open paren, make sure to move off the close
- _textParser.NextToken();
- }
- else if (_textParser.CurrentToken.Id == TokenId.Identifier) // a single argument
- {
- Expression right = ParsePrimary();
-
- if (!typeof(IEnumerable).IsAssignableFrom(right.Type))
+ // else, check for direct type match
+ else if (left.Type != right.Type)
{
- throw ParseError(_textParser.CurrentToken.Pos, Res.IdentifierImplementingInterfaceExpected, typeof(IEnumerable));
+ CheckAndPromoteOperands(typeof(IEqualitySignatures), TokenId.DoubleEqual, "==", ref left, ref right, op.Pos);
}
- var args = new[] { left };
-
- Expression? nullExpressionReference = null;
- if (_methodFinder.FindMethod(typeof(IEnumerableSignatures), nameof(IEnumerableSignatures.Contains), false, ref nullExpressionReference, ref args, out var containsSignature) != 1)
+ if (accumulate.Type != typeof(bool))
{
- throw ParseError(op.Pos, Res.NoApplicableAggregate, nameof(IEnumerableSignatures.Contains), string.Join(",", args.Select(a => a.Type.Name).ToArray()));
+ accumulate = _expressionHelper.GenerateEqual(left, right);
+ }
+ else
+ {
+ accumulate = Expression.OrElse(accumulate, _expressionHelper.GenerateEqual(left, right));
}
- var typeArgs = new[] { left.Type };
-
- args = new[] { right, left };
-
- accumulate = Expression.Call(typeof(Enumerable), containsSignature!.Name, typeArgs, args);
- }
- else
- {
- throw ParseError(op.Pos, Res.OpenParenOrIdentifierExpected);
+ if (_textParser.CurrentToken.Id == TokenId.End)
+ {
+ throw ParseError(op.Pos, Res.CloseParenOrCommaExpected);
+ }
}
- }
-
- return accumulate;
- }
- // &, | bitwise operators
- private Expression ParseLogicalAndOrOperator()
- {
- Expression left = ParseComparisonOperator();
- while (_textParser.CurrentToken.Id == TokenId.Ampersand || _textParser.CurrentToken.Id == TokenId.Bar)
- {
- Token op = _textParser.CurrentToken;
+ // Since this started with an open paren, make sure to move off the close
_textParser.NextToken();
- Expression right = ParseComparisonOperator();
+ }
+ else if (_textParser.CurrentToken.Id == TokenId.Identifier) // a single argument
+ {
+ Expression right = ParsePrimary();
- if (left.Type.GetTypeInfo().IsEnum)
+ if (!typeof(IEnumerable).IsAssignableFrom(right.Type))
{
- left = Expression.Convert(left, Enum.GetUnderlyingType(left.Type));
+ throw ParseError(_textParser.CurrentToken.Pos, Res.IdentifierImplementingInterfaceExpected, typeof(IEnumerable));
}
- if (right.Type.GetTypeInfo().IsEnum)
+ var args = new[] { left };
+
+ Expression? nullExpressionReference = null;
+ if (_methodFinder.FindMethod(typeof(IEnumerableSignatures), nameof(IEnumerableSignatures.Contains), false, ref nullExpressionReference, ref args, out var containsSignature) != 1)
{
- right = Expression.Convert(right, Enum.GetUnderlyingType(right.Type));
+ throw ParseError(op.Pos, Res.NoApplicableAggregate, nameof(IEnumerableSignatures.Contains), string.Join(",", args.Select(a => a.Type.Name).ToArray()));
}
- switch (op.Id)
- {
- case TokenId.Ampersand:
- if (left.Type == typeof(string) && left.NodeType == ExpressionType.Constant && int.TryParse((string)((ConstantExpression)left).Value, out var parseValue) && TypeHelper.IsNumericType(right.Type))
- {
- left = Expression.Constant(parseValue);
- }
- else if (right.Type == typeof(string) && right.NodeType == ExpressionType.Constant && int.TryParse((string)((ConstantExpression)right).Value, out parseValue) && TypeHelper.IsNumericType(left.Type))
- {
- right = Expression.Constant(parseValue);
- }
+ var typeArgs = new[] { left.Type };
- // When at least one side of the operator is a string, consider it's a VB-style concatenation operator.
- // Doesn't break any other function since logical AND with a string is invalid anyway.
- if (left.Type == typeof(string) || right.Type == typeof(string))
- {
- left = _expressionHelper.GenerateStringConcat(left, right);
- }
- else
- {
- _expressionHelper.ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref left, ref right);
- left = Expression.And(left, right);
- }
- break;
+ args = new[] { right, left };
- case TokenId.Bar:
- _expressionHelper.ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref left, ref right);
- left = Expression.Or(left, right);
- break;
- }
+ accumulate = Expression.Call(typeof(Enumerable), containsSignature!.Name, typeArgs, args);
+ }
+ else
+ {
+ throw ParseError(op.Pos, Res.OpenParenOrIdentifierExpected);
}
- return left;
}
- // =, ==, !=, <>, >, >=, <, <= operators
- private Expression ParseComparisonOperator()
+ return accumulate;
+ }
+
+ // &, | bitwise operators
+ private Expression ParseLogicalAndOrOperator()
+ {
+ Expression left = ParseComparisonOperator();
+ while (_textParser.CurrentToken.Id == TokenId.Ampersand || _textParser.CurrentToken.Id == TokenId.Bar)
{
- Expression left = ParseShiftOperator();
- while (_textParser.CurrentToken.Id == TokenId.Equal || _textParser.CurrentToken.Id == TokenId.DoubleEqual ||
- _textParser.CurrentToken.Id == TokenId.ExclamationEqual || _textParser.CurrentToken.Id == TokenId.LessGreater ||
- _textParser.CurrentToken.Id == TokenId.GreaterThan || _textParser.CurrentToken.Id == TokenId.GreaterThanEqual ||
- _textParser.CurrentToken.Id == TokenId.LessThan || _textParser.CurrentToken.Id == TokenId.LessThanEqual)
+ Token op = _textParser.CurrentToken;
+ _textParser.NextToken();
+ Expression right = ParseComparisonOperator();
+
+ if (left.Type.GetTypeInfo().IsEnum)
{
- ConstantExpression? constantExpr;
- TypeConverter typeConverter;
- Token op = _textParser.CurrentToken;
- _textParser.NextToken();
- Expression right = ParseShiftOperator();
- bool isEquality = op.Id == TokenId.Equal || op.Id == TokenId.DoubleEqual || op.Id == TokenId.ExclamationEqual || op.Id == TokenId.LessGreater;
+ left = Expression.Convert(left, Enum.GetUnderlyingType(left.Type));
+ }
- if (isEquality && (!left.Type.GetTypeInfo().IsValueType && !right.Type.GetTypeInfo().IsValueType || left.Type == typeof(Guid) && right.Type == typeof(Guid)))
- {
- // If left or right is NullLiteral, just continue. Else check if the types differ.
- if (!(Constants.IsNull(left) || Constants.IsNull(right)) && left.Type != right.Type)
+ if (right.Type.GetTypeInfo().IsEnum)
+ {
+ right = Expression.Convert(right, Enum.GetUnderlyingType(right.Type));
+ }
+
+ switch (op.Id)
+ {
+ case TokenId.Ampersand:
+ if (left.Type == typeof(string) && left.NodeType == ExpressionType.Constant && int.TryParse((string)((ConstantExpression)left).Value, out var parseValue) && TypeHelper.IsNumericType(right.Type))
{
- if (left.Type.IsAssignableFrom(right.Type) || HasImplicitConversion(right.Type, left.Type))
- {
- right = Expression.Convert(right, left.Type);
- }
- else if (right.Type.IsAssignableFrom(left.Type) || HasImplicitConversion(left.Type, right.Type))
- {
- left = Expression.Convert(left, right.Type);
- }
- else
- {
- throw IncompatibleOperandsError(op.Text, left, right, op.Pos);
- }
+ left = Expression.Constant(parseValue);
}
- }
- else if (TypeHelper.IsEnumType(left.Type) || TypeHelper.IsEnumType(right.Type))
+ else if (right.Type == typeof(string) && right.NodeType == ExpressionType.Constant && int.TryParse((string)((ConstantExpression)right).Value, out parseValue) && TypeHelper.IsNumericType(left.Type))
+ {
+ right = Expression.Constant(parseValue);
+ }
+
+ // When at least one side of the operator is a string, consider it's a VB-style concatenation operator.
+ // Doesn't break any other function since logical AND with a string is invalid anyway.
+ if (left.Type == typeof(string) || right.Type == typeof(string))
+ {
+ left = _expressionHelper.GenerateStringConcat(left, right);
+ }
+ else
+ {
+ _expressionHelper.ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref left, ref right);
+ left = Expression.And(left, right);
+ }
+ break;
+
+ case TokenId.Bar:
+ _expressionHelper.ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref left, ref right);
+ left = Expression.Or(left, right);
+ break;
+ }
+ }
+ return left;
+ }
+
+ // =, ==, !=, <>, >, >=, <, <= operators
+ private Expression ParseComparisonOperator()
+ {
+ Expression left = ParseShiftOperator();
+ while (_textParser.CurrentToken.Id == TokenId.Equal || _textParser.CurrentToken.Id == TokenId.DoubleEqual ||
+ _textParser.CurrentToken.Id == TokenId.ExclamationEqual || _textParser.CurrentToken.Id == TokenId.LessGreater ||
+ _textParser.CurrentToken.Id == TokenId.GreaterThan || _textParser.CurrentToken.Id == TokenId.GreaterThanEqual ||
+ _textParser.CurrentToken.Id == TokenId.LessThan || _textParser.CurrentToken.Id == TokenId.LessThanEqual)
+ {
+ ConstantExpression? constantExpr;
+ TypeConverter typeConverter;
+ Token op = _textParser.CurrentToken;
+ _textParser.NextToken();
+ Expression right = ParseShiftOperator();
+ bool isEquality = op.Id == TokenId.Equal || op.Id == TokenId.DoubleEqual || op.Id == TokenId.ExclamationEqual || op.Id == TokenId.LessGreater;
+
+ if (isEquality && (!left.Type.GetTypeInfo().IsValueType && !right.Type.GetTypeInfo().IsValueType || left.Type == typeof(Guid) && right.Type == typeof(Guid)))
+ {
+ // If left or right is NullLiteral, just continue. Else check if the types differ.
+ if (!(Constants.IsNull(left) || Constants.IsNull(right)) && left.Type != right.Type)
{
- if (left.Type != right.Type)
+ if (left.Type.IsAssignableFrom(right.Type) || HasImplicitConversion(right.Type, left.Type))
{
- Expression? e;
- if ((e = _parsingConfig.ExpressionPromoter.Promote(right, left.Type, true, false)) != null)
- {
- right = e;
- }
- else if ((e = _parsingConfig.ExpressionPromoter.Promote(left, right.Type, true, false)) != null)
- {
- left = e;
- }
- else if (TypeHelper.IsEnumType(left.Type) && (constantExpr = right as ConstantExpression) != null)
- {
- right = ParseEnumToConstantExpression(op.Pos, left.Type, constantExpr);
- }
- else if (TypeHelper.IsEnumType(right.Type) && (constantExpr = left as ConstantExpression) != null)
- {
- left = ParseEnumToConstantExpression(op.Pos, right.Type, constantExpr);
- }
- else
- {
- throw IncompatibleOperandsError(op.Text, left, right, op.Pos);
- }
+ right = Expression.Convert(right, left.Type);
+ }
+ else if (right.Type.IsAssignableFrom(left.Type) || HasImplicitConversion(left.Type, right.Type))
+ {
+ left = Expression.Convert(left, right.Type);
+ }
+ else
+ {
+ throw IncompatibleOperandsError(op.Text, left, right, op.Pos);
}
}
- else if ((constantExpr = right as ConstantExpression) != null && constantExpr.Value is string stringValueR && (typeConverter = _typeConverterFactory.GetConverter(left.Type)) != null)
+ }
+ else if (TypeHelper.IsEnumType(left.Type) || TypeHelper.IsEnumType(right.Type))
+ {
+ if (left.Type != right.Type)
{
- right = Expression.Constant(typeConverter.ConvertFromInvariantString(stringValueR), left.Type);
+ Expression? e;
+ if ((e = _parsingConfig.ExpressionPromoter.Promote(right, left.Type, true, false)) != null)
+ {
+ right = e;
+ }
+ else if ((e = _parsingConfig.ExpressionPromoter.Promote(left, right.Type, true, false)) != null)
+ {
+ left = e;
+ }
+ else if (TypeHelper.IsEnumType(left.Type) && (constantExpr = right as ConstantExpression) != null)
+ {
+ right = ParseEnumToConstantExpression(op.Pos, left.Type, constantExpr);
+ }
+ else if (TypeHelper.IsEnumType(right.Type) && (constantExpr = left as ConstantExpression) != null)
+ {
+ left = ParseEnumToConstantExpression(op.Pos, right.Type, constantExpr);
+ }
+ else
+ {
+ throw IncompatibleOperandsError(op.Text, left, right, op.Pos);
+ }
}
- else if ((constantExpr = left as ConstantExpression) != null && constantExpr.Value is string stringValueL && (typeConverter = _typeConverterFactory.GetConverter(right.Type)) != null)
+ }
+ else if ((constantExpr = right as ConstantExpression) != null && constantExpr.Value is string stringValueR && (typeConverter = _typeConverterFactory.GetConverter(left.Type)) != null)
+ {
+ right = Expression.Constant(typeConverter.ConvertFromInvariantString(stringValueR), left.Type);
+ }
+ else if ((constantExpr = left as ConstantExpression) != null && constantExpr.Value is string stringValueL && (typeConverter = _typeConverterFactory.GetConverter(right.Type)) != null)
+ {
+ left = Expression.Constant(typeConverter.ConvertFromInvariantString(stringValueL), right.Type);
+ }
+ else if (_expressionHelper.TryUnwrapConstantExpression(right, out var unwrappedStringValueR) && (typeConverter = _typeConverterFactory.GetConverter(left.Type)) != null)
+ {
+ right = Expression.Constant(typeConverter.ConvertFromInvariantString(unwrappedStringValueR), left.Type);
+ }
+ else if (_expressionHelper.TryUnwrapConstantExpression(left, out var unwrappedStringValueL) && (typeConverter = _typeConverterFactory.GetConverter(right.Type)) != null)
+ {
+ left = Expression.Constant(typeConverter.ConvertFromInvariantString(unwrappedStringValueL), right.Type);
+ }
+ else
+ {
+ bool typesAreSameAndImplementCorrectInterface = false;
+ if (left.Type == right.Type)
{
- left = Expression.Constant(typeConverter.ConvertFromInvariantString(stringValueL), right.Type);
+ var interfaces = left.Type.GetInterfaces().Where(x => x.GetTypeInfo().IsGenericType);
+ if (isEquality)
+ {
+ typesAreSameAndImplementCorrectInterface = interfaces.Any(x => x.GetGenericTypeDefinition() == typeof(IEquatable<>));
+ }
+ else
+ {
+ typesAreSameAndImplementCorrectInterface = interfaces.Any(x => x.GetGenericTypeDefinition() == typeof(IComparable<>));
+ }
}
- else
+
+ if (!typesAreSameAndImplementCorrectInterface)
{
- bool typesAreSameAndImplementCorrectInterface = false;
- if (left.Type == right.Type)
+ if ((TypeHelper.IsClass(left.Type) || TypeHelper.IsStruct(left.Type)) && right is ConstantExpression)
{
- var interfaces = left.Type.GetInterfaces().Where(x => x.GetTypeInfo().IsGenericType);
- if (isEquality)
+ if (HasImplicitConversion(left.Type, right.Type))
{
- typesAreSameAndImplementCorrectInterface = interfaces.Any(x => x.GetGenericTypeDefinition() == typeof(IEquatable<>));
+ left = Expression.Convert(left, right.Type);
}
- else
+ else if (HasImplicitConversion(right.Type, left.Type))
{
- typesAreSameAndImplementCorrectInterface = interfaces.Any(x => x.GetGenericTypeDefinition() == typeof(IComparable<>));
+ right = Expression.Convert(right, left.Type);
}
}
-
- if (!typesAreSameAndImplementCorrectInterface)
+ else if ((TypeHelper.IsClass(right.Type) || TypeHelper.IsStruct(right.Type)) && left is ConstantExpression)
{
- if ((TypeHelper.IsClass(left.Type) || TypeHelper.IsStruct(left.Type)) && right is ConstantExpression)
- {
- if (HasImplicitConversion(left.Type, right.Type))
- {
- left = Expression.Convert(left, right.Type);
- }
- else if (HasImplicitConversion(right.Type, left.Type))
- {
- right = Expression.Convert(right, left.Type);
- }
- }
- else if ((TypeHelper.IsClass(right.Type) || TypeHelper.IsStruct(right.Type)) && left is ConstantExpression)
+ if (HasImplicitConversion(right.Type, left.Type))
{
- if (HasImplicitConversion(right.Type, left.Type))
- {
- right = Expression.Convert(right, left.Type);
- }
- else if (HasImplicitConversion(left.Type, right.Type))
- {
- left = Expression.Convert(left, right.Type);
- }
+ right = Expression.Convert(right, left.Type);
}
- else
+ else if (HasImplicitConversion(left.Type, right.Type))
{
- CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
+ left = Expression.Convert(left, right.Type);
}
}
- }
-
- switch (op.Id)
- {
- case TokenId.Equal:
- case TokenId.DoubleEqual:
- left = _expressionHelper.GenerateEqual(left, right);
- break;
- case TokenId.ExclamationEqual:
- case TokenId.LessGreater:
- left = _expressionHelper.GenerateNotEqual(left, right);
- break;
- case TokenId.GreaterThan:
- left = _expressionHelper.GenerateGreaterThan(left, right);
- break;
- case TokenId.GreaterThanEqual:
- left = _expressionHelper.GenerateGreaterThanEqual(left, right);
- break;
- case TokenId.LessThan:
- left = _expressionHelper.GenerateLessThan(left, right);
- break;
- case TokenId.LessThanEqual:
- left = _expressionHelper.GenerateLessThanEqual(left, right);
- break;
+ else
+ {
+ CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
+ }
}
}
- return left;
+ switch (op.Id)
+ {
+ case TokenId.Equal:
+ case TokenId.DoubleEqual:
+ left = _expressionHelper.GenerateEqual(left, right);
+ break;
+ case TokenId.ExclamationEqual:
+ case TokenId.LessGreater:
+ left = _expressionHelper.GenerateNotEqual(left, right);
+ break;
+ case TokenId.GreaterThan:
+ left = _expressionHelper.GenerateGreaterThan(left, right);
+ break;
+ case TokenId.GreaterThanEqual:
+ left = _expressionHelper.GenerateGreaterThanEqual(left, right);
+ break;
+ case TokenId.LessThan:
+ left = _expressionHelper.GenerateLessThan(left, right);
+ break;
+ case TokenId.LessThanEqual:
+ left = _expressionHelper.GenerateLessThanEqual(left, right);
+ break;
+ }
}
- private bool HasImplicitConversion(Type baseType, Type targetType)
+ return left;
+ }
+
+ private bool HasImplicitConversion(Type baseType, Type targetType)
+ {
+ var baseTypeHasConversion = baseType.GetMethods(BindingFlags.Public | BindingFlags.Static)
+ .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType)
+ .Any(mi => mi.GetParameters().FirstOrDefault()?.ParameterType == baseType);
+
+ if (baseTypeHasConversion)
{
- var baseTypeHasConversion = baseType.GetMethods(BindingFlags.Public | BindingFlags.Static)
- .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType)
- .Any(mi => mi.GetParameters().FirstOrDefault()?.ParameterType == baseType);
+ return true;
+ }
- if (baseTypeHasConversion)
+ return targetType.GetMethods(BindingFlags.Public | BindingFlags.Static)
+ .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType)
+ .Any(mi => mi.GetParameters().FirstOrDefault()?.ParameterType == baseType);
+ }
+
+ private ConstantExpression ParseEnumToConstantExpression(int pos, Type leftType, ConstantExpression constantExpr)
+ {
+ return Expression.Constant(ParseConstantExpressionToEnum(pos, leftType, constantExpr), leftType);
+ }
+
+ private object ParseConstantExpressionToEnum(int pos, Type leftType, ConstantExpression constantExpr)
+ {
+ try
+ {
+ if (constantExpr.Value is string stringValue)
{
- return true;
+ return Enum.Parse(TypeHelper.GetNonNullableType(leftType), stringValue, true);
}
-
- return targetType.GetMethods(BindingFlags.Public | BindingFlags.Static)
- .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType)
- .Any(mi => mi.GetParameters().FirstOrDefault()?.ParameterType == baseType);
}
-
- private ConstantExpression ParseEnumToConstantExpression(int pos, Type leftType, ConstantExpression constantExpr)
+ catch
{
- return Expression.Constant(ParseConstantExpressionToEnum(pos, leftType, constantExpr), leftType);
+ throw ParseError(pos, Res.ExpressionTypeMismatch, leftType);
}
- private object ParseConstantExpressionToEnum(int pos, Type leftType, ConstantExpression constantExpr)
+ try
{
- try
- {
- if (constantExpr.Value is string stringValue)
- {
- return Enum.Parse(TypeHelper.GetNonNullableType(leftType), stringValue, true);
- }
- }
- catch
- {
- throw ParseError(pos, Res.ExpressionTypeMismatch, leftType);
- }
+ return Enum.ToObject(TypeHelper.GetNonNullableType(leftType), constantExpr.Value);
+ }
+ catch
+ {
+ throw ParseError(pos, Res.ExpressionTypeMismatch, leftType);
+ }
+ }
- try
- {
- return Enum.ToObject(TypeHelper.GetNonNullableType(leftType), constantExpr.Value);
- }
- catch
+ // <<, >> operators
+ private Expression ParseShiftOperator()
+ {
+ Expression left = ParseAdditive();
+ while (_textParser.CurrentToken.Id == TokenId.DoubleLessThan || _textParser.CurrentToken.Id == TokenId.DoubleGreaterThan)
+ {
+ Token op = _textParser.CurrentToken;
+ _textParser.NextToken();
+ Expression right = ParseAdditive();
+ switch (op.Id)
{
- throw ParseError(pos, Res.ExpressionTypeMismatch, leftType);
+ case TokenId.DoubleLessThan:
+ CheckAndPromoteOperands(typeof(IShiftSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
+ left = Expression.LeftShift(left, right);
+ break;
+ case TokenId.DoubleGreaterThan:
+ CheckAndPromoteOperands(typeof(IShiftSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
+ left = Expression.RightShift(left, right);
+ break;
}
}
+ return left;
+ }
- // <<, >> operators
- private Expression ParseShiftOperator()
+ // +, - operators
+ private Expression ParseAdditive()
+ {
+ Expression left = ParseMultiplicative();
+ while (_textParser.CurrentToken.Id == TokenId.Plus || _textParser.CurrentToken.Id == TokenId.Minus)
{
- Expression left = ParseAdditive();
- while (_textParser.CurrentToken.Id == TokenId.DoubleLessThan || _textParser.CurrentToken.Id == TokenId.DoubleGreaterThan)
+ Token op = _textParser.CurrentToken;
+ _textParser.NextToken();
+ Expression right = ParseMultiplicative();
+ switch (op.Id)
{
- Token op = _textParser.CurrentToken;
- _textParser.NextToken();
- Expression right = ParseAdditive();
- switch (op.Id)
- {
- case TokenId.DoubleLessThan:
- CheckAndPromoteOperands(typeof(IShiftSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
- left = Expression.LeftShift(left, right);
- break;
- case TokenId.DoubleGreaterThan:
- CheckAndPromoteOperands(typeof(IShiftSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
- left = Expression.RightShift(left, right);
- break;
- }
+ case TokenId.Plus:
+ if (left.Type == typeof(string) || right.Type == typeof(string))
+ {
+ left = _expressionHelper.GenerateStringConcat(left, right);
+ }
+ else
+ {
+ CheckAndPromoteOperands(typeof(IAddSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
+ left = _expressionHelper.GenerateAdd(left, right);
+ }
+ break;
+ case TokenId.Minus:
+ CheckAndPromoteOperands(typeof(ISubtractSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
+ left = _expressionHelper.GenerateSubtract(left, right);
+ break;
}
- return left;
}
+ return left;
+ }
- // +, - operators
- private Expression ParseAdditive()
+ // *, /, %, mod operators
+ private Expression ParseMultiplicative()
+ {
+ Expression left = ParseUnary();
+ while (_textParser.CurrentToken.Id == TokenId.Asterisk || _textParser.CurrentToken.Id == TokenId.Slash ||
+ _textParser.CurrentToken.Id == TokenId.Percent || TokenIdentifierIs("mod"))
{
- Expression left = ParseMultiplicative();
- while (_textParser.CurrentToken.Id == TokenId.Plus || _textParser.CurrentToken.Id == TokenId.Minus)
+ Token op = _textParser.CurrentToken;
+ _textParser.NextToken();
+ Expression right = ParseUnary();
+ CheckAndPromoteOperands(typeof(IArithmeticSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
+ switch (op.Id)
{
- Token op = _textParser.CurrentToken;
- _textParser.NextToken();
- Expression right = ParseMultiplicative();
- switch (op.Id)
- {
- case TokenId.Plus:
- if (left.Type == typeof(string) || right.Type == typeof(string))
- {
- left = _expressionHelper.GenerateStringConcat(left, right);
- }
- else
- {
- CheckAndPromoteOperands(typeof(IAddSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
- left = _expressionHelper.GenerateAdd(left, right);
- }
- break;
- case TokenId.Minus:
- CheckAndPromoteOperands(typeof(ISubtractSignatures), op.Id, op.Text, ref left, ref right, op.Pos);
- left = _expressionHelper.GenerateSubtract(left, right);
- break;
- }
+ 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 = Expression.Modulo(left, right);
+ break;
}
- return left;
}
+ return left;
+ }
- // *, /, %, mod operators
- private Expression ParseMultiplicative()
+ // -, !, not unary operators
+ private Expression ParseUnary()
+ {
+ if (_textParser.CurrentToken.Id == TokenId.Minus || _textParser.CurrentToken.Id == TokenId.Exclamation || TokenIdentifierIs("not"))
{
- Expression left = ParseUnary();
- while (_textParser.CurrentToken.Id == TokenId.Asterisk || _textParser.CurrentToken.Id == TokenId.Slash ||
- _textParser.CurrentToken.Id == TokenId.Percent || TokenIdentifierIs("mod"))
+ Token op = _textParser.CurrentToken;
+ _textParser.NextToken();
+ if (op.Id == TokenId.Minus && (_textParser.CurrentToken.Id == TokenId.IntegerLiteral || _textParser.CurrentToken.Id == TokenId.RealLiteral))
{
- Token op = _textParser.CurrentToken;
- _textParser.NextToken();
- Expression right = ParseUnary();
- CheckAndPromoteOperands(typeof(IArithmeticSignatures), op.Id, 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 = Expression.Modulo(left, right);
- break;
- }
+ _textParser.CurrentToken.Text = "-" + _textParser.CurrentToken.Text;
+ _textParser.CurrentToken.Pos = op.Pos;
+ return ParsePrimary();
}
- return left;
- }
- // -, !, not unary operators
- private Expression ParseUnary()
- {
- if (_textParser.CurrentToken.Id == TokenId.Minus || _textParser.CurrentToken.Id == TokenId.Exclamation || TokenIdentifierIs("not"))
+ Expression expr = ParseUnary();
+ if (op.Id == TokenId.Minus)
{
- Token op = _textParser.CurrentToken;
- _textParser.NextToken();
- if (op.Id == TokenId.Minus && (_textParser.CurrentToken.Id == TokenId.IntegerLiteral || _textParser.CurrentToken.Id == TokenId.RealLiteral))
- {
- _textParser.CurrentToken.Text = "-" + _textParser.CurrentToken.Text;
- _textParser.CurrentToken.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;
+ 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 ParsePrimary();
+ return expr;
}
- private Expression ParsePrimary()
- {
- var expr = ParsePrimaryStart();
- _expressionHelper.WrapConstantExpression(ref expr);
+ return ParsePrimary();
+ }
- while (true)
+ private Expression ParsePrimary()
+ {
+ var expr = ParsePrimaryStart();
+ _expressionHelper.WrapConstantExpression(ref expr);
+
+ while (true)
+ {
+ if (_textParser.CurrentToken.Id == TokenId.Dot)
{
- if (_textParser.CurrentToken.Id == TokenId.Dot)
- {
- _textParser.NextToken();
- expr = ParseMemberAccess(null, expr);
- }
- else if (_textParser.CurrentToken.Id == TokenId.NullPropagation)
- {
- throw new NotSupportedException("An expression tree lambda may not contain a null propagating operator. Use the 'np()' or 'np(...)' (null-propagation) function instead.");
- }
- else if (_textParser.CurrentToken.Id == TokenId.OpenBracket)
- {
- expr = ParseElementAccess(expr);
- }
- else
- {
- break;
- }
+ _textParser.NextToken();
+ expr = ParseMemberAccess(null, expr);
+ }
+ else if (_textParser.CurrentToken.Id == TokenId.NullPropagation)
+ {
+ throw new NotSupportedException("An expression tree lambda may not contain a null propagating operator. Use the 'np()' or 'np(...)' (null-propagation) function instead.");
+ }
+ else if (_textParser.CurrentToken.Id == TokenId.OpenBracket)
+ {
+ expr = ParseElementAccess(expr);
+ }
+ else
+ {
+ break;
}
-
- return expr;
}
- private Expression ParsePrimaryStart()
+ return expr;
+ }
+
+ private Expression ParsePrimaryStart()
+ {
+ switch (_textParser.CurrentToken.Id)
{
- switch (_textParser.CurrentToken.Id)
- {
- case TokenId.Identifier:
- return ParseIdentifier();
+ case TokenId.Identifier:
+ return ParseIdentifier();
- case TokenId.StringLiteral:
- var expressionOrType = ParseStringLiteral(false);
- return expressionOrType.IsFirst ? expressionOrType.First : ParseTypeAccess(expressionOrType.Second, false);
+ case TokenId.StringLiteral:
+ var expressionOrType = ParseStringLiteral(false);
+ return expressionOrType.IsFirst ? expressionOrType.First : ParseTypeAccess(expressionOrType.Second, false);
- case TokenId.IntegerLiteral:
- return ParseIntegerLiteral();
+ case TokenId.IntegerLiteral:
+ return ParseIntegerLiteral();
- case TokenId.RealLiteral:
- return ParseRealLiteral();
+ case TokenId.RealLiteral:
+ return ParseRealLiteral();
- case TokenId.OpenParen:
- return ParseParenExpression();
+ case TokenId.OpenParen:
+ return ParseParenExpression();
- default:
- throw ParseError(Res.ExpressionExpected);
- }
+ default:
+ throw ParseError(Res.ExpressionExpected);
}
+ }
- private AnyOf ParseStringLiteral(bool forceParseAsString)
- {
- _textParser.ValidateToken(TokenId.StringLiteral);
+ private AnyOf ParseStringLiteral(bool forceParseAsString)
+ {
+ _textParser.ValidateToken(TokenId.StringLiteral);
- var stringValue = StringParser.ParseString(_textParser.CurrentToken.Text);
+ var stringValue = StringParser.ParseString(_textParser.CurrentToken.Text);
- if (_textParser.CurrentToken.Text[0] == '\'')
+ if (_textParser.CurrentToken.Text[0] == '\'')
+ {
+ if (stringValue.Length > 1)
{
- if (stringValue.Length > 1)
- {
- throw ParseError(Res.InvalidCharacterLiteral);
- }
-
- _textParser.NextToken();
- return ConstantExpressionHelper.CreateLiteral(stringValue[0], stringValue);
+ throw ParseError(Res.InvalidCharacterLiteral);
}
_textParser.NextToken();
+ return ConstantExpressionHelper.CreateLiteral(stringValue[0], stringValue);
+ }
- if (_parsingConfig.SupportCastingToFullyQualifiedTypeAsString && !forceParseAsString && stringValue.Length > 2 && stringValue.Contains('.'))
+ _textParser.NextToken();
+
+ if (_parsingConfig.SupportCastingToFullyQualifiedTypeAsString && !forceParseAsString && stringValue.Length > 2 && stringValue.Contains('.'))
+ {
+ // Try to resolve this string as a type
+ var type = _typeFinder.FindTypeByName(stringValue, null, false);
+ if (type is { })
{
- // Try to resolve this string as a type
- var type = _typeFinder.FindTypeByName(stringValue, null, false);
- if (type is { })
- {
- return type;
- }
+ return type;
}
-
- return ConstantExpressionHelper.CreateLiteral(stringValue, stringValue);
}
- private Expression ParseIntegerLiteral()
- {
- _textParser.ValidateToken(TokenId.IntegerLiteral);
+ return ConstantExpressionHelper.CreateLiteral(stringValue, stringValue);
+ }
- string text = _textParser.CurrentToken.Text;
+ private Expression ParseIntegerLiteral()
+ {
+ _textParser.ValidateToken(TokenId.IntegerLiteral);
- var tokenPosition = _textParser.CurrentToken.Pos;
+ string text = _textParser.CurrentToken.Text;
- var constantExpression = _numberParser.ParseIntegerLiteral(tokenPosition, text);
- _textParser.NextToken();
- return constantExpression;
- }
+ var tokenPosition = _textParser.CurrentToken.Pos;
- private Expression ParseRealLiteral()
- {
- _textParser.ValidateToken(TokenId.RealLiteral);
+ var constantExpression = _numberParser.ParseIntegerLiteral(tokenPosition, text);
+ _textParser.NextToken();
+ return constantExpression;
+ }
- string text = _textParser.CurrentToken.Text;
+ private Expression ParseRealLiteral()
+ {
+ _textParser.ValidateToken(TokenId.RealLiteral);
- _textParser.NextToken();
+ string text = _textParser.CurrentToken.Text;
- return _numberParser.ParseRealLiteral(text, text[text.Length - 1], true);
- }
+ _textParser.NextToken();
- private Expression ParseParenExpression()
- {
- _textParser.ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
- _textParser.NextToken();
- Expression e = ParseConditionalOperator();
- _textParser.ValidateToken(TokenId.CloseParen, Res.CloseParenOrOperatorExpected);
- _textParser.NextToken();
- return e;
- }
+ return _numberParser.ParseRealLiteral(text, text[text.Length - 1], true);
+ }
- private Expression ParseIdentifier()
- {
- _textParser.ValidateToken(TokenId.Identifier);
+ private Expression ParseParenExpression()
+ {
+ _textParser.ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
+ _textParser.NextToken();
+ Expression e = ParseConditionalOperator();
+ _textParser.ValidateToken(TokenId.CloseParen, Res.CloseParenOrOperatorExpected);
+ _textParser.NextToken();
+ return e;
+ }
- var isValidKeyWord = _keywordsHelper.TryGetValue(_textParser.CurrentToken.Text, out var value);
+ private Expression ParseIdentifier()
+ {
+ _textParser.ValidateToken(TokenId.Identifier);
+
+ var isValidKeyWord = _keywordsHelper.TryGetValue(_textParser.CurrentToken.Text, out var value);
- var extraCondition = !_parsingConfig.PrioritizePropertyOrFieldOverTheType ||
- (_parsingConfig.PrioritizePropertyOrFieldOverTheType && !(value is Type && _it != null && FindPropertyOrField(_it.Type, _textParser.CurrentToken.Text, false) != null));
+ var extraCondition = !_parsingConfig.PrioritizePropertyOrFieldOverTheType ||
+ (_parsingConfig.PrioritizePropertyOrFieldOverTheType && !(value is Type && _it != null && FindPropertyOrField(_it.Type, _textParser.CurrentToken.Text, false) != null));
- if (isValidKeyWord && extraCondition)
+ if (isValidKeyWord && extraCondition)
+ {
+ if (value is Type typeValue)
{
- if (value is Type typeValue)
- {
- return ParseTypeAccess(typeValue, true);
- }
+ return ParseTypeAccess(typeValue, true);
+ }
- switch (value)
- {
- case KeywordsHelper.KEYWORD_IT:
- case KeywordsHelper.SYMBOL_IT:
- return ParseIt();
+ switch (value)
+ {
+ case KeywordsHelper.KEYWORD_IT:
+ case KeywordsHelper.SYMBOL_IT:
+ return ParseIt();
- case KeywordsHelper.KEYWORD_PARENT:
- case KeywordsHelper.SYMBOL_PARENT:
- return ParseParent();
+ case KeywordsHelper.KEYWORD_PARENT:
+ case KeywordsHelper.SYMBOL_PARENT:
+ return ParseParent();
- case KeywordsHelper.KEYWORD_ROOT:
- case KeywordsHelper.SYMBOL_ROOT:
- return ParseRoot();
+ case KeywordsHelper.KEYWORD_ROOT:
+ case KeywordsHelper.SYMBOL_ROOT:
+ return ParseRoot();
- case KeywordsHelper.FUNCTION_IIF:
- return ParseFunctionIif();
+ case KeywordsHelper.FUNCTION_IIF:
+ return ParseFunctionIif();
- case KeywordsHelper.FUNCTION_ISNULL:
- return ParseFunctionIsNull();
+ case KeywordsHelper.FUNCTION_ISNULL:
+ return ParseFunctionIsNull();
- case KeywordsHelper.FUNCTION_NEW:
- return ParseNew();
+ case KeywordsHelper.FUNCTION_NEW:
+ return ParseNew();
- case KeywordsHelper.FUNCTION_NULLPROPAGATION:
- return ParseFunctionNullPropagation();
+ case KeywordsHelper.FUNCTION_NULLPROPAGATION:
+ return ParseFunctionNullPropagation();
- case KeywordsHelper.FUNCTION_IS:
- return ParseFunctionIs();
+ case KeywordsHelper.FUNCTION_IS:
+ return ParseFunctionIs();
- case KeywordsHelper.FUNCTION_AS:
- return ParseFunctionAs();
+ case KeywordsHelper.FUNCTION_AS:
+ return ParseFunctionAs();
- case KeywordsHelper.FUNCTION_CAST:
- return ParseFunctionCast();
- }
+ case KeywordsHelper.FUNCTION_CAST:
+ return ParseFunctionCast();
+ }
- _textParser.NextToken();
+ _textParser.NextToken();
- return (Expression)value;
- }
+ return (Expression)value;
+ }
- if (_symbols.TryGetValue(_textParser.CurrentToken.Text, out value) ||
- _externals != null && _externals.TryGetValue(_textParser.CurrentToken.Text, out value) ||
- _internals.TryGetValue(_textParser.CurrentToken.Text, out value))
+ if (_symbols.TryGetValue(_textParser.CurrentToken.Text, out value) ||
+ _externals != null && _externals.TryGetValue(_textParser.CurrentToken.Text, out value) ||
+ _internals.TryGetValue(_textParser.CurrentToken.Text, out value))
+ {
+ var expr = value as Expression;
+ if (expr == null)
{
- var expr = value as Expression;
- if (expr == null)
- {
- expr = Expression.Constant(value);
- }
- else
+ expr = Expression.Constant(value);
+ }
+ else
+ {
+ if (expr is LambdaExpression lambdaExpression)
{
- if (expr is LambdaExpression lambdaExpression)
- {
- return ParseLambdaInvocation(lambdaExpression);
- }
+ return ParseLambdaInvocation(lambdaExpression);
}
-
- _textParser.NextToken();
-
- return expr;
}
- if (_it != null)
- {
- return ParseMemberAccess(null, _it);
- }
+ _textParser.NextToken();
- throw ParseError(Res.UnknownIdentifier, _textParser.CurrentToken.Text);
+ return expr;
}
- private Expression ParseIt()
+ if (_it != null)
{
- if (_it == null)
- {
- throw ParseError(Res.NoItInScope);
- }
- _textParser.NextToken();
- return _it;
+ return ParseMemberAccess(null, _it);
}
- private Expression ParseParent()
+ throw ParseError(Res.UnknownIdentifier, _textParser.CurrentToken.Text);
+ }
+
+ private Expression ParseIt()
+ {
+ if (_it == null)
{
- if (_parent == null)
- {
- throw ParseError(Res.NoParentInScope);
- }
- _textParser.NextToken();
- return _parent;
+ throw ParseError(Res.NoItInScope);
}
+ _textParser.NextToken();
+ return _it;
+ }
- private Expression ParseRoot()
+ private Expression ParseParent()
+ {
+ if (_parent == null)
{
- if (_root == null)
- {
- throw ParseError(Res.NoRootInScope);
- }
- _textParser.NextToken();
- return _root;
+ throw ParseError(Res.NoParentInScope);
}
+ _textParser.NextToken();
+ return _parent;
+ }
- // isnull(a,b) function
- private Expression ParseFunctionIsNull()
+ private Expression ParseRoot()
+ {
+ if (_root == null)
{
- int errorPos = _textParser.CurrentToken.Pos;
- _textParser.NextToken();
- Expression[] args = ParseArgumentList();
- if (args.Length != 2)
- {
- throw ParseError(errorPos, Res.IsNullRequiresTwoArgs);
- }
-
- return Expression.Coalesce(args[0], args[1]);
+ throw ParseError(Res.NoRootInScope);
}
+ _textParser.NextToken();
+ return _root;
+ }
- // iif(test, ifTrue, ifFalse) function
- private Expression ParseFunctionIif()
+ // isnull(a,b) function
+ private Expression ParseFunctionIsNull()
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ _textParser.NextToken();
+ Expression[] args = ParseArgumentList();
+ if (args.Length != 2)
{
- int errorPos = _textParser.CurrentToken.Pos;
- _textParser.NextToken();
+ throw ParseError(errorPos, Res.IsNullRequiresTwoArgs);
+ }
- Expression[] args = ParseArgumentList();
- if (args.Length != 3)
- {
- throw ParseError(errorPos, Res.IifRequiresThreeArgs);
- }
+ return Expression.Coalesce(args[0], args[1]);
+ }
- return GenerateConditional(args[0], args[1], args[2], false, errorPos);
+ // iif(test, ifTrue, ifFalse) function
+ private Expression ParseFunctionIif()
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ _textParser.NextToken();
+
+ Expression[] args = ParseArgumentList();
+ if (args.Length != 3)
+ {
+ throw ParseError(errorPos, Res.IifRequiresThreeArgs);
}
- // np(...) function
- private Expression ParseFunctionNullPropagation()
+ return GenerateConditional(args[0], args[1], args[2], false, errorPos);
+ }
+
+ // np(...) function
+ private Expression ParseFunctionNullPropagation()
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ _textParser.NextToken();
+
+ Expression[] args = ParseArgumentList();
+
+ if (args.Length != 1 && args.Length != 2)
{
- int errorPos = _textParser.CurrentToken.Pos;
- _textParser.NextToken();
+ throw ParseError(errorPos, Res.NullPropagationRequiresCorrectArgs);
+ }
- Expression[] args = ParseArgumentList();
+ if (_expressionHelper.ExpressionQualifiesForNullPropagation(args[0]))
+ {
+ bool hasDefaultParameter = args.Length == 2;
+ Expression expressionIfFalse = hasDefaultParameter ? args[1] : Expression.Constant(null);
- if (args.Length != 1 && args.Length != 2)
+ if (_expressionHelper.TryGenerateAndAlsoNotNullExpression(args[0], true, out Expression generatedExpression))
{
- throw ParseError(errorPos, Res.NullPropagationRequiresCorrectArgs);
+ return GenerateConditional(generatedExpression, args[0], expressionIfFalse, true, errorPos);
}
- if (_expressionHelper.ExpressionQualifiesForNullPropagation(args[0]))
- {
- bool hasDefaultParameter = args.Length == 2;
- Expression expressionIfFalse = hasDefaultParameter ? args[1] : Expression.Constant(null);
+ return args[0];
+ }
- if (_expressionHelper.TryGenerateAndAlsoNotNullExpression(args[0], true, out Expression generatedExpression))
- {
- return GenerateConditional(generatedExpression, args[0], expressionIfFalse, true, errorPos);
- }
+ throw ParseError(errorPos, Res.NullPropagationRequiresValidExpression);
+ }
- return args[0];
- }
+ // Is(...) function
+ private Expression ParseFunctionIs()
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ string functionName = _textParser.CurrentToken.Text;
+ _textParser.NextToken();
+
+ Expression[] args = ParseArgumentList();
- throw ParseError(errorPos, Res.NullPropagationRequiresValidExpression);
+ if (args.Length != 1 && args.Length != 2)
+ {
+ throw ParseError(errorPos, Res.FunctionRequiresOneOrTwoArgs, functionName);
}
- // Is(...) function
- private Expression ParseFunctionIs()
+ Expression typeArgument;
+ Expression it;
+ if (args.Length == 1)
{
- int errorPos = _textParser.CurrentToken.Pos;
- string functionName = _textParser.CurrentToken.Text;
- _textParser.NextToken();
+ typeArgument = args[0];
+ it = _it!;
+ }
+ else
+ {
+ typeArgument = args[1];
+ it = args[0];
+ }
- Expression[] args = ParseArgumentList();
+ return Expression.TypeIs(it, ResolveTypeFromArgumentExpression(functionName, typeArgument, args.Length));
+ }
- if (args.Length != 1 && args.Length != 2)
- {
- throw ParseError(errorPos, Res.FunctionRequiresOneOrTwoArgs, functionName);
- }
+ // As(...) function
+ private Expression ParseFunctionAs()
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ string functionName = _textParser.CurrentToken.Text;
+ _textParser.NextToken();
- Expression typeArgument;
- Expression it;
- if (args.Length == 1)
- {
- typeArgument = args[0];
- it = _it!;
- }
- else
- {
- typeArgument = args[1];
- it = args[0];
- }
+ Expression[] args = ParseArgumentList();
- return Expression.TypeIs(it, ResolveTypeFromArgumentExpression(functionName, typeArgument, args.Length));
+ if (args.Length != 1 && args.Length != 2)
+ {
+ throw ParseError(errorPos, Res.FunctionRequiresOneOrTwoArgs, functionName);
}
- // As(...) function
- private Expression ParseFunctionAs()
+ Expression typeArgument;
+ Expression it;
+ if (args.Length == 1)
{
- int errorPos = _textParser.CurrentToken.Pos;
- string functionName = _textParser.CurrentToken.Text;
- _textParser.NextToken();
+ typeArgument = args[0];
+ it = _it!;
+ }
+ else
+ {
+ typeArgument = args[1];
+ it = args[0];
+ }
- Expression[] args = ParseArgumentList();
+ return Expression.TypeAs(it, ResolveTypeFromArgumentExpression(functionName, typeArgument, args.Length));
+ }
- if (args.Length != 1 && args.Length != 2)
- {
- throw ParseError(errorPos, Res.FunctionRequiresOneOrTwoArgs, functionName);
- }
+ // Cast(...) function
+ private Expression ParseFunctionCast()
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ string functionName = _textParser.CurrentToken.Text;
+ _textParser.NextToken();
- Expression typeArgument;
- Expression it;
- if (args.Length == 1)
- {
- typeArgument = args[0];
- it = _it!;
- }
- else
- {
- typeArgument = args[1];
- it = args[0];
- }
+ Expression[] args = ParseArgumentList();
- return Expression.TypeAs(it, ResolveTypeFromArgumentExpression(functionName, typeArgument, args.Length));
+ if (args.Length != 1 && args.Length != 2)
+ {
+ throw ParseError(errorPos, Res.FunctionRequiresOneOrTwoArgs, functionName);
}
- // Cast(...) function
- private Expression ParseFunctionCast()
+ Expression typeArgument;
+ Expression it;
+ if (args.Length == 1)
{
- int errorPos = _textParser.CurrentToken.Pos;
- string functionName = _textParser.CurrentToken.Text;
- _textParser.NextToken();
-
- Expression[] args = ParseArgumentList();
-
- if (args.Length != 1 && args.Length != 2)
- {
- throw ParseError(errorPos, Res.FunctionRequiresOneOrTwoArgs, functionName);
- }
+ typeArgument = args[0];
+ it = _it!;
+ }
+ else
+ {
+ typeArgument = args[1];
+ it = args[0];
+ }
- Expression typeArgument;
- Expression it;
- if (args.Length == 1)
- {
- typeArgument = args[0];
- it = _it!;
- }
- else
- {
- typeArgument = args[1];
- it = args[0];
- }
+ return Expression.ConvertChecked(it, ResolveTypeFromArgumentExpression(functionName, typeArgument, args.Length));
+ }
- return Expression.ConvertChecked(it, ResolveTypeFromArgumentExpression(functionName, typeArgument, args.Length));
+ private Expression GenerateConditional(Expression test, Expression expressionIfTrue, Expression expressionIfFalse, bool nullPropagating, int errorPos)
+ {
+ if (test.Type != typeof(bool))
+ {
+ throw ParseError(errorPos, Res.FirstExprMustBeBool);
}
- private Expression GenerateConditional(Expression test, Expression expressionIfTrue, Expression expressionIfFalse, bool nullPropagating, int errorPos)
+ if (expressionIfTrue.Type != expressionIfFalse.Type)
{
- if (test.Type != typeof(bool))
- {
- throw ParseError(errorPos, Res.FirstExprMustBeBool);
- }
-
- if (expressionIfTrue.Type != expressionIfFalse.Type)
+ // If expressionIfTrue is a null constant and expressionIfFalse is ValueType:
+ if (Constants.IsNull(expressionIfTrue) && expressionIfFalse.Type.GetTypeInfo().IsValueType)
{
- // If expressionIfTrue is a null constant and expressionIfFalse is ValueType:
- if (Constants.IsNull(expressionIfTrue) && expressionIfFalse.Type.GetTypeInfo().IsValueType)
+ if (nullPropagating && _parsingConfig.NullPropagatingUseDefaultValueForNonNullableValueTypes)
{
- if (nullPropagating && _parsingConfig.NullPropagatingUseDefaultValueForNonNullableValueTypes)
- {
- // If expressionIfFalse is a non-nullable type:
- // generate default expression from the expressionIfFalse-type for expressionIfTrue
- // Else
- // create nullable constant from expressionIfTrue with type from expressionIfFalse
+ // If expressionIfFalse is a non-nullable type:
+ // generate default expression from the expressionIfFalse-type for expressionIfTrue
+ // Else
+ // create nullable constant from expressionIfTrue with type from expressionIfFalse
- if (!TypeHelper.IsNullableType(expressionIfFalse.Type))
- {
- expressionIfTrue = _expressionHelper.GenerateDefaultExpression(expressionIfFalse.Type);
- }
- else
- {
- expressionIfTrue = Expression.Constant(null, expressionIfFalse.Type);
- }
+ if (!TypeHelper.IsNullableType(expressionIfFalse.Type))
+ {
+ expressionIfTrue = _expressionHelper.GenerateDefaultExpression(expressionIfFalse.Type);
}
else
{
- // - create nullable constant from expressionIfTrue with type from expressionIfFalse
- // - convert expressionIfFalse to nullable (unless it's already nullable)
- var nullableType = TypeHelper.ToNullableType(expressionIfFalse.Type);
- expressionIfTrue = Expression.Constant(null, nullableType);
-
- if (!TypeHelper.IsNullableType(expressionIfFalse.Type))
- {
- expressionIfFalse = Expression.Convert(expressionIfFalse, nullableType);
- }
+ expressionIfTrue = Expression.Constant(null, expressionIfFalse.Type);
}
-
- return Expression.Condition(test, expressionIfTrue, expressionIfFalse);
}
-
- // If expressionIfFalse is a null constant and expressionIfTrue is a ValueType:
- if (Constants.IsNull(expressionIfFalse) && expressionIfTrue.Type.GetTypeInfo().IsValueType)
+ else
{
- if (nullPropagating && _parsingConfig.NullPropagatingUseDefaultValueForNonNullableValueTypes)
- {
- // If expressionIfTrue is a non-nullable type:
- // generate default expression from the expressionIfFalse-type for expressionIfFalse
- // Else
- // create nullable constant from expressionIfFalse with type from expressionIfTrue
+ // - create nullable constant from expressionIfTrue with type from expressionIfFalse
+ // - convert expressionIfFalse to nullable (unless it's already nullable)
+ var nullableType = TypeHelper.ToNullableType(expressionIfFalse.Type);
+ expressionIfTrue = Expression.Constant(null, nullableType);
- if (!TypeHelper.IsNullableType(expressionIfTrue.Type))
- {
- expressionIfFalse = _expressionHelper.GenerateDefaultExpression(expressionIfTrue.Type);
- }
- else
- {
- expressionIfFalse = Expression.Constant(null, expressionIfTrue.Type);
- }
- }
- else
+ if (!TypeHelper.IsNullableType(expressionIfFalse.Type))
{
- // - create nullable constant from expressionIfFalse with type from expressionIfTrue
- // - convert expressionIfTrue to nullable (unless it's already nullable)
-
- Type nullableType = TypeHelper.ToNullableType(expressionIfTrue.Type);
- expressionIfFalse = Expression.Constant(null, nullableType);
- if (!TypeHelper.IsNullableType(expressionIfTrue.Type))
- {
- expressionIfTrue = Expression.Convert(expressionIfTrue, nullableType);
- }
+ expressionIfFalse = Expression.Convert(expressionIfFalse, nullableType);
}
-
- return Expression.Condition(test, expressionIfTrue, expressionIfFalse);
}
- var expr1As2 = !Constants.IsNull(expressionIfFalse) ? _parsingConfig.ExpressionPromoter.Promote(expressionIfTrue, expressionIfFalse.Type, true, false) : null;
- var expr2As1 = !Constants.IsNull(expressionIfTrue) ? _parsingConfig.ExpressionPromoter.Promote(expressionIfFalse, expressionIfTrue.Type, true, false) : null;
- if (expr1As2 != null && expr2As1 == null)
- {
- expressionIfTrue = expr1As2;
- }
- else if (expr2As1 != null && expr1As2 == null)
+ return Expression.Condition(test, expressionIfTrue, expressionIfFalse);
+ }
+
+ // If expressionIfFalse is a null constant and expressionIfTrue is a ValueType:
+ if (Constants.IsNull(expressionIfFalse) && expressionIfTrue.Type.GetTypeInfo().IsValueType)
+ {
+ if (nullPropagating && _parsingConfig.NullPropagatingUseDefaultValueForNonNullableValueTypes)
{
- expressionIfFalse = expr2As1;
+ // If expressionIfTrue is a non-nullable type:
+ // generate default expression from the expressionIfFalse-type for expressionIfFalse
+ // Else
+ // create nullable constant from expressionIfFalse with type from expressionIfTrue
+
+ if (!TypeHelper.IsNullableType(expressionIfTrue.Type))
+ {
+ expressionIfFalse = _expressionHelper.GenerateDefaultExpression(expressionIfTrue.Type);
+ }
+ else
+ {
+ expressionIfFalse = Expression.Constant(null, expressionIfTrue.Type);
+ }
}
else
{
- string type1 = !Constants.IsNull(expressionIfTrue) ? expressionIfTrue.Type.Name : "null";
- string type2 = !Constants.IsNull(expressionIfFalse) ? expressionIfFalse.Type.Name : "null";
- if (expr1As2 != null)
+ // - create nullable constant from expressionIfFalse with type from expressionIfTrue
+ // - convert expressionIfTrue to nullable (unless it's already nullable)
+
+ Type nullableType = TypeHelper.ToNullableType(expressionIfTrue.Type);
+ expressionIfFalse = Expression.Constant(null, nullableType);
+ if (!TypeHelper.IsNullableType(expressionIfTrue.Type))
{
- throw ParseError(errorPos, Res.BothTypesConvertToOther, type1, type2);
+ expressionIfTrue = Expression.Convert(expressionIfTrue, nullableType);
}
+ }
- throw ParseError(errorPos, Res.NeitherTypeConvertsToOther, type1, type2);
+ return Expression.Condition(test, expressionIfTrue, expressionIfFalse);
+ }
+
+ var expr1As2 = !Constants.IsNull(expressionIfFalse) ? _parsingConfig.ExpressionPromoter.Promote(expressionIfTrue, expressionIfFalse.Type, true, false) : null;
+ var expr2As1 = !Constants.IsNull(expressionIfTrue) ? _parsingConfig.ExpressionPromoter.Promote(expressionIfFalse, expressionIfTrue.Type, true, false) : null;
+ if (expr1As2 != null && expr2As1 == null)
+ {
+ expressionIfTrue = expr1As2;
+ }
+ else if (expr2As1 != null && expr1As2 == null)
+ {
+ expressionIfFalse = expr2As1;
+ }
+ else
+ {
+ string type1 = !Constants.IsNull(expressionIfTrue) ? expressionIfTrue.Type.Name : "null";
+ string type2 = !Constants.IsNull(expressionIfFalse) ? expressionIfFalse.Type.Name : "null";
+ if (expr1As2 != null)
+ {
+ throw ParseError(errorPos, Res.BothTypesConvertToOther, type1, type2);
}
+
+ throw ParseError(errorPos, Res.NeitherTypeConvertsToOther, type1, type2);
}
+ }
+
+ return Expression.Condition(test, expressionIfTrue, expressionIfFalse);
+ }
- return Expression.Condition(test, expressionIfTrue, expressionIfFalse);
+ // new (...) function
+ private Expression ParseNew()
+ {
+ _textParser.NextToken();
+ if (_textParser.CurrentToken.Id != TokenId.OpenParen &&
+ _textParser.CurrentToken.Id != TokenId.OpenCurlyParen &&
+ _textParser.CurrentToken.Id != TokenId.OpenBracket &&
+ _textParser.CurrentToken.Id != TokenId.Identifier)
+ {
+ throw ParseError(Res.OpenParenOrIdentifierExpected);
}
- // new (...) function
- private Expression ParseNew()
+ Type? newType = null;
+ if (_textParser.CurrentToken.Id == TokenId.Identifier)
{
+ var newTypeName = _textParser.CurrentToken.Text;
_textParser.NextToken();
- if (_textParser.CurrentToken.Id != TokenId.OpenParen &&
- _textParser.CurrentToken.Id != TokenId.OpenCurlyParen &&
- _textParser.CurrentToken.Id != TokenId.OpenBracket &&
- _textParser.CurrentToken.Id != TokenId.Identifier)
- {
- throw ParseError(Res.OpenParenOrIdentifierExpected);
- }
- Type? newType = null;
- if (_textParser.CurrentToken.Id == TokenId.Identifier)
+ while (_textParser.CurrentToken.Id == TokenId.Dot || _textParser.CurrentToken.Id == TokenId.Plus)
{
- var newTypeName = _textParser.CurrentToken.Text;
+ var sep = _textParser.CurrentToken.Text;
_textParser.NextToken();
-
- while (_textParser.CurrentToken.Id == TokenId.Dot || _textParser.CurrentToken.Id == TokenId.Plus)
- {
- var sep = _textParser.CurrentToken.Text;
- _textParser.NextToken();
- if (_textParser.CurrentToken.Id != TokenId.Identifier)
- {
- throw ParseError(Res.IdentifierExpected);
- }
- newTypeName += sep + _textParser.CurrentToken.Text;
- _textParser.NextToken();
- }
-
- newType = _typeFinder.FindTypeByName(newTypeName, new[] { _it, _parent, _root }, false);
- if (newType == null)
+ if (_textParser.CurrentToken.Id != TokenId.Identifier)
{
- throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, newTypeName);
+ throw ParseError(Res.IdentifierExpected);
}
+ newTypeName += sep + _textParser.CurrentToken.Text;
+ _textParser.NextToken();
+ }
- if (_textParser.CurrentToken.Id != TokenId.OpenParen &&
- _textParser.CurrentToken.Id != TokenId.OpenBracket &&
- _textParser.CurrentToken.Id != TokenId.OpenCurlyParen)
- {
- throw ParseError(Res.OpenParenExpected);
- }
+ newType = _typeFinder.FindTypeByName(newTypeName, new[] { _it, _parent, _root }, false);
+ if (newType == null)
+ {
+ throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, newTypeName);
}
- bool arrayInitializer = false;
- if (_textParser.CurrentToken.Id == TokenId.OpenBracket)
+ if (_textParser.CurrentToken.Id != TokenId.OpenParen &&
+ _textParser.CurrentToken.Id != TokenId.OpenBracket &&
+ _textParser.CurrentToken.Id != TokenId.OpenCurlyParen)
{
- _textParser.NextToken();
- _textParser.ValidateToken(TokenId.CloseBracket, Res.CloseBracketExpected);
- _textParser.NextToken();
- _textParser.ValidateToken(TokenId.OpenCurlyParen, Res.OpenCurlyParenExpected);
- arrayInitializer = true;
+ throw ParseError(Res.OpenParenExpected);
}
+ }
+ bool arrayInitializer = false;
+ if (_textParser.CurrentToken.Id == TokenId.OpenBracket)
+ {
_textParser.NextToken();
+ _textParser.ValidateToken(TokenId.CloseBracket, Res.CloseBracketExpected);
+ _textParser.NextToken();
+ _textParser.ValidateToken(TokenId.OpenCurlyParen, Res.OpenCurlyParenExpected);
+ arrayInitializer = true;
+ }
- var properties = new List();
- var expressions = new List();
+ _textParser.NextToken();
- while (_textParser.CurrentToken.Id != TokenId.CloseParen && _textParser.CurrentToken.Id != TokenId.CloseCurlyParen)
+ var properties = new List();
+ var expressions = new List();
+
+ while (_textParser.CurrentToken.Id != TokenId.CloseParen && _textParser.CurrentToken.Id != TokenId.CloseCurlyParen)
+ {
+ int exprPos = _textParser.CurrentToken.Pos;
+ Expression expr = ParseConditionalOperator();
+ if (!arrayInitializer)
{
- int exprPos = _textParser.CurrentToken.Pos;
- Expression expr = ParseConditionalOperator();
- if (!arrayInitializer)
+ string? propName;
+ if (TokenIdentifierIs("as"))
{
- string? propName;
- if (TokenIdentifierIs("as"))
- {
- _textParser.NextToken();
- propName = GetIdentifier();
- _textParser.NextToken();
- }
- else
+ _textParser.NextToken();
+ propName = GetIdentifier();
+ _textParser.NextToken();
+ }
+ else
+ {
+ if (!TryGetMemberName(expr, out propName)) // TODO : investigate this
{
- if (!TryGetMemberName(expr, out propName)) // TODO : investigate this
+ if (expr is MethodCallExpression methodCallExpression
+ && methodCallExpression.Arguments.Count == 1
+ && methodCallExpression.Arguments[0] is ConstantExpression methodCallExpressionArgument
+ && methodCallExpressionArgument.Type == typeof(string)
+ && properties.All(x => x.Name != (string)methodCallExpressionArgument.Value))
{
- if (expr is MethodCallExpression methodCallExpression
- && methodCallExpression.Arguments.Count == 1
- && methodCallExpression.Arguments[0] is ConstantExpression methodCallExpressionArgument
- && methodCallExpressionArgument.Type == typeof(string)
- && properties.All(x => x.Name != (string)methodCallExpressionArgument.Value))
- {
- propName = (string)methodCallExpressionArgument.Value;
- }
- else
- {
- throw ParseError(exprPos, Res.MissingAsClause);
- }
+ propName = (string)methodCallExpressionArgument.Value;
+ }
+ else
+ {
+ throw ParseError(exprPos, Res.MissingAsClause);
}
- }
-
- if (!string.IsNullOrEmpty(propName))
- {
- properties.Add(new DynamicProperty(propName!, expr.Type));
}
}
- expressions.Add(expr);
-
- if (_textParser.CurrentToken.Id != TokenId.Comma)
+ if (!string.IsNullOrEmpty(propName))
{
- break;
+ properties.Add(new DynamicProperty(propName!, expr.Type));
}
-
- _textParser.NextToken();
}
- if (_textParser.CurrentToken.Id != TokenId.CloseParen && _textParser.CurrentToken.Id != TokenId.CloseCurlyParen)
- {
- throw ParseError(Res.CloseParenOrCommaExpected);
- }
- _textParser.NextToken();
+ expressions.Add(expr);
- if (arrayInitializer)
+ if (_textParser.CurrentToken.Id != TokenId.Comma)
{
- return CreateArrayInitializerExpression(expressions, newType);
+ break;
}
- return CreateNewExpression(properties, expressions, newType);
+ _textParser.NextToken();
}
- private Expression CreateArrayInitializerExpression(List expressions, Type? newType)
+ if (_textParser.CurrentToken.Id != TokenId.CloseParen && _textParser.CurrentToken.Id != TokenId.CloseCurlyParen)
{
- if (expressions.Count == 0)
- {
- return Expression.NewArrayInit(newType ?? typeof(object));
- }
+ throw ParseError(Res.CloseParenOrCommaExpected);
+ }
+ _textParser.NextToken();
- if (newType != null)
- {
- return Expression.NewArrayInit(newType, expressions.Select(expression => _parsingConfig.ExpressionPromoter.Promote(expression, newType, true, true)));
- }
+ if (arrayInitializer)
+ {
+ return CreateArrayInitializerExpression(expressions, newType);
+ }
+
+ return CreateNewExpression(properties, expressions, newType);
+ }
- return Expression.NewArrayInit(expressions.All(expression => expression.Type == expressions[0].Type) ? expressions[0].Type : typeof(object), expressions);
+ private Expression CreateArrayInitializerExpression(List expressions, Type? newType)
+ {
+ if (expressions.Count == 0)
+ {
+ return Expression.NewArrayInit(newType ?? typeof(object));
}
- private Expression CreateNewExpression(List properties, List expressions, Type? newType)
+ if (newType != null)
{
- // http://solutionizing.net/category/linq/
- Type? type = newType ?? _resultType;
+ return Expression.NewArrayInit(newType, expressions.Select(expression => _parsingConfig.ExpressionPromoter.Promote(expression, newType, true, true)));
+ }
- if (type == null)
- {
+ return Expression.NewArrayInit(expressions.All(expression => expression.Type == expressions[0].Type) ? expressions[0].Type : typeof(object), expressions);
+ }
+
+ private Expression CreateNewExpression(List properties, List expressions, Type? newType)
+ {
+ // http://solutionizing.net/category/linq/
+ Type? type = newType ?? _resultType;
+
+ if (type == null)
+ {
#if UAP10_0
- type = typeof(DynamicClass);
- Type typeForKeyValuePair = typeof(KeyValuePair);
+ type = typeof(DynamicClass);
+ Type typeForKeyValuePair = typeof(KeyValuePair);
- ConstructorInfo constructorForKeyValuePair = typeForKeyValuePair.GetTypeInfo().DeclaredConstructors.First();
+ ConstructorInfo constructorForKeyValuePair = typeForKeyValuePair.GetTypeInfo().DeclaredConstructors.First();
- var arrayIndexParams = new List();
- for (int i = 0; i < expressions.Count; i++)
- {
- // Just convert the expression always to an object expression.
- UnaryExpression boxingExpression = Expression.Convert(expressions[i], typeof(object));
- NewExpression parameter = Expression.New(constructorForKeyValuePair, (Expression)Expression.Constant(properties[i].Name), boxingExpression);
+ var arrayIndexParams = new List();
+ for (int i = 0; i < expressions.Count; i++)
+ {
+ // Just convert the expression always to an object expression.
+ UnaryExpression boxingExpression = Expression.Convert(expressions[i], typeof(object));
+ NewExpression parameter = Expression.New(constructorForKeyValuePair, (Expression)Expression.Constant(properties[i].Name), boxingExpression);
- arrayIndexParams.Add(parameter);
- }
+ arrayIndexParams.Add(parameter);
+ }
- // Create an expression tree that represents creating and initializing a one-dimensional array of type KeyValuePair.
- NewArrayExpression newArrayExpression = Expression.NewArrayInit(typeof(KeyValuePair), arrayIndexParams);
+ // Create an expression tree that represents creating and initializing a one-dimensional array of type KeyValuePair.
+ NewArrayExpression newArrayExpression = Expression.NewArrayInit(typeof(KeyValuePair), arrayIndexParams);
- // Get the "public DynamicClass(KeyValuePair[] propertylist)" constructor
- ConstructorInfo constructor = type.GetTypeInfo().DeclaredConstructors.First();
+ // Get the "public DynamicClass(KeyValuePair[] propertylist)" constructor
+ ConstructorInfo constructor = type.GetTypeInfo().DeclaredConstructors.First();
- return Expression.New(constructor, newArrayExpression);
+ return Expression.New(constructor, newArrayExpression);
#else
- type = DynamicClassFactory.CreateType(properties, _createParameterCtor);
+ type = DynamicClassFactory.CreateType(properties, _createParameterCtor);
#endif
- }
+ }
- // Option 1. Try to bind via properties (TODO : investigate if this code block is 100% correct and is needed)
- var propertyInfos = type.GetProperties();
- if (type.GetTypeInfo().BaseType == typeof(DynamicClass))
- {
- propertyInfos = propertyInfos.Where(x => x.Name != "Item").ToArray();
- }
- var propertyTypes = propertyInfos.Select(p => p.PropertyType).ToArray();
- var ctor = type.GetConstructor(propertyTypes);
- if (ctor != null)
+ // Option 1. Try to bind via properties (TODO : investigate if this code block is 100% correct and is needed)
+ var propertyInfos = type.GetProperties();
+ if (type.GetTypeInfo().BaseType == typeof(DynamicClass))
+ {
+ propertyInfos = propertyInfos.Where(x => x.Name != "Item").ToArray();
+ }
+ var propertyTypes = propertyInfos.Select(p => p.PropertyType).ToArray();
+ var ctor = type.GetConstructor(propertyTypes);
+ if (ctor != null)
+ {
+ var constructorParameters = ctor.GetParameters();
+ if (constructorParameters.Length == expressions.Count)
{
- var constructorParameters = ctor.GetParameters();
- if (constructorParameters.Length == expressions.Count)
- {
- bool bindParametersSequentially = !properties.All(p => constructorParameters
- .Any(cp => cp.Name == p.Name && (cp.ParameterType == p.Type || p.Type == Nullable.GetUnderlyingType(cp.ParameterType))));
- var expressionsPromoted = new List();
+ bool bindParametersSequentially = !properties.All(p => constructorParameters
+ .Any(cp => cp.Name == p.Name && (cp.ParameterType == p.Type || p.Type == Nullable.GetUnderlyingType(cp.ParameterType))));
+ var expressionsPromoted = new List();
- // Loop all expressions and promote if needed
- for (int i = 0; i < constructorParameters.Length; i++)
+ // Loop all expressions and promote if needed
+ for (int i = 0; i < constructorParameters.Length; i++)
+ {
+ if (bindParametersSequentially)
{
- if (bindParametersSequentially)
- {
- expressionsPromoted.Add(_parsingConfig.ExpressionPromoter.Promote(expressions[i], propertyTypes[i], true, true));
- }
- else
- {
- Type propertyType = constructorParameters[i].ParameterType;
- string cParameterName = constructorParameters[i].Name;
- var propertyAndIndex = properties.Select((p, index) => new { p, index })
- .First(p => p.p.Name == cParameterName && (p.p.Type == propertyType || p.p.Type == Nullable.GetUnderlyingType(propertyType)));
- // Promote from Type to Nullable Type if needed
- expressionsPromoted.Add(_parsingConfig.ExpressionPromoter.Promote(expressions[propertyAndIndex.index], propertyType, true, true));
- }
+ expressionsPromoted.Add(_parsingConfig.ExpressionPromoter.Promote(expressions[i], propertyTypes[i], true, true));
+ }
+ else
+ {
+ Type propertyType = constructorParameters[i].ParameterType;
+ string cParameterName = constructorParameters[i].Name;
+ var propertyAndIndex = properties.Select((p, index) => new { p, index })
+ .First(p => p.p.Name == cParameterName && (p.p.Type == propertyType || p.p.Type == Nullable.GetUnderlyingType(propertyType)));
+ // Promote from Type to Nullable Type if needed
+ expressionsPromoted.Add(_parsingConfig.ExpressionPromoter.Promote(expressions[propertyAndIndex.index], propertyType, true, true));
}
-
- return Expression.New(ctor, expressionsPromoted, (IEnumerable)propertyInfos);
}
+
+ return Expression.New(ctor, expressionsPromoted, (IEnumerable)propertyInfos);
}
+ }
- // Option 2. Try to find a constructor with the exact argument-types and exact same order
- var constructorArgumentTypes = properties.Select(p => p.Type).ToArray();
- var exactConstructor = type.GetConstructor(constructorArgumentTypes);
- if (exactConstructor != null)
- {
- // Promote from Type to Nullable Type if needed
- var expressionsPromoted = exactConstructor.GetParameters()
- .Select((t, i) => _parsingConfig.ExpressionPromoter.Promote(expressions[i], t.ParameterType, true, true))
- .ToArray();
+ // Option 2. Try to find a constructor with the exact argument-types and exact same order
+ var constructorArgumentTypes = properties.Select(p => p.Type).ToArray();
+ var exactConstructor = type.GetConstructor(constructorArgumentTypes);
+ if (exactConstructor != null)
+ {
+ // Promote from Type to Nullable Type if needed
+ var expressionsPromoted = exactConstructor.GetParameters()
+ .Select((t, i) => _parsingConfig.ExpressionPromoter.Promote(expressions[i], t.ParameterType, true, true))
+ .ToArray();
- return Expression.New(exactConstructor, expressionsPromoted);
- }
+ return Expression.New(exactConstructor, expressionsPromoted);
+ }
- // Option 2. Call the default (empty) constructor and set the members
- var memberBindings = new MemberBinding[properties.Count];
- for (int i = 0; i < memberBindings.Length; i++)
+ // Option 2. Call the default (empty) constructor and set the members
+ var memberBindings = new MemberBinding[properties.Count];
+ for (int i = 0; i < memberBindings.Length; i++)
+ {
+ string propertyOrFieldName = properties[i].Name;
+ Type propertyOrFieldType;
+ MemberInfo memberInfo;
+ var propertyInfo = type.GetProperty(propertyOrFieldName);
+ if (propertyInfo != null)
+ {
+ memberInfo = propertyInfo;
+ propertyOrFieldType = propertyInfo.PropertyType;
+ }
+ else
{
- string propertyOrFieldName = properties[i].Name;
- Type propertyOrFieldType;
- MemberInfo memberInfo;
- var propertyInfo = type.GetProperty(propertyOrFieldName);
- if (propertyInfo != null)
+ var fieldInfo = type.GetField(propertyOrFieldName);
+ if (fieldInfo == null)
{
- memberInfo = propertyInfo;
- propertyOrFieldType = propertyInfo.PropertyType;
+ throw ParseError(Res.UnknownPropertyOrField, propertyOrFieldName, TypeHelper.GetTypeName(type));
}
- else
- {
- var fieldInfo = type.GetField(propertyOrFieldName);
- if (fieldInfo == null)
- {
- throw ParseError(Res.UnknownPropertyOrField, propertyOrFieldName, TypeHelper.GetTypeName(type));
- }
- memberInfo = fieldInfo;
- propertyOrFieldType = fieldInfo.FieldType;
- }
+ memberInfo = fieldInfo;
+ propertyOrFieldType = fieldInfo.FieldType;
+ }
- // Promote from Type to Nullable Type if needed
- var promoted = _parsingConfig.ExpressionPromoter.Promote(expressions[i], propertyOrFieldType, true, true);
- if (promoted is null)
- {
- throw new NotSupportedException($"Unable to promote expression '{expressions[i]}'.");
- }
- memberBindings[i] = Expression.Bind(memberInfo, promoted);
+ // Promote from Type to Nullable Type if needed
+ var promoted = _parsingConfig.ExpressionPromoter.Promote(expressions[i], propertyOrFieldType, true, true);
+ if (promoted is null)
+ {
+ throw new NotSupportedException($"Unable to promote expression '{expressions[i]}'.");
}
+ memberBindings[i] = Expression.Bind(memberInfo, promoted);
+ }
+
+ return Expression.MemberInit(Expression.New(type), memberBindings);
+ }
+
+ private Expression ParseLambdaInvocation(LambdaExpression lambda)
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ _textParser.NextToken();
+ Expression[] args = ParseArgumentList();
- return Expression.MemberInit(Expression.New(type), memberBindings);
+ Expression? nullExpressionReference = null;
+ if (_methodFinder.FindMethod(lambda.Type, nameof(Expression.Invoke), false, ref nullExpressionReference, ref args, out _) != 1)
+ {
+ throw ParseError(errorPos, Res.ArgsIncompatibleWithLambda);
}
- private Expression ParseLambdaInvocation(LambdaExpression lambda)
+ return Expression.Invoke(lambda, args);
+ }
+
+ private Expression ParseTypeAccess(Type type, bool getNext)
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ if (getNext)
{
- int errorPos = _textParser.CurrentToken.Pos;
_textParser.NextToken();
- Expression[] args = ParseArgumentList();
+ }
- Expression? nullExpressionReference = null;
- if (_methodFinder.FindMethod(lambda.Type, nameof(Expression.Invoke), false, ref nullExpressionReference, ref args, out _) != 1)
+ if (_textParser.CurrentToken.Id == TokenId.Question)
+ {
+ if (!type.GetTypeInfo().IsValueType || TypeHelper.IsNullableType(type))
{
- throw ParseError(errorPos, Res.ArgsIncompatibleWithLambda);
+ throw ParseError(errorPos, Res.TypeHasNoNullableForm, TypeHelper.GetTypeName(type));
}
- return Expression.Invoke(lambda, args);
+ type = typeof(Nullable<>).MakeGenericType(type);
+ _textParser.NextToken();
}
- private Expression ParseTypeAccess(Type type, bool getNext)
+ // This is a shorthand for explicitly converting a string to something
+ bool shorthand = _textParser.CurrentToken.Id == TokenId.StringLiteral;
+ if (_textParser.CurrentToken.Id == TokenId.OpenParen || shorthand)
{
- int errorPos = _textParser.CurrentToken.Pos;
- if (getNext)
+ Expression[] args;
+ if (shorthand)
{
- _textParser.NextToken();
+ var expressionOrType = ParseStringLiteral(true);
+ args = new[] { expressionOrType.First };
}
-
- if (_textParser.CurrentToken.Id == TokenId.Question)
+ else
{
- if (!type.GetTypeInfo().IsValueType || TypeHelper.IsNullableType(type))
- {
- throw ParseError(errorPos, Res.TypeHasNoNullableForm, TypeHelper.GetTypeName(type));
- }
-
- type = typeof(Nullable<>).MakeGenericType(type);
- _textParser.NextToken();
+ args = ParseArgumentList();
}
- // This is a shorthand for explicitly converting a string to something
- bool shorthand = _textParser.CurrentToken.Id == TokenId.StringLiteral;
- if (_textParser.CurrentToken.Id == TokenId.OpenParen || shorthand)
+ // If only 1 argument and
+ // - the arg is ConstantExpression, return the conversion
+ // OR
+ // - the arg is null, return the conversion (Can't use constructor)
+ //
+ // Then try to GenerateConversion
+
+ // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
+ if (args.Length == 1 && (args[0] == null || args[0] is ConstantExpression) && TryGenerateConversion(args[0], type, out var generatedExpression))
{
- Expression[] args;
- if (shorthand)
- {
- var expressionOrType = ParseStringLiteral(true);
- args = new[] { expressionOrType.First };
- }
- else
- {
- args = ParseArgumentList();
- }
+ return generatedExpression!;
+ }
- // If only 1 argument and
- // - the arg is ConstantExpression, return the conversion
- // OR
- // - the arg is null, return the conversion (Can't use constructor)
- //
- // Then try to GenerateConversion
+ // If only 1 argument, and if the type is a ValueType and argType is also a ValueType, just Convert
+ if (args.Length == 1)
+ {
+ Type argType = args[0].Type;
- // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
- if (args.Length == 1 && (args[0] == null || args[0] is ConstantExpression) && TryGenerateConversion(args[0], type, out var generatedExpression))
+ if (type.GetTypeInfo().IsValueType && TypeHelper.IsNullableType(type) && argType.GetTypeInfo().IsValueType)
{
- return generatedExpression!;
+ return Expression.Convert(args[0], type);
}
+ }
- // If only 1 argument, and if the type is a ValueType and argType is also a ValueType, just Convert
- if (args.Length == 1)
- {
- Type argType = args[0].Type;
-
- if (type.GetTypeInfo().IsValueType && TypeHelper.IsNullableType(type) && argType.GetTypeInfo().IsValueType)
+ var constructorsWithOutPointerArguments = type.GetConstructors()
+ .Where(c => !c.GetParameters().Any(p => p.ParameterType.GetTypeInfo().IsPointer))
+ .ToArray();
+ switch (_methodFinder.FindBestMethodBasedOnArguments(constructorsWithOutPointerArguments, ref args, out var method))
+ {
+ case 0:
+ if (args.Length == 1 && TryGenerateConversion(args[0], type, out generatedExpression))
{
- return Expression.Convert(args[0], type);
+ return generatedExpression!;
}
- }
-
- var constructorsWithOutPointerArguments = type.GetConstructors()
- .Where(c => !c.GetParameters().Any(p => p.ParameterType.GetTypeInfo().IsPointer))
- .ToArray();
- switch (_methodFinder.FindBestMethodBasedOnArguments(constructorsWithOutPointerArguments, ref args, out var method))
- {
- case 0:
- if (args.Length == 1 && TryGenerateConversion(args[0], type, out generatedExpression))
- {
- return generatedExpression!;
- }
- throw ParseError(errorPos, Res.NoMatchingConstructor, TypeHelper.GetTypeName(type));
+ throw ParseError(errorPos, Res.NoMatchingConstructor, TypeHelper.GetTypeName(type));
- case 1:
- return Expression.New((ConstructorInfo)method!, args);
+ case 1:
+ return Expression.New((ConstructorInfo)method!, args);
- default:
- throw ParseError(errorPos, Res.AmbiguousConstructorInvocation, TypeHelper.GetTypeName(type));
- }
+ default:
+ throw ParseError(errorPos, Res.AmbiguousConstructorInvocation, TypeHelper.GetTypeName(type));
}
+ }
- _textParser.ValidateToken(TokenId.Dot, Res.DotOrOpenParenOrStringLiteralExpected);
- _textParser.NextToken();
+ _textParser.ValidateToken(TokenId.Dot, Res.DotOrOpenParenOrStringLiteralExpected);
+ _textParser.NextToken();
- return ParseMemberAccess(type, null);
+ return ParseMemberAccess(type, null);
+ }
+
+ private bool TryGenerateConversion(Expression sourceExpression, Type type, out Expression? expression)
+ {
+ Type exprType = sourceExpression.Type;
+ if (exprType == type)
+ {
+ expression = sourceExpression;
+ return true;
}
- private bool TryGenerateConversion(Expression sourceExpression, Type type, out Expression? expression)
+ if (exprType.GetTypeInfo().IsValueType && type.GetTypeInfo().IsValueType)
{
- Type exprType = sourceExpression.Type;
- if (exprType == type)
+ if ((TypeHelper.IsNullableType(exprType) || TypeHelper.IsNullableType(type)) && TypeHelper.GetNonNullableType(exprType) == TypeHelper.GetNonNullableType(type))
{
- expression = sourceExpression;
+ expression = Expression.Convert(sourceExpression, type);
return true;
}
- if (exprType.GetTypeInfo().IsValueType && type.GetTypeInfo().IsValueType)
- {
- if ((TypeHelper.IsNullableType(exprType) || TypeHelper.IsNullableType(type)) && TypeHelper.GetNonNullableType(exprType) == TypeHelper.GetNonNullableType(type))
- {
- expression = Expression.Convert(sourceExpression, type);
- return true;
- }
-
- if ((TypeHelper.IsNumericType(exprType) || TypeHelper.IsEnumType(exprType)) && TypeHelper.IsNumericType(type) || TypeHelper.IsEnumType(type))
- {
- expression = Expression.ConvertChecked(sourceExpression, type);
- return true;
- }
- }
-
- if (exprType.IsAssignableFrom(type) || type.IsAssignableFrom(exprType) || exprType.GetTypeInfo().IsInterface || type.GetTypeInfo().IsInterface)
+ if ((TypeHelper.IsNumericType(exprType) || TypeHelper.IsEnumType(exprType)) && TypeHelper.IsNumericType(type) || TypeHelper.IsEnumType(type))
{
- expression = Expression.Convert(sourceExpression, type);
+ expression = Expression.ConvertChecked(sourceExpression, type);
return true;
}
+ }
- // Try to Parse the string rather than just generate the convert statement
- if (sourceExpression.NodeType == ExpressionType.Constant && exprType == typeof(string))
- {
- string text = (string)((ConstantExpression)sourceExpression).Value;
+ if (exprType.IsAssignableFrom(type) || type.IsAssignableFrom(exprType) || exprType.GetTypeInfo().IsInterface || type.GetTypeInfo().IsInterface)
+ {
+ expression = Expression.Convert(sourceExpression, type);
+ return true;
+ }
- var typeConvertor = _typeConverterFactory.GetConverter(type);
- // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
- if (typeConvertor != null && typeConvertor.CanConvertFrom(typeof(string)))
- {
- var value = typeConvertor.ConvertFromInvariantString(text);
- expression = Expression.Constant(value, type);
- return true;
- }
- }
+ // Try to Parse the string rather than just generate the convert statement
+ if (sourceExpression.NodeType == ExpressionType.Constant && exprType == typeof(string))
+ {
+ string text = (string)((ConstantExpression)sourceExpression).Value;
- // Check if there are any explicit conversion operators on the source type which fit the requirement (cast to the return type).
- bool explicitOperatorAvailable = exprType.GetTypeInfo().GetDeclaredMethods("op_Explicit").Any(m => m.ReturnType == type);
- if (explicitOperatorAvailable)
+ var typeConvertor = _typeConverterFactory.GetConverter(type);
+ // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
+ if (typeConvertor != null && typeConvertor.CanConvertFrom(typeof(string)))
{
- expression = Expression.Convert(sourceExpression, type);
+ var value = typeConvertor.ConvertFromInvariantString(text);
+ expression = Expression.Constant(value, type);
return true;
}
+ }
- expression = null;
- return false;
+ // Check if there are any explicit conversion operators on the source type which fit the requirement (cast to the return type).
+ bool explicitOperatorAvailable = exprType.GetTypeInfo().GetDeclaredMethods("op_Explicit").Any(m => m.ReturnType == type);
+ if (explicitOperatorAvailable)
+ {
+ expression = Expression.Convert(sourceExpression, type);
+ return true;
}
- private Expression ParseMemberAccess(Type? type, Expression? expression)
+ expression = null;
+ return false;
+ }
+
+ private Expression ParseMemberAccess(Type? type, Expression? expression)
+ {
+ if (expression != null)
{
- if (expression != null)
- {
- type = expression.Type;
- }
+ type = expression.Type;
+ }
- int errorPos = _textParser.CurrentToken.Pos;
- string id = GetIdentifier();
- _textParser.NextToken();
+ int errorPos = _textParser.CurrentToken.Pos;
+ string id = GetIdentifier();
+ _textParser.NextToken();
- if (_textParser.CurrentToken.Id == TokenId.OpenParen)
+ if (_textParser.CurrentToken.Id == TokenId.OpenParen)
+ {
+ if (expression != null && type != typeof(string))
{
- if (expression != null && type != typeof(string))
- {
- var enumerableType = TypeHelper.FindGenericType(typeof(IEnumerable<>), type);
- if (enumerableType != null)
- {
- Type elementType = enumerableType.GetTypeInfo().GetGenericTypeArguments()[0];
- return ParseEnumerable(expression, elementType, id, errorPos, type);
- }
- }
-
- Expression[] args = ParseArgumentList();
- switch (_methodFinder.FindMethod(type, id, expression == null, ref expression, ref args, out var mb))
+ var enumerableType = TypeHelper.FindGenericType(typeof(IEnumerable<>), type);
+ if (enumerableType != null)
{
- case 0:
- throw ParseError(errorPos, Res.NoApplicableMethod, id, TypeHelper.GetTypeName(type));
-
- case 1:
- MethodInfo method = (MethodInfo)mb!;
- if (!PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.DeclaringType!) && !(method.IsPublic && PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.ReturnType)))
- {
- throw ParseError(errorPos, Res.MethodsAreInaccessible, TypeHelper.GetTypeName(method.DeclaringType!));
- }
-
- if (method.IsGenericMethod)
- {
- var genericParameters = method.GetParameters().Where(p => p.ParameterType.IsGenericParameter);
- var typeArguments = genericParameters.Select(a => args[a.Position].Type);
- var constructedMethod = method.MakeGenericMethod(typeArguments.ToArray());
- return Expression.Call(expression, constructedMethod, args);
- }
-
- return Expression.Call(expression, method, args);
-
- default:
- throw ParseError(errorPos, Res.AmbiguousMethodInvocation, id, TypeHelper.GetTypeName(type));
+ Type elementType = enumerableType.GetTypeInfo().GetGenericTypeArguments()[0];
+ return ParseEnumerable(expression, elementType, id, errorPos, type);
}
}
- var @enum = TypeHelper.ParseEnum(id, type);
- if (@enum != null)
+ Expression[] args = ParseArgumentList();
+ switch (_methodFinder.FindMethod(type, id, expression == null, ref expression, ref args, out var mb))
{
- return Expression.Constant(@enum);
- }
+ case 0:
+ throw ParseError(errorPos, Res.NoApplicableMethod, id, TypeHelper.GetTypeName(type));
-#if UAP10_0 || NETSTANDARD1_3
- if (type == typeof(DynamicClass))
- {
- return Expression.MakeIndex(expression, typeof(DynamicClass).GetProperty("Item"), new[] { Expression.Constant(id) });
- }
-#endif
- MemberInfo? member = FindPropertyOrField(type!, id, expression == null);
- if (member is PropertyInfo property)
- {
- return Expression.Property(expression, property);
- }
+ case 1:
+ MethodInfo method = (MethodInfo)mb!;
+ if (!PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.DeclaringType!) && !(method.IsPublic && PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.ReturnType)))
+ {
+ throw ParseError(errorPos, Res.MethodsAreInaccessible, TypeHelper.GetTypeName(method.DeclaringType!));
+ }
- if (member is FieldInfo field)
- {
- return Expression.Field(expression, field);
- }
+ if (method.IsGenericMethod)
+ {
+ var genericParameters = method.GetParameters().Where(p => p.ParameterType.IsGenericParameter);
+ var typeArguments = genericParameters.Select(a => args[a.Position].Type);
+ var constructedMethod = method.MakeGenericMethod(typeArguments.ToArray());
+ return Expression.Call(expression, constructedMethod, args);
+ }
- // #357 #662
- var extraCheck = !_parsingConfig.PrioritizePropertyOrFieldOverTheType ||
- _parsingConfig.PrioritizePropertyOrFieldOverTheType && expression != null;
+ return Expression.Call(expression, method, args);
- if (!_parsingConfig.DisableMemberAccessToIndexAccessorFallback && extraCheck)
- {
- var indexerMethod = expression?.Type.GetMethod("get_Item", new[] { typeof(string) });
- if (indexerMethod != null)
- {
- return Expression.Call(expression, indexerMethod, Expression.Constant(id));
- }
+ default:
+ throw ParseError(errorPos, Res.AmbiguousMethodInvocation, id, TypeHelper.GetTypeName(type));
}
+ }
-#if !NET35 && !UAP10_0 && !NETSTANDARD1_3
- if (type == typeof(object))
- {
- // The member is a dynamic or ExpandoObject, so convert this
- return _expressionHelper.ConvertToExpandoObjectAndCreateDynamicExpression(expression, type, id);
- }
+ var @enum = TypeHelper.ParseEnum(id, type);
+ if (@enum != null)
+ {
+ return Expression.Constant(@enum);
+ }
+
+#if UAP10_0 || NETSTANDARD1_3
+ if (type == typeof(DynamicClass))
+ {
+ return Expression.MakeIndex(expression, typeof(DynamicClass).GetProperty("Item"), new[] { Expression.Constant(id) });
+ }
#endif
- // Parse as Lambda
- if (_textParser.CurrentToken.Id == TokenId.Lambda && _it?.Type == type)
- {
- return ParseAsLambda(id);
- }
+ MemberInfo? member = FindPropertyOrField(type!, id, expression == null);
+ if (member is PropertyInfo property)
+ {
+ return Expression.Property(expression, property);
+ }
- // This could be enum like "A.B.C.MyEnum.Value1" or "A.B.C+MyEnum.Value1"
- if (_textParser.CurrentToken.Id == TokenId.Dot || _textParser.CurrentToken.Id == TokenId.Plus)
+ if (member is FieldInfo field)
+ {
+ return Expression.Field(expression, field);
+ }
+
+ // #357 #662
+ var extraCheck = !_parsingConfig.PrioritizePropertyOrFieldOverTheType ||
+ _parsingConfig.PrioritizePropertyOrFieldOverTheType && expression != null;
+
+ if (!_parsingConfig.DisableMemberAccessToIndexAccessorFallback && extraCheck)
+ {
+ var indexerMethod = expression?.Type.GetMethod("get_Item", new[] { typeof(string) });
+ if (indexerMethod != null)
{
- return ParseAsEnum(id);
+ return Expression.Call(expression, indexerMethod, Expression.Constant(id));
}
+ }
- throw ParseError(errorPos, Res.UnknownPropertyOrField, id, TypeHelper.GetTypeName(type));
+#if !NET35 && !UAP10_0 && !NETSTANDARD1_3
+ if (type == typeof(object))
+ {
+ // The member is a dynamic or ExpandoObject, so convert this
+ return _expressionHelper.ConvertToExpandoObjectAndCreateDynamicExpression(expression, type, id);
+ }
+#endif
+ // Parse as Lambda
+ if (_textParser.CurrentToken.Id == TokenId.Lambda && _it?.Type == type)
+ {
+ return ParseAsLambda(id);
}
- private Expression ParseAsLambda(string id)
+ // This could be enum like "A.B.C.MyEnum.Value1" or "A.B.C+MyEnum.Value1"
+ if (_textParser.CurrentToken.Id == TokenId.Dot || _textParser.CurrentToken.Id == TokenId.Plus)
{
- // This might be an internal variable for use within a lambda expression, so store it as such
- _internals.Add(id, _it!);
- string previousItName = ItName;
+ return ParseAsEnum(id);
+ }
- // Also store ItName (only once)
- if (string.Equals(ItName, KeywordsHelper.KEYWORD_IT))
- {
- ItName = id;
- }
+ throw ParseError(errorPos, Res.UnknownPropertyOrField, id, TypeHelper.GetTypeName(type));
+ }
- // next
- _textParser.NextToken();
+ private Expression ParseAsLambda(string id)
+ {
+ // This might be an internal variable for use within a lambda expression, so store it as such
+ _internals.Add(id, _it!);
+ string previousItName = ItName;
- LastLambdaItName = ItName;
- var exp = ParseConditionalOperator();
+ // Also store ItName (only once)
+ if (string.Equals(ItName, KeywordsHelper.KEYWORD_IT))
+ {
+ ItName = id;
+ }
- // Restore previous context and clear internals
- _internals.Remove(id);
- ItName = previousItName;
+ // next
+ _textParser.NextToken();
- return exp;
- }
+ LastLambdaItName = ItName;
+ var exp = ParseConditionalOperator();
- private Expression ParseAsEnum(string id)
- {
- var parts = new List { id };
+ // Restore previous context and clear internals
+ _internals.Remove(id);
+ ItName = previousItName;
- while (_textParser.CurrentToken.Id == TokenId.Dot || _textParser.CurrentToken.Id == TokenId.Plus)
- {
- if (_textParser.CurrentToken.Id == TokenId.Dot || _textParser.CurrentToken.Id == TokenId.Plus)
- {
- parts.Add(_textParser.CurrentToken.Text);
- _textParser.NextToken();
- }
+ return exp;
+ }
- if (_textParser.CurrentToken.Id == TokenId.Identifier)
- {
- parts.Add(_textParser.CurrentToken.Text);
- _textParser.NextToken();
- }
- }
+ private Expression ParseAsEnum(string id)
+ {
+ var parts = new List { id };
- var enumTypeAsString = string.Concat(parts.Take(parts.Count - 2).ToArray());
- var enumType = _typeFinder.FindTypeByName(enumTypeAsString, null, true);
- if (enumType == null)
+ while (_textParser.CurrentToken.Id == TokenId.Dot || _textParser.CurrentToken.Id == TokenId.Plus)
+ {
+ if (_textParser.CurrentToken.Id == TokenId.Dot || _textParser.CurrentToken.Id == TokenId.Plus)
{
- throw ParseError(_textParser.CurrentToken.Pos, Res.EnumTypeNotFound, enumTypeAsString);
+ parts.Add(_textParser.CurrentToken.Text);
+ _textParser.NextToken();
}
- var enumValueAsString = parts.LastOrDefault();
- if (enumValueAsString == null)
+ if (_textParser.CurrentToken.Id == TokenId.Identifier)
{
- throw ParseError(_textParser.CurrentToken.Pos, Res.EnumValueExpected);
+ parts.Add(_textParser.CurrentToken.Text);
+ _textParser.NextToken();
}
+ }
- var enumValue = TypeHelper.ParseEnum(enumValueAsString, enumType);
- if (enumValue == null)
- {
- throw ParseError(_textParser.CurrentToken.Pos, Res.EnumValueNotDefined, enumValueAsString, enumTypeAsString);
- }
+ var enumTypeAsString = string.Concat(parts.Take(parts.Count - 2).ToArray());
+ var enumType = _typeFinder.FindTypeByName(enumTypeAsString, null, true);
+ if (enumType == null)
+ {
+ throw ParseError(_textParser.CurrentToken.Pos, Res.EnumTypeNotFound, enumTypeAsString);
+ }
- return Expression.Constant(enumValue);
+ var enumValueAsString = parts.LastOrDefault();
+ if (enumValueAsString == null)
+ {
+ throw ParseError(_textParser.CurrentToken.Pos, Res.EnumValueExpected);
}
- private Expression ParseEnumerable(Expression instance, Type elementType, string methodName, int errorPos, Type? type)
+ var enumValue = TypeHelper.ParseEnum(enumValueAsString, enumType);
+ if (enumValue == null)
{
- bool isQueryable = TypeHelper.FindGenericType(typeof(IQueryable<>), type) != null;
- bool isDictionary = TypeHelper.IsDictionary(type);
+ throw ParseError(_textParser.CurrentToken.Pos, Res.EnumValueNotDefined, enumValueAsString, enumTypeAsString);
+ }
- var oldParent = _parent;
+ return Expression.Constant(enumValue);
+ }
- ParameterExpression? outerIt = _it;
- ParameterExpression innerIt = ParameterExpressionHelper.CreateParameterExpression(elementType, string.Empty, _parsingConfig.RenameEmptyParameterExpressionNames);
+ private Expression ParseEnumerable(Expression instance, Type elementType, string methodName, int errorPos, Type? type)
+ {
+ bool isQueryable = TypeHelper.FindGenericType(typeof(IQueryable<>), type) != null;
+ bool isDictionary = TypeHelper.IsDictionary(type);
- _parent = _it;
+ var oldParent = _parent;
- if (methodName == "Contains" || methodName == "ContainsKey" || methodName == "Skip" || methodName == "Take")
- {
- // for any method that acts on the parent element type, we need to specify the outerIt as scope.
- _it = outerIt;
- }
- else
- {
- _it = innerIt;
- }
+ ParameterExpression? outerIt = _it;
+ ParameterExpression innerIt = ParameterExpressionHelper.CreateParameterExpression(elementType, string.Empty, _parsingConfig.RenameEmptyParameterExpressionNames);
- Expression[] args = ParseArgumentList();
+ _parent = _it;
+ if (methodName == "Contains" || methodName == "ContainsKey" || methodName == "Skip" || methodName == "Take")
+ {
+ // for any method that acts on the parent element type, we need to specify the outerIt as scope.
_it = outerIt;
- _parent = oldParent;
+ }
+ else
+ {
+ _it = innerIt;
+ }
- if (isDictionary && _methodFinder.ContainsMethod(typeof(IDictionarySignatures), methodName, false, null, ref args))
- {
- var method = type!.GetMethod(methodName)!;
- return Expression.Call(instance, method, args);
- }
+ Expression[] args = ParseArgumentList();
- if (!_methodFinder.ContainsMethod(typeof(IEnumerableSignatures), methodName, false, null, ref args))
- {
- throw ParseError(errorPos, Res.NoApplicableAggregate, methodName, string.Join(",", args.Select(a => a.Type.Name).ToArray()));
- }
+ _it = outerIt;
+ _parent = oldParent;
- Type callType = typeof(Enumerable);
- if (isQueryable && _methodFinder.ContainsMethod(typeof(IQueryableSignatures), methodName, false, null, ref args))
- {
- callType = typeof(Queryable);
- }
+ if (isDictionary && _methodFinder.ContainsMethod(typeof(IDictionarySignatures), methodName, false, null, ref args))
+ {
+ var method = type!.GetMethod(methodName)!;
+ return Expression.Call(instance, method, args);
+ }
- Type[] typeArgs;
- if (new[] { "OfType", "Cast" }.Contains(methodName))
- {
- if (args.Length != 1)
- {
- throw ParseError(_textParser.CurrentToken.Pos, Res.FunctionRequiresOneArg, methodName);
- }
+ if (!_methodFinder.ContainsMethod(typeof(IEnumerableSignatures), methodName, false, null, ref args))
+ {
+ throw ParseError(errorPos, Res.NoApplicableAggregate, methodName, string.Join(",", args.Select(a => a.Type.Name).ToArray()));
+ }
- typeArgs = new[] { ResolveTypeFromArgumentExpression(methodName, args[0]) };
- args = new Expression[0];
- }
- else if (new[] { "Min", "Max", "Select", "OrderBy", "OrderByDescending", "ThenBy", "ThenByDescending", "GroupBy" }.Contains(methodName))
+ Type callType = typeof(Enumerable);
+ if (isQueryable && _methodFinder.ContainsMethod(typeof(IQueryableSignatures), methodName, false, null, ref args))
+ {
+ callType = typeof(Queryable);
+ }
+
+ Type[] typeArgs;
+ if (new[] { "OfType", "Cast" }.Contains(methodName))
+ {
+ if (args.Length != 1)
{
- if (args.Length == 2)
- {
- typeArgs = new[] { elementType, args[0].Type, args[1].Type };
- }
- else
- {
- typeArgs = new[] { elementType, args[0].Type };
- }
+ throw ParseError(_textParser.CurrentToken.Pos, Res.FunctionRequiresOneArg, methodName);
}
- else if (methodName == "SelectMany")
+
+ typeArgs = new[] { ResolveTypeFromArgumentExpression(methodName, args[0]) };
+ args = new Expression[0];
+ }
+ else if (new[] { "Min", "Max", "Select", "OrderBy", "OrderByDescending", "ThenBy", "ThenByDescending", "GroupBy" }.Contains(methodName))
+ {
+ if (args.Length == 2)
{
- var bodyType = Expression.Lambda(args[0], innerIt).Body.Type;
- var interfaces = bodyType.GetInterfaces().Union(new[] { bodyType });
- Type interfaceType = interfaces.Single(i => i.Name == typeof(IEnumerable<>).Name);
- Type resultType = interfaceType.GetTypeInfo().GetGenericTypeArguments()[0];
- typeArgs = new[] { elementType, resultType };
+ typeArgs = new[] { elementType, args[0].Type, args[1].Type };
}
else
{
- typeArgs = new[] { elementType };
+ typeArgs = new[] { elementType, args[0].Type };
}
+ }
+ else if (methodName == "SelectMany")
+ {
+ var bodyType = Expression.Lambda(args[0], innerIt).Body.Type;
+ var interfaces = bodyType.GetInterfaces().Union(new[] { bodyType });
+ Type interfaceType = interfaces.Single(i => i.Name == typeof(IEnumerable<>).Name);
+ Type resultType = interfaceType.GetTypeInfo().GetGenericTypeArguments()[0];
+ typeArgs = new[] { elementType, resultType };
+ }
+ else
+ {
+ typeArgs = new[] { elementType };
+ }
- if (args.Length == 0)
+ if (args.Length == 0)
+ {
+ args = new[] { instance };
+ }
+ else
+ {
+ if (new[] { "Concat", "Contains", "DefaultIfEmpty", "Except", "Intersect", "Skip", "Take", "Union" }.Contains(methodName))
{
- args = new[] { instance };
+ args = new[] { instance, args[0] };
}
else
{
- if (new[] { "Concat", "Contains", "DefaultIfEmpty", "Except", "Intersect", "Skip", "Take", "Union" }.Contains(methodName))
+ if (args.Length == 2)
{
- args = new[] { instance, args[0] };
+ args = new[] { instance, Expression.Lambda(args[0], innerIt), Expression.Lambda(args[1], innerIt) };
}
else
{
- if (args.Length == 2)
- {
- args = new[] { instance, Expression.Lambda(args[0], innerIt), Expression.Lambda(args[1], innerIt) };
- }
- else
- {
- args = new[] { instance, Expression.Lambda(args[0], innerIt) };
- }
+ args = new[] { instance, Expression.Lambda(args[0], innerIt) };
}
}
-
- return Expression.Call(callType, methodName, typeArgs, args);
}
- private Type ResolveTypeFromArgumentExpression(string functionName, Expression argumentExpression, int? arguments = null)
+ return Expression.Call(callType, methodName, typeArgs, args);
+ }
+
+ private Type ResolveTypeFromArgumentExpression(string functionName, Expression argumentExpression, int? arguments = null)
+ {
+ string argument = arguments == null ? string.Empty : arguments == 1 ? "first " : "second ";
+ switch (argumentExpression)
{
- string argument = arguments == null ? string.Empty : arguments == 1 ? "first " : "second ";
- switch (argumentExpression)
- {
- case ConstantExpression constantExpression:
- switch (constantExpression.Value)
- {
- case string typeName:
- return ResolveTypeStringFromArgument(typeName);
+ case ConstantExpression constantExpression:
+ switch (constantExpression.Value)
+ {
+ case string typeName:
+ return ResolveTypeStringFromArgument(typeName);
- case Type type:
- return type;
+ case Type type:
+ return type;
- default:
- throw ParseError(_textParser.CurrentToken.Pos, Res.FunctionRequiresNotNullArgOfType, functionName, argument, "string or System.Type");
- }
+ default:
+ throw ParseError(_textParser.CurrentToken.Pos, Res.FunctionRequiresNotNullArgOfType, functionName, argument, "string or System.Type");
+ }
- default:
- throw ParseError(_textParser.CurrentToken.Pos, Res.FunctionRequiresNotNullArgOfType, functionName, argument, "ConstantExpression");
- }
+ default:
+ throw ParseError(_textParser.CurrentToken.Pos, Res.FunctionRequiresNotNullArgOfType, functionName, argument, "ConstantExpression");
}
+ }
- private Type ResolveTypeStringFromArgument(string typeName)
+ private Type ResolveTypeStringFromArgument(string typeName)
+ {
+ bool typeIsNullable = false;
+ if (typeName.EndsWith("?"))
{
- bool typeIsNullable = false;
- if (typeName.EndsWith("?"))
- {
- typeName = typeName.TrimEnd('?');
- typeIsNullable = true;
- }
-
- var resultType = _typeFinder.FindTypeByName(typeName, new[] { _it, _parent, _root }, true);
- if (resultType == null)
- {
- throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, typeName);
- }
-
- return typeIsNullable ? TypeHelper.ToNullableType(resultType) : resultType;
+ typeName = typeName.TrimEnd('?');
+ typeIsNullable = true;
}
- private Expression[] ParseArgumentList()
+ var resultType = _typeFinder.FindTypeByName(typeName, new[] { _it, _parent, _root }, true);
+ if (resultType == null)
{
- _textParser.ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
- _textParser.NextToken();
- Expression[] args = _textParser.CurrentToken.Id != TokenId.CloseParen ? ParseArguments() : new Expression[0];
- _textParser.ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);
- _textParser.NextToken();
- return args;
+ throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, typeName);
}
- private Expression[] ParseArguments()
- {
- var argList = new List();
- while (true)
- {
- var argumentExpression = ParseConditionalOperator();
+ return typeIsNullable ? TypeHelper.ToNullableType(resultType) : resultType;
+ }
+
+ private Expression[] ParseArgumentList()
+ {
+ _textParser.ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
+ _textParser.NextToken();
+ Expression[] args = _textParser.CurrentToken.Id != TokenId.CloseParen ? ParseArguments() : new Expression[0];
+ _textParser.ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);
+ _textParser.NextToken();
+ return args;
+ }
- _expressionHelper.WrapConstantExpression(ref argumentExpression);
+ private Expression[] ParseArguments()
+ {
+ var argList = new List();
+ while (true)
+ {
+ var argumentExpression = ParseConditionalOperator();
- argList.Add(argumentExpression);
+ _expressionHelper.WrapConstantExpression(ref argumentExpression);
- if (_textParser.CurrentToken.Id != TokenId.Comma)
- {
- break;
- }
+ argList.Add(argumentExpression);
- _textParser.NextToken();
+ if (_textParser.CurrentToken.Id != TokenId.Comma)
+ {
+ break;
}
- return argList.ToArray();
- }
-
- private Expression ParseElementAccess(Expression expr)
- {
- int errorPos = _textParser.CurrentToken.Pos;
- _textParser.ValidateToken(TokenId.OpenBracket, Res.OpenParenExpected);
_textParser.NextToken();
+ }
- Expression[] args = ParseArguments();
- _textParser.ValidateToken(TokenId.CloseBracket, Res.CloseBracketOrCommaExpected);
- _textParser.NextToken();
+ return argList.ToArray();
+ }
- if (expr.Type.IsArray)
- {
- if (expr.Type.GetArrayRank() != 1 || args.Length != 1)
- {
- throw ParseError(errorPos, Res.CannotIndexMultiDimArray);
- }
+ private Expression ParseElementAccess(Expression expr)
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ _textParser.ValidateToken(TokenId.OpenBracket, Res.OpenParenExpected);
+ _textParser.NextToken();
- var indexExpression = _parsingConfig.ExpressionPromoter.Promote(args[0], typeof(int), true, false);
- if (indexExpression == null)
- {
- throw ParseError(errorPos, Res.InvalidIndex);
- }
+ Expression[] args = ParseArguments();
+ _textParser.ValidateToken(TokenId.CloseBracket, Res.CloseBracketOrCommaExpected);
+ _textParser.NextToken();
- return Expression.ArrayIndex(expr, indexExpression);
+ if (expr.Type.IsArray)
+ {
+ if (expr.Type.GetArrayRank() != 1 || args.Length != 1)
+ {
+ throw ParseError(errorPos, Res.CannotIndexMultiDimArray);
}
- switch (_methodFinder.FindIndexer(expr.Type, args, out var mb))
+ var indexExpression = _parsingConfig.ExpressionPromoter.Promote(args[0], typeof(int), true, false);
+ if (indexExpression == null)
{
- case 0:
- throw ParseError(errorPos, Res.NoApplicableIndexer,
- TypeHelper.GetTypeName(expr.Type));
-
- case 1:
- var indexMethod = (MethodInfo)mb!;
- var indexParameterType = indexMethod.GetParameters().First().ParameterType;
-
- var indexArgumentExpression = args[0]; // Indexer only has 1 parameter, so we can use args[0] here
- if (indexParameterType != indexArgumentExpression.Type)
- {
- indexArgumentExpression = Expression.Convert(indexArgumentExpression, indexParameterType);
- }
-
- return Expression.Call(expr, indexMethod, indexArgumentExpression);
-
- default:
- throw ParseError(errorPos, Res.AmbiguousIndexerInvocation, TypeHelper.GetTypeName(expr.Type));
+ throw ParseError(errorPos, Res.InvalidIndex);
}
+
+ return Expression.ArrayIndex(expr, indexExpression);
}
- internal static Type ToNullableType(Type type)
+ switch (_methodFinder.FindIndexer(expr.Type, args, out var mb))
{
- Check.NotNull(type, nameof(type));
+ case 0:
+ throw ParseError(errorPos, Res.NoApplicableIndexer,
+ TypeHelper.GetTypeName(expr.Type));
- if (!type.GetTypeInfo().IsValueType || TypeHelper.IsNullableType(type))
- {
- throw ParseError(-1, Res.TypeHasNoNullableForm, TypeHelper.GetTypeName(type));
- }
+ case 1:
+ var indexMethod = (MethodInfo)mb!;
+ var indexParameterType = indexMethod.GetParameters().First().ParameterType;
+
+ var indexArgumentExpression = args[0]; // Indexer only has 1 parameter, so we can use args[0] here
+ if (indexParameterType != indexArgumentExpression.Type)
+ {
+ indexArgumentExpression = Expression.Convert(indexArgumentExpression, indexParameterType);
+ }
- return typeof(Nullable<>).MakeGenericType(type);
+ return Expression.Call(expr, indexMethod, indexArgumentExpression);
+
+ default:
+ throw ParseError(errorPos, Res.AmbiguousIndexerInvocation, TypeHelper.GetTypeName(expr.Type));
}
+ }
- private static bool TryGetMemberName(Expression expression, out string? memberName)
+ internal static Type ToNullableType(Type type)
+ {
+ Check.NotNull(type, nameof(type));
+
+ if (!type.GetTypeInfo().IsValueType || TypeHelper.IsNullableType(type))
{
- var memberExpression = expression as MemberExpression;
- if (memberExpression == null && expression.NodeType == ExpressionType.Coalesce)
- {
- memberExpression = (expression as BinaryExpression)?.Left as MemberExpression;
- }
+ throw ParseError(-1, Res.TypeHasNoNullableForm, TypeHelper.GetTypeName(type));
+ }
- if (memberExpression != null)
- {
- memberName = memberExpression.Member.Name;
- return true;
- }
+ return typeof(Nullable<>).MakeGenericType(type);
+ }
-#if NETFX_CORE
- var indexExpression = expression as IndexExpression;
- if (indexExpression != null && indexExpression.Indexer.DeclaringType == typeof(DynamicObjectClass))
- {
- memberName = ((ConstantExpression)indexExpression.Arguments.First()).Value as string;
- return true;
- }
-#endif
- memberName = null;
- return false;
+ private static bool TryGetMemberName(Expression expression, out string? memberName)
+ {
+ var memberExpression = expression as MemberExpression;
+ if (memberExpression == null && expression.NodeType == ExpressionType.Coalesce)
+ {
+ memberExpression = (expression as BinaryExpression)?.Left as MemberExpression;
}
- private void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos)
+ if (memberExpression != null)
{
- Expression[] args = { expr };
-
- if (!_methodFinder.ContainsMethod(signatures, "F", false, null, ref args))
- {
- throw IncompatibleOperandError(opName, expr, errorPos);
- }
+ memberName = memberExpression.Member.Name;
+ return true;
+ }
- expr = args[0];
+#if NETFX_CORE
+ var indexExpression = expression as IndexExpression;
+ if (indexExpression != null && indexExpression.Indexer.DeclaringType == typeof(DynamicObjectClass))
+ {
+ memberName = ((ConstantExpression)indexExpression.Arguments.First()).Value as string;
+ return true;
}
+#endif
+ memberName = null;
+ return false;
+ }
- private static string? GetOverloadedOperationName(TokenId tokenId)
+ private void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos)
+ {
+ Expression[] args = { expr };
+
+ if (!_methodFinder.ContainsMethod(signatures, "F", false, null, ref args))
{
- switch (tokenId)
- {
- case TokenId.DoubleEqual:
- case TokenId.Equal:
- return "op_Equality";
- case TokenId.ExclamationEqual:
- return "op_Inequality";
- default:
- return null;
- }
+ throw IncompatibleOperandError(opName, expr, errorPos);
}
- private void CheckAndPromoteOperands(Type signatures, TokenId opId, string opName, ref Expression left, ref Expression right, int errorPos)
+ expr = args[0];
+ }
+
+ private static string? GetOverloadedOperationName(TokenId tokenId)
+ {
+ switch (tokenId)
{
- Expression[] args = { left, right };
+ case TokenId.DoubleEqual:
+ case TokenId.Equal:
+ return "op_Equality";
+ case TokenId.ExclamationEqual:
+ return "op_Inequality";
+ default:
+ return null;
+ }
+ }
- // support operator overloading
- var nativeOperation = GetOverloadedOperationName(opId);
- bool found = false;
+ private void CheckAndPromoteOperands(Type signatures, TokenId opId, string opName, ref Expression left, ref Expression right, int errorPos)
+ {
+ Expression[] args = { left, right };
- if (nativeOperation != null)
- {
- // first try left operand's equality operators
- found = _methodFinder.ContainsMethod(left.Type, nativeOperation, true, null, ref args);
- if (!found)
- {
- found = _methodFinder.ContainsMethod(right.Type, nativeOperation, true, null, ref args);
- }
- }
+ // support operator overloading
+ var nativeOperation = GetOverloadedOperationName(opId);
+ bool found = false;
- if (!found && !_methodFinder.ContainsMethod(signatures, "F", false, null, ref args))
+ if (nativeOperation != null)
+ {
+ // first try left operand's equality operators
+ found = _methodFinder.ContainsMethod(left.Type, nativeOperation, true, null, ref args);
+ if (!found)
{
- throw IncompatibleOperandsError(opName, left, right, errorPos);
+ found = _methodFinder.ContainsMethod(right.Type, nativeOperation, true, null, ref args);
}
-
- left = args[0];
- right = args[1];
}
- private static Exception IncompatibleOperandError(string opName, Expression expr, int errorPos)
+ if (!found && !_methodFinder.ContainsMethod(signatures, "F", false, null, ref args))
{
- return ParseError(errorPos, Res.IncompatibleOperand, opName, TypeHelper.GetTypeName(expr.Type));
+ throw IncompatibleOperandsError(opName, left, right, errorPos);
}
- private static Exception IncompatibleOperandsError(string opName, Expression left, Expression right, int errorPos)
- {
- return ParseError(errorPos, Res.IncompatibleOperands, opName, TypeHelper.GetTypeName(left.Type), TypeHelper.GetTypeName(right.Type));
- }
+ left = args[0];
+ right = args[1];
+ }
- private MemberInfo? FindPropertyOrField(Type type, string memberName, bool staticAccess)
- {
-#if !(NETFX_CORE || WINDOWS_APP || UAP10_0 || NETSTANDARD)
- var extraBindingFlag = _parsingConfig.PrioritizePropertyOrFieldOverTheType && staticAccess ? BindingFlags.Static : BindingFlags.Instance;
- var bindingFlags = BindingFlags.Public | BindingFlags.DeclaredOnly | extraBindingFlag;
- foreach (Type t in TypeHelper.GetSelfAndBaseTypes(type))
- {
- var findMembersType = _parsingConfig?.IsCaseSensitive == true ? Type.FilterName : Type.FilterNameIgnoreCase;
- var members = t.FindMembers(MemberTypes.Property | MemberTypes.Field, bindingFlags, findMembersType, memberName);
+ private static Exception IncompatibleOperandError(string opName, Expression expr, int errorPos)
+ {
+ return ParseError(errorPos, Res.IncompatibleOperand, opName, TypeHelper.GetTypeName(expr.Type));
+ }
- if (members.Length != 0)
- {
- return members[0];
- }
- }
- return null;
-#else
- var isCaseSensitive = _parsingConfig?.IsCaseSensitive == true;
- foreach (Type t in TypeHelper.GetSelfAndBaseTypes(type))
- {
- // Try to find a property with the specified memberName
- MemberInfo? member = t.GetTypeInfo().DeclaredProperties.FirstOrDefault(x => (!staticAccess || x.GetAccessors(true)[0].IsStatic) && ((x.Name == memberName) || (!isCaseSensitive && x.Name.Equals(memberName, StringComparison.OrdinalIgnoreCase))));
- if (member != null)
- {
- return member;
- }
+ private static Exception IncompatibleOperandsError(string opName, Expression left, Expression right, int errorPos)
+ {
+ return ParseError(errorPos, Res.IncompatibleOperands, opName, TypeHelper.GetTypeName(left.Type), TypeHelper.GetTypeName(right.Type));
+ }
- // If no property is found, try to get a field with the specified memberName
- member = t.GetTypeInfo().DeclaredFields.FirstOrDefault(x => (!staticAccess || x.IsStatic) && ((x.Name == memberName) || (!isCaseSensitive && x.Name.Equals(memberName, StringComparison.OrdinalIgnoreCase))));
- if (member != null)
- {
- return member;
- }
+ private MemberInfo? FindPropertyOrField(Type type, string memberName, bool staticAccess)
+ {
+#if !(NETFX_CORE || WINDOWS_APP || UAP10_0 || NETSTANDARD)
+ var extraBindingFlag = _parsingConfig.PrioritizePropertyOrFieldOverTheType && staticAccess ? BindingFlags.Static : BindingFlags.Instance;
+ var bindingFlags = BindingFlags.Public | BindingFlags.DeclaredOnly | extraBindingFlag;
+ foreach (Type t in TypeHelper.GetSelfAndBaseTypes(type))
+ {
+ var findMembersType = _parsingConfig?.IsCaseSensitive == true ? Type.FilterName : Type.FilterNameIgnoreCase;
+ var members = t.FindMembers(MemberTypes.Property | MemberTypes.Field, bindingFlags, findMembersType, memberName);
- // No property or field is found, try the base type.
+ if (members.Length != 0)
+ {
+ return members[0];
}
- return null;
-#endif
}
-
- private bool TokenIdentifierIs(string id)
+ return null;
+#else
+ var isCaseSensitive = _parsingConfig?.IsCaseSensitive == true;
+ foreach (Type t in TypeHelper.GetSelfAndBaseTypes(type))
{
- return _textParser.CurrentToken.Id == TokenId.Identifier && string.Equals(id, _textParser.CurrentToken.Text, StringComparison.OrdinalIgnoreCase);
- }
+ // Try to find a property with the specified memberName
+ MemberInfo? member = t.GetTypeInfo().DeclaredProperties.FirstOrDefault(x => (!staticAccess || x.GetAccessors(true)[0].IsStatic) && ((x.Name == memberName) || (!isCaseSensitive && x.Name.Equals(memberName, StringComparison.OrdinalIgnoreCase))));
+ if (member != null)
+ {
+ return member;
+ }
- private string GetIdentifier()
- {
- _textParser.ValidateToken(TokenId.Identifier, Res.IdentifierExpected);
- string id = _textParser.CurrentToken.Text;
- if (id.Length > 1 && id[0] == '@')
+ // If no property is found, try to get a field with the specified memberName
+ member = t.GetTypeInfo().DeclaredFields.FirstOrDefault(x => (!staticAccess || x.IsStatic) && ((x.Name == memberName) || (!isCaseSensitive && x.Name.Equals(memberName, StringComparison.OrdinalIgnoreCase))));
+ if (member != null)
{
- id = id.Substring(1);
+ return member;
}
- return id;
+ // No property or field is found, try the base type.
}
+ return null;
+#endif
+ }
- private Exception ParseError(string format, params object[] args)
- {
- return ParseError(_textParser.CurrentToken.Pos, format, args);
- }
+ private bool TokenIdentifierIs(string id)
+ {
+ return _textParser.CurrentToken.Id == TokenId.Identifier && string.Equals(id, _textParser.CurrentToken.Text, StringComparison.OrdinalIgnoreCase);
+ }
- private static Exception ParseError(int pos, string format, params object[] args)
+ private string GetIdentifier()
+ {
+ _textParser.ValidateToken(TokenId.Identifier, Res.IdentifierExpected);
+ string id = _textParser.CurrentToken.Text;
+ if (id.Length > 1 && id[0] == '@')
{
- return new ParseException(string.Format(CultureInfo.CurrentCulture, format, args), pos);
+ id = id.Substring(1);
}
+
+ return id;
+ }
+
+ private Exception ParseError(string format, params object[] args)
+ {
+ return ParseError(_textParser.CurrentToken.Pos, format, args);
+ }
+
+ private static Exception ParseError(int pos, string format, params object[] args)
+ {
+ return new ParseException(string.Format(CultureInfo.CurrentCulture, format, args), pos);
}
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/IConstantExpressionWrapper.cs b/src/System.Linq.Dynamic.Core/Parser/IConstantExpressionWrapper.cs
index 42acec1d..e2cb4904 100644
--- a/src/System.Linq.Dynamic.Core/Parser/IConstantExpressionWrapper.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/IConstantExpressionWrapper.cs
@@ -1,9 +1,11 @@
-using System.Linq.Expressions;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq.Expressions;
-namespace System.Linq.Dynamic.Core.Parser
+namespace System.Linq.Dynamic.Core.Parser;
+
+internal interface IConstantExpressionWrapper
{
- internal interface IConstantExpressionWrapper
- {
- void Wrap(ref Expression expression);
- }
-}
+ void Wrap(ref Expression expression);
+
+ bool TryUnwrap(MemberExpression? expression, [NotNullWhen(true)] out TValue? value);
+}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs b/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs
index 8fb14e71..b276a712 100644
--- a/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs
@@ -1,43 +1,45 @@
-using System.Linq.Expressions;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq.Expressions;
-namespace System.Linq.Dynamic.Core.Parser
+namespace System.Linq.Dynamic.Core.Parser;
+
+internal interface IExpressionHelper
{
- internal interface IExpressionHelper
- {
- void ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref Expression left, ref Expression right);
+ void ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref Expression left, ref Expression right);
+
+ Expression GenerateAdd(Expression left, Expression right);
- Expression GenerateAdd(Expression left, Expression right);
+ Expression GenerateEqual(Expression left, Expression right);
- Expression GenerateEqual(Expression left, Expression right);
+ Expression GenerateGreaterThan(Expression left, Expression right);
- Expression GenerateGreaterThan(Expression left, Expression right);
+ Expression GenerateGreaterThanEqual(Expression left, Expression right);
- Expression GenerateGreaterThanEqual(Expression left, Expression right);
+ Expression GenerateLessThan(Expression left, Expression right);
- Expression GenerateLessThan(Expression left, Expression right);
+ Expression GenerateLessThanEqual(Expression left, Expression right);
- Expression GenerateLessThanEqual(Expression left, Expression right);
+ Expression GenerateNotEqual(Expression left, Expression right);
- Expression GenerateNotEqual(Expression left, Expression right);
+ Expression GenerateStringConcat(Expression left, Expression right);
- Expression GenerateStringConcat(Expression left, Expression right);
+ Expression GenerateSubtract(Expression left, Expression right);
- Expression GenerateSubtract(Expression left, Expression right);
+ void OptimizeForEqualityIfPossible(ref Expression left, ref Expression right);
- void OptimizeForEqualityIfPossible(ref Expression left, ref Expression right);
+ Expression? OptimizeStringForEqualityIfPossible(string text, Type type);
- Expression? OptimizeStringForEqualityIfPossible(string text, Type type);
+ bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, bool addSelf, out Expression generatedExpression);
- bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, bool addSelf, out Expression generatedExpression);
+ bool ExpressionQualifiesForNullPropagation(Expression expression);
- bool ExpressionQualifiesForNullPropagation(Expression expression);
+ void WrapConstantExpression(ref Expression argument);
- void WrapConstantExpression(ref Expression argument);
+ bool TryUnwrapConstantExpression(Expression? expression, [NotNullWhen(true)] out TValue? value);
- bool MemberExpressionIsDynamic(Expression expression);
+ bool MemberExpressionIsDynamic(Expression expression);
- Expression ConvertToExpandoObjectAndCreateDynamicExpression(Expression expression, Type type, string propertyName);
+ Expression ConvertToExpandoObjectAndCreateDynamicExpression(Expression expression, Type type, string propertyName);
- Expression GenerateDefaultExpression(Type type);
- }
-}
+ Expression GenerateDefaultExpression(Type type);
+}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/SupportedOperands/IEqualitySignatures.cs b/src/System.Linq.Dynamic.Core/Parser/SupportedOperands/IEqualitySignatures.cs
index 8b379b53..e7c79c31 100644
--- a/src/System.Linq.Dynamic.Core/Parser/SupportedOperands/IEqualitySignatures.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/SupportedOperands/IEqualitySignatures.cs
@@ -1,23 +1,22 @@
-namespace System.Linq.Dynamic.Core.Parser.SupportedOperands
+namespace System.Linq.Dynamic.Core.Parser.SupportedOperands;
+
+internal interface IEqualitySignatures : IRelationalSignatures
{
- internal interface IEqualitySignatures : IRelationalSignatures
- {
- void F(bool x, bool y);
- void F(bool? x, bool? y);
+ void F(bool x, bool y);
+ void F(bool? x, bool? y);
- // Disabled 4 lines below because of : https://github.com/StefH/System.Linq.Dynamic.Core/issues/19
- //void F(DateTime x, string y);
- //void F(DateTime? x, string y);
- //void F(string x, DateTime y);
- //void F(string x, DateTime? y);
+ // Disabled 4 lines below because of : https://github.com/StefH/System.Linq.Dynamic.Core/issues/19
+ //void F(DateTime x, string y);
+ //void F(DateTime? x, string y);
+ //void F(string x, DateTime y);
+ //void F(string x, DateTime? y);
- void F(Guid x, Guid y);
- void F(Guid? x, Guid? y);
+ void F(Guid x, Guid y);
+ void F(Guid? x, Guid? y);
- // Disabled 4 lines below because of : https://github.com/StefH/System.Linq.Dynamic.Core/pull/200
- //void F(Guid x, string y);
- //void F(Guid? x, string y);
- //void F(string x, Guid y);
- //void F(string x, Guid? y);
- }
-}
+ // Disabled 4 lines below because of : https://github.com/StefH/System.Linq.Dynamic.Core/pull/200
+ //void F(Guid x, string y);
+ //void F(Guid? x, string y);
+ //void F(string x, Guid y);
+ //void F(string x, Guid? y);
+}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/WrappedValue.cs b/src/System.Linq.Dynamic.Core/Parser/WrappedValue.cs
index 34a923e5..3121f4af 100644
--- a/src/System.Linq.Dynamic.Core/Parser/WrappedValue.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/WrappedValue.cs
@@ -1,12 +1,11 @@
-namespace System.Linq.Dynamic.Core.Parser
+namespace System.Linq.Dynamic.Core.Parser;
+
+internal class WrappedValue
{
- internal class WrappedValue
- {
- public TValue Value { get; private set; }
+ public TValue Value { get; }
- public WrappedValue(TValue value)
- {
- Value = value;
- }
+ public WrappedValue(TValue value)
+ {
+ Value = value;
}
-}
+}
\ No newline at end of file
diff --git a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.UseParameterizedNamesInDynamicQuery .cs b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.UseParameterizedNamesInDynamicQuery .cs
new file mode 100644
index 00000000..698e63fe
--- /dev/null
+++ b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.UseParameterizedNamesInDynamicQuery .cs
@@ -0,0 +1,85 @@
+using System.Collections.Generic;
+using FluentAssertions;
+using Xunit;
+
+namespace System.Linq.Dynamic.Core.Tests;
+
+public partial class QueryableTests
+{
+ ///
+ /// Issue #645
+ ///
+ [Fact]
+ public void When_UseParameterizedNamesInDynamicQuery_IsTrue_WrappedStringValue_Should_Be_Unwrapped()
+ {
+ // Arrange
+ var list = new List
+ {
+ new()
+ {
+ Name = "Terri Lee Duffy",
+ CompanyName = "ABC",
+ City = "Paris",
+ Phone = "333-444444",
+ Location = new Location { Name = "test" },
+ LastContact = DateTimeOffset.Parse("2022-11-14")
+ },
+ new()
+ {
+ Name = "Garry Moore",
+ CompanyName = "ABC",
+ City = "Paris",
+ Phone = "54654-444444",
+ Location = new Location { Name = "other test", UpdateAt = DateTimeOffset.Parse("2022-11-16") },
+ LastContact = DateTimeOffset.Parse("2022-11-16")
+ }
+ };
+
+ var config = new ParsingConfig
+ {
+ UseParameterizedNamesInDynamicQuery = true
+ };
+
+ // Act 1A
+ var result1A = list.AsQueryable().Where(config, "LastContact = \"2022-11-16\"").ToArray();
+
+ // Assert 1A
+ result1A.Should().HaveCount(1);
+
+ // Act 1B
+ var result1B = list.AsQueryable().Where(config, "\"2022-11-16\" == LastContact").ToArray();
+
+ // Assert 1B
+ result1B.Should().HaveCount(1);
+
+ // Act 2A
+ var result2A = list.AsQueryable().Where("Location.UpdateAt = \"2022-11-16\"").ToArray();
+
+ // Assert 2A
+ result2A.Should().HaveCount(1);
+
+ // Act 2B
+ var result2B = list.AsQueryable().Where("\"2022-11-16\" == Location.UpdateAt").ToArray();
+
+ // Assert 2B
+ result2B.Should().HaveCount(1);
+ }
+}
+
+public class Customer
+{
+ public int CustomerID { get; set; }
+ public string Name { get; set; }
+ public string CompanyName { get; set; }
+ public string City { get; set; }
+ public string Phone { get; set; }
+ public Location Location { get; set; }
+ public DateTimeOffset? LastContact { get; set; }
+}
+
+public class Location
+{
+ public int LocationID { get; set; }
+ public string Name { get; set; }
+ public DateTimeOffset UpdateAt { get; set; }
+}
\ No newline at end of file