修复了脚本语言中构造器赋值的 bug

This commit is contained in:
fengjiayi
2025-07-16 16:16:19 +08:00
parent 01ab905155
commit 88a82046b8
16 changed files with 825 additions and 1376 deletions

View File

@@ -5,6 +5,7 @@ using System.Reflection.Emit;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace Serein.Library.Utils
{
@@ -188,8 +189,142 @@ namespace Serein.Library.Utils
return dynamicType;
}
/// <summary>
/// 创建继承通知接口、具有属性的类型
/// </summary>
/// <param name="properties"></param>
/// <param name="typeName"></param>
/// <param name="isOverlay"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static Type CreateTypeWithINotifyPropertyChanged(IDictionary<string, Type> properties, string typeName, bool isOverlay = false)
{
if (typeCache.ContainsKey(typeName) && !isOverlay)
return typeCache[typeName];
var typeBuilder = ModuleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class);
typeBuilder.AddInterfaceImplementation(typeof(INotifyPropertyChanged));
// 添加 PropertyChanged 字段
var eventHandlerType = typeof(PropertyChangedEventHandler);
var propertyChangedField = typeBuilder.DefineField("PropertyChanged", eventHandlerType, FieldAttributes.Private);
// 添加 PropertyChanged 事件
var eventBuilder = typeBuilder.DefineEvent("PropertyChanged", EventAttributes.None, eventHandlerType);
// add_PropertyChanged
var addMethod = typeBuilder.DefineMethod("add_PropertyChanged", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual | MethodAttributes.HideBySig, null, new[] { eventHandlerType });
{
var il = addMethod.GetILGenerator();
var combine = typeof(Delegate).GetMethod("Combine", new[] { typeof(Delegate), typeof(Delegate) });
// this.PropertyChanged = (PropertyChangedEventHandler)Delegate.Combine(this.PropertyChanged, value);
il.Emit(OpCodes.Ldarg_0); // 加载 this当前实例到栈
il.Emit(OpCodes.Ldarg_0); // 再次加载 this用于访问字段
il.Emit(OpCodes.Ldfld, propertyChangedField); // 加载 this.PropertyChanged 字段值
il.Emit(OpCodes.Ldarg_1); // 加载方法参数 value订阅者委托
il.Emit(OpCodes.Call, combine); // 调用 Delegate.Combine(a, b)
il.Emit(OpCodes.Castclass, eventHandlerType); // 强转回 PropertyChangedEventHandler 类型
il.Emit(OpCodes.Stfld, propertyChangedField); // 赋值给 this.PropertyChanged
il.Emit(OpCodes.Ret); // return
}
// remove_PropertyChanged
var removeMethod = typeBuilder.DefineMethod("remove_PropertyChanged", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual | MethodAttributes.HideBySig, null, new[] { eventHandlerType });
{
var il = removeMethod.GetILGenerator();
var remove = typeof(Delegate).GetMethod("Remove", new[] { typeof(Delegate), typeof(Delegate) });
// this.PropertyChanged = (PropertyChangedEventHandler)Delegate.Remove(this.PropertyChanged, value);
il.Emit(OpCodes.Ldarg_0); // 加载 this
il.Emit(OpCodes.Ldarg_0); // 再次加载 this
il.Emit(OpCodes.Ldfld, propertyChangedField); // 加载字段 this.PropertyChanged
il.Emit(OpCodes.Ldarg_1); // 加载 value要移除的委托
il.Emit(OpCodes.Call, remove); // 调用 Delegate.Remove(a, b)
il.Emit(OpCodes.Castclass, eventHandlerType); // 转换为 PropertyChangedEventHandler
il.Emit(OpCodes.Stfld, propertyChangedField); // 设置 this.PropertyChanged = 结果
il.Emit(OpCodes.Ret); // return
}
eventBuilder.SetAddOnMethod(addMethod);
eventBuilder.SetRemoveOnMethod(removeMethod);
// 定义 OnPropertyChanged 方法
var onPropertyChangedMethod = typeBuilder.DefineMethod(
"OnPropertyChanged",
MethodAttributes.Private,
null,
new[] { typeof(string) });
{
var il = onPropertyChangedMethod.GetILGenerator();
var label = il.DefineLabel();
// if (PropertyChanged == null) return;
il.Emit(OpCodes.Ldarg_0); // 加载 this
il.Emit(OpCodes.Ldfld, propertyChangedField); // 加载 this.PropertyChanged
il.Emit(OpCodes.Brfalse_S, label); // 如果为空null跳转到 label跳过通知调用
// PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
il.Emit(OpCodes.Ldarg_0); // 加载 this委托调用目标
il.Emit(OpCodes.Ldfld, propertyChangedField); // 加载 PropertyChanged 委托
il.Emit(OpCodes.Ldarg_0); // 加载 this 作为 sender 参数
il.Emit(OpCodes.Ldarg_1); // 加载 propertyName 参数string
il.Emit(OpCodes.Newobj, typeof(PropertyChangedEventArgs).GetConstructor(new[] { typeof(string) })); // 构造 new PropertyChangedEventArgs(propertyName)
il.Emit(OpCodes.Callvirt, typeof(PropertyChangedEventHandler).GetMethod("Invoke")); // 调用委托 Invoke(sender, args)
il.MarkLabel(label); // 跳转目标(如果委托为 null 则跳转至此)
il.Emit(OpCodes.Ret); // return
}
// 为每个属性生成字段 + get/set + OnPropertyChanged 调用
foreach (var kv in properties)
{
string propName = kv.Key;
Type propType = kv.Value ?? typeof(object);
var fieldBuilder = typeBuilder.DefineField("_" + propName, propType, FieldAttributes.Private);
var propertyBuilder = typeBuilder.DefineProperty(propName, PropertyAttributes.HasDefault, propType, null);
var getter = typeBuilder.DefineMethod("get_" + propName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
propType, Type.EmptyTypes);
{
// get_PropertyName()
var il = getter.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // 加载 this
il.Emit(OpCodes.Ldfld, fieldBuilder); // 读取私有字段 _PropertyName
il.Emit(OpCodes.Ret); // 返回字段值
}
var setter = typeBuilder.DefineMethod("set_" + propName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
null, new[] { propType });
{
// set_PropertyName(value)
var il = setter.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // 加载 this
il.Emit(OpCodes.Ldarg_1); // 加载 value 参数
il.Emit(OpCodes.Stfld, fieldBuilder); // 设置字段 _PropertyName = value
il.Emit(OpCodes.Ldarg_0); // 加载 this
il.Emit(OpCodes.Ldstr, propName); // 加载属性名字符串作为通知参数
il.Emit(OpCodes.Call, onPropertyChangedMethod); // 调用 OnPropertyChanged(propName)
il.Emit(OpCodes.Ret); // return
}
propertyBuilder.SetGetMethod(getter);
propertyBuilder.SetSetMethod(setter);
}
var dynamicType = typeBuilder.CreateType();
typeCache[typeName] = dynamicType;
return dynamicType;
}
#region
// 方法 1: 创建动态类型及其对象实例

