前幾篇文章,我把前提都鋪墊好了,下面就說說具體的代碼。
我為這個轉換類取名為 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; }
}
}
如果你感覺這篇文章對你有幫助,請幫忙點擊一下推薦,謝謝了!
