From 79af278b70fe4789df123c40da0fe6d6fd7e996e Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Sat, 2 Aug 2025 22:04:13 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E7=A7=BB=E9=99=A4=E4=BA=86FlipflopContext?= =?UTF-8?q?=EF=BC=8C=E7=BB=9F=E4=B8=80=E6=B5=81=E7=A8=8BAPI=202.=20Script?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E8=84=9A=E6=9C=AC=E4=BF=AE=E5=A4=8D=E4=BA=86?= =?UTF-8?q?=20RawString=20=E5=8E=9F=E5=A7=8B=E5=AD=97=E7=AC=A6=E4=B8=B2?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E7=9A=84=E9=97=AE=E9=A2=98=203.=20Script?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E4=BA=86ValueNode=E7=BB=9F=E4=B8=80=E4=BA=86?= =?UTF-8?q?=E5=80=BC=E7=B1=BB=E5=9E=8B=E8=8A=82=E7=82=B9=EF=BC=8C=E4=B8=BA?= =?UTF-8?q?=E5=90=8E=E7=BB=AD=E6=89=A9=E5=B1=95=E6=9B=B4=E5=A4=9A=E7=9A=84?= =?UTF-8?q?=E5=80=BC=E7=B1=BB=E5=9E=8B=E5=81=9A=E5=87=86=E5=A4=87=204.=20T?= =?UTF-8?q?ypeHelper.ToTypeOfString()=E6=96=B9=E6=B3=95=E4=B8=AD=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=BA=86=E9=83=A8=E5=88=86=E5=80=BC=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E7=9A=84"Type[]=E2=80=9D=E4=B8=8E=E2=80=9CList=E2=80=9D?= =?UTF-8?q?=E7=9A=84=E6=98=BE=E5=BC=8F=E5=AE=9A=E4=B9=89=EF=BC=8C=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E8=84=9A=E6=9C=AC=E5=9C=A8=E7=B1=BB=E5=9E=8B=E4=B8=AD?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E6=95=B0=E7=BB=84=E6=88=90=E5=91=98=205.=20S?= =?UTF-8?q?cript=E9=A1=B9=E7=9B=AE=E8=84=9A=E6=9C=AC=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E6=8C=82=E8=BD=BD=E7=9A=84json=E6=96=B9=E6=B3=95=E6=8B=86?= =?UTF-8?q?=E5=88=86=E4=B8=BAjsonObj(String)=E4=B8=8EjsonStr(Object)?= =?UTF-8?q?=E4=BB=A5=E6=94=AF=E6=8C=81=E5=BA=8F=E5=88=97=E5=8C=96=E4=B8=8E?= =?UTF-8?q?=E5=8F=8D=E5=BA=8F=E5=88=97=E5=8C=96=206.=20=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E4=B8=BAdnf=E9=A1=B9=E7=9B=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=97=B6=EF=BC=8C=E5=B0=86=E4=B8=8D=E5=86=8D=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E5=90=8D=E7=A7=B0=E4=B8=BA=E2=80=9DDefault"=E5=B9=B6?= =?UTF-8?q?=E4=B8=94=E6=B2=A1=E6=9C=89=E8=8A=82=E7=82=B9=E7=9A=84=E7=94=BB?= =?UTF-8?q?=E5=B8=83=EF=BC=8C=E9=81=BF=E5=85=8D=E9=87=8D=E5=A4=8D=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E6=97=B6=E9=BB=98=E8=AE=A4=E7=94=BB=E5=B8=83=E5=A2=9E?= =?UTF-8?q?=E5=A4=9A=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library/Api/IFlipflopContext.cs | 2 + Library/Api/IFlowContext.cs | 12 +- Library/Api/IFlowEnvironment.cs | 6 - Library/Ex/FlipflopException.cs | 2 +- Library/FlowNode/FlowContext.cs | 15 +- Library/ScriptBaseFunc.cs | 16 +- Library/Serein.Library.csproj | 4 + Library/Utils/TypeHelper.cs | 167 +++++++++++++++++- NodeFlow/Env/FlowEdit.cs | 52 +++--- NodeFlow/Env/LocalFlowEnvironment.cs | 11 +- NodeFlow/FlowNodeExtension.cs | 4 +- NodeFlow/Model/Nodes/SingleFlipflopNode.cs | 21 +-- NodeFlow/Tool/FlowWorkManagement.cs | 10 -- NodeFlow/Tool/NodeMethodDetailsHelper.cs | 24 ++- Serein.Script/Node/ValueNode/BooleanNode.cs | 9 +- Serein.Script/Node/ValueNode/CharNode.cs | 11 +- Serein.Script/Node/ValueNode/NumberNode.cs | 8 +- Serein.Script/Node/ValueNode/RawStringNode.cs | 8 + Serein.Script/Node/ValueNode/StringNode.cs | 11 +- Serein.Script/Node/ValueNode/ValueNode.cs | 26 +++ Serein.Script/SereinScriptInterpreter.cs | 2 + Serein.Script/SereinScriptLexer.cs | 48 +++-- Serein.Script/SereinScriptParser.cs | 36 +++- Serein.Script/SereinScriptToCsharpScript.cs | 12 +- Serein.Script/SereinScriptTypeAnalysis.cs | 18 ++ Workbench/Test.cs | 12 -- 26 files changed, 398 insertions(+), 149 deletions(-) create mode 100644 Serein.Script/Node/ValueNode/RawStringNode.cs create mode 100644 Serein.Script/Node/ValueNode/ValueNode.cs delete mode 100644 Workbench/Test.cs diff --git a/Library/Api/IFlipflopContext.cs b/Library/Api/IFlipflopContext.cs index 5931213..8c83b5c 100644 --- a/Library/Api/IFlipflopContext.cs +++ b/Library/Api/IFlipflopContext.cs @@ -15,10 +15,12 @@ namespace Serein.Library.Api /// 触发器完成的状态(根据业务场景手动设置) /// FlipflopStateType State { get; set; } + /// /// 触发类型 /// TriggerDescription Type { get; set; } + /// /// 触发时传递的数据 /// diff --git a/Library/Api/IFlowContext.cs b/Library/Api/IFlowContext.cs index 3f2ffaa..5dc0344 100644 --- a/Library/Api/IFlowContext.cs +++ b/Library/Api/IFlowContext.cs @@ -14,16 +14,22 @@ namespace Serein.Library.Api /// public interface IFlowContext { + /// + /// 标识流程 + /// + string Guid {get; } + /// /// 是否记录流程信息 /// bool IsRecordInvokeInfo { get; set; } - /// - /// 标识流程 + /// 用于同一个流程上下文中共享、存储任意数据 + /// 流程完毕时,如果存储的对象实现了 IDisposable 接口,将会自动调用 + /// 谨慎使用,注意数据的生命周期和内存管理 /// - string Guid {get; } + object? Tag { get; set; } /// /// 运行环境 diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index 6ce4008..12e946a 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -1012,7 +1012,6 @@ namespace Serein.Library.Api void SetUIContextOperation(UIContextOperation uiContextOperation); #endregion - #region 项目相关操作 /// @@ -1068,7 +1067,6 @@ namespace Serein.Library.Api bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails del); #endregion - #region 类库依赖相关 /// @@ -1099,8 +1097,6 @@ namespace Serein.Library.Api #endregion - - #region 远程相关 /*/// /// 启动远程服务 @@ -1142,8 +1138,6 @@ namespace Serein.Library.Api #endregion - - #region 节点中断、表达式(暂时没用) #if false diff --git a/Library/Ex/FlipflopException.cs b/Library/Ex/FlipflopException.cs index 94a5747..963a2c3 100644 --- a/Library/Ex/FlipflopException.cs +++ b/Library/Ex/FlipflopException.cs @@ -37,7 +37,7 @@ namespace Serein.Library /// /// /// - public FlipflopException(string message, bool isCancel = true,CancelClass clsss = CancelClass.CancelBranch) :base(message) + public FlipflopException(string message, bool isCancel = true, CancelClass clsss = CancelClass.CancelBranch) :base(message) { IsCancel = isCancel; Type = clsss; diff --git a/Library/FlowNode/FlowContext.cs b/Library/FlowNode/FlowContext.cs index 9e48a7e..f5227a1 100644 --- a/Library/FlowNode/FlowContext.cs +++ b/Library/FlowNode/FlowContext.cs @@ -27,6 +27,14 @@ namespace Serein.Library RunState = RunState.Running; } + /// + /// 用于同一个流程上下文中共享、存储任意数据 + /// 流程完毕时,如果存储的对象实现了 IDisposable 接口,将会自动调用 + /// 谨慎使用,注意数据的生命周期和内存管理 + /// + public object? Tag { get; set; } + + /// /// 是否记录流程调用信息 /// @@ -78,7 +86,6 @@ namespace Serein.Library private readonly ConcurrentDictionary> dictNodeParams = new ConcurrentDictionary>(); - /// /// 记录流程调用信息 @@ -255,11 +262,15 @@ namespace Serein.Library /// public void Reset() { + if(Tag is IDisposable disposable) + { + disposable.Dispose(); // 释放 Tag 中的资源 + } this.dictNodeFlowData?.Clear(); ExceptionOfRuning = null; + flowInvokeInfos.Clear(); NextOrientation = ConnectionInvokeType.None; RunState = RunState.Running; - flowInvokeInfos.Clear(); Guid = global::System.Guid.NewGuid().ToString(); } diff --git a/Library/ScriptBaseFunc.cs b/Library/ScriptBaseFunc.cs index 25b782b..952e7c1 100644 --- a/Library/ScriptBaseFunc.cs +++ b/Library/ScriptBaseFunc.cs @@ -143,7 +143,7 @@ namespace Serein.Library /// /// /// - public static IJsonToken json(string content) + public static IJsonToken jsonObj(string content) { /*if (string.IsNullOrWhiteSpace(content)) { @@ -152,6 +152,20 @@ namespace Serein.Library return JsonHelper.Parse(content); } + /// + /// 转为JSON字符串 + /// + /// + /// + public static string jsonStr(object data) + { + if (data is null) + { + return "{}"; + } + return JsonHelper.Serialize(data); + } + #endregion diff --git a/Library/Serein.Library.csproj b/Library/Serein.Library.csproj index 3942ca7..33995fb 100644 --- a/Library/Serein.Library.csproj +++ b/Library/Serein.Library.csproj @@ -55,11 +55,15 @@ + + + + diff --git a/Library/Utils/TypeHelper.cs b/Library/Utils/TypeHelper.cs index 063cead..42c326d 100644 --- a/Library/Utils/TypeHelper.cs +++ b/Library/Utils/TypeHelper.cs @@ -70,7 +70,6 @@ namespace Serein.Library.Utils }; - /// /// 字面量转为对应类型 /// @@ -84,11 +83,13 @@ namespace Serein.Library.Utils return Type.GetType(valueStr); } + #region 常见值类型 if (valueStr.Equals("bool", StringComparison.OrdinalIgnoreCase)) { return typeof(bool); } + #region 整数型 else if (valueStr.Equals("sbyte", StringComparison.OrdinalIgnoreCase) || valueStr.Equals(nameof(SByte), StringComparison.OrdinalIgnoreCase)) @@ -167,6 +168,170 @@ namespace Serein.Library.Utils } #endregion + + #endregion + + #region Array数组类型 + if (valueStr.Equals("bool" + "[]", StringComparison.OrdinalIgnoreCase)) + { + return typeof(bool[]); + } + #region 整数型 + else if (valueStr.Equals("sbyte" + "[]", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(SByte) + "[]", StringComparison.OrdinalIgnoreCase)) + { + return typeof(SByte[]); + } + else if (valueStr.Equals("short" + "[]", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Int16) + "[]", StringComparison.OrdinalIgnoreCase)) + { + return typeof(Int16[]); + } + else if (valueStr.Equals("int" + "[]", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Int32) + "[]", StringComparison.OrdinalIgnoreCase)) + { + return typeof(Int32[]); + } + else if (valueStr.Equals("long" + "[]", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Int64) + "[]", StringComparison.OrdinalIgnoreCase)) + { + return typeof(Int64[]); + } + + else if (valueStr.Equals("byte" + "[]", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Byte) + "[]", StringComparison.OrdinalIgnoreCase)) + { + return typeof(Byte[]); + } + else if (valueStr.Equals("ushort" + "[]", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(UInt16) + "[]", StringComparison.OrdinalIgnoreCase)) + { + return typeof(UInt16[]); + } + else if (valueStr.Equals("uint" + "[]", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(UInt32) + "[]", StringComparison.OrdinalIgnoreCase)) + { + return typeof(UInt32[]); + } + else if (valueStr.Equals("ulong" + "[]", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(UInt64) + "[]", StringComparison.OrdinalIgnoreCase)) + { + return typeof(UInt64[]); + } + #endregion + + #region 浮点型 + else if (valueStr.Equals("float" + "[]", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Single) + "[]", StringComparison.OrdinalIgnoreCase)) + { + return typeof(Single[]); + } + else if (valueStr.Equals("double" + "[]", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Double) + "[]", StringComparison.OrdinalIgnoreCase)) + { + return typeof(Double[]); + } + #endregion + + #region 小数型 + + else if (valueStr.Equals("decimal" + "[]", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Decimal) + "[]", StringComparison.OrdinalIgnoreCase)) + { + return typeof(Decimal[]); + } + #endregion + + #region 其他常见的类型 + else if (valueStr.Equals(nameof(String) + "[]", StringComparison.OrdinalIgnoreCase)) + { + return typeof(String[]); + } + #endregion + #endregion + + #region List<> 数组类型 + if (valueStr.Equals("list", StringComparison.OrdinalIgnoreCase)) + { + return typeof(List); + } + + #region 整数型 + else if (valueStr.Equals("list", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(List), StringComparison.OrdinalIgnoreCase)) + { + return typeof(List); + } + else if (valueStr.Equals("list", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(List), StringComparison.OrdinalIgnoreCase)) + { + return typeof(List); + } + else if (valueStr.Equals("list", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(List), StringComparison.OrdinalIgnoreCase)) + { + return typeof(List); + } + else if (valueStr.Equals("list", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(List), StringComparison.OrdinalIgnoreCase)) + { + return typeof(List); + } + + else if (valueStr.Equals("list", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(List), StringComparison.OrdinalIgnoreCase)) + { + return typeof(List); + } + else if (valueStr.Equals("list", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(List), StringComparison.OrdinalIgnoreCase)) + { + return typeof(List); + } + else if (valueStr.Equals("list", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(List), StringComparison.OrdinalIgnoreCase)) + { + return typeof(List); + } + else if (valueStr.Equals("list", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(List), StringComparison.OrdinalIgnoreCase)) + { + return typeof(List); + } + #endregion + + #region 浮点型 + else if (valueStr.Equals("list", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(List), StringComparison.OrdinalIgnoreCase)) + { + return typeof(List); + } + else if (valueStr.Equals("list", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(List), StringComparison.OrdinalIgnoreCase)) + { + return typeof(List); + } + #endregion + + #region 小数型 + + else if (valueStr.Equals("list", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(List), StringComparison.OrdinalIgnoreCase)) + { + return typeof(List); + } + #endregion + + #region 其他常见的类型 + + else if (valueStr.Equals(nameof(List), StringComparison.OrdinalIgnoreCase)) + { + return typeof(List); + } + #endregion + + #endregion + else { throw new ArgumentException($"无法解析的字面量类型[{valueStr}]"); diff --git a/NodeFlow/Env/FlowEdit.cs b/NodeFlow/Env/FlowEdit.cs index 088e4be..26cad00 100644 --- a/NodeFlow/Env/FlowEdit.cs +++ b/NodeFlow/Env/FlowEdit.cs @@ -48,8 +48,6 @@ namespace Serein.NodeFlow.Env private readonly FlowLibraryService flowLibraryManagement; private readonly FlowOperationService flowOperationService; private readonly FlowModelService flowModelService; - //private readonly NodeMVVMService nodeMVVMService; - /// /// 注册基本节点类型 @@ -633,33 +631,31 @@ namespace Serein.NodeFlow.Env } - /* var nodeModels = flowModelService.GetAllNodeModel(); - foreach (var toNode in nodeModels) - { - var canvasGuid = toNode.CanvasDetails.Guid; - if (toNode.MethodDetails.ParameterDetailss == null) - { - continue; - } - for (var i = 0; i < toNode.MethodDetails.ParameterDetailss.Length; i++) - { - var pd = toNode.MethodDetails.ParameterDetailss[i]; - if (!string.IsNullOrEmpty(pd.ArgDataSourceNodeGuid) - && TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var fromNode)) - { - *//*if (fromNode.NeedResultNodes[pd.ArgDataSourceType].Contains(toNode) - && pd.ArgDataSourceNodeGuid == fromNode.Guid - && ) - { - continue; - }*//* - ConnectArgSourceNode(canvasGuid, fromNode.Guid, toNode.Guid, JunctionType.ReturnData, JunctionType.ArgData, pd.ArgDataSourceType, pd.Index); - } - } - }*/ + /* var nodeModels = flowModelService.GetAllNodeModel(); + foreach (var toNode in nodeModels) + { + var canvasGuid = toNode.CanvasDetails.Guid; + if (toNode.MethodDetails.ParameterDetailss == null) + { + continue; + } + for (var i = 0; i < toNode.MethodDetails.ParameterDetailss.Length; i++) + { + var pd = toNode.MethodDetails.ParameterDetailss[i]; + if (!string.IsNullOrEmpty(pd.ArgDataSourceNodeGuid) + && TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var fromNode)) + { + *//*if (fromNode.NeedResultNodes[pd.ArgDataSourceType].Contains(toNode) + && pd.ArgDataSourceNodeGuid == fromNode.Guid + && ) + { + continue; + }*//* + ConnectArgSourceNode(canvasGuid, fromNode.Guid, toNode.Guid, JunctionType.ReturnData, JunctionType.ArgData, pd.ArgDataSourceType, pd.Index); + } + } + }*/ #endregion - - } #endregion diff --git a/NodeFlow/Env/LocalFlowEnvironment.cs b/NodeFlow/Env/LocalFlowEnvironment.cs index c9e6049..7577f4f 100644 --- a/NodeFlow/Env/LocalFlowEnvironment.cs +++ b/NodeFlow/Env/LocalFlowEnvironment.cs @@ -339,9 +339,14 @@ namespace Serein.NodeFlow.Env var projectData = new SereinProjectData() { Librarys = this._flowLibraryService.GetAllLibraryInfo().ToArray(), - Nodes = _flowModelService.GetAllNodeModel().Select(node => node.ToInfo()).Where(info => info is not null).ToArray(), - Canvass = _flowModelService.GetAllCanvasModel().Select(canvas => canvas.ToInfo()).ToArray(), - //StartNode = NodeModels.Values.FirstOrDefault(it => it.IsStart)?.Guid, + Nodes = _flowModelService.GetAllNodeModel() + .Select(node => node.ToInfo()) + .Where(info => info is not null) + .ToArray(), + Canvass = _flowModelService.GetAllCanvasModel() + .Where(canvas => canvas.Nodes.Count != 0 && !"Default".Equals(canvas.Name)) + .Select(canvas => canvas.ToInfo()) + .ToArray(), }; return projectData; diff --git a/NodeFlow/FlowNodeExtension.cs b/NodeFlow/FlowNodeExtension.cs index f880a2d..68bccb3 100644 --- a/NodeFlow/FlowNodeExtension.cs +++ b/NodeFlow/FlowNodeExtension.cs @@ -110,7 +110,7 @@ namespace Serein.NodeFlow - /// + /*/// /// 触发器运行后状态转为对应的后继分支类别 /// /// @@ -126,7 +126,7 @@ namespace Serein.NodeFlow FlipflopStateType.Cancel => ConnectionInvokeType.None, _ => throw new NotImplementedException("未定义的流程状态") }; - } + }*/ diff --git a/NodeFlow/Model/Nodes/SingleFlipflopNode.cs b/NodeFlow/Model/Nodes/SingleFlipflopNode.cs index e39422e..00be761 100644 --- a/NodeFlow/Model/Nodes/SingleFlipflopNode.cs +++ b/NodeFlow/Model/Nodes/SingleFlipflopNode.cs @@ -72,23 +72,12 @@ namespace Serein.NodeFlow.Model.Nodes instance = ioc.Get(md.ActingInstanceType); } - var args = MethodDetails.ParameterDetailss.Length == 0 ? [] : await this.GetParametersAsync(context, token); + var args = MethodDetails.ParameterDetailss.Length == 0 ? [] + : await this.GetParametersAsync(context, token); - // 因为这里会返回不确定的泛型 IFlipflopContext - // 而我们只需要获取到 State 和 Value(返回的数据) - // 所以使用 dynamic 类型接收 - dynamic flipflopContext = await dd.InvokeAsync(instance, args); - FlipflopStateType flipflopStateType = flipflopContext.State; - context.NextOrientation = flipflopStateType.ToContentType(); - - - if (flipflopContext.Type == TriggerDescription.Overtime) - { - throw new FlipflopException(MethodDetails.MethodName + "触发器超时触发。Guid" + Guid); - } - object result = flipflopContext.Value; - var flowReslt = FlowResult.OK(this.Guid, context, result); - return flowReslt; + var result = await dd.InvokeAsync(instance, args); + var flowResult = FlowResult.OK(this.Guid, context, result); + return flowResult; } } diff --git a/NodeFlow/Tool/FlowWorkManagement.cs b/NodeFlow/Tool/FlowWorkManagement.cs index a64bb70..a823be9 100644 --- a/NodeFlow/Tool/FlowWorkManagement.cs +++ b/NodeFlow/Tool/FlowWorkManagement.cs @@ -409,16 +409,6 @@ namespace Serein.NodeFlow.Services continue; } await CallSuccessorNodesAsync(flipflopNode, token, pool, context); - /*if (flipflopNode.IsWaitSuccessorNodes) - { - _ = Task.Run(async () => await CallSuccessorNodesAsync(flipflopNode, token, pool, context)); - } - else - { - await CallSuccessorNodesAsync(flipflopNode, token, pool, context); - }*/ - - } catch (FlipflopException ex) { diff --git a/NodeFlow/Tool/NodeMethodDetailsHelper.cs b/NodeFlow/Tool/NodeMethodDetailsHelper.cs index 9ec2a74..b0450f7 100644 --- a/NodeFlow/Tool/NodeMethodDetailsHelper.cs +++ b/NodeFlow/Tool/NodeMethodDetailsHelper.cs @@ -103,20 +103,34 @@ public static class NodeMethodDetailsHelper if (methodInfo.ReturnType.IsGenericType && methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) { // 获取 Task<> 的泛型参数类型 - var innerType = methodInfo.ReturnType.GetGenericArguments()[0]; - if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(IFlipflopContext<>)) + var innerTypes = methodInfo.ReturnType.GetGenericArguments(); + if(innerTypes.Length == 1) { - var flipflopType = innerType.GetGenericArguments()[0]; - returnType = flipflopType; + var innerType = innerTypes[0]; + returnType = innerType; } else { - SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建,返回类型非预期的Task>。"); + SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建,返回类型非预期的Task。"); outMethodInfo = null; methodDetails = null; delegateDetails = null; return false; } + //var innerType = methodInfo.ReturnType.GetGenericArguments()[0]; + //if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(IFlipflopContext<>)) + //{ + // var flipflopType = innerType.GetGenericArguments()[0]; + // returnType = flipflopType; + //} + //else + //{ + // SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建,返回类型非预期的Task>。"); + // outMethodInfo = null; + // methodDetails = null; + // delegateDetails = null; + // return false; + //} } else { diff --git a/Serein.Script/Node/ValueNode/BooleanNode.cs b/Serein.Script/Node/ValueNode/BooleanNode.cs index c41d133..19c3cf8 100644 --- a/Serein.Script/Node/ValueNode/BooleanNode.cs +++ b/Serein.Script/Node/ValueNode/BooleanNode.cs @@ -9,14 +9,7 @@ namespace Serein.Script.Node /// /// 布尔字面量 /// - public class BooleanNode : ASTNode + public class BooleanNode(bool value) : ValueNode(value) { - public bool Value { get; } - public BooleanNode(bool value) => Value = value; - - public override string ToString() - { - return $"{Value}"; - } } } diff --git a/Serein.Script/Node/ValueNode/CharNode.cs b/Serein.Script/Node/ValueNode/CharNode.cs index b717d25..6a37f6b 100644 --- a/Serein.Script/Node/ValueNode/CharNode.cs +++ b/Serein.Script/Node/ValueNode/CharNode.cs @@ -6,16 +6,7 @@ using System.Threading.Tasks; namespace Serein.Script.Node { - internal class CharNode : ASTNode + internal class CharNode(char value) : ValueNode(value) { - public char Value { get; } - public CharNode(string value) - { - Value = char.Parse(value); - } - public override string ToString() - { - return $"'{Value}'"; - } } } diff --git a/Serein.Script/Node/ValueNode/NumberNode.cs b/Serein.Script/Node/ValueNode/NumberNode.cs index 13fc133..55b6dce 100644 --- a/Serein.Script/Node/ValueNode/NumberNode.cs +++ b/Serein.Script/Node/ValueNode/NumberNode.cs @@ -9,15 +9,9 @@ namespace Serein.Script.Node /// /// 数值型节点 /// - public abstract class NumberNode : ASTNode where T : struct, IComparable + public abstract class NumberNode : ValueNode where T : struct, IComparable { - public T Value { get; } public NumberNode(T value) => Value = value; - - public override string ToString() - { - return $"{Value}"; - } } diff --git a/Serein.Script/Node/ValueNode/RawStringNode.cs b/Serein.Script/Node/ValueNode/RawStringNode.cs new file mode 100644 index 0000000..883305d --- /dev/null +++ b/Serein.Script/Node/ValueNode/RawStringNode.cs @@ -0,0 +1,8 @@ +namespace Serein.Script.Node +{ + public class RawStringNode(string value) : ValueNode(value) + { + } + + +} diff --git a/Serein.Script/Node/ValueNode/StringNode.cs b/Serein.Script/Node/ValueNode/StringNode.cs index 7e310ea..1b99678 100644 --- a/Serein.Script/Node/ValueNode/StringNode.cs +++ b/Serein.Script/Node/ValueNode/StringNode.cs @@ -9,11 +9,9 @@ namespace Serein.Script.Node /// /// 字符串字面量节点 /// - public class StringNode : ASTNode + public class StringNode : ValueNode { - public string Value { get; } - - public StringNode(string input) + public StringNode(string input) : base() { // 使用 StringBuilder 来构建输出 StringBuilder output = new StringBuilder(input.Length); @@ -59,11 +57,6 @@ namespace Serein.Script.Node } Value = output.ToString(); } - - public override string ToString() - { - return $"\"{Value}\""; - } } diff --git a/Serein.Script/Node/ValueNode/ValueNode.cs b/Serein.Script/Node/ValueNode/ValueNode.cs new file mode 100644 index 0000000..b509400 --- /dev/null +++ b/Serein.Script/Node/ValueNode/ValueNode.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Script.Node +{ + public class ValueNode : ASTNode + { + public T Value { get; protected set; } + + public ValueNode() + { + + } + public ValueNode(T value) + { + this.Value = value; + } + public override string ToString() + { + return $"{Value}"; + } + } +} diff --git a/Serein.Script/SereinScriptInterpreter.cs b/Serein.Script/SereinScriptInterpreter.cs index a381f85..139d6a7 100644 --- a/Serein.Script/SereinScriptInterpreter.cs +++ b/Serein.Script/SereinScriptInterpreter.cs @@ -56,6 +56,8 @@ namespace Serein.Script return null; // 返回 null case CharNode charNode: // char字面量 return charNode.Value; // 返回字符值 + case RawStringNode rawStringNode: + return rawStringNode.Value; // 返回原始字符串值 case StringNode stringNode: // 字符串字面量 return stringNode.Value; // 返回字符串值 case BooleanNode booleanNode: // 布尔值字面量 diff --git a/Serein.Script/SereinScriptLexer.cs b/Serein.Script/SereinScriptLexer.cs index 2eb2363..dca7162 100644 --- a/Serein.Script/SereinScriptLexer.cs +++ b/Serein.Script/SereinScriptLexer.cs @@ -1,4 +1,7 @@ -namespace Serein.Script +using System.Data.Common; +using System.Numerics; + +namespace Serein.Script { /// /// Serein脚本词法分析器的Token类型 @@ -38,6 +41,10 @@ /// String, /// + /// 原始字符串(多行字符串) + /// + RawString, + /// /// Char字符 /// Char, @@ -217,7 +224,7 @@ if (_input[_index + 1] == '"' && _input[_index + 2] == '"') { - var value = _input.Slice(_index, 4).ToString(); + //var value = _input.Slice(_index, 4).ToString(); // 原始字符串 return ReadRawString(); @@ -450,27 +457,38 @@ private Token ReadRawString() { - // skip opening triple quotes - _index += 3; - - var start = _index; - while (_index + 2 < _input.Length) + int startLine = _row; + _index += 3; // 跳过开头 """ + int index = _index; + var contentStart = index; + while (index + 2 < _input.Length) { - if (_input[_index] == '"' && _input[_index + 1] == '"' && _input[_index + 2] == '"') + char current = _input[index]; + + // 行号处理 + if (current == '\n') { - var value = _input.Slice(start, _index - start).ToString(); - _index += 3; // skip closing """ - return CreateToken(TokenType.String, value); + _row++; + } + + // 检查是否是结束符 """ + if (_input[index] != '"' || _input[index + 1] != '"' || _input[index + 2] != '"') + { + index++; + } + else + { + var value = _input.Slice(contentStart, index - contentStart).ToString(); + _index += 3; + // 构建带行号信息的 Token(假设 Token 有 StartLine 属性) + return CreateToken(TokenType.RawString, value); } - _index++; } - throw new Exception("Unterminated raw string literal"); - + throw new Exception($"Unterminated raw string literal starting at line {startLine}"); } - /// /// 读取硬编码的文本 /// diff --git a/Serein.Script/SereinScriptParser.cs b/Serein.Script/SereinScriptParser.cs index 1afdac2..c90ab49 100644 --- a/Serein.Script/SereinScriptParser.cs +++ b/Serein.Script/SereinScriptParser.cs @@ -112,9 +112,9 @@ namespace Serein.Script isAssignment = true; break; } - if(peekCount > 19999) + if(peekCount > 3999) { - throw new Exception("解析异常,peek次数过多,请减少脚本代码"); + throw new Exception("解析异常,peek次数过多,可能是解析器出了bug"); } } #endregion @@ -122,16 +122,19 @@ namespace Serein.Script #region 生成赋值语句/一般语句的ASTNode if (isAssignment) { + // 以赋值语句的形式进行处理 var assignmentNode = ParseAssignmentNode(); // 解析复制表达式 - NextToken();// 消耗 ";" + //if(_currentToken.Type == TokenType.Semicolon) + NextToken();// 消耗 ";" return assignmentNode; } else { // 以一般语句的形式进行处理,可当作表达式进行解析 - var targetNode = ParserExpression(); - NextToken();// 消耗 ";" + var targetNode = ParserExpression(); + //if (_currentToken.Type == TokenType.Semicolon) + NextToken();// 消耗 ";" return targetNode; } #endregion @@ -308,6 +311,7 @@ namespace Serein.Script } else { + var c = _currentToken; // 反转赋值。 NextToken(); // 消耗 "=" 并获取赋值语句的右值表达式。 ASTNode valueNode = ParserExpression(); @@ -515,6 +519,13 @@ namespace Serein.Script fieldTypeName = $"{fieldTypeName}.{_currentToken.Value}"; // 向后扩充 continue; } + else if (peekToken.Type == TokenType.SquareBracketsLeft) + { + NextToken(); // 消耗数组类型定义的 "[" + NextToken(); // 消耗数组类型定义的 "]" + fieldTypeName += "[]"; + continue; + } else if (peekToken.Type == TokenType.Identifier) { // 尝试解析变量名称 @@ -677,7 +688,7 @@ namespace Serein.Script } else if (_currentToken.Type == TokenType.BraceRight) { - break; + continue; } } else @@ -1009,6 +1020,7 @@ namespace Serein.Script if(peekToken.Type == TokenType.ParenthesisRight // 可能解析完了方法参数 || peekToken.Type == TokenType.Comma // 可能解析完了方法参数 || peekToken.Type == TokenType.Operator // 可能解析完了方法参数 + || peekToken.Type == TokenType.BraceRight // 可能解析完了方法参数 || peekToken.Type == TokenType.ParenthesisRight) // 可能解析完了下标索引 { return source; @@ -1065,6 +1077,13 @@ namespace Serein.Script NextToken(); // 消耗布尔量 return new BooleanNode(value).SetTokenInfo(factorToken); } + else if (_currentToken.Type == TokenType.RawString) + { + var value = _currentToken.Value; + NextToken(); // 消耗字符串 + var node = new RawStringNode(value).SetTokenInfo(factorToken); + return node; + } else if (_currentToken.Type == TokenType.String) { var value = _currentToken.Value; @@ -1076,7 +1095,8 @@ namespace Serein.Script { var value = _currentToken.Value; NextToken(); ; // 消耗Char - return new CharNode(value).SetTokenInfo(factorToken); + var @char = char.Parse(value); + return new CharNode(@char).SetTokenInfo(factorToken); } else if (_currentToken.Type == TokenType.InterpolatedString) { @@ -1186,4 +1206,6 @@ namespace Serein.Script } + + } diff --git a/Serein.Script/SereinScriptToCsharpScript.cs b/Serein.Script/SereinScriptToCsharpScript.cs index 5673bcb..2a20dbc 100644 --- a/Serein.Script/SereinScriptToCsharpScript.cs +++ b/Serein.Script/SereinScriptToCsharpScript.cs @@ -167,7 +167,7 @@ namespace Serein.Script }); return string.Join(',', values); } - + private void ConvertCode(ASTNode node) { switch (node) @@ -198,6 +198,12 @@ namespace Serein.Script case CharNode charNode: // char字面量 Append($"'{charNode.Value}'"); break; + case RawStringNode rawStringNode: // 原始字符串 + AppendLine(""); + Append("\"\"\""); + Append(rawStringNode.Value); + Append("\"\"\""); + break; case StringNode stringNode: // 字符串字面量 Append($"\"{stringNode.Value}\""); break; @@ -502,9 +508,9 @@ namespace Serein.Script } - - } } + + diff --git a/Serein.Script/SereinScriptTypeAnalysis.cs b/Serein.Script/SereinScriptTypeAnalysis.cs index adebee7..6fd60f7 100644 --- a/Serein.Script/SereinScriptTypeAnalysis.cs +++ b/Serein.Script/SereinScriptTypeAnalysis.cs @@ -128,6 +128,9 @@ namespace Serein.Script case CharNode charNode: // char字面量 NodeSymbolInfos[charNode] = typeof(char); return typeof(char); + case RawStringNode rawStringNode: // 原始字符串字面量(多行字符串) + NodeSymbolInfos[rawStringNode] = typeof(string); + return typeof(string); case StringNode stringNode: // 字符串字面量 NodeSymbolInfos[stringNode] = typeof(string); return typeof(string); @@ -507,6 +510,7 @@ namespace Serein.Script break; case NullNode nullNode: // null case CharNode charNode: // char字面量 + case RawStringNode rawStringNode: case StringNode stringNode: // 字符串字面量 case BooleanNode booleanNode: // 布尔值字面量 case NumberIntNode numberIntNode: // int整型数值字面量 @@ -558,6 +562,20 @@ namespace Serein.Script } AnalysisCollectionIndexNode(collectionIndexNode); break; + case CollectionAssignmentNode collectionAssignmentNode: // 集合赋值操作 + void AnalysisCollectionAssignmentNode(CollectionAssignmentNode collectionAssignmentNode) + { + Analysis(collectionAssignmentNode); + } + AnalysisCollectionAssignmentNode(collectionAssignmentNode); + break; + case ArrayDefintionNode arrayDefintionNode: + void AnalysisArrayDefintionNode(ArrayDefintionNode arrayDefintionNode) + { + Analysis(arrayDefintionNode); + } + AnalysisArrayDefintionNode(arrayDefintionNode); + break; case ClassTypeDefinitionNode classTypeDefinitionNode: // 类型定义 Analysis(classTypeDefinitionNode); break; diff --git a/Workbench/Test.cs b/Workbench/Test.cs deleted file mode 100644 index b2fa14c..0000000 --- a/Workbench/Test.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Serein.Workbench -{ - internal class Test - { - } -}