View File

@@ -33,19 +33,20 @@ namespace Serein.NodeFlow.Model
/// </summary>
public override bool IsBase => true;
private IScriptFlowApi ScriptFlowApi;
private ProgramNode programNode;
private readonly SereinScriptInterpreter scriptInterpreter;
private bool IsScriptChanged = false;
/// <summary>
/// 脚本解释器
/// </summary>
private readonly SereinScript sereinScript;
/// <summary>
/// 构建流程脚本节点
/// </summary>
/// <param name="environment"></param>
public SingleScriptNode(IFlowEnvironment environment):base(environment)
public SingleScriptNode(IFlowEnvironment environment) : base(environment)
{
ScriptFlowApi = new ScriptFlowApi(environment, this);
scriptInterpreter = new SereinScriptInterpreter();
sereinScript = new SereinScript();
}
static SingleScriptNode()
@@ -60,7 +61,7 @@ namespace Serein.NodeFlow.Model
// 加载基础方法
foreach ((string name, MethodInfo method) item in tempMethods)
{
SereinScriptInterpreter.AddStaticFunction(item.name, item.method);
SereinScript.AddStaticFunction(item.name, item.method);
}
}
@@ -157,24 +158,10 @@ namespace Serein.NodeFlow.Model
varNames.Add(pd.Name);
}
/*var sb = new StringBuilder();
foreach (var pd in MethodDetails.ParameterDetailss)
{
sb.AppendLine($"let {pd.Name};"); // 提前声明这些变量
}
sb.Append(Script);
var script = sb.ToString();*/
var parser = new SereinScriptParser(); // 准备解析器
var typeAnalysis = new SereinScriptTypeAnalysis(); // 准备分析器
programNode = parser.Parse(Script); // 开始解析获取程序主节点
Dictionary<string, Type> dict = MethodDetails.ParameterDetailss.ToDictionary(pd => pd.Name, pd => pd.DataType); // 准备预定义类型
var dict = MethodDetails.ParameterDetailss.ToDictionary(pd => pd.Name, pd => pd.DataType);
typeAnalysis.NodeSymbolInfos.Clear(); // 清空符号表
typeAnalysis.LoadSymbol(dict); // 提前加载脚本节点定义的符号
typeAnalysis.AnalysisProgramNode(programNode); // 分析节点类型
var returnType = typeAnalysis.NodeSymbolInfos[programNode]; // 获取返回类型
var returnType = sereinScript.ParserScript(dict, Script); // 开始解析获取程序主节点
MethodDetails.ReturnType = returnType;
//scriptInterpreter.SetTypeAnalysis(typeAnalysis); // 设置类型分析器
}
catch (Exception ex)
@@ -244,8 +231,7 @@ namespace Serein.NodeFlow.Model
if (token.IsCancellationRequested) return null;
var result = await scriptInterpreter.InterpretAsync(scriptContext, programNode); // 从入口节点执行
var result = await sereinScript.InterpreterAsync(scriptContext); // 从入口节点执行
envEvent.FlowRunComplete -= onFlowStop;
return new FlowResult(this.Guid, context, result);
}
@@ -259,12 +245,7 @@ namespace Serein.NodeFlow.Model
private static class ScriptBaseFunc
{
public static DateTime GetNow() => DateTime.Now;
public static int Add(int Left, int Right)
{
return Left + Right;
}
public static DateTime now() => DateTime.Now;
#region
public static bool @bool(object value)
@@ -321,6 +302,21 @@ namespace Serein.NodeFlow.Model
}
}
public static void regType(Type type, string name = "")
{
SereinScript.AddClassType(type, name);
}
public static string str(object obj)
{
return obj?.ToString() ?? string.Empty;
}
public static object obj(Type type)
{
return Activator.CreateInstance(type);
}
public static Type type(object type)
{
return type.GetType();

View File

@@ -15,41 +15,40 @@ namespace Serein.Script.Node
public bool IsOverlay { get; set; }
/// <summary>
/// 类名称
/// 类名称
/// </summary>
public string ClassName { get; }
public TypeNode ClassType { get; }
/// <summary>
/// 字段名称及字段类型
/// 类型中的属性
/// </summary>
[Obsolete("此属性已经过时,将会改为Dictionary<string, string>", false)]
public Dictionary<string, Type> Fields { get; }
/// <summary>
/// 字段名称及字段类型(Kvp[fididName:fidleTypeName])
/// </summary>
public Dictionary<string, string> FieldInfos { get; }
public Dictionary<string, TypeNode> Propertys { get; }
public ClassTypeDefinitionNode(Dictionary<string, string> fields, string className)
public ClassTypeDefinitionNode(Dictionary<string, TypeNode> propertys, TypeNode className)
{
this.FieldInfos = fields;
this.ClassName = className;
this.Propertys = propertys;
this.ClassType = className;
}
[Obsolete("此构造方法已经过时,可能在下一个版本中移除", false)]
public ClassTypeDefinitionNode(Dictionary<string, Type> fields, string className)
{
this.Fields = fields;
this.ClassName = className;
}
[Obsolete("此构造方法已经过时,可能在下一个版本中移除", false)]
public ClassTypeDefinitionNode(Dictionary<string, Type> fields, string className, bool isOverlay)
{
this.Fields = fields;
this.ClassName = className;
IsOverlay = isOverlay;
}
/* /// <summary>
/// 字段名称及字段类型
/// </summary>
[Obsolete("此属性已经过时将会改为Dictionary<string, string>", false)]
public Dictionary<TypeNode, Type> Fields { get; }
*/
/* /// <summary>
/// 字段名称及字段类型(Kvp[fididName:fidleTypeName])
/// </summary>
public Dictionary<TypeNode, string> FieldInfos { get; }
*/
//[Obsolete("此构造方法已经过时,可能在下一个版本中移除", false)]
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Script.Node
{
/// <summary>
/// 构造器对对象成员赋值
/// </summary>
public class CtorAssignmentNode : ASTNode
{
/// <summary>
/// 成员来源类型
/// </summary>
public TypeNode Class { get; }
/// <summary>
/// 成员名称
/// </summary>
public string MemberName { get; }
/// <summary>
/// 值来源
/// </summary>
public ASTNode Value { get; }
/// <summary>
/// 构造器赋值
/// </summary>
/// <param name="typeNode">成员来源类型</param>
/// <param name="memberName">成员名称</param>
/// <param name="value">成员值来源</param>
public CtorAssignmentNode(TypeNode typeNode, string memberName, ASTNode value)
{
Class = typeNode;
MemberName = memberName;
Value = value;
}
}
}

View File

@@ -12,19 +12,31 @@ namespace Serein.Script.Node
public class ObjectInstantiationNode : ASTNode
{
/// <summary>
/// 类型名称
/// 类型来源
/// </summary>
public string TypeName { get; }
public TypeNode Type { get; }
/// <summary>
/// 构造方法的参数来源
/// </summary>
public List<ASTNode> Arguments { get; }
public ObjectInstantiationNode(string typeName, List<ASTNode> arguments)
/// <summary>
/// 构造器赋值
/// </summary>
public List<CtorAssignmentNode> CtorAssignments { get; private set; } = [];
public ObjectInstantiationNode(TypeNode type, List<ASTNode> arguments)
{
this.TypeName = typeName;
this.Type = type;
this.Arguments = arguments;
}
public ObjectInstantiationNode SetCtorAssignments(List<CtorAssignmentNode> ctorAssignments)
{
CtorAssignments = ctorAssignments;
return this;
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Script.Node
{
/// <summary>
/// 类型节点
/// </summary>
public class TypeNode : ASTNode
{
public string TypeName { get; }
public TypeNode(string typeName)
{
TypeName = typeName;
}
}
}

View File

@@ -0,0 +1,131 @@
using Serein.Library;
using Serein.Script.Node;
using Serein.Script.Node.FlowControl;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Script
{
public class SereinScript
{
/// <summary>
/// 类型分析
/// </summary>
public SereinScriptTypeAnalysis TypeAnalysis { get; set; } = new SereinScriptTypeAnalysis();
private ProgramNode? programNode;
public Type ParserScript(Dictionary<string, Type> argTypes, string script)
{
SereinScriptParser parser = new SereinScriptParser();
var programNode = parser.Parse(script);
TypeAnalysis.NodeSymbolInfos.Clear(); // 清空符号表
TypeAnalysis.LoadSymbol(argTypes); // 提前加载脚本节点定义的符号
TypeAnalysis.Analysis(programNode); // 分析节点类型
var returnType = TypeAnalysis.NodeSymbolInfos[programNode]; // 获取返回类型
this.programNode = programNode;
return returnType; // 脚本返回类型
}
public async Task<object?> InterpreterAsync(IScriptInvokeContext context)
{
if(programNode is null)
{
throw new ArgumentNullException(nameof(programNode));
}
Dictionary<ASTNode, Type> symbolInfos = TypeAnalysis.NodeSymbolInfos.ToDictionary();
SereinScriptInterpreter Interpreter = new SereinScriptInterpreter(symbolInfos);
return await Interpreter.InterpretAsync(context, programNode);
}
/// <summary>
/// 挂载的函数
/// </summary>
public static Dictionary<string, DelegateDetails> FunctionDelegates { get; private set; } = [];
/// <summary>
/// 挂载方法的信息
/// </summary>
public static Dictionary<string, MethodInfo> FunctionInfos { get; private set; } = [];
/// <summary>
/// 挂载的类型
/// </summary>
public static Dictionary<string, Type> MountType = new Dictionary<string, Type>();
/// <summary>
/// 挂载的函数调用的对象(用于解决函数需要实例才能调用的场景)
/// </summary>
public static Dictionary<string, Func<object>> DelegateInstances = new Dictionary<string, Func<object>>();
/// <summary>
/// 挂载静态函数
/// </summary>
/// <param name="functionName"></param>
/// <param name="methodInfo"></param>
public static void AddStaticFunction(string functionName, MethodInfo methodInfo)
{
FunctionDelegates[functionName] = new DelegateDetails(methodInfo);
FunctionInfos[functionName] = methodInfo;
}
/// <summary>
/// 挂载函数
/// </summary>
/// <param name="functionName">函数名称</param>
/// <param name="methodInfo">方法信息</param>
public static void AddFunction(string functionName, MethodInfo methodInfo, Func<object>? callObj = null)
{
if (!methodInfo.IsStatic && callObj is null)
{
SereinEnv.WriteLine(InfoType.WARN, "函数挂载失败:试图挂载非静态的函数,但没有传入相应的获取实例的方法。");
return;
}
if (!methodInfo.IsStatic && callObj is not null && !DelegateInstances.ContainsKey(functionName))
{
// 非静态函数需要给定类型
DelegateInstances.Add(functionName, callObj);
}
if (!FunctionDelegates.ContainsKey(functionName))
{
FunctionDelegates[functionName] = new DelegateDetails(methodInfo);
}
}
/// <summary>
/// 挂载类型
/// </summary>
/// <param name="typeName">函数名称</param>
/// <param name="typeName">指定类型名称</param>
public static void AddClassType(Type type, string typeName = "")
{
if (string.IsNullOrEmpty(typeName))
{
typeName = type.Name;
}
if (!MountType.ContainsKey(typeName))
{
MountType[typeName] = type;
}
}
}
}

View File

@@ -18,98 +18,15 @@ namespace Serein.Script
/// </summary>
public class SereinScriptInterpreter
{
/// <summary>
/// 挂载的函数
/// </summary>
private static Dictionary<string, DelegateDetails> _functionTable = new Dictionary<string, DelegateDetails>();
private static Dictionary<string, MethodInfo> _functionInfoTable = new Dictionary<string, MethodInfo>();
private readonly Dictionary<ASTNode, Type> symbolInfos;
public static Dictionary<string, MethodInfo> FunctionInfoTable { get { return _functionInfoTable; } }
/// <summary>
/// 挂载的函数调用的对象(用于函数需要实例才能调用的场景)
/// </summary>
private Dictionary<string, Func<object>> _callFuncOfGetObjects = new Dictionary<string, Func<object>>();
/// <summary>
/// 定义的类型
/// </summary>
private Dictionary<string, Type> _classDefinition = new Dictionary<string, Type>();
/// <summary>
/// 类型分析器
/// </summary>
private SereinScriptTypeAnalysis typeAnalysis;
/// <summary>
/// 挂载静态函数
/// </summary>
/// <param name="functionName"></param>
/// <param name="methodInfo"></param>
public static void AddStaticFunction(string functionName, MethodInfo methodInfo)
public SereinScriptInterpreter(Dictionary<ASTNode, Type> symbolInfos)
{
_functionTable[functionName] = new DelegateDetails(methodInfo);
_functionInfoTable[functionName] = methodInfo;
this.symbolInfos = symbolInfos;
}
/// <summary>
/// 挂载函数
/// </summary>
/// <param name="functionName">函数名称</param>
/// <param name="methodInfo">方法信息</param>
public void AddFunction(string functionName, MethodInfo methodInfo, Func<object>? callObj = null)
{
//if (!_functionTable.ContainsKey(functionName))
//{
// _functionTable[functionName] = new DelegateDetails(methodInfo);
//}
if(!methodInfo.IsStatic && callObj is null)
{
SereinEnv.WriteLine(InfoType.WARN, "函数挂载失败:试图挂载非静态的函数,但没有传入相应的获取实例的方法。");
return;
}
if(!methodInfo.IsStatic && callObj is not null && !_callFuncOfGetObjects.ContainsKey(functionName))
{
// 静态函数不需要
_callFuncOfGetObjects.Add(functionName, callObj);
}
if (!_functionTable.ContainsKey(functionName))
{
_functionTable[functionName] = new DelegateDetails(methodInfo);
}
//_functionTable[functionName] = new DelegateDetails(methodInfo);
}
/// <summary>
/// 挂载类型
/// </summary>
/// <param name="typeName">函数名称</param>
/// <param name="type">方法信息</param>
public void AddClassType(Type type , string typeName = "")
{
if (string.IsNullOrEmpty(typeName))
{
typeName = type.Name;
}
if (!_classDefinition.ContainsKey(typeName))
{
_classDefinition[typeName] = type;
}
}
/// <summary>
/// 设置类型分析器
/// </summary>
/// <param name="typeAnalysis"></param>
public void SetTypeAnalysis(SereinScriptTypeAnalysis typeAnalysis)
{
this.typeAnalysis = typeAnalysis;
}
/// <summary>
/// 入口节点
@@ -159,17 +76,19 @@ namespace Serein.Script
/// <returns></returns>
private void ExecutionClassTypeDefinitionNode(ClassTypeDefinitionNode classTypeDefinitionNode)
{
if (_classDefinition.ContainsKey(classTypeDefinitionNode.ClassName) && !classTypeDefinitionNode.IsOverlay)
var className = classTypeDefinitionNode.ClassType.TypeName;
if (SereinScript.MountType.ContainsKey(className) && !classTypeDefinitionNode.IsOverlay)
{
//SereinEnv.WriteLine(InfoType.WARN, $"异常信息 : 类型重复定义,代码在第{classTypeDefinitionNode.Row}行: {classTypeDefinitionNode.Code.Trim()}");
return;
}
//var isOverlay = true; // classTypeDefinitionNode.IsOverlay;
var type = DynamicObjectHelper.CreateTypeWithProperties(classTypeDefinitionNode.Fields, classTypeDefinitionNode.ClassName); // 覆盖
//classTypeDefinitionNode.IsOverlay = false; // 已经加载过,则不再覆盖
_classDefinition[classTypeDefinitionNode.ClassName] = type; // 定义对象
if(DynamicObjectHelper.GetCacheType(className) == null)
{
var propertyTypes = classTypeDefinitionNode.Propertys.ToDictionary(p => p.Key, p => symbolInfos[p.Value]);
var type = DynamicObjectHelper.CreateTypeWithProperties(propertyTypes, className); // 覆盖
SereinScript.MountType[className] = type; // 定义对象
}
}
/// <summary>
@@ -305,11 +224,11 @@ namespace Serein.Script
// 查找并执行对应的函数
if (_functionTable.TryGetValue(funcName, out DelegateDetails? function))
if (SereinScript.FunctionDelegates .TryGetValue(funcName, out DelegateDetails? function))
{
if (!function.EmitMethodInfo.IsStatic)
{
if(_callFuncOfGetObjects.TryGetValue(funcName, out var action))
if(SereinScript.DelegateInstances.TryGetValue(funcName, out var action))
{
instance = action.Invoke();// 非静态的方法需要获取相应的实例
@@ -429,27 +348,34 @@ namespace Serein.Script
//if (right == null) throw new SereinSciptException(binOpNode.Right, "右值尝试使用计算 null");
return EvaluateBinaryOperation(left, binOpNode.Operator, right);
case ObjectInstantiationNode objectInstantiationNode: // 对象实例化
if (_classDefinition.TryGetValue(objectInstantiationNode.TypeName,out var type ))
if (!SereinScript.MountType.TryGetValue(objectInstantiationNode.Type.TypeName, out var type))
{
object?[] args = new object[objectInstantiationNode.Arguments.Count];
for (int i = 0; i < objectInstantiationNode.Arguments.Count; i++)
type = symbolInfos[objectInstantiationNode.Type];
if (type is null)
{
var argNode = objectInstantiationNode.Arguments[i];
args[i] = await EvaluateAsync(context, argNode);
throw new SereinSciptException(objectInstantiationNode, $"使用了未定义的类型\"{objectInstantiationNode.Type.TypeName}\"");
}
var obj = Activator.CreateInstance(type,args: args);// 创建对象
if (obj == null)
{
throw new SereinSciptException(objectInstantiationNode, $"类型创建失败\"{objectInstantiationNode.TypeName}\"");
}
return obj;
}
else
object?[] args = new object[objectInstantiationNode.Arguments.Count];
for (int i = 0; i < objectInstantiationNode.Arguments.Count; i++)
{
throw new SereinSciptException(objectInstantiationNode, $"使用了未定义的类型\"{objectInstantiationNode.TypeName}\"");
var argNode = objectInstantiationNode.Arguments[i];
args[i] = await EvaluateAsync(context, argNode);
}
var obj = Activator.CreateInstance(type, args: args);// 创建对象
if (obj == null)
{
throw new SereinSciptException(objectInstantiationNode, $"类型创建失败\"{objectInstantiationNode.Type.TypeName}\"");
}
for (int i = 0; i < objectInstantiationNode.CtorAssignments.Count; i++)
{
var ctorAssignmentNode = objectInstantiationNode.CtorAssignments[i];
var propertyName = ctorAssignmentNode.MemberName;
var value = await EvaluateAsync(context, ctorAssignmentNode.Value);
SetPropertyValue(obj, propertyName, value);
}
}
return obj;
case FunctionCallNode callNode: // 调用方法
return await InterpretFunctionCallAsync(context, callNode); // 调用方法返回函数的返回值
case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用
@@ -570,6 +496,48 @@ namespace Serein.Script
}
}
public void SetPropertyValue(object target, string memberName, object? value)
{
var targetType = target?.GetType();
if (targetType is null) return;
var propertyInfo = targetType.GetProperty(memberName);
if (propertyInfo is null)
{
var fieldInfo = target?.GetType().GetRuntimeField(memberName);
if (fieldInfo is null)
{
throw new Exception($"类型 {targetType} 对象没有成员\"{memberName}\"");
}
else
{
var convertedValue = Convert.ChangeType(value, fieldInfo.FieldType);
fieldInfo.SetValue(target, convertedValue);
}
}
else
{
if (value is null)
{
propertyInfo.SetValue(target, null);
return;
}
var valueTtpe = value.GetType();
if (propertyInfo.PropertyType.IsAssignableFrom(valueTtpe))
{
propertyInfo.SetValue(target, value);
}
else if (propertyInfo.PropertyType.FullName == valueTtpe.FullName)
{
propertyInfo.SetValue(target, value);
}
else
{
throw new Exception($"类型 {targetType} 对象成员\"{memberName}\" 赋值时异常");
}
//var convertedValue = Convert.ChangeType(value, );
}
}
/// <summary>
/// 获取对象成员
/// </summary>

View File

@@ -126,6 +126,11 @@ namespace Serein.Script
Type = type;
Value = value;
}
public override string ToString()
{
return $"token in {Row} row, type is \"{Type}\", value is \"{Value}\".";
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
@@ -26,11 +27,6 @@ namespace Serein.Script
/// </summary>
public Dictionary<ASTNode, Type> NodeSymbolInfos { get; } = new Dictionary<ASTNode, Type>();
public SereinScriptTypeAnalysis()
{
}
public void LoadSymbol(Dictionary<string,Type> identifierNodes)
{
foreach(var kvp in identifierNodes)
@@ -43,7 +39,7 @@ namespace Serein.Script
}
public void AnalysisProgramNode(ProgramNode astNode)
public void Analysis(ProgramNode astNode)
{
//NodeSymbolInfos.Clear();
for (int i = 0; i < astNode.Statements.Count; i++)
@@ -52,8 +48,8 @@ namespace Serein.Script
Analysis(node);
}
var returnNodes = astNode.Statements.Where(node => node is ReturnNode).ToArray();
var returnNodes = NodeSymbolInfos.Keys.Where(node => node is ReturnNode).ToArray();
if (returnNodes.Length == 0)
{
NodeSymbolInfos[astNode] = typeof(void); // 程序无返回值
@@ -66,7 +62,15 @@ namespace Serein.Script
}
else
{
var firstReturnType = NodeSymbolInfos[returnNodes[0]]; // 第一个返回值
foreach(var item in returnNodes)
{
if(NodeSymbolInfos[item] != firstReturnType)
{
throw new Exception("类型检查异常,存在不同分支返回值类型不一致");
}
}
NodeSymbolInfos[astNode] = NodeSymbolInfos[returnNodes[0]]; // 确定的返回值
}
}
@@ -235,32 +239,61 @@ namespace Serein.Script
case ClassTypeDefinitionNode classTypeDefinitionNode: // 类型定义
Type AnalysisClassTypeDefinitionNode(ClassTypeDefinitionNode classTypeDefinitionNode)
{
var classType = DynamicObjectHelper.GetCacheType(classTypeDefinitionNode.ClassName);
if (classType is null)
classType = DynamicObjectHelper.CreateTypeWithProperties(classTypeDefinitionNode.Fields, classTypeDefinitionNode.ClassName);
var classType = Analysis(classTypeDefinitionNode.ClassType); // 查询类型
NodeSymbolInfos[classTypeDefinitionNode] = classType;
foreach (var kvp in classTypeDefinitionNode.Propertys)
{
TypeNode propertyNode = kvp.Value;
var propertyType = Analysis(propertyNode); // 查询属性类型
NodeSymbolInfos[propertyNode] = propertyType;
}
NodeSymbolInfos[classTypeDefinitionNode] = classType;
return classType;
}
return AnalysisClassTypeDefinitionNode(classTypeDefinitionNode);
case TypeNode typeNode:
Type AnalysisTypeNode(TypeNode typeNode)
{
// 类型搜寻优先级: 挂载类型 > 脚本中定义类型 > C#类型
Type resultType = GetTypeOfString(typeNode.TypeName); // 从自定义类型查询类型
NodeSymbolInfos[typeNode] = resultType;
return resultType;
}
return AnalysisTypeNode(typeNode);
case ObjectInstantiationNode objectInstantiationNode: // 类型实例化
Type AnalysisObjectInstantiationNode(ObjectInstantiationNode objectInstantiationNode)
{
Type? resultType = null;
try
Type resultType = Analysis(objectInstantiationNode.Type);
foreach(var item in objectInstantiationNode.CtorAssignments)
{
resultType = Type.GetType(objectInstantiationNode.TypeName); // 从命名空间查询类型
}
finally
{
if (resultType is null)
{
resultType = DynamicObjectHelper.GetCacheType(objectInstantiationNode.TypeName); // 从自定义类型查询类型
}
Analysis(item);
}
NodeSymbolInfos[objectInstantiationNode] = resultType;
return resultType;
}
return AnalysisObjectInstantiationNode(objectInstantiationNode);
case CtorAssignmentNode ctorAssignmentNode: // 构造器赋值
Type AnalysisCtorAssignmentNode(CtorAssignmentNode ctorAssignmentNode)
{
Type classType = Analysis(ctorAssignmentNode.Class);
Type valueType = Analysis(ctorAssignmentNode.Value);
//ctorAssignmentNode.MemberName
var property = classType.GetProperty(ctorAssignmentNode.MemberName);
if (property is null)
throw new Exception($"类型 {classType} 没有成员 {ctorAssignmentNode.MemberName}");
var propertyType = property.PropertyType;
if (!propertyType.IsAssignableFrom(valueType))
throw new Exception($"类型异常:构造器赋值需要 {propertyType},实际为 {valueType}");
NodeSymbolInfos[ctorAssignmentNode.Class] = classType;
NodeSymbolInfos[ctorAssignmentNode.Value] = valueType;
NodeSymbolInfos[ctorAssignmentNode] = propertyType;
return valueType;
}
return AnalysisCtorAssignmentNode(ctorAssignmentNode);
case ExpressionNode expressionNode: // 类型表达式(链式调用)
Type AnalysisObjectMemberExpressionNode(ExpressionNode expressionNode)
{
@@ -289,16 +322,33 @@ namespace Serein.Script
Type AnalysisMemberAssignmentNode(MemberAssignmentNode memberAssignmentNode)
{
var objectType = Analysis(memberAssignmentNode.Object);
var property = objectType.GetProperty(memberAssignmentNode.MemberName);
if(property is null)
throw new Exception($"类型异常:类型 {objectType} 没有成员 {memberAssignmentNode.MemberName}");
var propertyType = property.PropertyType;
var valueType = Analysis(memberAssignmentNode.Value);
if (!propertyType.IsAssignableFrom(valueType))
throw new Exception($"类型异常:赋值需要 {propertyType},实际为 {valueType}");
NodeSymbolInfos[memberAssignmentNode.Object] = propertyType;
NodeSymbolInfos[memberAssignmentNode.Value] = valueType;
NodeSymbolInfos[memberAssignmentNode] = typeof(void);
if(objectType == typeof(object))
{
/*var property = objectType.GetProperty(memberAssignmentNode.MemberName);
if (property is null)
throw new Exception($"类型异常:类型 {objectType} 没有成员 {memberAssignmentNode.MemberName}");
var propertyType = property.PropertyType;
var valueType = Analysis(memberAssignmentNode.Value);
if (!propertyType.IsAssignableFrom(valueType))
throw new Exception($"类型异常:赋值需要 {propertyType},实际为 {valueType}");*/
NodeSymbolInfos[memberAssignmentNode.Object] = typeof(object);
NodeSymbolInfos[memberAssignmentNode.Value] = typeof(object);
NodeSymbolInfos[memberAssignmentNode] = typeof(void);
}
else
{
var property = objectType.GetProperty(memberAssignmentNode.MemberName);
if (property is null)
throw new Exception($"类型异常:类型 {objectType} 没有成员 {memberAssignmentNode.MemberName}");
var propertyType = property.PropertyType;
var valueType = Analysis(memberAssignmentNode.Value);
if (!propertyType.IsAssignableFrom(valueType))
throw new Exception($"类型异常:赋值需要 {propertyType},实际为 {valueType}");
NodeSymbolInfos[memberAssignmentNode.Object] = propertyType;
NodeSymbolInfos[memberAssignmentNode.Value] = valueType;
NodeSymbolInfos[memberAssignmentNode] = typeof(void);
}
return typeof(void); // 对象成员赋值语句不产生类型
}
return AnalysisMemberAssignmentNode(memberAssignmentNode);
@@ -319,12 +369,14 @@ namespace Serein.Script
NodeSymbolInfos[memberFunctionCallNode.Object] = objectType;
NodeSymbolInfos[memberFunctionCallNode] = methodInfo.ReturnType;
return methodInfo.ReturnType;
}
return AnalysisMemberFunctionCallNode(memberFunctionCallNode);
case FunctionCallNode functionCallNode: // 外部挂载的函数调用
Type AnalysisFunctionCallNode(FunctionCallNode functionCallNode)
{
if(!SereinScriptInterpreter.FunctionInfoTable.TryGetValue(functionCallNode.FunctionName, out var methodInfo))
if(!SereinScript.FunctionInfos.TryGetValue(functionCallNode.FunctionName, out var methodInfo))
{
throw new Exception($"脚本没有挂载方法 {functionCallNode.FunctionName}");
}
@@ -486,6 +538,8 @@ namespace Serein.Script
break;
case ClassTypeDefinitionNode classTypeDefinitionNode: // 类型定义
break;
case TypeNode typeNode: // 类型
break;
case ObjectInstantiationNode objectInstantiationNode: // 类型实例化
break;
case ExpressionNode expressionNode: // 类型表达式(链式调用)
@@ -504,6 +558,29 @@ namespace Serein.Script
}
public static Type GetTypeOfString(string typeName)
{
Type? resultType = null;
resultType = DynamicObjectHelper.GetCacheType(typeName); // 从自定义类型查询类型
if (resultType != null)
{
return resultType;
}
try
{
resultType = Type.GetType(typeName); // 从命名空间查询类型
if (resultType != null)
{
return resultType;
}
throw new InvalidOperationException($"无法匹配类型 {typeName}");
}
catch (Exception ex)
{
throw;
}
}

View File

@@ -8,6 +8,7 @@ using Serein.NodeFlow.Services;
using Serein.Workbench.Api;
using Serein.Workbench.Services;
using Serein.Workbench.ViewModels;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
@@ -55,6 +56,23 @@ namespace Serein.Workbench
// 这里是测试代码,可以删除
private async Task LoadLocalProjectAsync()
{
var properties = new Dictionary<string, Type>
{
{ "Id", typeof(int) },
{ "Name", typeof(string) },
{ "CreateTime", typeof(DateTime) }
};
var type = DynamicObjectHelper.CreateTypeWithINotifyPropertyChanged(properties, "MyDynamicClass");
dynamic? obj = Activator.CreateInstance(type);
if(obj is null) return;
if (obj is INotifyPropertyChanged npc)
{
npc.PropertyChanged += (s, e) => Debug.WriteLine($"属性改变: {e.PropertyName}");
}
obj.Name = "下北泽";
obj.Id = 114514;
if (1 == 11)
{

View File

@@ -6,6 +6,7 @@
xmlns:local="clr-namespace:Serein.Workbench.Node.View"
xmlns:vm="clr-namespace:Serein.Workbench.Node.ViewModel"
xmlns:themes="clr-namespace:Serein.Workbench.Themes"
xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
d:DataContext="{d:DesignInstance vm:ScriptNodeControlViewModel}"
mc:Ignorable="d"
MinWidth="50">
@@ -56,7 +57,17 @@
<Button Content="执行" Margin="3,0,1,0" Command="{Binding CommandExecuting}" Height="17.2"></Button>
</StackPanel>
<themes:MethodDetailsControl Grid.Row="1" x:Name="MethodDetailsControl" MethodDetails="{Binding NodeModel.MethodDetails}" NodeViewModel="{Binding}"/>
<TextBox Grid.Row="2" MinHeight="20" MinWidth="100" MaxWidth="270" TextWrapping="Wrap" AcceptsReturn="True" IsEnabled="{Binding IsEnabledOnView}" Text="{Binding Script}"></TextBox>
<!--<TextBox Grid.Row="2" MinHeight="20" MinWidth="100" TextWrapping="Wrap" AcceptsReturn="True" IsEnabled="{Binding IsEnabledOnView}" Text="{Binding Script}"></TextBox>-->
<avalonEdit:TextEditor Grid.Row="2"
x:Name="codeEditor"
FontFamily="Consolas"
FontSize="12"
SyntaxHighlighting="C#"
TextChanged="codeEditor_TextChanged"
ShowLineNumbers="True"
Margin="10"/>
<Grid Grid.Row="3" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>

View File

@@ -43,6 +43,7 @@ namespace Serein.Workbench.Node.View
DataContext = viewModel;
viewModel.NodeModel.DisplayName = "[脚本节点]";
InitializeComponent();
codeEditor.Text = viewModel.Script ?? string.Empty; // 更新代码编辑器内容
}
@@ -96,6 +97,11 @@ namespace Serein.Workbench.Node.View
return [];
}
private void codeEditor_TextChanged(object sender, EventArgs e)
{
viewModel.Script = codeEditor.Text;
}

View File

@@ -76,6 +76,7 @@
<PackageReference Include="AvalonEdit" Version="6.3.0.90" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
<!--<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />-->
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />

View File

@@ -25,6 +25,9 @@ namespace Serein.Workbench.Views
{
this.DataContext = App.GetService<Locator>().MainViewModel;
InitializeComponent();
Window window = new System.Windows.Window();
window.Show();
}
}
}