共享一個從字符串轉 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; }
    }
}

如果你感覺這篇文章對你有幫助,請幫忙點擊一下推薦,謝謝了!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM