共享一个从字符串转 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; }
}
}
如果你感觉这篇文章对你有帮助,请帮忙点击一下推荐,谢谢了!