From a6d6790d31258f6cf60b721c2a45d8f7728e4fe5 Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Mon, 28 Jul 2025 20:21:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86Serein.Script?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E5=AF=B9=E8=B1=A1=E6=96=B9=E6=B3=95=E6=97=B6?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E5=8C=B9=E9=85=8D=E9=87=8D=E8=BD=BD=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NodeFlow/Model/Node/SingleConditionNode.cs | 16 ++-- README.md | 55 ++++++++++++ Serein.Script/SereinScriptInterpreter.cs | 98 ++-------------------- 3 files changed, 69 insertions(+), 100 deletions(-) diff --git a/NodeFlow/Model/Node/SingleConditionNode.cs b/NodeFlow/Model/Node/SingleConditionNode.cs index 9849f7d..f8455c4 100644 --- a/NodeFlow/Model/Node/SingleConditionNode.cs +++ b/NodeFlow/Model/Node/SingleConditionNode.cs @@ -200,16 +200,16 @@ namespace Serein.NodeFlow.Model var dataType = data is null ? typeof(object) : data.GetType(); if (expression[0..4].Equals("@get", StringComparison.CurrentCultureIgnoreCase)) { - expression = expression[4..]; + getValueExpression = expression[4..]; // 表达式默认包含 “.” - expression = $"return {dataName}{expression};"; + getValueExpression = $"return {dataName}{expression};"; } else { // 表达式默认包含 “.” - expression = $"return {expression};"; + getValueExpression = $"return {getValueExpression};"; } - var resultType = getValueScript.ParserScript(expression, new Dictionary + var resultType = getValueScript.ParserScript(getValueExpression, new Dictionary { { dataName, dataType}, }); @@ -239,18 +239,18 @@ namespace Serein.NodeFlow.Model conditionExpression = expression; conditionScript = new SereinScript(); var dataType = data is null ? typeof(object) : data.GetType(); - expression = expression.Trim(); + conditionExpression = expression.Trim(); if (expression[0] == '.') { // 对象取值 - expression = $"return {dataName}{expression};"; + conditionExpression = $"return {dataName}{expression};"; } else { // 直接表达式 - expression = $"return {dataName}{expression};"; + conditionExpression = $"return {dataName}.{expression};"; } - _ = conditionScript.ParserScript(expression, new Dictionary + var resultType = conditionScript.ParserScript(conditionExpression, new Dictionary { { dataName, dataType}, }); diff --git a/README.md b/README.md index 8805196..7b0668c 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,61 @@ https://space.bilibili.com/33526379 * 参数描述:Type,触发状态描述(External外部触发,Overtime超时触发),当你在代码中的其他地方主动触发了触发器,则该次触发类型为External,当你在创建触发器后超过了指定时间(创建触发器时会要求声明超时时间),则会自动触发,但触发类型为Overtime,触发参数未你在创建触发器时指定的值) * 参数描述:Value,触发时传递的参数。 * 使用场景:配合 FlowTrigger`` 使用,例如定时从PLC中获取状态,当某个变量发生改变时,会通知相应的触发器,如果需要,可以传递对应的数据。 + +* * **ExpOp- 表达式节点** + * 入参: 自定义的表达式。 + * 取值表达式:@Get + * 描述:有时节点返回了object,但下一个节点只需要对象中某个属性,而非整个对象。如果修改节点的定义,有可能破坏了代码的封装,为了解决这个痛点,于是增加了表达式节点。 + * 使用方法: + 1. 获取对象的属性成员: + ~~~~ + @Get .[property]/[field] + ~~~~ + 2. 获取对象的数组成员中下标为22的项: + ~~~~ + @Get .array[22] + ~~~~ + 3. 获取对象的字典成员中键为“zhangsan”的值: + ~~~ + @Get .dict["zhangsan"] + ~~~ +* **ExpCondition - 条件表达式节点** + * 入参: 自定义。 + * 描述:与表达式节点不同,条件表达式节点是判断条件是否成立,如果成立,返回true,否则返回false,如果表达式执行失败,而进入 error 分支。 + * 使用方式: + * 入参说明:默认从上一节点获取,也可以显式设定值,也可以参考表达式节点,使用“@Get .[property]/[field]”的方式重新定义入参数据,用以条件表达式判断。 + * 条件表达式:默认“PASS”,代表跳过判断直接进入下一个分支。 + * 条件表达式格式“.[property]/[field]< type> [op] value”,注意,开头必须使用“.”符号,这有助于显然的表达需要从入参对象中取内部某个值。 + * 表达式符号说明: + * [property] /[field] : 属性/字段 + * [op] : 操作符 + 1. bool表达式:== + 2. 数值表达式 :==、>=、 <=、in a-b (表示判断是否在a至b的数值范围内), !in a-b(取反); + 3. 文本表达式:==/equals(等于)、!=/notequals(不等于)、c/contains(出现过)、nc/doesnotcontain(没有出现过)、sw/startswith(开头等于)、ew/endswith(结尾等于) + * < type> :指定需要转换为某个类型,可不选。 + * [value] : 条件值 + * 使用示例: + ~~~ + 场景1:上一个节点传入了该对象(伪代码): + class Data + { + string Name; // 性能 + string Age; // 年龄,外部传入了文本类型 + string IdentityCardNumber; // 身份证号 + + } + 需求:需要判断年龄是否在某个区间,例如需要大于18岁,小于35岁。 + 条件表达式:.Age in 18-35 + + + + 需求:需要判断是否是北京身份证(开头为”1100”)。 + 条件表达式:.IdentityCardNumber sw 1100 + 另一种方法: + 入参使用表达式:@Get .IdentityCardNumber + 条件表达式:sw 1100 + + ~~~ * **UI - 自定义控件** * 入参:默认使用上一节点返回值。 * 返回值:IEmbeddedContent 接口 diff --git a/Serein.Script/SereinScriptInterpreter.cs b/Serein.Script/SereinScriptInterpreter.cs index f2bd5b4..c7d723d 100644 --- a/Serein.Script/SereinScriptInterpreter.cs +++ b/Serein.Script/SereinScriptInterpreter.cs @@ -261,7 +261,12 @@ namespace Serein.Script if (!ASTDelegateDetails.TryGetValue(memberFunctionCallNode, out DelegateDetails? delegateDetails)) { var methodName = memberFunctionCallNode.FunctionName; - var methodInfo = memberFunctionCallNode.Arguments.Count == 0 ? target?.GetType().GetMethod(methodName, []) : target?.GetType().GetMethod(methodName);// 获取参数列表的类型 + var argTypes = (memberFunctionCallNode.Arguments.Count == 0) switch + { + true => [], + false => memberFunctionCallNode.Arguments.Select(arg => symbolInfos[arg]).ToArray() + }; + var methodInfo = target?.GetType().GetMethod(methodName, argTypes); // 获取参数列表的类型 if (methodInfo is null) throw new SereinSciptParserException(memberFunctionCallNode, $"对象没有方法\"{memberFunctionCallNode.FunctionName}\""); delegateDetails = new DelegateDetails(methodInfo); ASTDelegateDetails[memberFunctionCallNode] = delegateDetails; @@ -468,52 +473,6 @@ namespace Serein.Script return; } -#if false - - // 解析数组/集合名与索引部分 - var targetType = collectionValue.GetType(); // 目标对象的类型 - #region 处理键值对 - if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) - { - // 目标是键值对 - var methodInfo = targetType.GetMethod("set_Item", BindingFlags.Public | BindingFlags.Instance); - if (methodInfo is not null) - { - - methodInfo.Invoke(collectionValue, [indexValue, valueValue]); - } - } - #endregion - #region 处理集合对象 - else - { - if (indexValue is int index) - { - // 获取数组或集合对象 - // 访问数组或集合中的指定索引 - if (collectionValue is Array array) - { - if (index < 0 || index >= array.Length) - { - throw new ArgumentException($"解析{collectionValue}节点时,数组下标越界。"); - } - array.SetValue(valueValue, index); - return; - } - else if (collectionValue is IList list) - { - if (index < 0 || index >= list.Count) - { - throw new ArgumentException($"解析{collectionValue}节点时,数组下标越界。"); - } - list[index] = valueValue; - return; - } - } - } - #endregion - throw new ArgumentException($"解析异常, {collectionValue} 并非有效集合。"); -#endif } /// @@ -545,51 +504,6 @@ namespace Serein.Script return result; } -#if false - // 解析数组/集合名与索引部分 - var targetType = collectionValue.GetType(); // 目标对象的类型 - #region 处理键值对 - if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) - { - // 目标是键值对 - var method = targetType.GetMethod("get_Item", BindingFlags.Public | BindingFlags.Instance); - if (method is not null) - { - var value = method.Invoke(collectionValue, [indexValue]); - return value; - } - } - #endregion - #region 处理集合对象 - else - { - if (indexValue is int index) - { - // 获取数组或集合对象 - // 访问数组或集合中的指定索引 - if (collectionValue is Array array) - { - if (index < 0 || index >= array.Length) - { - throw new ArgumentException($"解析{collectionValue}节点时,数组下标越界。"); - } - - return array.GetValue(index); - } - else if (collectionValue is IList list) - { - if (index < 0 || index >= list.Count) - { - throw new ArgumentException($"解析{collectionValue}节点时,数组下标越界。"); - } - return list[index]; - } - } - } - #endregion - - throw new ArgumentException($"解析{collectionValue}节点时,左值并非有效集合。"); -#endif }