Browse Source

feature: DEP vs CSP

Local 4 years ago
parent
commit
1694fdfeba

+ 1 - 1
FormulaEnginePoC/CSharpProvider/FormulaCompiler.cs

@@ -1,5 +1,5 @@
 using System;
-using FormulaEnginePoC.Common;
+using FormulaEnginePoC.FormulaEngine;
 
 namespace FormulaEnginePoC.CSharpProvider
 {

+ 1 - 1
FormulaEnginePoC/DynamicExpressionParse/ExpressionParser.cs

@@ -4,8 +4,8 @@ using System.Globalization;
 using System.Linq;
 using System.Linq.Expressions;
 using System.Reflection;
-using FormulaEnginePoC.Common;
 using FormulaEnginePoC.FormulaEngine;
+using Convert = System.Convert;
 
 namespace FormulaEnginePoC.DynamicExpressionParse
 {

+ 2 - 2
FormulaEnginePoC/FormulaEngine/CalcFormula.cs

@@ -4,7 +4,7 @@ namespace FormulaEnginePoC.FormulaEngine
 {
     public abstract class CalcFormula<TSource, TResult> : Formula
     {
-        public abstract Func<TSource, TResult> CalcFunc { get; }
+        public abstract Func<TSource, TResult> CompiledFunc { get; }
 
         public CalcFormula(string expression) : base(expression, typeof(TSource))
         {
@@ -12,7 +12,7 @@ namespace FormulaEnginePoC.FormulaEngine
 
         public TResult Calc(TSource source)
         {
-            return CalcFunc(source);
+            return CompiledFunc(source);
         }
     }
 }

+ 13 - 0
FormulaEnginePoC/FormulaEngine/Convert.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace FormulaEnginePoC.FormulaEngine
+{
+    public enum FormulaValueTypeConvert
+    {
+        ConvertToDouble = 1,
+        ConvertToDecimal = 2,
+    }
+}

+ 9 - 2
FormulaEnginePoC/FormulaEngine/CspImpl/CspCalcFormula.cs

@@ -5,11 +5,18 @@ namespace FormulaEnginePoC.FormulaEngine.CspImpl
 {
     public class CspCalcFormula<TSource, TResult> : CalcFormula<TSource, TResult>
     {
-        public override Func<TSource, TResult> CalcFunc { get; }
+        private Func<TSource, TResult> _compiledFunc;
+
+        public override Func<TSource, TResult> CompiledFunc => _compiledFunc;
 
         public CspCalcFormula(string expression) : base(expression)
         {
-            CalcFunc = FormulaCompiler.Compile<TSource, TResult>(CodeExpression);
+
+        }
+
+        public override void Compile()
+        {
+            _compiledFunc = FormulaCompiler.Compile<TSource, TResult>(CodeExpression);
         }
     }
 }

+ 11 - 5
FormulaEnginePoC/FormulaEngine/CspImpl/CspFilterFormula.cs

@@ -1,15 +1,21 @@
-using System;
-using FormulaEnginePoC.CSharpProvider;
+using FormulaEnginePoC.CSharpProvider;
+using System;
 
 namespace FormulaEnginePoC.FormulaEngine.CspImpl
 {
     public class CspFilterFormula<TSource> : FilterFormula<TSource>
     {
-        public override Func<TSource, bool> FilterFunc { get; }
+        private Func<TSource, bool> _compiledFunc;
 
-        public CspFilterFormula(string userExpression, Type mappingType) : base(userExpression, mappingType)
+        public override Func<TSource, bool> CompiledFunc => _compiledFunc;
+
+        public CspFilterFormula(string userExpression) : base(userExpression)
+        {
+        }
+
+        public override void Compile()
         {
-            FilterFunc = FormulaCompiler.Compile<TSource, bool>(CodeExpression);
+            _compiledFunc = FormulaCompiler.Compile<TSource, bool>(CodeExpression);
         }
     }
 }

+ 8 - 2
FormulaEnginePoC/FormulaEngine/DepImpl/DepCalcFormula.cs

@@ -5,11 +5,17 @@ namespace FormulaEnginePoC.FormulaEngine.DepImpl
 {
     public class DepCalcFormula<TSource, TResult> : CalcFormula<TSource, TResult>
     {
-        public override Func<TSource, TResult> CalcFunc { get; }
+        private Func<TSource, TResult> _compiledFunc;
+
+        public override Func<TSource, TResult> CompiledFunc => _compiledFunc;
 
         public DepCalcFormula(string expression) : base(expression)
         {
-            CalcFunc = FormulaCompiler.Compile<TSource, TResult>(CodeExpression);
+        }
+
+        public override void Compile()
+        {
+            _compiledFunc = FormulaCompiler.Compile<TSource, TResult>(CodeExpression);
         }
     }
 }

+ 12 - 6
FormulaEnginePoC/FormulaEngine/DepImpl/DepFilterFormula.cs

@@ -1,15 +1,21 @@
-using System;
-using FormulaEnginePoC.DynamicExpressionParse;
+using FormulaEnginePoC.DynamicExpressionParse;
+using System;
 
 namespace FormulaEnginePoC.FormulaEngine.DepImpl
 {
     public class DepFilterFormula<TSource> : FilterFormula<TSource>
     {
-        public override Func<TSource, bool> FilterFunc { get; }
+        private Func<TSource, bool> _compiledFunc;
 
-        public DepFilterFormula(string userExpression, Type mappingType) : base(userExpression, mappingType)
+        public override Func<TSource, bool> CompiledFunc => _compiledFunc;
+
+        public DepFilterFormula(string userExpression) : base(userExpression)
+        {
+        }
+
+        public override void Compile()
         {
-            FilterFunc = FormulaCompiler.Compile<TSource, bool>(CodeExpression);
+            _compiledFunc = FormulaCompiler.Compile<TSource, bool>(CodeExpression);
         }
     }
-}
+}

+ 371 - 0
FormulaEnginePoC/FormulaEngine/ExpressionTokenSplitter.cs

@@ -0,0 +1,371 @@
+using System;
+using System.Globalization;
+
+namespace FormulaEnginePoC.FormulaEngine
+{
+    /// <summary>
+    /// 修改过的表达式项分割器,来自System.Linq.Dynamic,增加decimal字面值支持
+    /// </summary>
+    internal class ExpressionTokenSplitter
+    {
+        public struct Token
+        {
+            public TokenId Id;
+            public string Text;
+            public int Pos;
+        }
+
+        public enum TokenId
+        {
+            // ReSharper disable once UnusedMember.Global
+            Unknown,
+
+            End,
+            Identifier,
+            StringLiteral,
+            IntegerLiteral,
+            RealLiteral,
+            Exclamation,
+            Percent,
+            Amphersand,
+            OpenParen,
+            CloseParen,
+            Asterisk,
+            Plus,
+            Comma,
+            Minus,
+            Dot,
+            Slash,
+            Colon,
+            LessThan,
+            Equal,
+            GreaterThan,
+            Question,
+            OpenBracket,
+            CloseBracket,
+            Bar,
+            ExclamationEqual,
+            DoubleAmphersand,
+            LessThanEqual,
+            LessGreater,
+            DoubleEqual,
+            GreaterThanEqual,
+            DoubleBar
+        }
+
+        private readonly string _text;
+        private char _ch;
+
+        protected int TextPos;
+        protected readonly int TextLen;
+        protected Token CurrentToken;
+
+        protected ExpressionTokenSplitter(string expression)
+        {
+            _text = expression ?? throw new ArgumentNullException(nameof(expression));
+            TextLen = _text.Length;
+
+            SetTextPos(0);
+        }
+
+        private void NextChar()
+        {
+            if (TextPos < TextLen)
+                TextPos++;
+            _ch = TextPos < TextLen ? _text[TextPos] : '\0';
+        }
+
+        protected void SetTextPos(int pos)
+        {
+            TextPos = pos;
+            _ch = TextPos < TextLen ? _text[TextPos] : '\0';
+        }
+
+        private void ValidateDigit()
+        {
+            if (!char.IsDigit(_ch))
+                throw ParseError(TextPos, Res.DigitExpected);
+        }
+
+        protected Exception ParseError(string format, params object[] args)
+        {
+            return ParseError(CurrentToken.Pos, format, args);
+        }
+
+        protected Exception ParseError(int pos, string format, params object[] args)
+        {
+            return new ParseException(string.Format(CultureInfo.CurrentCulture, format, args), pos);
+        }
+
+        protected void NextToken()
+        {
+            while (char.IsWhiteSpace(_ch))
+                NextChar();
+
+            var tokenPos = TextPos;
+
+            if (false == ParseToken(out var t))
+            {
+                throw ParseError(TextPos, Res.InvalidCharacter, _ch);
+            }
+
+            CurrentToken.Id = t;
+            CurrentToken.Text = _text.Substring(tokenPos, TextPos - tokenPos);
+            CurrentToken.Pos = tokenPos;
+        }
+
+        private bool ParseToken(out TokenId t)
+        {
+            switch (_ch)
+            {
+                case '!':
+                    NextChar();
+                    if (_ch == '=')
+                    {
+                        NextChar();
+                        t = TokenId.ExclamationEqual;
+                    }
+                    else
+                    {
+                        t = TokenId.Exclamation;
+                    }
+
+                    break;
+
+                case '%':
+                    NextChar();
+                    t = TokenId.Percent;
+                    break;
+
+                case '&':
+                    NextChar();
+                    if (_ch == '&')
+                    {
+                        NextChar();
+                        t = TokenId.DoubleAmphersand;
+                    }
+                    else
+                    {
+                        t = TokenId.Amphersand;
+                    }
+
+                    break;
+
+                case '(':
+                    NextChar();
+                    t = TokenId.OpenParen;
+                    break;
+
+                case ')':
+                    NextChar();
+                    t = TokenId.CloseParen;
+                    break;
+
+                case '*':
+                    NextChar();
+                    t = TokenId.Asterisk;
+                    break;
+
+                case '+':
+                    NextChar();
+                    t = TokenId.Plus;
+                    break;
+
+                case ',':
+                    NextChar();
+                    t = TokenId.Comma;
+                    break;
+
+                case '-':
+                    NextChar();
+                    t = TokenId.Minus;
+                    break;
+
+                case '.':
+                    NextChar();
+                    t = TokenId.Dot;
+                    break;
+
+                case '/':
+                    NextChar();
+                    t = TokenId.Slash;
+                    break;
+
+                case ':':
+                    NextChar();
+                    t = TokenId.Colon;
+                    break;
+
+                case '<':
+                    NextChar();
+                    if (_ch == '=')
+                    {
+                        NextChar();
+                        t = TokenId.LessThanEqual;
+                    }
+                    else if (_ch == '>')
+                    {
+                        NextChar();
+                        t = TokenId.LessGreater;
+                    }
+                    else
+                    {
+                        t = TokenId.LessThan;
+                    }
+
+                    break;
+
+                case '=':
+                    NextChar();
+                    if (_ch == '=')
+                    {
+                        NextChar();
+                        t = TokenId.DoubleEqual;
+                    }
+                    else
+                    {
+                        t = TokenId.Equal;
+                    }
+
+                    break;
+
+                case '>':
+                    NextChar();
+                    if (_ch == '=')
+                    {
+                        NextChar();
+                        t = TokenId.GreaterThanEqual;
+                    }
+                    else
+                    {
+                        t = TokenId.GreaterThan;
+                    }
+
+                    break;
+
+                case '?':
+                    NextChar();
+                    t = TokenId.Question;
+                    break;
+
+                case '[':
+                    NextChar();
+                    t = TokenId.OpenBracket;
+                    break;
+
+                case ']':
+                    NextChar();
+                    t = TokenId.CloseBracket;
+                    break;
+
+                case '|':
+                    NextChar();
+                    if (_ch == '|')
+                    {
+                        NextChar();
+                        t = TokenId.DoubleBar;
+                    }
+                    else
+                    {
+                        t = TokenId.Bar;
+                    }
+
+                    break;
+
+                case '"':
+                case '\'':
+                    var quote = _ch;
+                    do
+                    {
+                        NextChar();
+                        while (TextPos < TextLen && _ch != quote)
+                            NextChar();
+                        if (TextPos == TextLen)
+                            throw ParseError(TextPos, Res.UnterminatedStringLiteral);
+                        NextChar();
+                    } while (_ch == quote);
+
+                    t = TokenId.StringLiteral;
+                    break;
+
+                default:
+                    if (char.IsLetter(_ch) || _ch == '@' || _ch == '_')
+                    {
+                        do
+                        {
+                            NextChar();
+                        } while (char.IsLetterOrDigit(_ch) || _ch == '_');
+
+                        t = TokenId.Identifier;
+                        break;
+                    }
+
+                    if (char.IsDigit(_ch))
+                    {
+                        t = TokenId.IntegerLiteral;
+                        do
+                        {
+                            NextChar();
+                        } while (char.IsDigit(_ch));
+
+                        if (_ch == '.')
+                        {
+                            t = TokenId.RealLiteral;
+                            NextChar();
+                            ValidateDigit();
+                            do
+                            {
+                                NextChar();
+                            } while (char.IsDigit(_ch));
+                        }
+
+                        if (_ch == 'E' || _ch == 'e')
+                        {
+                            t = TokenId.RealLiteral;
+                            NextChar();
+                            if (_ch == '+' || _ch == '-')
+                                NextChar();
+                            ValidateDigit();
+                            do
+                            {
+                                NextChar();
+                            } while (char.IsDigit(_ch));
+                        }
+
+                        if (_ch == 'F' || _ch == 'f' || _ch == 'M' || _ch == 'm')
+                            NextChar();
+                        break;
+                    }
+
+                    if (TextPos == TextLen)
+                    {
+                        t = TokenId.End;
+                        break;
+                    }
+
+                    t = TokenId.Unknown;
+                    return false;
+            }
+
+            return true;
+        }
+
+        internal sealed class ParseException : Exception
+        {
+            public ParseException(string message, int position)
+                : base(message)
+            {
+                Position = position;
+            }
+
+            public int Position { get; }
+        }
+
+        internal static class Res
+        {
+            public const string UnterminatedStringLiteral = "字符串未结束";
+            public const string InvalidCharacter = "语法错误 '{0}'";
+            public const string DigitExpected = "数字不完整";
+        }
+    }
+}

+ 5 - 12
FormulaEnginePoC/FormulaEngine/FilterFormula.cs

@@ -1,24 +1,17 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.Linq;
 
 namespace FormulaEnginePoC.FormulaEngine
 {
-    public abstract class FilterFormula<TSource> : Formula
+    public abstract class FilterFormula<TSource> : CalcFormula<TSource, bool>
     {
-        public abstract Func<TSource, bool> FilterFunc { get; }
-
-        protected FilterFormula(string userExpression, Type mappingType) : base(userExpression, mappingType)
+        protected FilterFormula(string userExpression) : base(userExpression)
         {
         }
 
-
-
         public IEnumerable<TSource> Filter(IEnumerable<TSource> source)
         {
-            return source.Where(FilterFunc);
+            return source.Where(CompiledFunc);
         }
-
-
     }
-}
+}

+ 3 - 1
FormulaEnginePoC/FormulaEngine/Formula.cs

@@ -1,6 +1,6 @@
 using System;
 using System.Collections.Concurrent;
-using FormulaEnginePoC.Mapping;
+using FormulaEnginePoC.FormulaEngine.Mapping;
 
 namespace FormulaEnginePoC.FormulaEngine
 {
@@ -22,5 +22,7 @@ namespace FormulaEnginePoC.FormulaEngine
             UserExpression = userExpression;
             CodeExpression = FormulaConverter.Convert(UserExpression, mapping);
         }
+
+        public abstract void Compile();
     }
 }

