mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-02 15:50:47 +08:00
217 lines
6.9 KiB
C#
217 lines
6.9 KiB
C#
using System.Data;
|
||
|
||
namespace Serein.LibraryCore.SerinExpression
|
||
{
|
||
public class SerinArithmeticExpressionEvaluator
|
||
{
|
||
private static readonly DataTable table = new DataTable();
|
||
|
||
public static double Evaluate(string expression, double inputValue)
|
||
{
|
||
// 替换占位符@为输入值
|
||
expression = expression.Replace("@", inputValue.ToString());
|
||
try
|
||
{
|
||
// 使用 DataTable.Compute 方法计算表达式
|
||
var result = table.Compute(expression, string.Empty);
|
||
return Convert.ToDouble(result);
|
||
}
|
||
catch
|
||
{
|
||
throw new ArgumentException("Invalid arithmetic expression.");
|
||
}
|
||
}
|
||
}
|
||
|
||
public class SerinExpressionEvaluator
|
||
{
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="expression">表达式</param>
|
||
/// <param name="targetObJ">操作对象</param>
|
||
/// <param name="isChange">是否改变了对象(get语法)</param>
|
||
/// <returns></returns>
|
||
/// <exception cref="ArgumentException"></exception>
|
||
/// <exception cref="NotSupportedException"></exception>
|
||
public static object Evaluate(string expression, object targetObJ, out bool isChange)
|
||
{
|
||
var parts = expression.Split([' '], 2);
|
||
if (parts.Length != 2)
|
||
{
|
||
throw new ArgumentException("Invalid expression format.");
|
||
}
|
||
|
||
var operation = parts[0].ToLower();
|
||
var operand = parts[1][0] == '.' ? parts[1][1..] : parts[1];
|
||
|
||
var result = operation switch
|
||
{
|
||
"@num" => ComputedNumber(targetObJ, operand),
|
||
"@call" => InvokeMethod(targetObJ, operand),
|
||
"@get" => GetMember(targetObJ, operand),
|
||
"@set" => SetMember(targetObJ, operand),
|
||
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
||
};
|
||
|
||
isChange = operation switch
|
||
{
|
||
"@num" => true,
|
||
"@call" => true,
|
||
"@get" => true,
|
||
"@set" => false,
|
||
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
||
};
|
||
|
||
return result;
|
||
}
|
||
|
||
|
||
private static readonly char[] separator = ['(', ')'];
|
||
private static readonly char[] separatorArray = [','];
|
||
|
||
private static object InvokeMethod(object target, string methodCall)
|
||
{
|
||
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
||
if (methodParts.Length != 2)
|
||
{
|
||
throw new ArgumentException("Invalid method call format.");
|
||
}
|
||
|
||
var methodName = methodParts[0];
|
||
var parameterList = methodParts[1];
|
||
var parameters = parameterList.Split(separatorArray, StringSplitOptions.RemoveEmptyEntries)
|
||
.Select(p => p.Trim())
|
||
.ToArray();
|
||
|
||
var method = target.GetType().GetMethod(methodName);
|
||
if (method == null)
|
||
{
|
||
throw new ArgumentException($"Method {methodName} not found on target.");
|
||
}
|
||
|
||
var parameterValues = method.GetParameters()
|
||
.Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType))
|
||
.ToArray();
|
||
|
||
|
||
return method.Invoke(target, parameterValues);
|
||
|
||
}
|
||
|
||
private static object GetMember(object target, string memberPath)
|
||
{
|
||
var members = memberPath.Split('.');
|
||
foreach (var member in members)
|
||
{
|
||
|
||
if (target == null) return null;
|
||
|
||
|
||
var property = target.GetType().GetProperty(member);
|
||
if (property != null)
|
||
{
|
||
|
||
target = property.GetValue(target);
|
||
|
||
}
|
||
else
|
||
{
|
||
var field = target.GetType().GetField(member);
|
||
if (field != null)
|
||
{
|
||
|
||
target = field.GetValue(target);
|
||
|
||
}
|
||
else
|
||
{
|
||
throw new ArgumentException($"Member {member} not found on target.");
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
return target;
|
||
|
||
}
|
||
|
||
private static object SetMember(object target, string assignment)
|
||
{
|
||
var parts = assignment.Split(new[] { '=' }, 2);
|
||
if (parts.Length != 2)
|
||
{
|
||
throw new ArgumentException("Invalid assignment format.");
|
||
}
|
||
|
||
var memberPath = parts[0].Trim();
|
||
var value = parts[1].Trim();
|
||
|
||
var members = memberPath.Split('.');
|
||
for (int i = 0; i < members.Length - 1; i++)
|
||
{
|
||
var member = members[i];
|
||
|
||
var property = target.GetType().GetProperty(member);
|
||
|
||
if (property != null)
|
||
{
|
||
|
||
target = property.GetValue(target);
|
||
|
||
}
|
||
else
|
||
{
|
||
var field = target.GetType().GetField(member);
|
||
if (field != null)
|
||
{
|
||
|
||
target = field.GetValue(target);
|
||
|
||
}
|
||
else
|
||
{
|
||
throw new ArgumentException($"Member {member} not found on target.");
|
||
}
|
||
}
|
||
}
|
||
|
||
var lastMember = members.Last();
|
||
|
||
var lastProperty = target.GetType().GetProperty(lastMember);
|
||
|
||
if (lastProperty != null)
|
||
{
|
||
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
|
||
lastProperty.SetValue(target, convertedValue);
|
||
}
|
||
else
|
||
{
|
||
var lastField = target.GetType().GetField(lastMember);
|
||
if (lastField != null)
|
||
{
|
||
var convertedValue = Convert.ChangeType(value, lastField.FieldType);
|
||
lastField.SetValue(target, convertedValue);
|
||
}
|
||
else
|
||
{
|
||
throw new ArgumentException($"Member {lastMember} not found on target.");
|
||
}
|
||
}
|
||
|
||
return target;
|
||
}
|
||
|
||
private static double ComputedNumber(object value, string expression)
|
||
{
|
||
double numericValue = Convert.ToDouble(value);
|
||
if (!string.IsNullOrEmpty(expression))
|
||
{
|
||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(expression, numericValue);
|
||
}
|
||
|
||
return numericValue;
|
||
}
|
||
}
|
||
}
|