diff --git a/Library.Core/DynamicContext.cs b/Library.Core/DynamicContext.cs index f2ade60..7102b02 100644 --- a/Library.Core/DynamicContext.cs +++ b/Library.Core/DynamicContext.cs @@ -20,6 +20,9 @@ namespace Serein.Library.Core RunState = RunState.Running; } + private readonly string _guid = global::System.Guid.NewGuid().ToString(); + string IDynamicContext.Guid => _guid; + /// /// 运行环境 /// diff --git a/Library.Framework/DynamicContext.cs b/Library.Framework/DynamicContext.cs index bc29516..5ff508d 100644 --- a/Library.Framework/DynamicContext.cs +++ b/Library.Framework/DynamicContext.cs @@ -19,6 +19,8 @@ namespace Serein.Library.Framework.NodeFlow RunState = RunState.Running; } + private readonly string _guid = global::System.Guid.NewGuid().ToString(); + string IDynamicContext.Guid => _guid; /// /// 运行环境 diff --git a/Library/Api/IDynamicContext.cs b/Library/Api/IDynamicContext.cs index 8efd5e4..8088ae2 100644 --- a/Library/Api/IDynamicContext.cs +++ b/Library/Api/IDynamicContext.cs @@ -11,6 +11,11 @@ namespace Serein.Library.Api /// public interface IDynamicContext { + /// + /// 标识流程 + /// + string Guid {get; } + /// /// 运行环境,包含IOC容器。 /// diff --git a/Library/Api/IScriptFlowApi.cs b/Library/Api/IScriptFlowApi.cs index e7539b2..27f48e9 100644 --- a/Library/Api/IScriptFlowApi.cs +++ b/Library/Api/IScriptFlowApi.cs @@ -30,13 +30,13 @@ namespace Serein.Library.Api /// /// /// - object GetDataOfParams(int index); + object GetArgData(int index); /// /// 根据入参名称从入参数据获取数据 /// /// /// - object GetDataOfParams(string name); + // object GetDataOfParams(string name); /// /// 获取全局数据 /// diff --git a/Library/FlowNode/NodeModelBaseFunc.cs b/Library/FlowNode/NodeModelBaseFunc.cs index d296fd7..ee5a643 100644 --- a/Library/FlowNode/NodeModelBaseFunc.cs +++ b/Library/FlowNode/NodeModelBaseFunc.cs @@ -393,7 +393,7 @@ namespace Serein.Library md.ActingInstance = context.Env.IOC.Get(md.ActingInstanceType); } - object[] args = await GetParametersAsync(context, this, md); + object[] args = await GetParametersAsync(context, this); var result = await dd.InvokeAsync(md.ActingInstance, args); return result; @@ -403,10 +403,10 @@ namespace Serein.Library /// 获取对应的参数数组 /// public static async Task GetParametersAsync(IDynamicContext context, - NodeModelBase nodeModel, - MethodDetails md) + NodeModelBase nodeModel) { // 用正确的大小初始化参数数组 + var md = nodeModel.MethodDetails; if (md.ParameterDetailss.Length == 0) { return null;// md.ActingInstance @@ -670,8 +670,6 @@ namespace Serein.Library // parameters[i] = result; //} - - } diff --git a/Library/Utils/DynamicObjectHelper.cs b/Library/Utils/DynamicObjectHelper.cs index 0261177..a6d7535 100644 --- a/Library/Utils/DynamicObjectHelper.cs +++ b/Library/Utils/DynamicObjectHelper.cs @@ -65,11 +65,18 @@ namespace Serein.Library.Utils } } - - public static Type CreateTypeWithProperties(IDictionary properties, string typeName) + /// + /// 创建具有属性的类型 + /// + /// 成员属性名称及类型 + /// 类型名称 + /// 是否覆盖 + /// + /// + public static Type CreateTypeWithProperties(IDictionary properties, string typeName, bool isOverlay = false) { - // 如果类型已经缓存,直接返回缓存的类型 - if (typeCache.ContainsKey(typeName)) + // 如果类型已经缓存,且没有显示指定需要覆盖,直接返回缓存的类型 + if (typeCache.ContainsKey(typeName) && !isOverlay) { return typeCache[typeName]; } diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index aea4500..6196196 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -1026,6 +1026,10 @@ namespace Serein.NodeFlow.Env #region 参数调用关系 foreach (var toNode in NodeModels.Values) { + if(toNode.MethodDetails.ParameterDetailss == null) + { + continue; + } for (var i = 0; i < toNode.MethodDetails.ParameterDetailss.Length; i++) { var pd = toNode.MethodDetails.ParameterDetailss[i]; @@ -1589,6 +1593,8 @@ namespace Serein.NodeFlow.Env NodeConnectChangeEventArgs.ConnectChangeType.Remote // 是创建连接还是删除连接 ))); // 通知UI } + + for (int i = 0; i < argInfo.Length; i++) { ParameterDetails? pd = nodeModel.MethodDetails.ParameterDetailss[i]; diff --git a/NodeFlow/Env/FlowFunc.cs b/NodeFlow/Env/FlowFunc.cs index 215505a..9c61896 100644 --- a/NodeFlow/Env/FlowFunc.cs +++ b/NodeFlow/Env/FlowFunc.cs @@ -22,7 +22,8 @@ namespace Serein.NodeFlow.Env { if(nodeControlType == NodeControlType.ExpCondition || nodeControlType == NodeControlType.ExpOp - || nodeControlType == NodeControlType.GlobalData) + || nodeControlType == NodeControlType.GlobalData + || nodeControlType == NodeControlType.Script) { return true; } @@ -70,6 +71,8 @@ namespace Serein.NodeFlow.Env } + + /// /// 从节点信息读取节点类型 /// diff --git a/NodeFlow/Model/SingleFlipflopNode.cs b/NodeFlow/Model/SingleFlipflopNode.cs index 3358f5e..f16b6a1 100644 --- a/NodeFlow/Model/SingleFlipflopNode.cs +++ b/NodeFlow/Model/SingleFlipflopNode.cs @@ -41,7 +41,7 @@ namespace Serein.NodeFlow.Model } object instance = md.ActingInstance; - var args = await GetParametersAsync(context, this, md); + var args = await GetParametersAsync(context, this); // 因为这里会返回不确定的泛型 IFlipflopContext // 而我们只需要获取到 State 和 Value(返回的数据) // 所以使用 dynamic 类型接收 diff --git a/NodeFlow/Model/SingleScriptNode.cs b/NodeFlow/Model/SingleScriptNode.cs index 0c9b04c..1b06921 100644 --- a/NodeFlow/Model/SingleScriptNode.cs +++ b/NodeFlow/Model/SingleScriptNode.cs @@ -5,7 +5,9 @@ using Serein.Script; using Serein.Script.Node; using System; using System.Collections.Generic; +using System.Dynamic; using System.Linq; +using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -28,7 +30,7 @@ namespace Serein.NodeFlow.Model private IScriptFlowApi ScriptFlowApi { get; } private ASTNode mainNode; - + private SereinScriptInterpreter ScriptInterpreter; /// /// 构建流程脚本节点 /// @@ -37,14 +39,11 @@ namespace Serein.NodeFlow.Model { //ScriptFlowApi = environment.IOC.Get(); ScriptFlowApi = new ScriptFlowApi(environment, this); - - - MethodInfo? method = this.GetType().GetMethod(nameof(GetFlowApi)); - if (method != null) - { - SereinScriptInterpreter.AddFunction(nameof(GetFlowApi), method, () => this); // 挂载获取流程接口 - } + ScriptInterpreter = new SereinScriptInterpreter(); + } + static SingleScriptNode() + { // 挂载静态方法 var tempMethods = typeof(BaseFunc).GetMethods().Where(method => !(method.Name.Equals("GetHashCode") @@ -52,21 +51,74 @@ namespace Serein.NodeFlow.Model || method.Name.Equals("ToString") || method.Name.Equals("GetType") )).Select(method => (method.Name, method)).ToArray(); - + // 加载基础方法 foreach ((string name, MethodInfo method) item in tempMethods) { - SereinScriptInterpreter.AddFunction(item.name, item.method); // 加载基础方法 + SereinScriptInterpreter.AddStaticFunction(item.name, item.method); } } + + public override void OnCreating() + { + MethodInfo? method = this.GetType().GetMethod(nameof(GetFlowApi)); + if (method != null) + { + ScriptInterpreter.AddFunction(nameof(GetFlowApi), method, () => this); // 挂载获取流程接口 + } + + var md = MethodDetails; + var pd = md.ParameterDetailss ??= new ParameterDetails[1]; + md.ParamsArgIndex = 0; + pd[0] = new ParameterDetails + { + Index = 0, + Name = "object", + IsExplicitData = true, + DataValue = string.Empty, + DataType = typeof(object), + ExplicitType = typeof(object), + ArgDataSourceNodeGuid = string.Empty, + ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData, + NodeModel = this, + ExplicitTypeName = "Value", + Items = null, + IsParams = true, + }; + + } + /// - /// 加载脚本代码 + /// 导出脚本代码 /// - public void LoadScript() + /// + /// + public override NodeInfo SaveCustomData(NodeInfo nodeInfo) + { + dynamic data = new ExpandoObject(); + data.Script = Script ?? ""; + nodeInfo.CustomData = data; + return nodeInfo; + } + + /// + /// 加载自定义数据 + /// + /// + public override void LoadCustomData(NodeInfo nodeInfo) + { + this.Script = nodeInfo.CustomData?.Script ?? ""; + } + + /// + /// 重新加载脚本代码 + /// + public void ReloadScript() { try { - mainNode = new SereinScriptParser(Script).Parse(); + var p = new SereinScriptParser(Script); + mainNode = p.Parse(); } catch (Exception ex) { @@ -81,27 +133,33 @@ namespace Serein.NodeFlow.Model /// public override async Task ExecutingAsync(IDynamicContext context) { + var @params = await NodeModelBase.GetParametersAsync(context, this); + ScriptFlowApi.Context= context; + context.AddOrUpdate($"{context.Guid}_{this.Guid}_Params", @params[0]); // 后面再改 mainNode ??= new SereinScriptParser(Script).Parse(); - SereinScriptInterpreter scriptInterpreter = new SereinScriptInterpreter(); - var result = await scriptInterpreter.InterpretAsync(mainNode); // 从入口节点执行 - scriptInterpreter.ResetVar(); + IScriptInvokeContext scriptContext = new ScriptInvokeContext(); + var result = await ScriptInterpreter.InterpretAsync(scriptContext, mainNode); // 从入口节点执行 return result; } - - public IScriptFlowApi GetFlowApi() - { - return ScriptFlowApi; + + #region 挂载的方法 + + public IScriptFlowApi GetFlowApi() + { + return ScriptFlowApi; } private static class BaseFunc { + public static DateTime GetNow() => DateTime.Now; + public static Type TypeOf(object type) { return type.GetType(); } - + public static void Print(object value) { @@ -120,7 +178,7 @@ namespace Serein.NodeFlow.Model public static bool ToBool(object value) { return bool.Parse(value.ToString()); - } + } #endregion public static async Task Delay(object value) @@ -138,6 +196,7 @@ namespace Serein.NodeFlow.Model } } - } + } + #endregion } } diff --git a/NodeFlow/ScriptFlowApi.cs b/NodeFlow/ScriptFlowApi.cs index b48851c..79684df 100644 --- a/NodeFlow/ScriptFlowApi.cs +++ b/NodeFlow/ScriptFlowApi.cs @@ -1,5 +1,6 @@ using Serein.Library; using Serein.Library.Api; +using Serein.Library.Utils; using System; using System.Collections.Generic; using System.Linq; @@ -23,42 +24,47 @@ namespace Serein.NodeFlow /// public NodeModelBase NodeModel { get; private set; } - IDynamicContext IScriptFlowApi.Context { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public IDynamicContext? Context{ get; set; } + + private string _paramsKey => $"{Context?.Guid}_{NodeModel.Guid}_Params"; + + /// /// 创建流程脚本接口 /// /// 运行环境 /// 节点 - public ScriptFlowApi(IFlowEnvironment environment, IDynamicContext dynamicContext, NodeModelBase nodeModel) + public ScriptFlowApi(IFlowEnvironment environment, NodeModelBase nodeModel) { Env = environment; NodeModel = nodeModel; } - Task IScriptFlowApi.CallNode(string nodeGuid) + public Task CallNode(string nodeGuid) { throw new NotImplementedException(); } - object IScriptFlowApi.GetDataOfParams(int index) + public object? GetArgData(int index) { - throw new NotImplementedException(); + var obj = Context?.GetFlowData(_paramsKey); + if (obj is object[] @params && index < @params.Length) + { + return @params[index]; + } + return null; } - object IScriptFlowApi.GetDataOfParams(string name) + + public object? GetFlowData() { - throw new NotImplementedException(); + return Context?.GetFlowData(NodeModel.Guid); } - object IScriptFlowApi.GetFlowData() + public object? GetGlobalData(string keyName) { - throw new NotImplementedException(); - } - - object IScriptFlowApi.GetGlobalData(string keyName) - { - throw new NotImplementedException(); + return SereinEnv.GetFlowGlobalData(keyName); } } diff --git a/Serein.Script/Node/ClassTypeDefinitionNode.cs b/Serein.Script/Node/ClassTypeDefinitionNode.cs index 2affd7a..b4ff727 100644 --- a/Serein.Script/Node/ClassTypeDefinitionNode.cs +++ b/Serein.Script/Node/ClassTypeDefinitionNode.cs @@ -11,13 +11,15 @@ namespace Serein.Script.Node /// public class ClassTypeDefinitionNode : ASTNode { + public bool IsOverlay { get; set; } public string ClassName { get; } public Dictionary Fields { get; } - public ClassTypeDefinitionNode(Dictionary fields, string className) + public ClassTypeDefinitionNode(Dictionary fields, string className, bool isOverlay) { this.Fields = fields; this.ClassName = className; + IsOverlay = isOverlay; } } diff --git a/Serein.Script/SereinScriptInterpreter.cs b/Serein.Script/SereinScriptInterpreter.cs index d03f2c4..15723e7 100644 --- a/Serein.Script/SereinScriptInterpreter.cs +++ b/Serein.Script/SereinScriptInterpreter.cs @@ -21,8 +21,45 @@ namespace Serein.Script } } + /// + /// 脚本运行上下文 + /// + public interface IScriptInvokeContext + { + /// + /// 是否该退出了 + /// + bool IsReturn { get; } - public class SereinScriptInterpreter + /// + /// 是否严格检查 Null 值 (禁止使用 Null) + /// + bool IsCheckNullValue { get; } + + /// + /// 获取变量的值 + /// + /// + /// + object GetVarValue(string varName); + + /// + /// 设置变量的值 + /// + /// + /// + /// + bool SetVarValue(string varName, object value); + + /// + /// 结束调用 + /// + /// + bool Exit(); + } + + + public class ScriptInvokeContext : IScriptInvokeContext { /// /// 定义的变量 @@ -30,25 +67,37 @@ namespace Serein.Script private Dictionary _variables = new Dictionary(); /// - /// 挂载的函数 + /// 是否该退出了 /// - private static Dictionary _functionTable = new Dictionary(); + public bool IsReturn { get; set; } /// - /// 挂载的函数调用的对象(用于函数需要实例才能调用的场景) + /// 是否严格检查 Null 值 (禁止使用 Null) /// - private static Dictionary> _callFuncOfGetObjects = new Dictionary>(); + public bool IsCheckNullValue { get; set; } - /// - /// 定义的类型 - /// - private static Dictionary _classDefinition = new Dictionary(); - - /// - /// 重置的变量 - /// - public void ResetVar() + object IScriptInvokeContext.GetVarValue(string varName) { + _variables.TryGetValue(varName, out var value); + return value; + } + + + bool IScriptInvokeContext.SetVarValue(string varName, object? value) + { + if (!_variables.TryAdd(varName, value)) + { + _variables[varName] = value; + } + return true; + } + + + + + bool IScriptInvokeContext.Exit() + { + // 清理脚本中加载的非托管资源 foreach (var nodeObj in _variables.Values) { if (nodeObj is not null) @@ -64,14 +113,47 @@ namespace Serein.Script } } _variables.Clear(); + return true ; } + } + + + public class SereinScriptInterpreter + { + + /// + /// 挂载的函数 + /// + private static Dictionary _functionTable = new Dictionary(); + + /// + /// 挂载的函数调用的对象(用于函数需要实例才能调用的场景) + /// + private Dictionary> _callFuncOfGetObjects = new Dictionary>(); + + /// + /// 定义的类型 + /// + private Dictionary _classDefinition = new Dictionary(); + + /// + /// 挂载静态函数 + /// + /// + /// + public static void AddStaticFunction(string functionName, MethodInfo methodInfo) + { + _functionTable[functionName] = new DelegateDetails(methodInfo); + } + + /// /// 挂载函数 /// /// 函数名称 /// 方法信息 - public static void AddFunction(string functionName, MethodInfo methodInfo, Func? callObj = null) + public void AddFunction(string functionName, MethodInfo methodInfo, Func? callObj = null) { //if (!_functionTable.ContainsKey(functionName)) //{ @@ -84,12 +166,16 @@ namespace Serein.Script } - if(!methodInfo.IsStatic && callObj is not null) + if(!methodInfo.IsStatic && callObj is not null && !_callFuncOfGetObjects.ContainsKey(functionName)) { // 静态函数不需要 _callFuncOfGetObjects.Add(functionName, callObj); } - _functionTable[functionName] = new DelegateDetails(methodInfo); + if (!_functionTable.ContainsKey(functionName)) + { + _functionTable[functionName] = new DelegateDetails(methodInfo); + } + //_functionTable[functionName] = new DelegateDetails(methodInfo); } /// @@ -97,7 +183,7 @@ namespace Serein.Script /// /// 函数名称 /// 方法信息 - public static void AddClassType(Type type , string typeName = "") + public void AddClassType(Type type , string typeName = "") { if (string.IsNullOrEmpty(typeName)) { @@ -115,7 +201,7 @@ namespace Serein.Script /// /// /// - private async Task ExecutionProgramNodeAsync(ProgramNode programNode) + private async Task ExecutionProgramNodeAsync(IScriptInvokeContext context, ProgramNode programNode) { // 遍历 ProgramNode 中的所有语句并执行它们 foreach (var statement in programNode.Statements) @@ -123,11 +209,11 @@ namespace Serein.Script // 直接退出 if (statement is ReturnNode returnNode) // 遇到 Return 语句 提前退出 { - return await EvaluateAsync(statement); + return await EvaluateAsync(context, statement); } else { - await InterpretAsync(statement); + await InterpretAsync(context, statement); } } @@ -141,12 +227,16 @@ namespace Serein.Script /// private void ExecutionClassTypeDefinitionNode(ClassTypeDefinitionNode classTypeDefinitionNode) { - if (_classDefinition.ContainsKey(classTypeDefinitionNode.ClassName)) + if (_classDefinition.ContainsKey(classTypeDefinitionNode.ClassName) && !classTypeDefinitionNode.IsOverlay) { //SereinEnv.WriteLine(InfoType.WARN, $"异常信息 : 类型重复定义,代码在第{classTypeDefinitionNode.Row}行: {classTypeDefinitionNode.Code.Trim()}"); return; } - var type = DynamicObjectHelper.CreateTypeWithProperties(classTypeDefinitionNode.Fields, classTypeDefinitionNode.ClassName); + + var isOverlay = true; // classTypeDefinitionNode.IsOverlay; + + var type = DynamicObjectHelper.CreateTypeWithProperties(classTypeDefinitionNode.Fields, classTypeDefinitionNode.ClassName, isOverlay); // 覆盖 + classTypeDefinitionNode.IsOverlay = false; // 已经加载过,则不再覆盖 _classDefinition[classTypeDefinitionNode.ClassName] = type; // 定义对象 } @@ -156,9 +246,9 @@ namespace Serein.Script /// /// /// - private async Task ExecutionIfNodeAsync(IfNode ifNode) + private async Task ExecutionIfNodeAsync(IScriptInvokeContext context, IfNode ifNode) { - var result = await EvaluateAsync(ifNode.Condition) ?? throw new SereinSciptException(ifNode, $"条件语句返回了 null"); + var result = await EvaluateAsync(context, ifNode.Condition) ?? throw new SereinSciptException(ifNode, $"条件语句返回了 null"); if (result is not bool condition) { @@ -169,14 +259,14 @@ namespace Serein.Script { foreach (var trueNode in ifNode.TrueBranch) { - await InterpretAsync(trueNode); + await InterpretAsync(context, trueNode); } } else { foreach (var falseNode in ifNode.FalseBranch) { - await InterpretAsync(falseNode); + await InterpretAsync(context,falseNode); } } } @@ -187,11 +277,11 @@ namespace Serein.Script /// /// /// - private async Task ExectutionWhileNodeAsync(WhileNode whileNode) + private async Task ExectutionWhileNodeAsync(IScriptInvokeContext context, WhileNode whileNode) { while (true) { - var result = await EvaluateAsync(whileNode.Condition) ?? throw new SereinSciptException(whileNode, $"条件语句返回了 null"); + var result = await EvaluateAsync(context, whileNode.Condition) ?? throw new SereinSciptException(whileNode, $"条件语句返回了 null"); if (result is not bool condition) { throw new SereinSciptException(whileNode, $"条件语句返回值不为 bool 类型(当前返回值类型为 {result.GetType()})"); @@ -202,7 +292,7 @@ namespace Serein.Script } foreach(var node in whileNode.Body) { - await InterpretAsync(node); + await InterpretAsync(context, node); } } } @@ -212,19 +302,19 @@ namespace Serein.Script /// /// /// - private async Task ExecutionAssignmentNodeAsync(AssignmentNode assignmentNode) + private async Task ExecutionAssignmentNodeAsync(IScriptInvokeContext context, AssignmentNode assignmentNode) { - var tmp = await EvaluateAsync(assignmentNode.Value); - _variables[assignmentNode.Variable] = tmp; + var tmp = await EvaluateAsync(context, assignmentNode.Value); + context.SetVarValue(assignmentNode.Variable, tmp); } - private async Task InterpretFunctionCallAsync(FunctionCallNode functionCallNode) + private async Task InterpretFunctionCallAsync(IScriptInvokeContext context, FunctionCallNode functionCallNode) { // 评估函数参数 var arguments = new object?[functionCallNode.Arguments.Count]; for (int i = 0; i < functionCallNode.Arguments.Count; i++) { ASTNode? arg = functionCallNode.Arguments[i]; - arguments[i] = await EvaluateAsync(arg); // 评估每个参数 + arguments[i] = await EvaluateAsync(context, arg); // 评估每个参数 } var funcName = functionCallNode.FunctionName; @@ -263,7 +353,7 @@ namespace Serein.Script - public async Task InterpretAsync(ASTNode node) + public async Task InterpretAsync(IScriptInvokeContext context, ASTNode node) { if(node == null) { @@ -273,30 +363,29 @@ namespace Serein.Script switch (node) { case ProgramNode programNode: // AST树入口 - var scritResult = await ExecutionProgramNodeAsync(programNode); + var scritResult = await ExecutionProgramNodeAsync(context, programNode); return scritResult; // 遍历 ProgramNode 中的所有语句并执行它们 case ClassTypeDefinitionNode classTypeDefinitionNode: // 定义类型 ExecutionClassTypeDefinitionNode(classTypeDefinitionNode); break; case AssignmentNode assignment: // 出现在 = 右侧的表达式 - await ExecutionAssignmentNodeAsync(assignment); + await ExecutionAssignmentNodeAsync(context, assignment); break; case MemberAssignmentNode memberAssignmentNode: // 设置对象属性 - await SetMemberValue(memberAssignmentNode); + await SetMemberValue(context, memberAssignmentNode); break; case MemberFunctionCallNode memberFunctionCallNode: - return await CallMemberFunction(memberFunctionCallNode); - break; + return await CallMemberFunction(context, memberFunctionCallNode); case IfNode ifNode: // 执行 if...else... 语句块 - await ExecutionIfNodeAsync(ifNode); + await ExecutionIfNodeAsync(context, ifNode); break; case WhileNode whileNode: // 循环语句块 - await ExectutionWhileNodeAsync(whileNode); + await ExectutionWhileNodeAsync(context, whileNode); break; case FunctionCallNode functionCallNode: // 方法调用节点 - return await InterpretFunctionCallAsync(functionCallNode); + return await InterpretFunctionCallAsync(context, functionCallNode); case ReturnNode returnNode: - return await EvaluateAsync(returnNode); + return await EvaluateAsync(context, returnNode); default: throw new SereinSciptException(node, "解释器 InterpretAsync() 未实现节点行为"); } @@ -304,7 +393,7 @@ namespace Serein.Script } - private async Task EvaluateAsync(ASTNode node) + private async Task EvaluateAsync(IScriptInvokeContext context, ASTNode node) { if(node == null) { @@ -321,27 +410,17 @@ namespace Serein.Script case StringNode stringNode: return stringNode.Value; // 返回字符串值 case IdentifierNode identifierNode: - if (_variables.TryGetValue(identifierNode.Name, out var result)) - { - //if(result == null) - //{ - // throw new SereinSciptException(identifierNode, "尝试使用值为null的变量"); - //} - return result; // 获取变量值 - } - else - { - throw new SereinSciptException(identifierNode, "尝试使用未声明的变量"); - } + return context.GetVarValue(identifierNode.Name); + //throw new SereinSciptException(identifierNode, "尝试使用值为null的变量"); + //throw new SereinSciptException(identifierNode, "尝试使用未声明的变量"); case BinaryOperationNode binOpNode: // 递归计算二元操作 - var left = await EvaluateAsync(binOpNode.Left); + var left = await EvaluateAsync(context, binOpNode.Left); //if (left == null ) //{ // throw new SereinSciptException(binOpNode.Left, $"左值尝试使用 null"); //} - - var right = await EvaluateAsync(binOpNode.Right); + var right = await EvaluateAsync(context, binOpNode.Right); //if (right == null) //{ // throw new SereinSciptException(binOpNode.Right, "右值尝试使用计算 null"); @@ -354,7 +433,7 @@ namespace Serein.Script for (int i = 0; i < objectInstantiationNode.Arguments.Count; i++) { var argNode = objectInstantiationNode.Arguments[i]; - args[i] = await EvaluateAsync(argNode); + args[i] = await EvaluateAsync(context, argNode); } var obj = Activator.CreateInstance(type,args: args);// 创建对象 if (obj == null) @@ -370,11 +449,13 @@ namespace Serein.Script } case FunctionCallNode callNode: - return await InterpretFunctionCallAsync(callNode); // 调用方法返回函数的返回值 + return await InterpretFunctionCallAsync(context, callNode); // 调用方法返回函数的返回值 + case MemberFunctionCallNode memberFunctionCallNode: + return await CallMemberFunction(context, memberFunctionCallNode); case MemberAccessNode memberAccessNode: - return await GetValue(memberAccessNode); + return await GetValue(context, memberAccessNode); case ReturnNode returnNode: // - return await EvaluateAsync(returnNode.Value); // 直接返回响应的内容 + return await EvaluateAsync(context, returnNode.Value); // 直接返回响应的内容 default: throw new SereinSciptException(node, "解释器 EvaluateAsync() 未实现节点行为"); } @@ -440,10 +521,10 @@ namespace Serein.Script /// /// /// - public async Task SetMemberValue(MemberAssignmentNode memberAssignmentNode) + public async Task SetMemberValue(IScriptInvokeContext context, MemberAssignmentNode memberAssignmentNode) { - var target = await EvaluateAsync(memberAssignmentNode.Object); - var value = await EvaluateAsync(memberAssignmentNode.Value); + var target = await EvaluateAsync(context, memberAssignmentNode.Object); + var value = await EvaluateAsync(context, memberAssignmentNode.Value); // 设置值 var lastMember = memberAssignmentNode.MemberName; @@ -474,9 +555,9 @@ namespace Serein.Script /// /// /// - public async Task GetValue(MemberAccessNode memberAccessNode) + public async Task GetValue(IScriptInvokeContext context, MemberAccessNode memberAccessNode) { - var target = await EvaluateAsync(memberAccessNode.Object); + var target = await EvaluateAsync(context, memberAccessNode.Object); var lastMember = memberAccessNode.MemberName; var lastProperty = target?.GetType().GetProperty(lastMember); @@ -503,9 +584,9 @@ namespace Serein.Script /// private Dictionary MethodToDelegateCaches { get; } = new Dictionary(); - public async Task CallMemberFunction(MemberFunctionCallNode memberFunctionCallNode) + public async Task CallMemberFunction(IScriptInvokeContext context, MemberFunctionCallNode memberFunctionCallNode) { - var target = await EvaluateAsync(memberFunctionCallNode.Object); + var target = await EvaluateAsync(context, memberFunctionCallNode.Object); var lastMember = memberFunctionCallNode.FunctionName; var methodInfo = target?.GetType().GetMethod(lastMember) ?? throw new SereinSciptException(memberFunctionCallNode, $"对象没有方法\"{memberFunctionCallNode.FunctionName}\""); @@ -521,7 +602,7 @@ namespace Serein.Script for (int i = 0; i < memberFunctionCallNode.Arguments.Count; i++) { ASTNode? arg = memberFunctionCallNode.Arguments[i]; - arguments[i] = await EvaluateAsync(arg); // 评估每个参数 + arguments[i] = await EvaluateAsync(context, arg); // 评估每个参数 } return await delegateDetails.InvokeAsync(target, arguments); diff --git a/Serein.Script/SereinScriptLexer.cs b/Serein.Script/SereinScriptLexer.cs index 40149d2..23bc11f 100644 --- a/Serein.Script/SereinScriptLexer.cs +++ b/Serein.Script/SereinScriptLexer.cs @@ -27,6 +27,10 @@ namespace Serein.Script /// String, /// + /// 插值字符串 + /// + InterpolatedString, + /// /// 关键字 /// Keyword, @@ -273,6 +277,9 @@ namespace Serein.Script } case '.': return CreateToken(TokenType.Dot, "."); + + //case '$': + // return CreateToken(TokenType.InterpolatedString, "$"); } throw new Exception("Unexpected character: " + currentChar); diff --git a/Serein.Script/SereinScriptParser.cs b/Serein.Script/SereinScriptParser.cs index dc329b5..07b631c 100644 --- a/Serein.Script/SereinScriptParser.cs +++ b/Serein.Script/SereinScriptParser.cs @@ -3,6 +3,7 @@ using Serein.Library; using Serein.Script.Node; using System.Collections.Generic; using System.Linq.Expressions; +using System.Reflection; namespace Serein.Script { @@ -32,6 +33,7 @@ namespace Serein.Script Statements.Clear(); while (_currentToken.Type != TokenType.EOF) { + var astNode = Statement(); if (astNode == null) { @@ -41,8 +43,7 @@ namespace Serein.Script //if (astNode is ClassTypeDefinitionNode) //{ - // // 类型定义置顶 - // statements = [astNode, ..statements]; + // statements = [astNode, ..statements]; // 类型定义置顶 //} //else //{ @@ -61,9 +62,17 @@ namespace Serein.Script } if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "class") { - return ParseClassDefinition(); + return ParseClassDefinition(); // 加载类,如果已经加载过,则忽略 } - + if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "new") + { + var _peekToken = _lexer.PeekToken(); + if (_peekToken.Type == TokenType.Keyword && _peekToken.Value == "class") + { + return ParseClassDefinition(); // 重新加载类 + } + } + if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "if") { return ParseIf(); @@ -134,20 +143,30 @@ namespace Serein.Script private ASTNode ParseAssignment() { string variableName = _currentToken.Value.ToString(); - _currentToken = _lexer.NextToken(); // consume identifier - if(_currentToken.Type == TokenType.ParenthesisRight) + var _peekToken = _lexer.PeekToken(); + if (_peekToken.Type == TokenType.ParenthesisRight) { + _currentToken = _lexer.NextToken(); // 消耗标识符 + return new IdentifierNode(variableName).SetTokenInfo(_currentToken); } - if(_currentToken.Type == TokenType.Operator && _currentToken.Value == "=") + if(_peekToken.Type == TokenType.Operator && _peekToken.Value == "=") { // 赋值行为 - _currentToken = _lexer.NextToken(); // consume "=" + _currentToken = _lexer.NextToken(); // 消耗标识符 + _currentToken = _lexer.NextToken(); // 消耗 "=" var _tempToken = _lexer.PeekToken(); ASTNode valueNode; - if (_tempToken.Type == TokenType.ParenthesisLeft) + + if(_tempToken.Type == TokenType.Operator && _tempToken.Value != "=") + { + //_currentToken = _lexer.NextToken(); // 消耗操作符 + //_currentToken = _lexer.NextToken(); // 消耗操作符 + valueNode = Expression(); + } + else if (_tempToken.Type == TokenType.ParenthesisLeft) { // 解析赋值右边的表达式 // 是函数调用,解析函数调用 @@ -160,10 +179,16 @@ namespace Serein.Script } return new AssignmentNode(variableName, valueNode).SetTokenInfo(_currentToken); } - if (_currentToken.Type == TokenType.Dot) + if (_peekToken.Type == TokenType.Dot) { + // 可能是方法调用 return ParseMemberAccessOrAssignment(); } + + if(_peekToken.Type == TokenType.Operator) + { + return new IdentifierNode(variableName).SetTokenInfo(_currentToken); + } @@ -204,13 +229,19 @@ namespace Serein.Script private ASTNode ParseClassDefinition() { + bool isOverlay = false; + if (_currentToken.Value == "new") + { + isOverlay = true; // 重新加载类 + _currentToken = _lexer.NextToken(); // 消耗 new 关键字 + } _currentToken = _lexer.NextToken(); // 消耗 class 关键字 var className = _currentToken.Value.ToString(); // 获取定义的类名 - _currentToken = _lexer.NextToken(); // 消耗括号 + _currentToken = _lexer.NextToken(); // 消耗类名 if (_currentToken.Type != TokenType.BraceLeft || _currentToken.Value != "{") throw new Exception("Expected '{' after class definition"); var classFields = new Dictionary(); - _currentToken = _lexer.NextToken(); + _currentToken = _lexer.NextToken(); // 消耗括号 while (_currentToken.Type != TokenType.BraceRight) { var fieldType = _currentToken.Value.ToString().ToTypeOfString(); // 获取定义的类名 @@ -231,7 +262,7 @@ namespace Serein.Script _currentToken = _lexer.NextToken(); _currentToken = _lexer.NextToken(); - return new ClassTypeDefinitionNode(classFields, className).SetTokenInfo(_currentToken); + return new ClassTypeDefinitionNode(classFields, className, isOverlay).SetTokenInfo(_currentToken); } public ASTNode ParseObjectInstantiation() @@ -310,7 +341,6 @@ namespace Serein.Script else { - if(_peekToken.Type == TokenType.ParenthesisLeft) { // 成员方法调用 obj.Member(xxx); @@ -323,6 +353,8 @@ namespace Serein.Script } else { + + _currentToken = _lexer.NextToken(); // 消耗 成员名称 // 成员获取 return new MemberAccessNode(identifierNode, memberName).SetTokenInfo(_currentToken); } @@ -361,7 +393,7 @@ namespace Serein.Script } } - _currentToken = _lexer.NextToken(); // consume ")" + //_currentToken = _lexer.NextToken(); // consume ")" return new MemberFunctionCallNode(targetNode, functionName, arguments).SetTokenInfo(_currentToken); } @@ -548,6 +580,15 @@ namespace Serein.Script _currentToken = _lexer.NextToken(); // 消耗数字 return new StringNode(text.ToString()).SetTokenInfo(_currentToken); } + if( _currentToken.Type == TokenType.InterpolatedString) + { + // 可能是插值字符串; + // let context = $"a{A}b{B}c"; + // let context = "a" + A + "b" + B + c; + _currentToken = _lexer.NextToken(); // 消耗字符串 + while (_currentToken.Type == TokenType.String) { + } + } if (_currentToken.Type == TokenType.Number) { @@ -589,51 +630,6 @@ namespace Serein.Script if (_identifierPeekToken.Type == TokenType.Dot) { return ParseMemberAccessOrAssignment(); - - var identifierNode = new IdentifierNode(identifier).SetTokenInfo(_currentToken); - // 处理成员访问:identifier.member - if (_currentToken.Type == TokenType.Dot) - { - _currentToken = _lexer.NextToken(); // 消耗 "." - if (_currentToken.Type != TokenType.Identifier) - { - throw new Exception("Expected member name after dot."); - } - - var memberName = _currentToken.Value; - _currentToken = _lexer.NextToken(); // 消耗成员名 - if (_currentToken.Type == TokenType.Operator && _currentToken.Value == "=") - { - // 成员赋值 obj.Member = xxx; - _currentToken = _lexer.NextToken(); // 消耗 "=" - var valueNode = Expression(); // 解析右值 - return new MemberAssignmentNode(identifierNode, memberName, valueNode).SetTokenInfo(_currentToken); - } - else - { - var _peekToken = _lexer.PeekToken(); - if (_peekToken.Type == TokenType.ParenthesisLeft) - { - // 成员方法调用 obj.Member(xxx); - return ParseFunctionCall(); - } - else if (_peekToken.Type == TokenType.SquareBracketsLeft) - { - // 数组 index; 字典 key obj.Member[xxx]; - return ParseCollectionIndex(); - } - else - { - // 成员获取 - return new MemberAccessNode(identifierNode, memberName).SetTokenInfo(_currentToken); - } - - } - - } - - return identifierNode; - } _currentToken = _lexer.NextToken(); // 消耗标识符 return new IdentifierNode(identifier.ToString()).SetTokenInfo(_currentToken); diff --git a/WorkBench/Themes/MethodDetailsControl.xaml b/WorkBench/Themes/MethodDetailsControl.xaml index a2c9bb5..39749d4 100644 --- a/WorkBench/Themes/MethodDetailsControl.xaml +++ b/WorkBench/Themes/MethodDetailsControl.xaml @@ -16,10 +16,10 @@ - + - + diff --git a/Workbench/Node/View/ScriptNodeControl.xaml b/Workbench/Node/View/ScriptNodeControl.xaml index 550d5d0..b7d0808 100644 --- a/Workbench/Node/View/ScriptNodeControl.xaml +++ b/Workbench/Node/View/ScriptNodeControl.xaml @@ -44,6 +44,8 @@ + + @@ -56,9 +58,25 @@ + + + + + + + + + + + + + + - - + + + + - + diff --git a/Workbench/Node/View/ScriptNodeControl.xaml.cs b/Workbench/Node/View/ScriptNodeControl.xaml.cs index 6dc95b7..f16decf 100644 --- a/Workbench/Node/View/ScriptNodeControl.xaml.cs +++ b/Workbench/Node/View/ScriptNodeControl.xaml.cs @@ -21,7 +21,7 @@ namespace Serein.Workbench.Node.View /// /// ScriptNodeControl.xaml 的交互逻辑 /// - public partial class ScriptNodeControl : NodeControlBase + public partial class ScriptNodeControl : NodeControlBase , INodeJunction { private ScriptNodeControlViewModel viewModel => (ScriptNodeControlViewModel)ViewModel; private DispatcherTimer _debounceTimer; // 用于延迟更新 @@ -45,6 +45,73 @@ namespace Serein.Workbench.Node.View } + + + /// + /// 入参控制点(可能有,可能没) + /// + JunctionControlBase INodeJunction.ExecuteJunction => this.ExecuteJunctionControl; + + /// + /// 下一个调用方法控制点(可能有,可能没) + /// + JunctionControlBase INodeJunction.NextStepJunction => this.NextStepJunctionControl; + + /// + /// 返回值控制点(可能有,可能没) + /// + JunctionControlBase INodeJunction.ReturnDataJunction => this.ResultJunctionControl; + + /// + /// 方法入参控制点(可能有,可能没) + /// + JunctionControlBase[] INodeJunction.ArgDataJunction + { + get + { + // 获取 MethodDetailsControl 实例 + var methodDetailsControl = this.MethodDetailsControl; + var itemsControl = FindVisualChild(methodDetailsControl); // 查找 ItemsControl + if (itemsControl != null) + { + var argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length]; + var controls = new List(); + + for (int i = 0; i < itemsControl.Items.Count; i++) + { + var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement; + if (container != null) + { + var argControl = FindVisualChild(container); + if (argControl != null) + { + controls.Add(argControl); // 收集 ArgJunctionControl 实例 + } + } + } + return argDataJunction = controls.ToArray(); + } + else + { + return []; + } + } + + + } + + + + + + + + + + + + + #if false // 每次输入时重置定时器 private void RichTextBox_TextChanged(object sender, TextChangedEventArgs e) diff --git a/Workbench/Node/ViewModel/ScriptNodeControlViewModel.cs b/Workbench/Node/ViewModel/ScriptNodeControlViewModel.cs index 90b8d7f..6b03113 100644 --- a/Workbench/Node/ViewModel/ScriptNodeControlViewModel.cs +++ b/Workbench/Node/ViewModel/ScriptNodeControlViewModel.cs @@ -41,7 +41,7 @@ namespace Serein.Workbench.Node.ViewModel CommandLoadScript = new RelayCommand( o => { - NodeModel.LoadScript(); + NodeModel.ReloadScript(); }); }