+ 43 - 18
FormulaEnginePoC/FormulaEngine/FormulaConverter.cs

@@ -1,30 +1,28 @@
-using System;
+using FormulaEnginePoC.FormulaEngine.Mapping;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
-using FormulaEnginePoC.Common;
-using FormulaEnginePoC.DynamicExpressionParse;
-using FormulaEnginePoC.Mapping;
 
 namespace FormulaEnginePoC.FormulaEngine
 {
     internal static class FormulaConverter
     {
-        private class ExpressTokenParser : ExpressionParserBase
+        private class ExpressTokenExtractor : ExpressionTokenSplitter
         {
             private readonly Token[] _tokens;
 
             public Token[] Tokens => _tokens.ToArray();
 
-            public ExpressTokenParser(string expression) : base(expression)
+            public ExpressTokenExtractor(string expression) : base(expression)
             {
                 var lst = new List<Token>();
 
-                while (TextPos < TextLen)
+                do
                 {
                     NextToken();
                     lst.Add(CurrentToken);
-                }
+                } while (CurrentToken.Id != TokenId.End);
 
                 _tokens = lst.ToArray();
             }
@@ -83,28 +81,47 @@ namespace FormulaEnginePoC.FormulaEngine
             };
         }
 
-        public static string Convert(string friendInput, TypeMapping mapping)
+        public static string Convert(string friendInput, TypeMapping mapping, FormulaValueTypeConvert valueConvert = FormulaValueTypeConvert.ConvertToDouble)
         {
+            var functions = new Dictionary<string, string>(Functions);
+
+            if (valueConvert == FormulaValueTypeConvert.ConvertToDecimal)
+            {
+                functions["NUM"] = $"{nameof(MoreFunction)}.{nameof(MoreFunction.ToDecimal)}";
+            }
+
+            var numFunction = functions["NUM"];
+
             var code = new StringBuilder();
 
-            var parser = new ExpressTokenParser(friendInput);
+            var parser = new ExpressTokenExtractor(friendInput);
             foreach (var token in parser.Tokens)
             {
                 code.Append(" ");
 
-                if (token.Id == ExpressionParserBase.TokenId.Identifier)
+                if (token.Id == ExpressionTokenSplitter.TokenId.Identifier)
                 {
-                    if (Operators.TryGetValue(token.Text, out var opExp))
+                    if (Operators.TryGetValue(token.Text, out var op))
                     {
-                        code.Append(opExp);
+                        code.Append(op);
                     }
-                    else if (Functions.TryGetValue(token.Text, out var funcExp))
+                    else if (functions.TryGetValue(token.Text, out var func))
                     {
-                        code.Append(funcExp);
+                        code.Append(func);
                     }
-                    else if (mapping.TryGetPropertyName(token.Text, out var propertyName))
+                    else if (mapping.TryGetProperty(token.Text, out var property))
                     {
-                        code.Append($"p.{propertyName}");
+                        if (property.DataType == typeof(decimal) && valueConvert == FormulaValueTypeConvert.ConvertToDouble)
+                        {
+                            //值类型转换
+
+                            code.Append($"{numFunction} ( p.{property.Name} )");
+
+                        }
+                        else
+                        {
+                            code.Append($"p.{property.Name}");
+                        }
                     }
                     else
                     {
@@ -113,7 +130,15 @@ namespace FormulaEnginePoC.FormulaEngine
                 }
                 else
                 {
-                    code.Append(token.Text);
+                    //值类型转换
+                    if (token.Id == ExpressionTokenSplitter.TokenId.RealLiteral && valueConvert == FormulaValueTypeConvert.ConvertToDouble)
+                    {
+                        code.Append($"{numFunction} ( {token.Text} )");
+                    }
+                    else
+                    {
+                        code.Append(token.Text);
+                    }
                 }
             }
 

+ 5 - 4
FormulaEnginePoC/Mapping/PropertyMapping.cs

@@ -1,8 +1,9 @@
-using System.ComponentModel;
+using System;
+using System.ComponentModel;
 using System.Linq;
 using System.Reflection;
 
-namespace FormulaEnginePoC.Mapping
+namespace FormulaEnginePoC.FormulaEngine.Mapping
 {
     public class PropertyMapping
     {
@@ -13,7 +14,7 @@ namespace FormulaEnginePoC.Mapping
         public string Name { get; }
 
         [DisplayName("数据类型")]
-        public string DataType { get; }
+        public Type DataType { get; }
 
         public PropertyMapping(PropertyInfo prop)
         {
@@ -21,7 +22,7 @@ namespace FormulaEnginePoC.Mapping
             Display = prop.GetCustomAttributes(typeof(DisplayNameAttribute), true)
                           .OfType<DisplayNameAttribute>()
                           .FirstOrDefault()?.DisplayName ?? Name;
-            DataType = prop.PropertyType.Name;
+            DataType = prop.PropertyType;
         }
     }
 }

+ 5 - 5
FormulaEnginePoC/Mapping/TypeMapping.cs

@@ -3,12 +3,12 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
 
-namespace FormulaEnginePoC.Mapping
+namespace FormulaEnginePoC.FormulaEngine.Mapping
 {
     internal class TypeMapping
     {
         private readonly PropertyMapping[] _props;
-        private readonly Dictionary<string, string> _nameDictionary;
+        private readonly Dictionary<string, PropertyMapping> _nameDictionary;
 
 
         public PropertyMapping[] Properties => _props.ToArray();
@@ -17,12 +17,12 @@ namespace FormulaEnginePoC.Mapping
         {
             var propertyInfos = dataType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead).ToArray();
             _props = propertyInfos.Select(p => new PropertyMapping(p)).ToArray();
-            _nameDictionary = _props.ToDictionary(p => p.Display, p => p.Name);
+            _nameDictionary = _props.ToDictionary(p => p.Display);
         }
 
-        public bool TryGetPropertyName(string display, out string name)
+        public bool TryGetProperty(string display, out PropertyMapping mapping)
         {
-            return _nameDictionary.TryGetValue(display, out name);
+            return _nameDictionary.TryGetValue(display, out mapping);
         }
     }
 }

+ 16 - 2
FormulaEnginePoC/Common/MoreFunction.cs

@@ -1,7 +1,7 @@
 using System;
 using System.Linq;
 
-namespace FormulaEnginePoC.Common
+namespace FormulaEnginePoC.FormulaEngine
 {
     public static class MoreFunction
     {
@@ -20,9 +20,23 @@ namespace FormulaEnginePoC.Common
         public static string ToString(object value) => null == value ? "" : value.ToString();
 
         public static double ToDouble(string value) => (double)Convert.ChangeType(value, typeof(double));
+
         public static double ToDouble(int value) => (double)Convert.ChangeType(value, typeof(double));
+
         public static double ToDouble(float value) => (double)Convert.ChangeType(value, typeof(double));
+
+        public static double ToDouble(decimal value) => (double)Convert.ChangeType(value, typeof(double));
+
         public static double ToDouble(double value) => (double)Convert.ChangeType(value, typeof(double));
-        public static double ToDouble(object value) => (double)Convert.ChangeType(value, typeof(double));
+
+        public static decimal ToDecimal(string value) => (decimal)Convert.ChangeType(value, typeof(decimal));
+
+        public static decimal ToDecimal(int value) => (decimal)Convert.ChangeType(value, typeof(decimal));
+
+        public static decimal ToDecimal(float value) => (decimal)Convert.ChangeType(value, typeof(decimal));
+
+        public static decimal ToDecimal(double value) => (decimal)Convert.ChangeType(value, typeof(decimal));
+
+        public static decimal ToDecimal(decimal value) => (decimal)Convert.ChangeType(value, typeof(decimal));
     }
 }

+ 5 - 3
FormulaEnginePoC/FormulaEnginePoC.csproj

@@ -47,6 +47,7 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="FormulaEngine\ExpressionTokenSplitter.cs" />
     <Compile Include="CSharpProvider\FormulaCompiler.cs" />
     <Compile Include="FormulaEngine\CalcFormula.cs" />
     <Compile Include="FormulaEngine\CspImpl\CspCalcFormula.cs" />
@@ -67,9 +68,10 @@
     <Compile Include="DynamicExpressionParse\Signature.cs" />
     <Compile Include="DynamicExpressionParse\FormulaCompiler.cs" />
     <Compile Include="FormulaEngine\FormulaConverter.cs" />
-    <Compile Include="Mapping\PropertyMapping.cs" />
-    <Compile Include="Mapping\TypeMapping.cs" />
-    <Compile Include="Common\MoreFunction.cs" />
+    <Compile Include="FormulaEngine\Convert.cs" />
+    <Compile Include="FormulaEngine\Mapping\PropertyMapping.cs" />
+    <Compile Include="FormulaEngine\Mapping\TypeMapping.cs" />
+    <Compile Include="FormulaEngine\MoreFunction.cs" />
     <Compile Include="CSharpProvider\CodeCompiler.cs" />
     <Compile Include="CSharpProvider\ExpressionCompiler.cs" />
     <Compile Include="UI\CodeExecPanel.cs">

+ 65 - 26
FormulaEnginePoC/UI/SampleForm.cs

@@ -1,10 +1,12 @@
-using System;
-using System.Collections.Generic;
-using System.Windows.Forms;
-using FormulaEnginePoC.FormulaEngine;
+using FormulaEnginePoC.FormulaEngine;
 using FormulaEnginePoC.FormulaEngine.CspImpl;
-using FormulaEnginePoC.Mapping;
+using FormulaEnginePoC.FormulaEngine.DepImpl;
 using FormulaEnginePoC.SampleClasses;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+using FormulaEnginePoC.FormulaEngine.Mapping;
 
 namespace FormulaEnginePoC.UI
 {
@@ -15,6 +17,8 @@ namespace FormulaEnginePoC.UI
 
         private CspCalcFormula<Book, object> _cspCalcFormula;
         private CspFilterFormula<Book> _cspFilterFormula;
+        private DepCalcFormula<Book, object> _depCalcFormula;
+        private DepFilterFormula<Book> _depFilterFormula;
 
         public SampleForm()
         {
@@ -45,6 +49,50 @@ namespace FormulaEnginePoC.UI
             DataSourceGrid.DataSource = _dataSource;
         }
 
+        private void CompileX<T>(out T field, CodeExecPanel eventSource, Func<string, T> newFormula) where T : Formula
+        {
+            field = null;
+            eventSource.Code = "";
+
+            try
+            {
+                field = newFormula(FormulaTextBox.Text);
+                eventSource.Code = field.CodeExpression;
+                field.Compile();
+            }
+            catch (Exception exception)
+            {
+                eventSource.Code += Environment.NewLine + Environment.NewLine + exception;
+            }
+        }
+
+        private void ExecuteFilterX(CodeExecPanel eventSource, FilterFormula<Book> formula)
+        {
+            try
+            {
+                var result = _dataSource.Where(formula.CompiledFunc).ToArray();
+                eventSource.Result = $"共 {result.Length} 项{Environment.NewLine}"
+                                     + string.Join(Environment.NewLine, result.Select(p => $" - {p.Name}"));
+            }
+            catch (Exception exception)
+            {
+                eventSource.Result = exception.ToString();
+            }
+        }
+
+        private void ExecuteCalcX(CodeExecPanel eventSource, CalcFormula<Book, object> formula)
+        {
+            try
+            {
+                var result = formula.CompiledFunc((Book)DataSourceGrid.SelectedRows[0].DataBoundItem);
+                eventSource.Result = result?.ToString() ?? "<null>";
+            }
+            catch (Exception exception)
+            {
+                eventSource.Result = exception.ToString();
+            }
+        }
+
         private void NewToolStripButton_Click(object sender, EventArgs e)
         {
             RefreshDataSource();
@@ -58,8 +106,11 @@ namespace FormulaEnginePoC.UI
         private void FieldGrid_DoubleClick(object sender, EventArgs e)
         {
             if (1 != FieldGrid.SelectedRows.Count)
+            {
                 return;
+            }
             FormulaTextBox.SelectedText = ((PropertyMapping)FieldGrid.SelectedRows[0].DataBoundItem).Display + " ";
+            FormulaTextBox.Focus();
         }
 
         private void Insert_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
@@ -69,64 +120,52 @@ namespace FormulaEnginePoC.UI
             {
                 FormulaTextBox.SelectedText = e.ClickedItem.Text + $" ( {selected} ) ";
                 FormulaTextBox.SelectionStart -= 3;
-                FormulaTextBox.Focus();
             }
             else
             {
                 FormulaTextBox.SelectedText = e.ClickedItem.Text + " ";
             }
+            FormulaTextBox.Focus();
         }
 
-
         private void DepCalcExecPanel_CompileButtonClicked(object sender, EventArgs e)
         {
+            CompileX(out _depCalcFormula, (CodeExecPanel)sender, formula => new DepCalcFormula<Book, object>(formula));
         }
 
         private void DepCalcExecPanel_ExecuteButtonClicked(object sender, EventArgs e)
         {
+            ExecuteCalcX((CodeExecPanel)sender, _depCalcFormula);
         }
 
         private void DepFilterExecPanel_CompileButtonClicked(object sender, EventArgs e)
         {
+            CompileX(out _depFilterFormula, (CodeExecPanel)sender, formula => new DepFilterFormula<Book>(formula));
         }
 
         private void DepFilterExecPanel_ExecuteButtonClicked(object sender, EventArgs e)
         {
+            ExecuteFilterX((CodeExecPanel)sender, _depFilterFormula);
         }
 
         private void CspCalcExecPanel_CompileButtonClicked(object sender, EventArgs e)
         {
-            try
-            {
-                _cspCalcFormula = null;
-                _cspCalcFormula = new CspCalcFormula<Book, object>(FormulaTextBox.Text);
-                CspCalcExecPanel.Code = _cspCalcFormula.CodeExpression;
-            }
-            catch (Exception exception)
-            {
-                CspCalcExecPanel.Code = exception.ToString();
-            }
+            CompileX(out _cspCalcFormula, (CodeExecPanel)sender, formula => new CspCalcFormula<Book, object>(formula));
         }
 
         private void CspCalcExecPanel_ExecuteButtonClicked(object sender, EventArgs e)
         {
-            try
-            {
-                var result = _cspCalcFormula.CalcFunc((Book)DataSourceGrid.SelectedRows[0].DataBoundItem);
-                CspCalcExecPanel.Result = result?.ToString() ?? "<null>";
-            }
-            catch (Exception exception)
-            {
-                CspCalcExecPanel.Result = exception.ToString();
-            }
+            ExecuteCalcX((CodeExecPanel)sender, _cspCalcFormula);
         }
 
         private void CspFilterExecPanel_CompileButtonClicked(object sender, EventArgs e)
         {
+            CompileX(out _cspFilterFormula, (CodeExecPanel)sender, formula => new CspFilterFormula<Book>(formula));
         }
 
         private void CspFilterExecPanel_ExecuteButtonClicked(object sender, EventArgs e)
         {
+            ExecuteFilterX((CodeExecPanel)sender, _cspFilterFormula);
         }
     }
 }