zoukankan      html  css  js  c++  java
  • 共享一个从字符串转 Lambda 表达式的类(5)

    前几篇文章,我把前提都铺垫好了,下面就说说具体的代码。

    我为这个转换类取名为 ExpressionParserCore :

    • 需要 SymbolParseResult 类的实例对象和 TypeParser 类的实例对象,分别用于字符串解析和类型解析
    • 需要待分析表达式树的传入参数和传出参数

    TypeParser 还需要分析用的命名空间数组、额外的 Assembly 列表。其中 Assembly 列表可以使用 Assembly.LoadFrom("D:\abc.dll") 的方式加载,这对于后续的插件扩展很有帮助。下面这段代码是类的构造函数:

    /// <summary>
    /// Initializes a new instance of the <see cref="ExpressionParserCore"/> class.
    /// </summary>
    /// <param name="spResult">The sp result.</param>
    /// <param name="delegateType">Type of the delegate.</param>
    /// <param name="namespaces">The namespaces.</param>
    public ExpressionParserCore(SymbolParseResult spResult, Type delegateType, IEnumerable<string> namespaces = null, IEnumerable<Assembly> assemblies = null)
    {
        this.spResult = spResult;
     
        this.typeParser = new TypeParser(ref this.spResult);
        typeParser.SetNamespaces(namespaces);
        typeParser.SetAssemblies(assemblies);
     
        var method = delegateType.GetMethod("Invoke");
        parameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();
        returnType = method.ReturnType;
    }

    但是这样还不够。一般 Lambda 表达式都是有参数的,而且在表达式中也有用到,所以解析的过程中对于 Lambda 参数的解析也必须包含进来:

    /// <summary>
    /// 处理表达式前缀.
    /// </summary>
    private void ProcessLambdaPrefix()
    {
        // 检查是否有 Lambda 前置符(如: m => )
        if (spResult.Any(p => p.ID == TokenId.LambdaPrefix))
        {
            Token token = spResult.Next();
            if (token.ID == TokenId.OpenParen)
            {
                var bracketContent = spResult.SkipUntil(p => p.ID == TokenId.CloseParen);
                bracketContent.RemoveAt(bracketContent.Count - 1);
     
                if (bracketContent.Any(p => p.ID == TokenId.OpenParen))
                    throw new ParserSyntaxErrorException();
     
                // 如果读取到 => 符号表示有
                if (!spResult.NextIs(TokenId.LambdaPrefix))
                {
                    spResult.ReturnToIndex(-1);
                    return;
                }
     
                // 解析参数
                ResolveParameters(bracketContent).Foreach((p, i) =>
                {
                    if (p.ExistType)
                        expParams.Add(Expression.Parameter(typeParser.GetType(p.Type), p.Variable));
                    else
                        expParams.Add(Expression.Parameter(parameterTypes[i], p.Variable));
                });
            }
            else if (token.ID == TokenId.Identifier &&
                        (char.IsLetter(token.Text[0]) || token.Text[0] == '_') &&
                        !(token.Text == "true" ||
                        token.Text == "false" ||
                        token.Text == "null" ||
                        token.Text == "sizeof" ||
                        token.Text == "new" ||
                        token.Text == "typeof"))
            {
                if (!spResult.NextIs(TokenId.LambdaPrefix))
                {
                    spResult.ReturnToIndex(-1);
                    return;
                }
     
                expParams.Add(Expression.Parameter(parameterTypes[0], token.Text));
            }
     
            // 参数表达式个数和传入委托参数个数不匹配判断
            if (expParams.Count != parameterTypes.Length)
                throw new ApplicationException("The count of parameters is not equal.");
        }
    }
     
    /// <summary>
    /// 解析参数并返回配对(类型/变量)的字符单元列表.
    /// </summary>
    /// <param name="obj">待解析的字符单元列表.</param>
    /// <returns>解析完成的字符单元列表.</returns>
    private TypeVariable[] ResolveParameters(IEnumerable<Token> obj)
    {
        Token selector = new Token { ID = TokenId.Comma, Text = "," };
        var result = new List<TypeVariable>();
        if (ReferenceEquals(obj, null) || !obj.Any())
            return result.ToArray();
     
        if (obj.Last() != selector)
        {
            var list = obj.ToList();
            list.Add(selector);
            obj = list;
        }
        var data = obj.ToArray();
     
        int firstIndex = 0, secondIndex = 0;
        while ((secondIndex = Array.IndexOf(data, selector, firstIndex)) != -1)
        {
            if (secondIndex == firstIndex + 1)
                result.Add(new TypeVariable(null, data[firstIndex]));
            else if (secondIndex == firstIndex + 2)
                result.Add(new TypeVariable(data[firstIndex], data[firstIndex + 1]));
            else
                throw new ParserSyntaxErrorException();
     
            firstIndex = secondIndex + 1;
        }
     
        return result.ToArray();
    }

    这个方法,可以把它放在构造函数中,也可以放在 ToLambdaExpression 方法里面。忘记了,这涉及到一个类,用于记录类型和变量的键值对:

     

    /// <summary>
    /// 【类型:变量】键值对
    /// </summary>
    [DebuggerStepThrough]
    [DebuggerDisplay("{Type.Text}, {Variable.Text}")]
    public class TypeVariable
    {
        /// <summary>
        /// 获取类型字符单元.
        /// </summary>
        public Token Type { get; private set; }
        /// <summary>
        /// 获取变量字符单元.
        /// </summary>
        public Token Variable { get; private set; }
        /// <summary>
        /// 获取一个值, 通过该值指示是否包含类型定义. <c>true</c> 表示包含类型定义.
        /// </summary>
        public bool ExistType { get; private set; }
     
        /// <summary>
        /// 初始化新建一个 <see cref="TypeVariable"/> 类的实例对象.
        /// </summary>
        /// <param name="type">类型字符单元.</param>
        /// <param name="variable">变量字符单元.</param>
        public TypeVariable(Token? type, Token variable)
        {
            Variable = variable;
            if (ExistType = type.HasValue)
                Type = type.Value;
        }
    }

    在这之后,就是正式的解析入口了:

    public Expression<T> ToLambdaExpression<T>()
    {
        ProcessLambdaPrefix();
        var exp = ReadExpression();
     
        return LambdaExpression.Lambda<T>(exp.Expression, expParams);
    }

    这里要说明的是 expParams 参数,这个是用作 Lambda 表达式的参数必不可少的部分。ReadExpression 方法,递归调用解析 Lambda 表达式:

    /// <summary>
    /// Reads the expression.
    /// </summary>
    /// <param name="level">The level.</param>
    /// <param name="wrapStart">The wrap start.</param>
    /// <returns>The read result.</returns>
    private ReadResult ReadExpression(int level = 0)
    {
        var exp = ReadFirstExpression();
     
        //int nextLevel = 0;
        //var next = spResult.PeekNext();
        //while (!exp.IsClosedWrap &&
        //       (nextLevel = PriorityManager.GetOperatorLevel(next)) > level)
        //{
        //    exp = ReadNextExpression(nextLevel, exp, next.ID);
        //    next = spResult.PeekNext();
        //}
     
        return exp;
    }
    具体的 ReadFirstExpression 方法如下:
    /// <summary>
    /// Reads the first expression.
    /// </summary>
    /// <returns>The read result.</returns>
    private ReadResult ReadFirstExpression()
    {
        ReadResult result = ReadResult.Empty;
     
        var token = spResult.Next();
        switch (token.ID)
        {
            case TokenId.Identifier:
                result = ParseIdentifier(token);
                break;
            case TokenId.StringLiteral:
                if (token.Text.StartsWith("\""))
                    result.Expression = Expression.Constant(token.Text.Substring(1, token.Text.Length - 2), typeof(string));
                else if (token.Text.StartsWith("'"))
                    result.Expression = Expression.Constant(token.Text[1], typeof(char));
                break;
            case TokenId.IntegerLiteral:
                result.Expression = Expression.Constant(int.Parse(token.Text), typeof(int));
                break;
            case TokenId.LongIntegerLiteral:
                result.Expression = Expression.Constant(long.Parse(DeleteDigitTypeReference(token.Text)), typeof(long));
                break;
            case TokenId.RealLiteral:
                result.Expression = Expression.Constant(double.Parse(DeleteDigitTypeReference(token.Text)), typeof(double));
                break;
            case TokenId.SingleRealLiteral:
                result.Expression = Expression.Constant(float.Parse(DeleteDigitTypeReference(token.Text)), typeof(float));
                break;
            case TokenId.DecimalRealLiteral:
                result.Expression = Expression.Constant(decimal.Parse(DeleteDigitTypeReference(token.Text)), typeof(decimal));
                break;
            case TokenId.Exclamation:
                result.Expression = Expression.Not(ReadExpression().Expression);
                break;
            case TokenId.Plus:
            case TokenId.Comma:
                result = ReadExpression();
                break;
            case TokenId.Minus:
                result.Expression = Expression.Negate(ReadExpression().Expression);
                break;
            case TokenId.OpenParen:
                result = ParseConvertType();
                break;
            case TokenId.CloseParen:
            case TokenId.CloseBracket:
            case TokenId.CloseBrace:
                result.IsClosedWrap = true;
                break;
            default:
                throw new ParserSyntaxErrorException();
        }
     
        return result;
    }
     
    private ReadResult ParseIdentifier(Token token)
    {
        ReadResult result = ReadResult.Empty;
     
        switch (token.Text)
        {
            case "true":
                result.Expression = Expression.Constant(true);
                break;
            case "false":
                result.Expression = Expression.Constant(false);
                break;
            case "null":
                result.Expression = Expression.Constant(null);
                break;
            case "sizeof":
                if (spResult.NextIs(TokenId.OpenParen))
                {
                    result.Expression = Expression.Constant(Marshal.SizeOf(typeParser.ReadType()));
                    spResult.NextIs(TokenId.CloseParen);
                }
                else
                    throw new ParserSyntaxErrorException();
                break;
            case "typeof":
                if (spResult.NextIs(TokenId.OpenParen))
                {
                    result.Expression = Expression.Constant(typeParser.ReadType(), typeof(Type));
                    spResult.NextIs(TokenId.CloseParen);
     
                }
                else
                    throw new ParserSyntaxErrorException();
                break;
            case "new":
                {
                    var type = typeParser.ReadType();
     
                    // 判断初始化的类型
                    token = spResult.Next();
     
                    // 构造函数成员初始化/集合项初始化
                    if (token.ID == TokenId.OpenParen || token.ID == TokenId.OpenBrace)
                    {
                        // 构建构造函数 new 的部分
                        if (token.ID == TokenId.OpenParen)
                        {
                            // 获取参数
                            var listParam = GetCollectionInits(true).ToArray();
                            var paramTypes = listParam.Select(m => m.Type).ToArray();
     
                            // 获取构造函数
                            var constructor = type.GetConstructors()
                                .Select(p => new
                                {
                                    Parameters = p.GetParameters().Select(r => r.ParameterType).ToArray(),
                                    Constructor = p,
                                })
                                .Where(p => p.Parameters.Length == paramTypes.Length)
                                .First(p => p.Parameters.Select(r => r.GetNoneNullableType())
                                                        .SequenceEqual(paramTypes.Select(r => r.GetNoneNullableType())))
                                .Constructor;
     
                            // 获取匹配的构造函数参数
                            var constructorParamTypes =
                                constructor.GetParameters()
                                            .Select(p => p.ParameterType)
                                            .Zip(listParam, (x, y) => new { Left = x, Right = y })
                                            .Select((p, i) =>
                                            {
                                                if (p.Left.IsNullable() && p.Left != p.Right.Type)
                                                    return Expression.Convert(p.Right, p.Left.GetNullableType());
                                                else
                                                    return p.Right;
                                            })
                                            .ToArray();
     
                            // 构造函数调用
                            result.Expression = Expression.New(constructor, constructorParamTypes);
                        }
                        else
                            result.Expression = Expression.New(type.GetConstructor(Type.EmptyTypes));
     
                        // 构建构造函数属性成员初始化或者集合初始化
                        if (spResult.PeekNextIs(TokenId.OpenBrace) || token.ID == TokenId.OpenBrace)
                        {
                            if (token.ID == TokenId.OpenParen)
                                spResult.Next();
     
                            // 测试是否属性成员初始化                            
                            bool isMemberInit = spResult.PeekNextIs(TokenId.Equal, 2);
     
                            if (isMemberInit)
                                result.Expression =
                                    Expression.MemberInit((NewExpression)result.Expression, GetObjectMembers(type).ToArray());
                            else
                                result.Expression =
                                    Expression.ListInit((NewExpression)result.Expression, GetCollectionInits().ToArray());
                        }
                    }
                    else if (token.ID == TokenId.OpenBracket)
                    {
                        Expression[] @params = null;
                        if (spResult.PeekNextIs(TokenId.CloseBracket))
                            spResult.Next();
                        else
                            @params = GetCollectionInits().ToArray();
     
                        if (spResult.PeekNextIs(TokenId.OpenBrace))
                            result.Expression = Expression.NewArrayInit(type, GetCollectionInits().ToArray());
                        else
                            result.Expression = Expression.NewArrayBounds(type, @params);
                    }
                    else
                        throw new ParserSyntaxErrorException();
                    break;
                }
            default:
                // 参数
                if (expParams.Any(p => p.Name == token.Text))
                    result.Expression = expParams.First(p => p.Name == token.Text);
                // 类型
                else
                {
                    var type = typeParser.ReadType(token.Text);
                    spResult.NextIs(TokenId.Dot, true);
                    var name = spResult.Next();
     
                    // 跳过尖括号 <> 中的元素
                    if (spResult.PeekNextIs(TokenId.LessThan))
                        spResult.SkipUntil(p => p.ID == TokenId.GreaterThan);
     
                    if (spResult.PeekNextIs(TokenId.OpenParen))
                    {
                        var @params = GetCollectionInits().ToArray();
                        var method = FindBestMethod(type, name, @params, true);
                        result.Expression = Expression.Call(method, @params);
                    }
                    else
                    {
                        var member = type.GetMember(name)[0];
                        if (member.MemberType == MemberTypes.Property)
                            result.Expression = Expression.Property(null, (PropertyInfo)member);
                        else
                            result.Expression = Expression.Field(null, (FieldInfo)member);
                    }
                }
                break;
        }
     
        return result;
    }
     
    private string DeleteDigitTypeReference(string number)
    {
        if (char.IsLetter(number.Last()))
            return number.Substring(0, number.Length - 1);
        return number;
    }
     
    private ReadResult ParseConvertType()
    {
        var originPos = spResult.Index;
        var type = typeParser.ReadType(ignoreException: true);
        if (type != null)
        {
            spResult.NextIs(TokenId.CloseParen, true);
            var inner = ReadExpression();
            return new ReadResult
            {
                Expression = Expression.Convert(inner.Expression, type),
                IsClosedWrap = inner.IsClosedWrap,
            };
        }
        else
        {
            spResult.ReturnToIndex(originPos);
            var result = ReadExpression();
            if (!spResult.PeekNextIs(TokenId.End))
                result.IsClosedWrap = false;
            return result;
        }
    }

    好了,这次就写到这里吧,下次把 ReadNextExpression 方法写出来,顺便也把整个的源码提供下载。没时间写了,不要说我不厚道,谢谢了!对了,补上 ReadResult 类的源码:

    /// <summary>
    /// 表达式读取结果
    /// </summary>
    [DebuggerStepThrough]
    [DebuggerDisplay("IsClosedWrap = {IsClosedWrap}, Expression = {Expression}")]
    public struct ReadResult
    {
        private Expression _expression;
        private bool _isClosedWrap;
     
        /// <summary>
        /// 获取一个空的读取结果.
        /// </summary>
        public static ReadResult Empty
        {
            get { return new ReadResult(); }
        }
     
        /// <summary>
        /// 获取或设置读取到的表达式.
        /// </summary>
        public Expression Expression
        {
            get { return _expression; }
            set { _expression = value; }
        }
     
        /// <summary>
        /// 获取或设置一个值, 通过该值指示是否已经读取了关闭符号.
        /// </summary>
        public bool IsClosedWrap
        {
            get { return _isClosedWrap; }
            set { _isClosedWrap = value; }
        }
    }

    如果你感觉这篇文章对你有帮助,请帮忙点击一下推荐,谢谢了!

  • 相关阅读:
    在Eclipse中使用JUnit4进行单元测试(上)
    SVN和Subclipse安装和使用指南汇总
    在windows下搭建SVN服务器
    学会SVN的应用源代码托管
    SVN中检出(check out) 和 导出(export) 的区别
    .NET平台三层应用程序框架搭建(一)
    Winform dataGridview 为每一个单元格制定一个tooptip
    SQL row_number() over() 来自动产生行号
    Winform datagridview 设置单元格为只读属性
    SQL 把字符创分割成两个字符串
  • 原文地址:https://www.cnblogs.com/lenic/p/2579340.html
Copyright © 2011-2022 走看看