From: <te...@us...> - 2008-08-14 17:04:27
|
Revision: 3702 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=3702&view=rev Author: tehlike Date: 2008-08-14 17:04:36 +0000 (Thu, 14 Aug 2008) Log Message: ----------- Added logical expression reducer. Modified Paths: -------------- trunk/nhibernate/src/NHibernate.Linq/NHibernate.Linq.csproj trunk/nhibernate/src/NHibernate.Linq.Test/NHibernate.Linq.Test.csproj Added Paths: ----------- trunk/nhibernate/src/NHibernate.Linq/Visitors/ trunk/nhibernate/src/NHibernate.Linq/Visitors/LogicalExpressionReducer.cs trunk/nhibernate/src/NHibernate.Linq.Test/VisitorTests/ trunk/nhibernate/src/NHibernate.Linq.Test/VisitorTests/LogicalExpressionSimplifierTests.cs Modified: trunk/nhibernate/src/NHibernate.Linq/NHibernate.Linq.csproj =================================================================== --- trunk/nhibernate/src/NHibernate.Linq/NHibernate.Linq.csproj 2008-08-13 14:59:18 UTC (rev 3701) +++ trunk/nhibernate/src/NHibernate.Linq/NHibernate.Linq.csproj 2008-08-14 17:04:36 UTC (rev 3702) @@ -3,7 +3,7 @@ <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <ProductVersion>9.0.21022</ProductVersion> + <ProductVersion>9.0.30729</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{3073F5DA-5D57-4E12-9F95-8516ED346E67}</ProjectGuid> <OutputType>Library</OutputType> @@ -46,6 +46,7 @@ </ProjectReference> </ItemGroup> <ItemGroup> + <Compile Include="Visitors\LogicalExpressionReducer.cs" /> <Compile Include="ExpressionVisitor.cs" /> <Compile Include="NHibernateExtensions.cs" /> <Compile Include="NHibernateQueryProvider.cs" /> Added: trunk/nhibernate/src/NHibernate.Linq/Visitors/LogicalExpressionReducer.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Linq/Visitors/LogicalExpressionReducer.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Linq/Visitors/LogicalExpressionReducer.cs 2008-08-14 17:04:36 UTC (rev 3702) @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +namespace NHibernate.Linq.Visitors +{ + /// <summary> + /// Reduces the expression using logical simplification + /// Please note that it doesn't evaluate the binary expressions with constant operands + /// </summary> + + //May be used for animal.Offspring.Any() == true + //Eventhough it is almost unnecessary to reduce not(not(true)) kind of expression, but simplified expressions are gold. + public class LogicalExpressionReducer:ExpressionVisitor + { + protected override Expression VisitBinary(BinaryExpression expr) + { + bool modified; + Expression e = ProcessBinaryExpression(expr.Left, expr.Right, expr.NodeType, out modified); + if (modified) + return Visit(e); + e = ProcessBinaryExpression(expr.Right, expr.Left, expr.NodeType, out modified); + if(modified) + return Visit(e); + return expr; + + } + + protected override Expression VisitUnary(UnaryExpression u) + { + switch(u.NodeType) + { + case ExpressionType.Not: + if (u.Operand.NodeType == ExpressionType.Not) + return Visit(((UnaryExpression) (u.Operand)).Operand); + else if (u.Operand.NodeType == ExpressionType.Equal) + { + var binaryExpression = u.Operand as BinaryExpression; + return Visit(Expression.NotEqual(binaryExpression.Left, binaryExpression.Right)); + } + else if (u.Operand.NodeType == ExpressionType.Equal) + { + var binaryExpression = u.Operand as BinaryExpression; + return Visit(Expression.Equal(binaryExpression.Left, binaryExpression.Right)); + } + else if(u.Operand.NodeType==ExpressionType.Constant) + { + var constantExpression = u.Operand as ConstantExpression; + return Visit(Expression.Constant(!((bool)constantExpression.Value))); + } + break; + } + return u; + } + + private Expression ProcessBinaryExpression(Expression exprToCompare, Expression exprToReturn, + ExpressionType nodeType, out bool modified) + { + modified = false; + + var visitor = new BooleanConstantFinder(); + visitor.Visit(exprToCompare); + + if (!visitor.Constant.HasValue) + return null; + + bool constantValue = visitor.Constant.Value; + switch (nodeType) + { + case ExpressionType.Equal: + modified = true; + return constantValue ? exprToReturn : Expression.Not(exprToReturn); + case ExpressionType.NotEqual: + modified = true; + return constantValue ? Expression.Not(exprToReturn) : exprToReturn; + case ExpressionType.Or: + case ExpressionType.OrElse: + modified = true; + return constantValue ? Expression.Constant(true) : exprToReturn; + case ExpressionType.And: + case ExpressionType.AndAlso: + modified = true; + return constantValue ? exprToReturn : Expression.Constant(false); + default: + return null; + } + + } + + class BooleanConstantFinder : ExpressionVisitor + { + private bool isNestedBinaryExpression; + + public bool? Constant { get; private set; } + + protected override Expression VisitConstant(ConstantExpression c) + { + if (c.Type == typeof(bool) && !isNestedBinaryExpression) + Constant = (bool)c.Value; + return c; + } + + protected override Expression VisitBinary(BinaryExpression b) + { + isNestedBinaryExpression = true; + return b; + } + } + } +} \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate.Linq.Test/NHibernate.Linq.Test.csproj =================================================================== --- trunk/nhibernate/src/NHibernate.Linq.Test/NHibernate.Linq.Test.csproj 2008-08-13 14:59:18 UTC (rev 3701) +++ trunk/nhibernate/src/NHibernate.Linq.Test/NHibernate.Linq.Test.csproj 2008-08-14 17:04:36 UTC (rev 3702) @@ -3,7 +3,7 @@ <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <ProductVersion>9.0.21022</ProductVersion> + <ProductVersion>9.0.30729</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{FEAC164E-DE15-418A-8A70-35085B33C548}</ProjectGuid> <OutputType>Library</OutputType> @@ -76,6 +76,7 @@ <Compile Include="GlobalSetup.cs" /> <Compile Include="Model\Animal.cs" /> <Compile Include="SelectTest.cs" /> + <Compile Include="VisitorTests\LogicalExpressionSimplifierTests.cs" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Added: trunk/nhibernate/src/NHibernate.Linq.Test/VisitorTests/LogicalExpressionSimplifierTests.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Linq.Test/VisitorTests/LogicalExpressionSimplifierTests.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Linq.Test/VisitorTests/LogicalExpressionSimplifierTests.cs 2008-08-14 17:04:36 UTC (rev 3702) @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using NHibernate.Criterion; +using NHibernate.Linq.Visitors; +using NUnit.Framework; +using Expression=System.Linq.Expressions.Expression; + +namespace NHibernate.Linq.Test.VisitorTests +{ + [TestFixture] + public class LogicalExpressionSimplifierTests + { + [Test] + public void CanReduceAndAlsoWithFalse() + { + var left = Expression.Constant(false); + var right = Expression.GreaterThan(Expression.Constant(3), Expression.Constant(1)); + var ex1 = Expression.AndAlso(left, right); + var ex2 = Expression.AndAlso(right, left); + var reduced1 = Reduce(ex1) as ConstantExpression; + var reduced2 = Reduce(ex2) as ConstantExpression; + Assert.IsNotNull(reduced1); + Assert.IsNotNull(reduced2); + Assert.IsFalse((bool)reduced1.Value); + Assert.IsFalse((bool)reduced2.Value); + + Console.WriteLine("Original expressions\n"); + Console.WriteLine(ex1.ToString()); + Console.WriteLine(ex2.ToString()); + Console.WriteLine(""); + Console.WriteLine("Reduced expressions\n"); + Console.WriteLine(reduced1.ToString()); + Console.WriteLine(reduced2.ToString()); + } + + [Test] + public void CanReduceOrWithTrue() + { + var left = Expression.Constant(true); + var right = Expression.GreaterThan(Expression.Constant(3), Expression.Constant(1)); + var ex1 = Expression.OrElse(left, right); + var ex2 = Expression.OrElse(right, left); + var reduced1 = Reduce(ex1) as ConstantExpression; + var reduced2 = Reduce(ex2) as ConstantExpression; + Assert.IsNotNull(reduced1); + Assert.IsNotNull(reduced2); + Assert.IsTrue((bool)reduced1.Value); + Assert.IsTrue((bool)reduced2.Value); + + Console.WriteLine("Original expressions"); + Console.WriteLine(""); + Console.WriteLine(ex1.ToString()); + Console.WriteLine(ex2.ToString()); + Console.WriteLine(""); + Console.WriteLine("Reduced expressions"); + Console.WriteLine(""); + Console.WriteLine(reduced1.ToString()); + Console.WriteLine(reduced2.ToString()); + } + + [Test] + public void CanReduceEqualWithTrue() + { + var left = Expression.Constant(true); + var right = Expression.GreaterThan(Expression.Constant(3), Expression.Constant(1)); + var ex1 = Expression.Equal(left, right); + var ex2 = Expression.Equal(right, left); + var reduced1 = Reduce(ex1); + var reduced2 = Reduce(ex2); + Assert.IsNotNull(reduced1); + Assert.IsNotNull(reduced2); + Assert.AreEqual(reduced1,right); + Assert.AreEqual(reduced2, right); + + Console.WriteLine("Original expressions"); + Console.WriteLine(""); + Console.WriteLine(ex1.ToString()); + Console.WriteLine(ex2.ToString()); + Console.WriteLine(""); + Console.WriteLine("Reduced expressions"); + Console.WriteLine(""); + Console.WriteLine(reduced1.ToString()); + Console.WriteLine(reduced2.ToString()); + } + [Test] + public void CanReduceEqualWithFalse() + { + var left = Expression.Constant(false); + var right = Expression.GreaterThan(Expression.Constant(3), Expression.Constant(1)); + var ex1 = Expression.Equal(left, right); + var ex2 = Expression.Equal(right, left); + var reduced1 = Reduce(ex1) as UnaryExpression; + var reduced2 = Reduce(ex2) as UnaryExpression; + Assert.IsNotNull(reduced1); + Assert.IsNotNull(reduced2); + Assert.AreEqual(ExpressionType.Not, reduced1.NodeType); + Assert.AreEqual(ExpressionType.Not, reduced2.NodeType); + Assert.AreEqual(right,reduced1.Operand); + Assert.AreEqual(right, reduced2.Operand); + + Console.WriteLine("Original expressions"); + Console.WriteLine(""); + Console.WriteLine(ex1.ToString()); + Console.WriteLine(ex2.ToString()); + Console.WriteLine(""); + Console.WriteLine("Reduced expressions"); + Console.WriteLine(""); + Console.WriteLine(reduced1.ToString()); + Console.WriteLine(reduced2.ToString()); + } + + [Test] + public void CanReduceNotEqualWithTrue() + { + var left = Expression.Constant(true); + var right = Expression.GreaterThan(Expression.Constant(3), Expression.Constant(1)); + var ex1 = Expression.NotEqual(left, right); + var ex2 = Expression.NotEqual(right, left); + var reduced1 = Reduce(ex1) as UnaryExpression; + var reduced2 = Reduce(ex2) as UnaryExpression; + + Assert.IsNotNull(reduced1); + Assert.IsNotNull(reduced2); + Assert.AreEqual(ExpressionType.Not, reduced1.NodeType); + Assert.AreEqual(ExpressionType.Not, reduced2.NodeType); + Assert.AreEqual(right, reduced1.Operand); + Assert.AreEqual(right, reduced2.Operand); + + Console.WriteLine("Original expressions"); + Console.WriteLine(""); + Console.WriteLine(ex1.ToString()); + Console.WriteLine(ex2.ToString()); + Console.WriteLine(""); + Console.WriteLine("Reduced expressions"); + Console.WriteLine(reduced1.ToString()); + Console.WriteLine(reduced2.ToString()); + } + [Test] + public void CanReduceNotEqualWithFalse() + { + var left = Expression.Constant(false); + var right = Expression.GreaterThan(Expression.Constant(3), Expression.Constant(1)); + var ex1 = Expression.NotEqual(left, right); + var ex2 = Expression.NotEqual(right, left); + var reduced1 = Reduce(ex1); + var reduced2 = Reduce(ex2); + Assert.IsNotNull(reduced1); + Assert.IsNotNull(reduced2); + Assert.AreEqual(reduced1, right); + Assert.AreEqual(reduced2, right); + + Console.WriteLine("Original expressions\n"); + Console.WriteLine(""); + Console.WriteLine(ex1.ToString()); + Console.WriteLine(ex2.ToString()); + Console.WriteLine(""); + Console.WriteLine("Reduced expressions\n"); + Console.WriteLine(""); + Console.WriteLine(reduced1.ToString()); + Console.WriteLine(reduced2.ToString()); + } + + + [Test] + public void CanReduceNested() + { + var trueExpression = Expression.Constant(true); + var falseExpression = Expression.Constant(false); + var testExpression = Expression.Equal(Expression.Equal(Expression.Equal(falseExpression, falseExpression), falseExpression),falseExpression); + var reduced = Reduce(testExpression) as ConstantExpression; + Assert.IsNotNull(reduced); + Assert.IsTrue((bool)reduced.Value); + + Console.WriteLine("Original expressions\n"); + Console.WriteLine(testExpression.ToString()); + + Console.WriteLine("Reduced expressions\n"); + Console.WriteLine(reduced.ToString()); + } + [Test] + public void CanReduceNested2() + { + var trueExpression = Expression.Constant(true); + var falseExpression = Expression.Constant(false); + var testExpression = Expression.Equal(Expression.Equal(Expression.NotEqual(trueExpression, falseExpression), falseExpression), falseExpression); + var reduced = Reduce(testExpression) as ConstantExpression; + Assert.IsNotNull(reduced); + Assert.IsTrue((bool)reduced.Value); + + Console.WriteLine("Original expressions\n"); + Console.WriteLine(testExpression.ToString()); + + Console.WriteLine("Reduced expressions\n"); + Console.WriteLine(reduced.ToString()); + } + + protected Expression Reduce(Expression expr) + { + var reducer = new LogicalExpressionReducer(); + return reducer.Visit(expr); + } + + } +} \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |