From 70f674ca1b70a8a1cbefe21dd7091383fe74882d Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Wed, 9 Jul 2025 21:49:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=86=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E7=94=9F=E6=88=90AST=E6=97=B6=E7=9A=84=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=EF=BC=8C=E5=A2=9E=E5=8A=A0=E4=BA=86=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E8=BF=90=E8=A1=8C=E6=97=B6=E9=94=99=E8=AF=AF=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library/Api/IFlowEdit.cs | 12 +- Library/Api/IFlowEnvironment.cs | 6 +- Library/FlowNode/MethodDetails.cs | 10 + Library/SereinBaseFunction.cs | 13 +- Library/Utils/DynamicObjectHelper.cs | 14 + Library/Utils/SereinEnv.cs | 31 +- NodeFlow/Env/FlowEdit.cs | 22 +- NodeFlow/Model/Node/NodeModelBaseData.cs | 30 +- NodeFlow/Model/Node/NodeModelBaseFunc.cs | 28 +- NodeFlow/Model/Node/SingleFlowCallNode.cs | 4 +- NodeFlow/Model/Node/SingleScriptNode.cs | 5 +- .../ChangeNodeConnectionOperation.cs | 70 ++++- NodeFlow/Model/Operation/OperationBase.cs | 18 +- .../Model/Operation/RemoveNodeOperation.cs | 98 ++++--- NodeFlow/Services/FlowCoreGenerateService.cs | 4 +- NodeFlow/Tool/NodeMethodDetailsHelper.cs | 2 + .../ParameterDetailsPropertyGenerator.cs | 2 +- Serein.Script/BinaryOperationEvaluator.cs | 163 +++++++++++ Serein.Script/Node/MemberAccessNode.cs | 7 + Serein.Script/Node/MemberAssignmentNode.cs | 9 + Serein.Script/Node/MemberFunctionCallNode.cs | 11 + Serein.Script/Node/NumberIntNode.cs | 90 ++++++ Serein.Script/Node/NumberNode.cs | 21 -- Serein.Script/SereinScriptInterpreter.cs | 102 ++++--- Serein.Script/SereinScriptLexer.cs | 136 ++++++++- Serein.Script/SereinScriptParser.cs | 265 ++++++++++++++---- Serein.Script/SereinScriptTypeAnalysis.cs | 173 ++++++++++++ Serein.Script/Symbol/SymbolInfo.cs | 37 +++ Workbench/Node/NodeControlBase.cs | 4 +- Workbench/Node/View/ConnectionControl.cs | 33 ++- Workbench/Services/FlowNodeService.cs | 11 +- Workbench/Views/FlowCanvasView.xaml.cs | 53 +++- 32 files changed, 1218 insertions(+), 266 deletions(-) create mode 100644 Serein.Script/BinaryOperationEvaluator.cs create mode 100644 Serein.Script/Node/NumberIntNode.cs delete mode 100644 Serein.Script/Node/NumberNode.cs create mode 100644 Serein.Script/SereinScriptTypeAnalysis.cs create mode 100644 Serein.Script/Symbol/SymbolInfo.cs diff --git a/Library/Api/IFlowEdit.cs b/Library/Api/IFlowEdit.cs index e848a65..b8aa2d1 100644 --- a/Library/Api/IFlowEdit.cs +++ b/Library/Api/IFlowEdit.cs @@ -69,12 +69,12 @@ namespace Serein.Library.Api /// 决定了方法参数来源 /// 设置第几个参数 void ConnectArgSourceNode(string canvasGuid, - string fromNodeGuid, - string toNodeGuid, - JunctionType fromNodeJunctionType, - JunctionType toNodeJunctionType, - ConnectionArgSourceType argSourceType, - int argIndex); + string fromNodeGuid, + string toNodeGuid, + JunctionType fromNodeJunctionType, + JunctionType toNodeJunctionType, + ConnectionArgSourceType argSourceType, + int argIndex); /// /// 移除两个节点之间的方法调用关系 diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index 23afb31..222e7c4 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -288,11 +288,11 @@ namespace Serein.Library.Api /// /// 连接类型 /// - public ConnectionInvokeType ConnectionInvokeType { get;} + public ConnectionInvokeType ConnectionInvokeType { get; } = ConnectionInvokeType.None; /// /// 表示此次需要在两个节点之间创建连接关系,或是移除连接关系 /// - public ConnectChangeType ChangeType { get;} + public ConnectChangeType ChangeType { get;} /// /// 指示需要创建什么类型的连接线 /// @@ -300,7 +300,7 @@ namespace Serein.Library.Api /// /// 节点对应的方法入参所需参数来源 /// - public ConnectionArgSourceType ConnectionArgSourceType { get;} + public ConnectionArgSourceType ConnectionArgSourceType { get;} /// /// 第几个参数 /// diff --git a/Library/FlowNode/MethodDetails.cs b/Library/FlowNode/MethodDetails.cs index 7269b59..d407fdd 100644 --- a/Library/FlowNode/MethodDetails.cs +++ b/Library/FlowNode/MethodDetails.cs @@ -105,6 +105,11 @@ namespace Serein.Library /// public bool HasParamsArg => _paramsArgIndex >= 0; + /// + /// 是否为静态方法 + /// + public bool IsStatic { get; set; } + /// /// 新增可变参数 /// @@ -119,10 +124,13 @@ namespace Serein.Library return false; } + var newPd = ParameterDetailss[index].CloneOfModel(this.NodeModel); // 复制出属于本身节点的参数描述 newPd.Index = ParameterDetailss.Length; // 更新索引 newPd.IsParams = true; ParameterDetailss = ArrayHelper.AddToArray(ParameterDetailss, newPd); // 新增 + + return true; } @@ -140,6 +148,7 @@ namespace Serein.Library parameterDetails.Index = ParameterDetailss.Length; // 更新索引 parameterDetails.IsParams = true; ParameterDetailss = ArrayHelper.AddToArray(ParameterDetailss, parameterDetails); // 新增 + return true; } @@ -299,6 +308,7 @@ namespace Serein.Library ParamsArgIndex = this.ParamsArgIndex, // 拷贝 ParameterDetailss = this.ParameterDetailss?.Select(p => p?.CloneOfModel(nodeModel)).ToArray(), // 拷贝属于节点方法的新入参描述 IsAsync = this.IsAsync, // 拷贝 + IsStatic = this.IsStatic, // 拷贝 }; return md; diff --git a/Library/SereinBaseFunction.cs b/Library/SereinBaseFunction.cs index 24e026a..c26945c 100644 --- a/Library/SereinBaseFunction.cs +++ b/Library/SereinBaseFunction.cs @@ -13,18 +13,17 @@ namespace Serein.Library /// /// 基础功能 /// - [DynamicFlow(Name ="[基础功能]")] public static class SereinBaseFunction { - + [NodeAction(NodeType.Action, "对象透传")] + public static object SereinTransmissionObject(object value) => value; [NodeAction(NodeType.Action, "键值对组装")] - public static Dictionary SereinKvDataCollectionNode(string argName, + public static Dictionary SereinKvDataCollection(string argName, params object[] value) { - var names = argName.Split(';'); var count = Math.Min(value.Length, names.Length); var dict = new Dictionary(); @@ -36,13 +35,13 @@ namespace Serein.Library } [NodeAction(NodeType.Action, "数组组装")] - public static object[] SereinListDataCollectionNode(params object[] value) + public static object[] SereinListDataCollection(params object[] value) { return value; } [NodeAction(NodeType.Action, "输出")] - public static object[] SereinConsoleNode(params object[] value) + public static object[] SereinConsole(params object[] value) { foreach (var item in value) { @@ -104,7 +103,7 @@ namespace Serein.Library } - [NodeAction(NodeType.Action, "设置/更新全局数据")] + [NodeAction(NodeType.Action, "设置或更新全局数据")] public static object SereinAddOrUpdateFlowGlobalData(string name, object data) { SereinEnv.AddOrUpdateFlowGlobalData(name, data); diff --git a/Library/Utils/DynamicObjectHelper.cs b/Library/Utils/DynamicObjectHelper.cs index 2863710..b52dbdc 100644 --- a/Library/Utils/DynamicObjectHelper.cs +++ b/Library/Utils/DynamicObjectHelper.cs @@ -16,6 +16,20 @@ namespace Serein.Library.Utils // 类型缓存,键为类型的唯一名称(可以根据实际需求调整生成方式) static Dictionary typeCache = new Dictionary(); + /// + /// 获取运行时创建过的类型 + /// + /// + /// + public static Type GetCacheType(string className) + { + if(typeCache.TryGetValue(className, out var type)) + { + return type; + } + return null; + } + public static object Resolve(IDictionary properties, string typeName) { var obj = CreateObjectWithProperties(properties, typeName); diff --git a/Library/Utils/SereinEnv.cs b/Library/Utils/SereinEnv.cs index bcecb5d..7b7f2ee 100644 --- a/Library/Utils/SereinEnv.cs +++ b/Library/Utils/SereinEnv.cs @@ -101,8 +101,6 @@ namespace Serein.Library #endregion - - /// /// 设置运行流程 /// @@ -138,8 +136,35 @@ namespace Serein.Library } - + /// + /// 尝试在UI线程上触发事件 + /// + /// + /// + public async static Task TriggerEvent(Action action) + { + if (environment is null) + { + action?.Invoke(); + } + else + { + var uco = environment.UIContextOperation; + if (uco is null) + { + action?.Invoke(); + } + else + { + await uco.InvokeAsync(() => + { + action?.Invoke(); + }); + } + } + + } } diff --git a/NodeFlow/Env/FlowEdit.cs b/NodeFlow/Env/FlowEdit.cs index a090bfe..f245b14 100644 --- a/NodeFlow/Env/FlowEdit.cs +++ b/NodeFlow/Env/FlowEdit.cs @@ -272,6 +272,7 @@ namespace Serein.NodeFlow.Env ToNodeGuid = toNodeGuid, ConnectionInvokeType = connectionType, ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Remove, + JunctionOfConnectionType = JunctionOfConnectionType.Invoke, }; flowOperationService.Execute(operation); } @@ -284,7 +285,8 @@ namespace Serein.NodeFlow.Env FromNodeGuid = fromNodeGuid, ToNodeGuid = toNodeGuid, ArgIndex = argIndex, - ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Remove + ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Remove, + JunctionOfConnectionType = JunctionOfConnectionType.Arg, }; flowOperationService.Execute(operation); } @@ -354,7 +356,8 @@ namespace Serein.NodeFlow.Env }*/ canvasModel.StartNode = newStartNodeModel; //newStartNode.IsStart = true; - _ = TriggerEvent(() => + + _ = SereinEnv.TriggerEvent(() => flowEnvironmentEvent.OnStartNodeChanged( new StartNodeChangeEventArgs(canvasGuid, oldNodeGuid, newStartNodeModel.Guid) )); @@ -651,20 +654,7 @@ namespace Serein.NodeFlow.Env - private async Task TriggerEvent(Action action) - { - if(UIContextOperation is null) - { - action?.Invoke(); - } - else - { - await UIContextOperation.InvokeAsync(() => - { - action?.Invoke(); - }); - } - } + private async Task TriggerEvent(Action action) => await SereinEnv.TriggerEvent(action); } diff --git a/NodeFlow/Model/Node/NodeModelBaseData.cs b/NodeFlow/Model/Node/NodeModelBaseData.cs index e6122be..e4ab624 100644 --- a/NodeFlow/Model/Node/NodeModelBaseData.cs +++ b/NodeFlow/Model/Node/NodeModelBaseData.cs @@ -146,24 +146,28 @@ namespace Serein.NodeFlow.Model partial void OnIsPublicChanged(bool oldValue, bool newValue) { var list = CanvasDetails.PublicNodes.ToList(); - if (newValue) + _ = SereinEnv.TriggerEvent(() => { - // 公开节点 - if (!CanvasDetails.PublicNodes.Contains(this)) + if (newValue) { - list.Add(this); - CanvasDetails.PublicNodes= list; + // 公开节点 + if (!CanvasDetails.PublicNodes.Contains(this)) + { + list.Add(this); + CanvasDetails.PublicNodes = list; + } } - } - else - { - // 取消公开 - if (CanvasDetails.PublicNodes.Contains(this)) + else { - list.Remove(this); - CanvasDetails.PublicNodes = list; + // 取消公开 + if (CanvasDetails.PublicNodes.Contains(this)) + { + list.Remove(this); + CanvasDetails.PublicNodes = list; + } } - } + }); + } diff --git a/NodeFlow/Model/Node/NodeModelBaseFunc.cs b/NodeFlow/Model/Node/NodeModelBaseFunc.cs index b38d5be..7916b40 100644 --- a/NodeFlow/Model/Node/NodeModelBaseFunc.cs +++ b/NodeFlow/Model/Node/NodeModelBaseFunc.cs @@ -132,19 +132,31 @@ namespace Serein.NodeFlow.Model throw new Exception($"节点{this.Guid}不存在对应委托"); } - var instance = Env.IOC.Get(md.ActingInstanceType); - if (instance is null) + if (md.IsStatic) { - Env.IOC.Register(md.ActingInstanceType).Build(); - instance = Env.IOC.Get(md.ActingInstanceType); + object[] args = await this.GetParametersAsync(context, token); + var result = await dd.InvokeAsync(null, args); + var flowReslt = new FlowResult(this.Guid, context, result); + return flowReslt; } - object[] args = await this.GetParametersAsync(context, token); - var result = await dd.InvokeAsync(instance, args); - var flowReslt = new FlowResult(this.Guid, context, result); - return flowReslt; + else + { + var instance = Env.IOC.Get(md.ActingInstanceType); + if (instance is null) + { + Env.IOC.Register(md.ActingInstanceType).Build(); + instance = Env.IOC.Get(md.ActingInstanceType); + } + object[] args = await this.GetParametersAsync(context, token); + var result = await dd.InvokeAsync(instance, args); + var flowReslt = new FlowResult(this.Guid, context, result); + return flowReslt; + } + } + } diff --git a/NodeFlow/Model/Node/SingleFlowCallNode.cs b/NodeFlow/Model/Node/SingleFlowCallNode.cs index 61036b7..9440e00 100644 --- a/NodeFlow/Model/Node/SingleFlowCallNode.cs +++ b/NodeFlow/Model/Node/SingleFlowCallNode.cs @@ -317,7 +317,8 @@ namespace Serein.NodeFlow.Model _ => base.ExecutingAsync(context, token) }); - + // 对于目标节点的后续节点,如果入参参数来源指定为它(目标节点)时,就需要从上下文中根据它的Guid获取流程数据 + context.AddOrUpdateFlowData(TargetNode.Guid, flowData); if (IsShareParam) { // 设置运行时上一节点 @@ -325,7 +326,6 @@ namespace Serein.NodeFlow.Model // 此处代码与SereinFlow.Library.FlowNode.ParameterDetails // ToMethodArgData()方法中判断流程接口节点分支逻辑耦合 // 不要轻易修改 - context.AddOrUpdateFlowData(TargetNode.Guid, flowData); foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes) { if (this.SuccessorNodes[ctType] == null) continue; diff --git a/NodeFlow/Model/Node/SingleScriptNode.cs b/NodeFlow/Model/Node/SingleScriptNode.cs index 1ce124c..e863c30 100644 --- a/NodeFlow/Model/Node/SingleScriptNode.cs +++ b/NodeFlow/Model/Node/SingleScriptNode.cs @@ -158,13 +158,14 @@ namespace Serein.NodeFlow.Model varNames.Add(pd.Name); } - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); foreach (var pd in MethodDetails.ParameterDetailss) { sb.AppendLine($"let {pd.Name};"); // 提前声明这些变量 } sb.Append(Script); - var p = new SereinScriptParser(sb.ToString()); + var script = sb.ToString(); + var p = new SereinScriptParser(script); //var p = new SereinScriptParser(Script); mainNode = p.Parse(); // 开始解析 diff --git a/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs b/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs index cf7a0e8..d4b2627 100644 --- a/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs +++ b/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs @@ -5,6 +5,8 @@ using Serein.NodeFlow.Model; using System; using System.Collections.Generic; using System.Linq; +using System.Reactive; +using System.Security.AccessControl; using System.Text; using System.Threading.Tasks; using static Serein.Library.Api.NodeConnectChangeEventArgs; @@ -163,11 +165,51 @@ namespace Serein.NodeFlow.Model.Operation return false; } - var isOverwriting = false; ConnectionInvokeType overwritingCt = ConnectionInvokeType.None; var isPass = false; + #region 类型检查 + bool checkTypeState = true; + List toPds = new List(); + if (ToNode.MethodDetails.ParameterDetailss.Length > 0) + { + var fromNoeReturnType = fromNode.MethodDetails.ReturnType; + if (fromNoeReturnType != null + && fromNoeReturnType != typeof(object) + && fromNoeReturnType != typeof(void) + && fromNoeReturnType != typeof(Unit)) + { + var toNodePds = toNode.MethodDetails.ParameterDetailss; + foreach (ParameterDetails toNodePd in toNodePds) + { + if (string.IsNullOrWhiteSpace(toNodePd.ArgDataSourceNodeGuid) // 入参没有设置数据来源节点 + && toNodePd.DataType.IsAssignableFrom(fromNoeReturnType)) // 返回值与目标入参相同(或可转换为目标入参) + { + + toPds.Add(toNodePd); + } + } + if (toPds.Count == 0) + { + var any = toNodePds.Any(pd => pd.ArgDataSourceNodeGuid == fromNode.Guid); // 判断目标节点是否已有该节点的连接 + checkTypeState = any; + } + else + { + checkTypeState = true; // 类型检查初步通过 + } + } + } + if (!checkTypeState) // 类型检查不通过 + { + SereinEnv.WriteLine(InfoType.ERROR, "创建失败,目标节点没有合适的入参接收返回值"); + return false; + } + #endregion + + + #region 检查是否存在对应的连接 foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes) { @@ -237,7 +279,27 @@ namespace Serein.NodeFlow.Model.Operation NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 )); }); - + + + /* foreach (var toPd in toPds) + { + string canvasGuid = CanvasGuid; + string fromNodeGuid = fromNode.Guid; + string toNodeGuid = toNode.Guid; + JunctionType fromNodeJunctionType = JunctionType.ReturnData; + JunctionType toNodeJunctionType = JunctionType.ArgData; + ConnectionArgSourceType argSourceType = ConnectionArgSourceType.GetOtherNodeData; + int argIndex = toPd.Index; + // 调用创建连线接口 + flowEnvironment.FlowEdit.ConnectArgSourceNode(canvasGuid, + fromNodeGuid, + toNodeGuid, + fromNodeJunctionType, + toNodeJunctionType, + argSourceType, + argIndex); + }*/ + // Invoke // GetResult return true; @@ -407,7 +469,9 @@ namespace Serein.NodeFlow.Model.Operation /// private async Task RemoveArgConnection() { - ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = null; + var type = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType; + FromNode.NeedResultNodes[type].Remove(ToNode); + ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = string.Empty; ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值 diff --git a/NodeFlow/Model/Operation/OperationBase.cs b/NodeFlow/Model/Operation/OperationBase.cs index d1ea2fe..1cc5e09 100644 --- a/NodeFlow/Model/Operation/OperationBase.cs +++ b/NodeFlow/Model/Operation/OperationBase.cs @@ -102,23 +102,7 @@ namespace Serein.NodeFlow.Model.Operation public abstract void ToInfo(); - protected async Task TriggerEvent(Action action) - { - /* if (OperatingSystem.IsWindows()) - { - }*/ - if (uiContextOperation is null) - { - action?.Invoke(); - } - else - { - await uiContextOperation.InvokeAsync(() => - { - action?.Invoke(); - }); - } - } + protected async Task TriggerEvent(Action action) => await SereinEnv.TriggerEvent(action); } diff --git a/NodeFlow/Model/Operation/RemoveNodeOperation.cs b/NodeFlow/Model/Operation/RemoveNodeOperation.cs index de14c46..59572cc 100644 --- a/NodeFlow/Model/Operation/RemoveNodeOperation.cs +++ b/NodeFlow/Model/Operation/RemoveNodeOperation.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Reflection.Metadata; using System.Text; using System.Threading.Tasks; +using System.Xml.Linq; namespace Serein.NodeFlow.Model.Operation { @@ -58,13 +59,17 @@ namespace Serein.NodeFlow.Model.Operation // 还需要记录移除的事件参数,用以撤销恢复 #region 移除方法调用关系 - foreach (var item in flowNode.PreviousNodes) + + // 检查该节点的前继节点,然后从这些前继节点中移除与该节点的连接关系 + var previousNodes = flowNode.PreviousNodes.Where(kvp => kvp.Value.Count > 0).ToDictionary(); + foreach (var item in previousNodes) { var connectionType = item.Key; // 连接类型 - var previousNodes = item.Value; // 对应类型的父节点集合 - foreach (IFlowNode previousNode in previousNodes) + var nodes = item.Value.ToArray(); // 对应类型的父节点集合 + foreach (IFlowNode previousNode in nodes) { + flowNode.PreviousNodes[connectionType].Remove(previousNode); previousNode.SuccessorNodes[connectionType].Remove(flowNode); var e = new NodeConnectChangeEventArgs( CanvasGuid, // 画布 @@ -74,13 +79,11 @@ namespace Serein.NodeFlow.Model.Operation connectionType, // 对应的连接关系 NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线 EventArgs.Add(e); // 缓存事件参数 - await TriggerEvent(() => - { - flowEnvironmentEvent.OnNodeConnectChanged(e); - }); } } + // 检查该节点的后续节点,然后从这些后续节点中移除与该节点的连接关系 + var successorNodes = flowNode.SuccessorNodes.Where(kvp => kvp.Value.Count > 0).ToDictionary(); if (flowNode.ControlType == NodeControlType.FlowCall) { // 根据流程接口节点目前的设计,暂未支持能连接下一个节点 @@ -88,14 +91,15 @@ namespace Serein.NodeFlow.Model.Operation else { // 遍历所有后继节点,从那些后继节点中的前置节点集合中移除该节点 - foreach (var item in flowNode.SuccessorNodes) + foreach (var item in successorNodes) { var connectionType = item.Key; // 方法调用连接类型 - var successorNodes = item.Value; // 对应类型的父节点集合 - foreach (IFlowNode successorNode in successorNodes) + var nodes = item.Value.ToArray(); // 对应类型的父节点集合 + foreach (IFlowNode successorNode in nodes) { - successorNode.SuccessorNodes[connectionType].Remove(flowNode); + successorNode.PreviousNodes[connectionType].Remove(flowNode); + flowNode.SuccessorNodes[connectionType].Remove(successorNode); var e = new NodeConnectChangeEventArgs( CanvasGuid, // 画布 flowNode.Guid, // 被移除的节点Guid @@ -104,10 +108,7 @@ namespace Serein.NodeFlow.Model.Operation connectionType, // 对应的连接关系 NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线 EventArgs.Add(e); // 缓存事件参数 - await TriggerEvent(() => - { - flowEnvironmentEvent.OnNodeConnectChanged(e); - }); + } } } @@ -120,7 +121,7 @@ namespace Serein.NodeFlow.Model.Operation foreach (var item in flowNode.NeedResultNodes) { var connectionType = item.Key; // 参数来源连接类型 - var argNodes = item.Value; // 对应类型的入参需求节点集合 + var argNodes = item.Value.ToArray(); // 对应类型的入参需求节点集合 foreach (var argNode in argNodes) { var md = argNode.MethodDetails; @@ -140,10 +141,6 @@ namespace Serein.NodeFlow.Model.Operation connectionType, // 对应的连接关系 NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线 EventArgs.Add(e); // 缓存事件参数 - await TriggerEvent(() => - { - flowEnvironmentEvent.OnNodeConnectChanged(e); - }); } } } @@ -152,7 +149,7 @@ namespace Serein.NodeFlow.Model.Operation if (flowNode.MethodDetails?.ParameterDetailss != null) { - var pds = flowNode.MethodDetails.ParameterDetailss; + var pds = flowNode.MethodDetails.ParameterDetailss.ToArray(); foreach (var pd in pds) { if (string.IsNullOrWhiteSpace(pd.ArgDataSourceNodeGuid)) continue; @@ -168,10 +165,6 @@ namespace Serein.NodeFlow.Model.Operation pd.ArgDataSourceType, // 对应的连接关系 NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线 EventArgs.Add(e); // 缓存事件参数 - await TriggerEvent(() => - { - flowEnvironmentEvent.OnNodeConnectChanged(e); - }); } } } @@ -179,28 +172,55 @@ namespace Serein.NodeFlow.Model.Operation #endregion flowModelService.RemoveNodeModel(flowNode); // 从记录中移除 - //flowNode.Remove(); // 调用节点的移除方法 + //flowNode.Remove(); // 调用节点的移除方法 - if(flowEnvironment.UIContextOperation is null) + + // 存在UI上下文操作,当前运行环境极有可能运行在有UI线程的平台上 + // 为了避免直接修改 ObservableCollection 集合导致异常产生,故而使用UI线程上下文操作运行 + NodeConnectChangeEventArgs[] es = EventArgs.ToArray(); + await TriggerEvent(() => + { + + /*flowCanvasDetails.Nodes.Remove(flowNode); + flowCanvasDetails.OnPropertyChanged(nameof(FlowCanvasDetails.Nodes)); + if (flowNode.IsPublic) + { + flowCanvasDetails.PublicNodes.Remove(flowNode); + flowCanvasDetails.OnPropertyChanged(nameof(FlowCanvasDetails.PublicNodes)); + }*/ + + // 手动赋值刷新UI显示 + var lsit = flowCanvasDetails.Nodes.ToList(); + lsit.Remove(flowNode); + flowCanvasDetails.Nodes = lsit; + if (flowNode.IsPublic) + { + var publicNodes = flowCanvasDetails.PublicNodes.ToList(); + publicNodes.Remove(flowNode); + flowCanvasDetails.PublicNodes = publicNodes; + } + + foreach (var e in es) + { + flowEnvironmentEvent.OnNodeConnectChanged(e); // 触发事件 + } + flowEnvironmentEvent.OnNodeRemoved(new NodeRemoveEventArgs(CanvasGuid, NodeGuid)); + }); + + /*if (flowEnvironment.UIContextOperation is null) { flowCanvasDetails?.Nodes.Remove(flowNode); } else { - // 存在UI上下文操作,当前运行环境极有可能运行在有UI线程的平台上 - // 为了避免直接修改 ObservableCollection 集合导致异常产生,故而使用UI线程上下文操作运行 - await TriggerEvent(() => - { - var lsit = flowCanvasDetails.Nodes.ToList(); - lsit.Remove(flowNode); - flowCanvasDetails.Nodes = lsit; - }); - } + + + }*/ - await TriggerEvent(() => + /* await TriggerEvent(() => { - flowEnvironmentEvent.OnNodeRemoved(new NodeRemoveEventArgs(CanvasGuid, NodeGuid)); - }); + + });*/ return true; } diff --git a/NodeFlow/Services/FlowCoreGenerateService.cs b/NodeFlow/Services/FlowCoreGenerateService.cs index 339af50..9c55642 100644 --- a/NodeFlow/Services/FlowCoreGenerateService.cs +++ b/NodeFlow/Services/FlowCoreGenerateService.cs @@ -255,7 +255,7 @@ namespace Serein.NodeFlow.Services } else if (pd.DataType.IsAssignableFrom(otherNodeReturnType)) { - sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {flowContext}.{nameof(IDynamicContext.GetFlowData)}(\"{pd.ArgDataSourceNodeGuid}\").Value; // 获取指定节点的数据"); + sb_invoke_login.AppendCode(3, $"{valueType} value{index} = ({valueType}){flowContext}.{nameof(IDynamicContext.GetFlowData)}(\"{pd.ArgDataSourceNodeGuid}\").Value; // 获取指定节点的数据"); } else { @@ -1058,7 +1058,7 @@ namespace Serein.NodeFlow.Services sb.AppendCode(3, $"{{"); sb.AppendCode(4, $"throw new ArgumentNullException($\"类型转换失败,{{(flowResult.Value is null ? \"返回数据为 null\" : $\"返回数据与需求类型不匹配,当前返回类型为[{{flowResult.Value.GetType().FullName}}。\")}}\");"); sb.AppendCode(3, $"}}"); - sb.AppendCode(3, $"return {flowResult};"); + //sb.AppendCode(3, $"return {flowResult};"); sb.AppendCode(2, $"}}"); return sb.ToString(); // throw new ArgumentNullException($"类型转换失败,{(flowResult.Value is null ? "返回数据为 null" : $"返回数据与需求类型不匹配,当前返回类型为[{flowResult.Value.GetType().FullName}。")}"); diff --git a/NodeFlow/Tool/NodeMethodDetailsHelper.cs b/NodeFlow/Tool/NodeMethodDetailsHelper.cs index 80e0cb3..a6e884b 100644 --- a/NodeFlow/Tool/NodeMethodDetailsHelper.cs +++ b/NodeFlow/Tool/NodeMethodDetailsHelper.cs @@ -65,6 +65,7 @@ public static class NodeMethodDetailsHelper Type? returnType; bool isAsync = IsGenericTask(methodInfo.ReturnType, out var taskResult); + bool isStatic = methodInfo.IsStatic; if (attribute.MethodDynamicType == Library.NodeType.UI) @@ -162,6 +163,7 @@ public static class NodeMethodDetailsHelper // 如果存在可变参数,取最后一个元素的下标,否则为-1; ParamsArgIndex = hasParamsArg ? explicitDataOfParameters.Length - 1 : -1, IsAsync = isAsync, + IsStatic = isStatic, }; //var emitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out var methodDelegate);// 返回值 diff --git a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs b/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs index 433c661..c28a902 100644 --- a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs +++ b/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs @@ -251,7 +251,7 @@ namespace Serein.Library.NodeGenerator sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); "); sb.AppendLine(" } "); - sb.AppendLine(" protected void OnPropertyChanged(string propertyName) => "); + sb.AppendLine(" public void OnPropertyChanged(string propertyName) => "); sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); "); sb.AppendLine(" "); sb.AppendLine(" "); diff --git a/Serein.Script/BinaryOperationEvaluator.cs b/Serein.Script/BinaryOperationEvaluator.cs new file mode 100644 index 0000000..47ae019 --- /dev/null +++ b/Serein.Script/BinaryOperationEvaluator.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Script +{ + public static class BinaryOperationEvaluator + { + public static object EvaluateValue(object left, string op, object right) + { + if (op == null) throw new ArgumentNullException(nameof(op)); + + // 特判字符串拼接 + if (op == "+" && (left is string || right is string)) + { + return (left?.ToString() ?? "") + (right?.ToString() ?? ""); + } + + // 支持 null 运算(可按需扩展) + if (left == null || right == null) + { + throw new InvalidOperationException($"无法对 null 执行操作:{op}"); + } + + // 尝试统一类型(浮点优先) + var leftType = left.GetType(); + var rightType = right.GetType(); + + Type resultType = GetWiderType(leftType, rightType); + dynamic leftValue = Convert.ChangeType(left, resultType); + dynamic rightValue = Convert.ChangeType(right, resultType); + + return op switch + { + "+" => leftValue + rightValue, + "-" => leftValue - rightValue, + "*" => leftValue * rightValue, + "/" => rightValue == 0 ? throw new DivideByZeroException() : leftValue / rightValue, + + ">" => leftValue > rightValue, + "<" => leftValue < rightValue, + ">=" => leftValue >= rightValue, + "<=" => leftValue <= rightValue, + "==" => Equals(leftValue, rightValue), + "!=" => !Equals(leftValue, rightValue), + + _ => throw new NotImplementedException($"未实现的操作符: {op}") + }; + } + + + + + /// + /// 推导两个类型的“最通用类型”(类型提升) + /// + private static Type GetWiderType(Type t1, Type t2) + { + if (t1 == typeof(string) || t2 == typeof(string)) return typeof(string); + if (t1 == typeof(decimal) || t2 == typeof(decimal)) return typeof(decimal); + if (t1 == typeof(double) || t2 == typeof(double)) return typeof(double); + if (t1 == typeof(float) || t2 == typeof(float)) return typeof(float); + if (t1 == typeof(ulong) || t2 == typeof(ulong)) return typeof(ulong); + if (t1 == typeof(long) || t2 == typeof(long)) return typeof(long); + if (t1 == typeof(uint) || t2 == typeof(uint)) return typeof(uint); + if (t1 == typeof(int) || t2 == typeof(int)) return typeof(int); + + // fallback + return typeof(object); + } + + + + + + + + + + + + + + + + public static Type EvaluateType(Type leftType, string op, Type rightType) + { + if (leftType == null || rightType == null) + throw new ArgumentNullException("操作数类型不能为 null"); + + // 字符串拼接 + if (op == "+" && (leftType == typeof(string) || rightType == typeof(string))) + return typeof(string); + + // 比较操作总是返回 bool + if (op is ">" or "<" or ">=" or "<=" or "==" or "!=") + return typeof(bool); + + // 数值类型推导 + if (IsNumeric(leftType) && IsNumeric(rightType)) + { + return PromoteNumericType(leftType, rightType); + } + + // 逻辑操作 + if (op is "&&" or "||") + { + if (leftType == typeof(bool) && rightType == typeof(bool)) + return typeof(bool); + throw new InvalidOperationException($"逻辑操作 '{op}' 仅适用于 bool 类型"); + } + + throw new NotImplementedException($"不支持操作符 '{op}' 对 {leftType.Name} 和 {rightType.Name} 进行类型推导"); + } + + private static bool IsNumeric(Type type) + { + return type == typeof(byte) || type == typeof(sbyte) || + type == typeof(short) || type == typeof(ushort) || + type == typeof(int) || type == typeof(uint) || + type == typeof(long) || type == typeof(ulong) || + type == typeof(float) || type == typeof(double) || + type == typeof(decimal); + } + + // 提升到更高精度类型 + private static Type[] types = new[] + { + typeof(decimal), + typeof(double), + typeof(float), + typeof(ulong), + typeof(long), + typeof(uint), + typeof(int), + typeof(ushort), + typeof(short), + typeof(byte), + typeof(sbyte) + }; + + private static Type PromoteNumericType(Type left, Type right) + { + var ranks = types; + foreach (var type in ranks) + { + if (type == left || type == right) + return type; + } + + return typeof(object); // fallback + } + } + + + + + +} + + diff --git a/Serein.Script/Node/MemberAccessNode.cs b/Serein.Script/Node/MemberAccessNode.cs index d889a3d..04cc5a3 100644 --- a/Serein.Script/Node/MemberAccessNode.cs +++ b/Serein.Script/Node/MemberAccessNode.cs @@ -11,7 +11,14 @@ namespace Serein.Script.Node /// public class MemberAccessNode : ASTNode { + /// + /// 对象token + /// public ASTNode Object { get; } + + /// + /// 成员名称 + /// public string MemberName { get; } public MemberAccessNode(ASTNode obj, string memberName) diff --git a/Serein.Script/Node/MemberAssignmentNode.cs b/Serein.Script/Node/MemberAssignmentNode.cs index ffa643a..7e429a0 100644 --- a/Serein.Script/Node/MemberAssignmentNode.cs +++ b/Serein.Script/Node/MemberAssignmentNode.cs @@ -11,8 +11,17 @@ namespace Serein.Script.Node /// public class MemberAssignmentNode : ASTNode { + /// + /// 作用的对象 + /// public ASTNode Object { get; } + /// + /// 被赋值的成员(属性/字段)名称 + /// public string MemberName { get; } + /// + /// 值来源 + /// public ASTNode Value { get; } public MemberAssignmentNode(ASTNode obj, string memberName, ASTNode value) diff --git a/Serein.Script/Node/MemberFunctionCallNode.cs b/Serein.Script/Node/MemberFunctionCallNode.cs index 4d030b1..0312226 100644 --- a/Serein.Script/Node/MemberFunctionCallNode.cs +++ b/Serein.Script/Node/MemberFunctionCallNode.cs @@ -11,8 +11,19 @@ namespace Serein.Script.Node /// public class MemberFunctionCallNode : ASTNode { + /// + /// 需要被调用的对象 + /// public ASTNode Object { get; } + + /// + /// 被调用的方法名称 + /// public string FunctionName { get; } + + /// + /// 方法参数 + /// public List Arguments { get; } public MemberFunctionCallNode(ASTNode @object, string functionName, List arguments) diff --git a/Serein.Script/Node/NumberIntNode.cs b/Serein.Script/Node/NumberIntNode.cs new file mode 100644 index 0000000..bdd8942 --- /dev/null +++ b/Serein.Script/Node/NumberIntNode.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Script.Node +{ + /// + /// 数值型节点 + /// + public abstract class NumberNode : ASTNode where T : struct, IComparable + { + public T Value { get; } + public NumberNode(T value) => Value = value; + } + + + /// + /// int 整数型字面量 + /// + public class NumberIntNode(int vlaue) : NumberNode(vlaue) + { + } + + /// + /// int 整数型字面量 + /// + public class NumberLongNode(long vlaue) : NumberNode(vlaue) + { + } + + /// + /// int 整数型字面量 + /// + public class NumberFloatNode(float vlaue) : NumberNode(vlaue) + { + } + + /// + /// int 整数型字面量 + /// + public class NumberDoubleNode(double vlaue) : NumberNode(vlaue) + { + } + + + + + /*/// + /// int 整数型字面量 + /// + public class NumberIntNode : ASTNode + { + public int Value { get; } + public NumberIntNode(int value) => Value = value; + } + + + /// + /// int 整数型字面量 + /// + public class NumberLongNode : ASTNode + { + public long Value { get; } + public NumberLongNode(long value) => Value = value; + } + + + /// + /// int 整数型字面量 + /// + public class NumberFloatNode : ASTNode + { + public float Value { get; } + public NumberFloatNode(float value) => Value = value; + } + + + /// + /// int 整数型字面量 + /// + public class NumberDoubleNode : ASTNode + { + public double Value { get; } + public NumberDoubleNode(double value) => Value = value; + }*/ + + +} diff --git a/Serein.Script/Node/NumberNode.cs b/Serein.Script/Node/NumberNode.cs deleted file mode 100644 index 0f1ad07..0000000 --- a/Serein.Script/Node/NumberNode.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Serein.Script.Node -{ - - - /// - /// 整数型字面量 - /// - public class NumberNode : ASTNode - { - public int Value { get; } - public NumberNode(int value) => Value = value; - } - - -} diff --git a/Serein.Script/SereinScriptInterpreter.cs b/Serein.Script/SereinScriptInterpreter.cs index e3f5d57..1e46d84 100644 --- a/Serein.Script/SereinScriptInterpreter.cs +++ b/Serein.Script/SereinScriptInterpreter.cs @@ -30,6 +30,9 @@ namespace Serein.Script /// public interface IScriptInvokeContext { + /// + /// 脚本运行的流程上下文,包含了流程上下文和变量等信息 + /// IDynamicContext FlowContext { get; } /// @@ -64,7 +67,6 @@ namespace Serein.Script void OnExit(); } - public class ScriptInvokeContext : IScriptInvokeContext { public ScriptInvokeContext(IDynamicContext dynamicContext) @@ -78,7 +80,10 @@ namespace Serein.Script /// 定义的变量 /// private Dictionary _variables = new Dictionary(); - + + /// + /// 取消令牌源,用于控制脚本的执行 + /// private CancellationTokenSource _tokenSource = new CancellationTokenSource(); /// @@ -109,8 +114,6 @@ namespace Serein.Script } - - void IScriptInvokeContext.OnExit() { // 清理脚本中加载的非托管资源 @@ -134,7 +137,9 @@ namespace Serein.Script } - + /// + /// 脚本解释器,负责解析和执行 Serein 脚本 + /// public class SereinScriptInterpreter { @@ -220,23 +225,34 @@ namespace Serein.Script private async Task ExecutionProgramNodeAsync(IScriptInvokeContext context, ProgramNode programNode) { // 加载变量 - - - // 遍历 ProgramNode 中的所有语句并执行它们 - foreach (var statement in programNode.Statements) + ASTNode statement = null; + try { - // 直接退出 - if (statement is ReturnNode returnNode) // 遇到 Return 语句 提前退出 - { - return await EvaluateAsync(context, statement); - } - else - { - await InterpretAsync(context, statement); - } - } - return null; + // 遍历 ProgramNode 中的所有语句并执行它们 + for (int index = 0; index < programNode.Statements.Count; index++) + { + statement = programNode.Statements[index]; + // 直接退出 + if (statement is ReturnNode returnNode) // 遇到 Return 语句 提前退出 + { + return await EvaluateAsync(context, statement); + } + else + { + await InterpretAsync(context, statement); + } + } + return null; + } + catch (Exception ex ) + { + if(statement is not null) + { + SereinEnv.WriteLine(InfoType.ERROR, $"脚本异常发生在[行{statement.Row}]:{ex.Message}{Environment.NewLine}\t{statement.Code}"); + } + throw; + } } /// @@ -385,6 +401,13 @@ namespace Serein.Script } + /// + /// 解释操作 + /// + /// + /// + /// + /// public async Task InterpretAsync(IScriptInvokeContext context, ASTNode node) { if(node == null) @@ -407,10 +430,10 @@ namespace Serein.Script case MemberAssignmentNode memberAssignmentNode: // 设置对象属性 await SetMemberValue(context, memberAssignmentNode); break; - case MemberFunctionCallNode memberFunctionCallNode: + case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用 return await CallMemberFunction(context, memberFunctionCallNode); case IfNode ifNode: // 执行 if...else... 语句块 - await ExecutionIfNodeAsync(context, ifNode); + await ExecutionIfNodeAsync(context, ifNode); break; case WhileNode whileNode: // 循环语句块 await ExectutionWhileNodeAsync(context, whileNode); @@ -425,7 +448,13 @@ namespace Serein.Script return null; } - + /// + /// 评估 + /// + /// + /// + /// + /// private async Task EvaluateAsync(IScriptInvokeContext context, ASTNode node) { if(node == null) @@ -438,8 +467,14 @@ namespace Serein.Script return null; case BooleanNode booleanNode: return booleanNode.Value; // 返回数值 - case NumberNode numberNode: - return numberNode.Value; // 返回数值 + case NumberIntNode numberNode: + return numberNode.Value; // 返回 int 整型数 + case NumberLongNode numberNode: + return numberNode.Value; // 返回 long 整型数 + case NumberFloatNode numberNode: + return numberNode.Value; // 返回 float 浮点型 + case NumberDoubleNode numberNode: + return numberNode.Value; // 返回 double 浮点型 case StringNode stringNode: return stringNode.Value; // 返回字符串值 case CharNode charNode: @@ -451,17 +486,11 @@ namespace Serein.Script case BinaryOperationNode binOpNode: // 递归计算二元操作 var left = await EvaluateAsync(context, binOpNode.Left); - //if (left == null ) - //{ - // throw new SereinSciptException(binOpNode.Left, $"左值尝试使用 null"); - //} + //if (left == null ) throw new SereinSciptException(binOpNode.Left, $"左值尝试使用 null"); var right = await EvaluateAsync(context, binOpNode.Right); - //if (right == null) - //{ - // throw new SereinSciptException(binOpNode.Right, "右值尝试使用计算 null"); - //} + //if (right == null) throw new SereinSciptException(binOpNode.Right, "右值尝试使用计算 null"); return EvaluateBinaryOperation(left, binOpNode.Operator, right); - case ObjectInstantiationNode objectInstantiationNode: + case ObjectInstantiationNode objectInstantiationNode: // 对象实例化 if (_classDefinition.TryGetValue(objectInstantiationNode.TypeName,out var type )) { object?[] args = new object[objectInstantiationNode.Arguments.Count]; @@ -493,6 +522,8 @@ namespace Serein.Script return await GetCollectionValue(context, collectionIndexNode); case ReturnNode returnNode: // 返回内容 return await EvaluateAsync(context, returnNode.Value); // 直接返回响应的内容 + //case ObjectInstantiationNode objectInstantiationNode: // 返回内容 + default: throw new SereinSciptException(node, $"解释器 EvaluateAsync() 未实现{node}节点行为"); } @@ -500,8 +531,7 @@ namespace Serein.Script private object EvaluateBinaryOperation(object left, string op, object right) { - - + return BinaryOperationEvaluator.EvaluateValue(left, op, right); // 根据运算符执行不同的运算 switch (op) diff --git a/Serein.Script/SereinScriptLexer.cs b/Serein.Script/SereinScriptLexer.cs index 9172190..900deb4 100644 --- a/Serein.Script/SereinScriptLexer.cs +++ b/Serein.Script/SereinScriptLexer.cs @@ -1,9 +1,13 @@ using Newtonsoft.Json.Linq; using System.Runtime.CompilerServices; using System.Xml.Linq; +using static System.Net.Mime.MediaTypeNames; namespace Serein.Script { + /// + /// Serein脚本词法分析器的Token类型 + /// internal enum TokenType { /// @@ -19,9 +23,21 @@ namespace Serein.Script /// Boolean, /// - /// 数值 + /// int 整数 /// - Number, + NumberInt, + /// + /// long 整数 + /// + NumberLong, + /// + /// float 浮点数 + /// + NumberFloat, + /// + /// double 浮点数 + /// + NumberDouble, /// /// 字符串 /// @@ -91,6 +107,9 @@ namespace Serein.Script EOF } + /// + /// Serein脚本词法分析器的Token结构体 + /// internal ref struct Token { public TokenType Type { get; } @@ -108,18 +127,26 @@ namespace Serein.Script } } + + /// + /// Serein脚本词法分析器 + /// internal ref struct SereinScriptLexer { private readonly ReadOnlySpan _input; private int _index; private int _row ; + private int coreRangeStartIndex = 0; + /// + /// 关键字,防止声明为变量 + /// private string[] _keywords = [ "let", - "func", - "if", - "else", + "func", + "if", + "else", "return", "while", "new", @@ -136,8 +163,10 @@ namespace Serein.Script internal Token PeekToken() { int currentIndex = _index; // 保存当前索引 + var currentRow = _row; // 保存当前行数 Token nextToken = NextToken(); // 获取下一个 token _index = currentIndex; // 恢复索引到当前位置 + _row = currentRow; // 恢复到当前行数 return nextToken; // 返回下一个 token } @@ -156,8 +185,7 @@ namespace Serein.Script } - - if (_index >= _input.Length) return new Token(TokenType.EOF, string.Empty); + if (_index >= _input.Length) return new Token(TokenType.EOF, string.Empty); // 程序结束 char currentChar = _input[_index]; @@ -230,12 +258,77 @@ namespace Serein.Script // 识别数字 if (char.IsDigit(currentChar)) { - var start = _index; - while (_index < _input.Length && char.IsDigit(_input[_index])) - _index++; - var value = _input.Slice(start, _index - start).ToString(); - _index = start; // 回退索引,索引必须只能在 CreateToken 方法内更新 - return CreateToken(TokenType.Number, value); + #region 数值分析 + if (char.IsDigit(currentChar)) + { + var start = _index; + bool hasDot = false; + bool hasSuffix = false; + + while (_index < _input.Length) + { + var ch = _input[_index]; + + if (char.IsDigit(ch)) + { + _index++; + } + else if (ch == '.' && !hasDot) + { + hasDot = true; + _index++; + } + else if (ch is 'f' or 'F' or 'd' or 'D' or 'l' or 'L') + { + hasSuffix = true; + _index++; + break; // 后缀后应结束 + } + else + { + break; + } + } + + var raw = _input.Slice(start, _index - start).ToString(); + _index = start; // 回退索引,仅 CreateToken 负责推进 + + TokenType type; + + // 判断类型 + if (hasDot) + { + if (raw.EndsWith("f", StringComparison.OrdinalIgnoreCase)) + type = TokenType.NumberFloat; + else if (raw.EndsWith("d", StringComparison.OrdinalIgnoreCase)) + type = TokenType.NumberDouble; + else + type = TokenType.NumberDouble; // 默认小数为 double + } + else + { + if (raw.EndsWith("l", StringComparison.OrdinalIgnoreCase)) + type = TokenType.NumberLong; + else + { + // 自动根据位数判断 int 或 long + if (long.TryParse(raw, out var val)) + { + if (val >= int.MinValue && val <= int.MaxValue) + type = TokenType.NumberInt; + else + type = TokenType.NumberLong; + } + else + { + type = TokenType.NumberLong; // 超出 long 会出错,默认成 long + } + } + } + + return CreateToken(type, raw); + } + #endregion } // 识别标识符(变量名、关键字) @@ -303,6 +396,12 @@ namespace Serein.Script throw new Exception("Unexpected character: " + currentChar); } + /// + /// 创建一个新的Token实例 + /// + /// + /// + /// private Token CreateToken(TokenType tokenType, string value) { var code = GetLine(_row).ToString(); @@ -314,6 +413,7 @@ namespace Serein.Script Code = code, }; _index += value.Length; + return token; } @@ -396,6 +496,16 @@ namespace Serein.Script } + public int GetIndex() + { + return _index; + } + public string GetCoreContent(int index) + { + ReadOnlySpan text = _input; + var content = text.Slice(index, _index - index); // 返回从start到当前位置的行文本 + return content.ToString(); + } } diff --git a/Serein.Script/SereinScriptParser.cs b/Serein.Script/SereinScriptParser.cs index 7f0d1ba..a45d6d0 100644 --- a/Serein.Script/SereinScriptParser.cs +++ b/Serein.Script/SereinScriptParser.cs @@ -5,9 +5,14 @@ using Serein.Script.Node; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; +using System.Text; namespace Serein.Script { + + /// + /// SereinScriptParser 用于解析 Serein 脚本语言的语法。 + /// public ref struct SereinScriptParser { private SereinScriptLexer _lexer; @@ -20,6 +25,11 @@ namespace Serein.Script _currentToken = _lexer.NextToken(); } + + /// + /// 解析脚本并返回 AST(抽象语法树)根节点。 + /// + /// public ASTNode Parse() { return Program(); @@ -29,40 +39,67 @@ namespace Serein.Script private List Statements { get; } = new List(); + /// + /// 解析整个程序,直到遇到文件结尾(EOF)为止。 + /// + /// private ASTNode Program() { Statements.Clear(); while (_currentToken.Type != TokenType.EOF) { - var astNode = Statement(); + var astNode = Statement(); // 解析单个语句 if (astNode == null) { continue; } - Statements.Add(astNode); - - //if (astNode is ClassTypeDefinitionNode) - //{ - // statements = [astNode, ..statements]; // 类型定义置顶 - //} - //else - //{ - // statements.Add(astNode); - //} - + Statements.Add(astNode); // 将解析得到的 AST 节点添加到语句列表中 } - return new ProgramNode(Statements).SetTokenInfo(_currentToken); + var programNode = new ProgramNode(Statements); + programNode.SetTokenInfo(_currentToken); // 程序节点,包含所有解析的语句列表 + + SereinScriptTypeAnalysis typeAnalysis = new SereinScriptTypeAnalysis(programNode); + return programNode; + + /*if (astNode is ClassTypeDefinitionNode) + { + statements = [astNode, ..statements]; // 类型定义置顶 + } + else + { + statements.Add(astNode); + }*/ } + /// + /// 解析单个语句。 + /// + /// + /// private ASTNode Statement() { + + // 处理其他语句(如表达式语句等) + while (_currentToken.Type == TokenType.Semicolon) + { + _currentToken = _lexer.NextToken(); + } + if(_currentToken.Type == TokenType.EOF) + { + return null; + } + + if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "let") { + // 处理 let 变量赋值语句 return ParseLetAssignment(); } if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "class") { + + // 加载类定义 return ParseClassDefinition(); // 加载类,如果已经加载过,则忽略 } if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "new") @@ -70,39 +107,44 @@ namespace Serein.Script var _peekToken = _lexer.PeekToken(); if (_peekToken.Type == TokenType.Keyword && _peekToken.Value == "class") { + // 重新加载类定义 return ParseClassDefinition(); // 重新加载类 } } if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "if") { + // 处理 if 语句 return ParseIf(); } if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "while") { + // 处理 while 循环语句 return ParseWhile(); } if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "return") { + // 处理 return 语句 return ParseReturn(); } if (_currentToken.Type == TokenType.Identifier) { + // 处理标识符,可能是函数调用、变量赋值或对象成员访问等行为 return ParseIdentifier(); } if (_currentToken.Type == TokenType.Null) { + // 处理 null 语句 return Expression(); } - - - // 处理其他语句(如表达式语句等) - if (_currentToken.Type == TokenType.Semicolon) + /*if (_currentToken.Type == TokenType.Semicolon) { _currentToken = _lexer.NextToken(); return null; // 表示空语句 - } + }*/ + + throw new Exception("Unexpected statement: " + _currentToken.Value.ToString()); } @@ -126,12 +168,13 @@ namespace Serein.Script else if (_tempToken.Type == TokenType.Dot) { // 对象成员的获取 - return ParseMemberAccessOrAssignment(); + return ParseMemberAccessOrAssignment(); } else if (_tempToken.Type == TokenType.SquareBracketsLeft) { // 数组 index; 字典 key obj.Member[xxx]; return ParseCollectionIndex(); + } else { @@ -197,7 +240,6 @@ namespace Serein.Script } - throw new Exception($"Expected '{_currentToken.Value}' after variable name"); } @@ -209,33 +251,55 @@ namespace Serein.Script /// private ASTNode ParseLetAssignment() { + _currentToken = _lexer.NextToken(); // Consume "let" string variable = _currentToken.Value.ToString(); // 变量名称 _currentToken = _lexer.NextToken(); // Consume identifier ASTNode value; + AssignmentNode assignmentNode; if (_currentToken.Type == TokenType.Semicolon) { // 定义一个变量,初始值为 null value = new NullNode(); + assignmentNode = new AssignmentNode(variable, value); // 生成node + assignmentNode.SetTokenInfo(_currentToken); // 设置token信息 } else { + // 如果定义了变量,后面紧跟着操作符,且操作符不是“=”话视为异常 + // let value = obj; if (_currentToken.Type != TokenType.Operator || _currentToken.Value != "=") throw new Exception("Expected '=' after variable name"); - _currentToken = _lexer.NextToken(); - value = Expression(); - _currentToken = _lexer.NextToken(); // Consume semicolon - + _currentToken = _lexer.NextToken(); // 消耗操作符(“=”) + var nodeToken = _currentToken; + value = Expression(); // 解析获取赋值表达式 + assignmentNode = new AssignmentNode(variable, value); // 生成node + assignmentNode.SetTokenInfo(nodeToken); // 设置token信息 + _currentToken = _lexer.NextToken(); // 消耗分号 } - return new AssignmentNode(variable, value).SetTokenInfo(_currentToken); + return assignmentNode; } + /// + /// 解析类定义 + /// + /// + /// private ASTNode ParseClassDefinition() { - bool isOverlay = false; + var sb = new StringBuilder(); // 收集代码信息 + /* + * 有两种定义类型的方式: + * 1. class MyClass{} + * 2. new class MyClass{} + * 解析执行时,第二种方式定义的类,会顶掉其他地方创建的“MyClass”同名类型; + */ + var coreStartRangeIndex = _lexer.GetIndex() - "class".Length; // 从“class”开始锚定代码范围 + bool isOverlay = false; // 指示是否覆盖缓存中创建过的同名其它类型 + if (_currentToken.Value == "new") { isOverlay = true; // 重新加载类 @@ -248,15 +312,19 @@ namespace Serein.Script throw new Exception("Expected '{' after class definition"); var classFields = new Dictionary(); _currentToken = _lexer.NextToken(); // 消耗括号 + while (_currentToken.Type != TokenType.BraceRight) { - var fieldType = _currentToken.Value.ToString().ToTypeOfString(); // 获取定义的类名 - _currentToken = _lexer.NextToken(); + // 获取类字段定义 + var fieldType = _currentToken.Value.ToString().ToTypeOfString(); // 获取字段的类型 + _currentToken = _lexer.NextToken(); // 消耗类型 var fieldName = _currentToken.Value.ToString(); // 获取定义的类名 - _currentToken = _lexer.NextToken(); - classFields.Add(fieldName,fieldType); - if (_currentToken.Type == TokenType.Semicolon && _lexer.PeekToken().Type == TokenType.BraceRight) + _currentToken = _lexer.NextToken(); // 消耗字段名称 + classFields.Add(fieldName,fieldType); // 添加字段 + if (_currentToken.Type == TokenType.Semicolon + && _lexer.PeekToken().Type == TokenType.BraceRight) { + // 如果遇到分号、大括号,退出字段定义。 break; } else @@ -265,37 +333,50 @@ namespace Serein.Script } } - - _currentToken = _lexer.NextToken(); - _currentToken = _lexer.NextToken(); - return new ClassTypeDefinitionNode(classFields, className, isOverlay).SetTokenInfo(_currentToken); + _currentToken = _lexer.NextToken(); // 消耗类型定义 } 括号 + var typeDefinitionCode = _lexer.GetCoreContent(coreStartRangeIndex); // 收集类型定义的代码。(在Statement方法中开始收集的) + var node = new ClassTypeDefinitionNode(classFields, className, isOverlay); + _currentToken.Code = typeDefinitionCode; + node.SetTokenInfo(_currentToken); + // _currentToken = _lexer.NextToken(); + _currentToken = _lexer.NextToken(); + return node; } + /// + /// 解析对象实例化行为 + /// + /// + /// public ASTNode ParseObjectInstantiation() { - _currentToken = _lexer.NextToken(); // Consume "new" - string typeName = _currentToken.Value.ToString(); // Get type name + _currentToken = _lexer.NextToken(); // 消耗 new 关键字 + string typeName = _currentToken.Value.ToString(); // 获取类型名称 _currentToken = _lexer.NextToken(); if (_currentToken.Type != TokenType.ParenthesisLeft) throw new Exception("Expected '(' after function name"); - _currentToken = _lexer.NextToken(); // consume "(" + _currentToken = _lexer.NextToken(); // 消耗 "(" var arguments = new List(); while (_currentToken.Type != TokenType.ParenthesisRight) { - arguments.Add(Expression()); + arguments.Add(Expression()); // 获取参数表达式 if (_currentToken.Type == TokenType.Comma) { _currentToken = _lexer.NextToken(); // consume "," } } - _currentToken = _lexer.NextToken(); // consume ")" + _currentToken = _lexer.NextToken(); // 消耗 ")" return new ObjectInstantiationNode(typeName, arguments).SetTokenInfo(_currentToken); } - + /// + /// 解析集合索引行为(数组或字典) + /// + /// + /// public ASTNode ParseCollectionIndex() { var identifierNode = new IdentifierNode(_currentToken.Value.ToString()).SetTokenInfo(_currentToken); @@ -375,10 +456,15 @@ namespace Serein.Script } - + /// + /// 解析成员函数调用行为 + /// + /// + /// + /// private ASTNode ParseMemberFunctionCall(ASTNode targetNode) { - string functionName = _currentToken.Value.ToString(); + string functionName = _currentToken.Value.ToString(); // 函数名称 _currentToken = _lexer.NextToken(); // consume identifier if (_currentToken.Type != TokenType.ParenthesisLeft) @@ -389,9 +475,10 @@ namespace Serein.Script var arguments = new List(); while (_currentToken.Type != TokenType.ParenthesisRight) { + // 获取参数表达式 var arg = Expression(); _currentToken = _lexer.NextToken(); // consume arg - arguments.Add(arg); + arguments.Add(arg); // 添加到参数列表 if (_currentToken.Type == TokenType.Comma) { _currentToken = _lexer.NextToken(); // consume "," @@ -408,10 +495,15 @@ namespace Serein.Script } + /// + /// 解析函数调用行为 + /// + /// + /// private ASTNode ParseFunctionCall() { string functionName = _currentToken.Value.ToString(); - _currentToken = _lexer.NextToken(); // consume identifier + _currentToken = _lexer.NextToken(); // consume identifier if (_currentToken.Type != TokenType.ParenthesisLeft) throw new Exception("Expected '(' after function name"); @@ -422,7 +514,7 @@ namespace Serein.Script bool isBreak = false; while (_currentToken.Type != TokenType.ParenthesisRight) { - var arg = Expression(); + var arg = Expression(); // 获取参数表达式 _currentToken = _lexer.NextToken(); // consume arg arguments.Add(arg); if (_currentToken.Type == TokenType.Comma) @@ -456,18 +548,28 @@ namespace Serein.Script } + /// + /// 解析 return 语句。 + /// + /// public ASTNode ParseReturn() { _currentToken = _lexer.NextToken(); if(_currentToken.Type == TokenType.Semicolon) { - return new ReturnNode().SetTokenInfo(_currentToken); + return new ReturnNode().SetTokenInfo(_currentToken); // 返回空的 ReturnNode } - var resultValue = Expression(); - _currentToken = _lexer.NextToken(); - return new ReturnNode(resultValue).SetTokenInfo(_currentToken); + var resultValue = Expression(); // 获取返回值表达式 + _currentToken = _lexer.NextToken(); + return new ReturnNode(resultValue).SetTokenInfo(_currentToken); } + + /// + /// 解析 if 语句。 + /// + /// + /// private ASTNode ParseIf() { _currentToken = _lexer.NextToken(); // Consume "if" @@ -487,10 +589,10 @@ namespace Serein.Script List falseBranch = new List(); while (_currentToken.Type != TokenType.BraceRight && _currentToken.Type != TokenType.EOF) { - var astNode = Statement(); + var astNode = Statement(); // 解析 if 分支中的语句 if (astNode != null) { - trueBranch.Add(astNode); + trueBranch.Add(astNode); // 将 if 分支的语句添加到 trueBranch 中 } } // 确保匹配右大括号 } @@ -505,10 +607,10 @@ namespace Serein.Script _currentToken = _lexer.NextToken(); // Consume "{" while (_currentToken.Type != TokenType.BraceRight && _currentToken.Type != TokenType.EOF) { - var astNode = Statement(); + var astNode = Statement(); // 解析 else 分支中的语句 if (astNode != null) { - falseBranch.Add(astNode); + falseBranch.Add(astNode); // 将 else 分支的语句添加到 falseBranch 中 } } // 确保匹配右大括号 } @@ -523,6 +625,10 @@ namespace Serein.Script return new IfNode(condition, trueBranch, falseBranch).SetTokenInfo(_currentToken); } + /// + /// 解析 while 循环语句。 + /// + /// private ASTNode ParseWhile() { _currentToken = _lexer.NextToken(); // Consume "while" @@ -533,13 +639,16 @@ namespace Serein.Script List body = new List(); while (_currentToken.Type != TokenType.BraceRight) { - body.Add(Statement()); + body.Add(Statement()); // 解析循环体中的语句 } _currentToken = _lexer.NextToken(); // Consume "}" return new WhileNode(condition, body).SetTokenInfo(_currentToken); } - + /// + /// 解析表达式。 + /// + /// private ASTNode Expression() { ASTNode left = Term(); @@ -555,6 +664,11 @@ namespace Serein.Script return left; } + + /// + /// 解析项(Term),用于处理加减乘除等运算符。 + /// + /// private ASTNode Term() { ASTNode left = Factor(); @@ -571,6 +685,11 @@ namespace Serein.Script return left; } + /// + /// 解析因子(Factor),用于处理基本的字面量、标识符、括号表达式等。 + /// + /// + /// private ASTNode Factor() { #region 返回字面量 @@ -588,8 +707,9 @@ namespace Serein.Script if (_currentToken.Type == TokenType.String) { var text = _currentToken.Value; + var node = new StringNode(text).SetTokenInfo(_currentToken); _currentToken = _lexer.NextToken(); // 消耗字符串 - return new StringNode(text).SetTokenInfo(_currentToken); + return node; } if (_currentToken.Type == TokenType.Char) { @@ -599,6 +719,7 @@ namespace Serein.Script } if( _currentToken.Type == TokenType.InterpolatedString) { + // 暂未实现插值字符串 // 可能是插值字符串; // let context = $"a{A}b{B}c"; // let context = "a" + A + "b" + B + c; @@ -607,11 +728,32 @@ namespace Serein.Script } } - if (_currentToken.Type == TokenType.Number) + if (_currentToken.Type == TokenType.NumberInt) { var value = int.Parse(_currentToken.Value); + _currentToken = _lexer.NextToken(); // 消耗 int 整型 + return new NumberIntNode(value).SetTokenInfo(_currentToken); + } + + if (_currentToken.Type == TokenType.NumberLong) + { + var value = long.Parse(_currentToken.Value); + _currentToken = _lexer.NextToken(); // 消耗 + return new NumberLongNode(value).SetTokenInfo(_currentToken); + } + + if (_currentToken.Type == TokenType.NumberFloat) + { + var value = float.Parse(_currentToken.Value); _currentToken = _lexer.NextToken(); // 消耗数字 - return new NumberNode(value).SetTokenInfo(_currentToken); + return new NumberFloatNode(value).SetTokenInfo(_currentToken); + } + + if (_currentToken.Type == TokenType.NumberDouble) + { + var value = float.Parse(_currentToken.Value); + _currentToken = _lexer.NextToken(); // 消耗数字 + return new NumberDoubleNode(value).SetTokenInfo(_currentToken); } #endregion @@ -629,6 +771,7 @@ namespace Serein.Script // 创建对象 if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "new") { + // 可能是对象实例化 return ParseObjectInstantiation(); } @@ -640,13 +783,15 @@ namespace Serein.Script // 该标识符是方法调用 if (_identifierPeekToken.Type == TokenType.ParenthesisLeft) { + // 可能是函数调用 return ParseFunctionCall(); } // 需要从该标识符调用另一个标识符 if (_identifierPeekToken.Type == TokenType.Dot) { - return ParseMemberAccessOrAssignment(); + // 可能是成员访问或成员赋值 + return ParseMemberAccessOrAssignment(); // 二元操作中获取对象成员 } diff --git a/Serein.Script/SereinScriptTypeAnalysis.cs b/Serein.Script/SereinScriptTypeAnalysis.cs new file mode 100644 index 0000000..86aeacc --- /dev/null +++ b/Serein.Script/SereinScriptTypeAnalysis.cs @@ -0,0 +1,173 @@ +using Serein.Library.Utils; +using Serein.Script.Node; +using Serein.Script.Symbol; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Serein.Script +{ + public class SereinScriptTypeAnalysis + { + private Dictionary SymbolInfos = new Dictionary(); + + public SereinScriptTypeAnalysis(ProgramNode programNode) + { + SymbolInfos.Clear(); // 清空符号表 + foreach (ASTNode astNode in programNode.Statements) + { + var type = Trace(astNode); + if (type is null) continue; + var info = Analyse(astNode, type); + if(info != null) + { + SymbolInfos[info.Name] = info; + } + /*if(astNode is AssignmentNode assignmentNode) + { + var name = assignmentNode.Variable; + var node = assignmentNode.Value; + var type = Analyse(node); + if(type is null) + { + continue; + } + var symbolInfo = new SymbolInfo + { + Type = type, + Node = node, + Name = name, + }; + SymbolInfos[name] = symbolInfo; + }*/ + } + } + + /// + /// 追踪类型 + /// + /// + /// + private Type Trace(ASTNode node) + { + if (node == null) + { + return null; + } + switch (node) + { + case NullNode nullNode: // 返回null + return typeof(object); + case BooleanNode booleanNode: // 返回布尔 + return typeof(bool); + case NumberIntNode numberNode: // 数值 + return typeof(int); + case StringNode stringNode: // 字符串 + return typeof(string); + case CharNode charNode: // char + return typeof(char); + case IdentifierNode identifierNode: // 定义变量 + return typeof(object); + case AssignmentNode assignmentNode: // 赋值行为 + var type = Trace(assignmentNode.Value); + return type; + //throw new SereinSciptException(identifierNode, "尝试使用值为null的变量"); + //throw new SereinSciptException(identifierNode, "尝试使用未声明的变量"); + case BinaryOperationNode binOpNode: // 递归计算二元操作 + var leftType = Trace(binOpNode.Left); + var op = binOpNode.Operator; + var rightType = Trace(binOpNode.Right); + var resultType = BinaryOperationEvaluator.EvaluateType(leftType, op, rightType); + return resultType; + case ClassTypeDefinitionNode classTypeDefinitionNode: + var definitionType = DynamicObjectHelper.CreateTypeWithProperties(classTypeDefinitionNode.Fields, classTypeDefinitionNode.ClassName, true); + return definitionType; + case ObjectInstantiationNode objectInstantiationNode: // 创建对象 + + var typeName = objectInstantiationNode.TypeName; + var objectType = Type.GetType(typeName); + objectType ??= DynamicObjectHelper.GetCacheType(typeName); + return objectType; + case FunctionCallNode callNode: // 调用方法 + return null; + case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用 + return null; + case MemberAccessNode memberAccessNode: // 对象成员访问 + var memberType = memberAccessNode.MemberName; + return null; + case CollectionIndexNode collectionIndexNode: + case ReturnNode returnNode: // 返回内容 + + default: + break; + //throw new SereinSciptException(node, $"解释器 EvaluateAsync() 未实现{node}节点行为"); + } + + return null; + } + + + + private SymbolInfo Analyse(ASTNode node, Type type) + { + if (node == null) + { + return null; + } + switch (node) + { + case IdentifierNode identifierNode: // 定义变量 + return new SymbolInfo + { + Name = identifierNode.Name, + Node = node, + Type = type, + }; + case AssignmentNode assignmentNode: // 赋值行为 + return new SymbolInfo + { + Name = assignmentNode.Variable, + Node = node, + Type = type, + }; + case BinaryOperationNode binOpNode: // 递归计算二元操作 + //case ClassTypeDefinitionNode classTypeDefinitionNode + case ObjectInstantiationNode objectInstantiationNode: // 创建对象 + case FunctionCallNode callNode: // 调用方法 + case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用 + case MemberAccessNode memberAccessNode: // 对象成员访问 + case CollectionIndexNode collectionIndexNode: + case ReturnNode returnNode: // 返回内容 + default: + break; + //throw new SereinSciptException(node, $"解释器 EvaluateAsync() 未实现{node}节点行为"); + } + + return null; + } + + /* + case NullNode nullNode: // 返回null + case BooleanNode booleanNode: // 返回布尔 + case NumberIntNode numberNode: // 数值 + case StringNode stringNode: // 字符串 + case CharNode charNode: // char + case IdentifierNode identifierNode: // 定义变量 + case AssignmentNode assignmentNode: // 赋值行为 + case BinaryOperationNode binOpNode: // 递归计算二元操作 + case ObjectInstantiationNode objectInstantiationNode: // 创建对象 + case FunctionCallNode callNode: // 调用方法 + case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用 + case MemberAccessNode memberAccessNode: // 对象成员访问 + case CollectionIndexNode collectionIndexNode: + case ReturnNode returnNode: // 返回内容 + default: + break; + */ + + } +} diff --git a/Serein.Script/Symbol/SymbolInfo.cs b/Serein.Script/Symbol/SymbolInfo.cs new file mode 100644 index 0000000..81338c3 --- /dev/null +++ b/Serein.Script/Symbol/SymbolInfo.cs @@ -0,0 +1,37 @@ +using Serein.Script.Node; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Script.Symbol +{ + public enum SymbolType + { + Identifier, + FunctionReturn, + } + + + /// + /// 符号信息 + /// + internal class SymbolInfo + { + /// + /// 符号名称 + /// + public string Name; + + /// + /// 对应类型 + /// + public Type Type; + + /// + /// 节点 + /// + public ASTNode Node; + } +} diff --git a/Workbench/Node/NodeControlBase.cs b/Workbench/Node/NodeControlBase.cs index f72b9a9..e222af7 100644 --- a/Workbench/Node/NodeControlBase.cs +++ b/Workbench/Node/NodeControlBase.cs @@ -101,7 +101,7 @@ namespace Serein.Workbench.Node.View public void RemoveConnection(ConnectionControl connection) { connectionControls.Remove(connection); - connection.Remote(); + connection.Remove(); // 主动删除连接 } /// @@ -111,7 +111,7 @@ namespace Serein.Workbench.Node.View { foreach (var connection in this.connectionControls) { - connection.Remote(); + connection.Remove(); // 主动删除连接 } } diff --git a/Workbench/Node/View/ConnectionControl.cs b/Workbench/Node/View/ConnectionControl.cs index b94272a..ef5423f 100644 --- a/Workbench/Node/View/ConnectionControl.cs +++ b/Workbench/Node/View/ConnectionControl.cs @@ -1,5 +1,6 @@ using Serein.Library; using Serein.Library.Api; +using Serein.Workbench.Api; using Serein.Workbench.Extension; using Serein.Workbench.Tool; using System; @@ -142,7 +143,7 @@ namespace Serein.Workbench.Node.View /// /// 连接线 /// - private ConnectionLineShape BezierLine; + public ConnectionLineShape BezierLine { get;private set; } @@ -225,17 +226,39 @@ namespace Serein.Workbench.Node.View private void ConfigureLineContextMenu() { var contextMenu = new ContextMenu(); - contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("删除连线", (s, e) => Remote())); + contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("移除该连接关系", (s, e) => Remove())); contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("于父节点调用顺序中置顶", (s, e) => Topping())); BezierLine.ContextMenu = contextMenu; } - + /// + /// 从画布删除 + /// + public void RemoveOnCanvas() + { + Canvas.Children.Remove(BezierLine); + } + /// /// 删除该连线 /// - public void Remote() + public void Remove() { + /*string startGuid = Start.MyNode.Guid; + string endGuid = End.MyNode.Guid; + if (flowEventSerice is null) flowEventSerice = App.GetService(); + NodeConnectChangeHandler handler = null; + handler = (e) => + { + if(e.ConnectionInvokeType == InvokeType && e.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remove) + { + + } + flowEventSerice.NodeConnectChanged -= handler; + }; + flowEventSerice.NodeConnectChanged += handler;*/ + + // Canvas.Children.Remove(BezierLine); var env = Start.MyNode.Env; var canvasGuid = Start.MyNode.CanvasDetails.Guid; @@ -247,7 +270,7 @@ namespace Serein.Workbench.Node.View } else if (jct == JunctionOfConnectionType.Arg) { - env.FlowEdit.RemoveArgSourceConnect(canvasGuid,Start.MyNode.Guid, End.MyNode.Guid, ArgIndex) ; + env.FlowEdit.RemoveArgSourceConnect(canvasGuid, Start.MyNode.Guid, End.MyNode.Guid, ArgIndex); } } diff --git a/Workbench/Services/FlowNodeService.cs b/Workbench/Services/FlowNodeService.cs index 41b6562..2071bdf 100644 --- a/Workbench/Services/FlowNodeService.cs +++ b/Workbench/Services/FlowNodeService.cs @@ -50,6 +50,11 @@ namespace Serein.Workbench.Services /// public Action OnViewMethodDetailsInfoChanged { get; set; } + /// + /// FlowCanvasView 监听,需要移除连接线(控件) + /// + public Action OnRemoveConnectionLine { get; set; } + #endregion #region 创建节点相关的属性 @@ -244,8 +249,11 @@ namespace Serein.Workbench.Services _ => null }; + /*if(e.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remove) + { + OnRemoveConnectionLine.Invoke(e); // 删除连线 + }*/ action?.Invoke(); - return; } @@ -408,7 +416,6 @@ namespace Serein.Workbench.Services } - #endregion /// diff --git a/Workbench/Views/FlowCanvasView.xaml.cs b/Workbench/Views/FlowCanvasView.xaml.cs index 3433c33..b220e0c 100644 --- a/Workbench/Views/FlowCanvasView.xaml.cs +++ b/Workbench/Views/FlowCanvasView.xaml.cs @@ -42,6 +42,7 @@ using Clipboard = System.Windows.Clipboard; using TextDataFormat = System.Windows.TextDataFormat; using System.Windows.Media.Animation; using Serein.NodeFlow.Model; +using Serein.NodeFlow.Services; namespace Serein.Workbench.Views { @@ -151,6 +152,7 @@ namespace Serein.Workbench.Views } + /// /// 设置绑定 /// @@ -175,9 +177,52 @@ namespace Serein.Workbench.Views private void InitEvent() { keyEventService.OnKeyDown += KeyEventService_OnKeyDown; + //flowNodeService.OnRemoveConnectionLine += FlowNodeService_OnRemoveConnectionLine; flowEEForwardingService.NodeLocated += FlowEEForwardingService_OnNodeLocated; } + private void FlowNodeService_OnRemoveConnectionLine(NodeConnectChangeEventArgs e) + { + if(e.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create || e.CanvasGuid != this.Guid) + { + return; + } + + var connectionControl = Connections.FirstOrDefault(c => + { + if (c.Start.MyNode.Guid != e.FromNodeGuid + || c.End.MyNode.Guid != e.ToNodeGuid) + { + return false; // 不是当前连接 + } + var jct1 = c.Start.JunctionType.ToConnectyionType(); + var jct2 = c.End.JunctionType.ToConnectyionType(); + if (e.JunctionOfConnectionType == JunctionOfConnectionType.Invoke) + { + if (jct1 == JunctionOfConnectionType.Invoke + && jct2 == JunctionOfConnectionType.Invoke) + { + return true; // 是当前连接 + } + } + else + { + if (c.ArgIndex == e.ArgIndex + && jct1 == JunctionOfConnectionType.Arg + && jct2 == JunctionOfConnectionType.Arg) + { + return true; // 是当前连接 + } + } + return true; + }); + if(connectionControl is null) + { + return; + } + connectionControl.RemoveOnCanvas(); // 移除连接线 + } + /// /// 节点需要定位 /// @@ -224,6 +269,7 @@ namespace Serein.Workbench.Views nodeControl.RenderTransform = translateTransform; ElasticAnimation(nodeControl, translateTransform, 6, 0.5, 0.5); } + /// /// 控件抖动 /// 来源:https://www.cnblogs.com/RedSky/p/17705411.html @@ -257,7 +303,6 @@ namespace Serein.Workbench.Views }; } - /// /// 加载完成后刷新显示 /// @@ -267,8 +312,6 @@ namespace Serein.Workbench.Views RefreshAllLine(); } - - /// /// 当前画布创建了节点 /// @@ -1490,9 +1533,9 @@ namespace Serein.Workbench.Views contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("设为起点", (s, e) => flowEnvironment.FlowEdit.SetStartNode(canvasGuid, nodeGuid))); - contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("删除", async (s, e) => + contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("删除", (s, e) => { - flowEnvironment.FlowEdit.RemoveNode(canvasGuid, nodeGuid); + flowNodeService.RemoteNode(nodeControl); })); #region 右键菜单功能 - 控件对齐