修改了脚本执行的一些BUG。

This commit is contained in:
fengjiayi
2024-12-21 20:47:31 +08:00
parent 6e864a9ae4
commit 0f9c9b9988
19 changed files with 451 additions and 191 deletions

View File

@@ -20,6 +20,9 @@ namespace Serein.Library.Core
RunState = RunState.Running; RunState = RunState.Running;
} }
private readonly string _guid = global::System.Guid.NewGuid().ToString();
string IDynamicContext.Guid => _guid;
/// <summary> /// <summary>
/// 运行环境 /// 运行环境
/// </summary> /// </summary>

View File

@@ -19,6 +19,8 @@ namespace Serein.Library.Framework.NodeFlow
RunState = RunState.Running; RunState = RunState.Running;
} }
private readonly string _guid = global::System.Guid.NewGuid().ToString();
string IDynamicContext.Guid => _guid;
/// <summary> /// <summary>
/// 运行环境 /// 运行环境

View File

@@ -11,6 +11,11 @@ namespace Serein.Library.Api
/// </summary> /// </summary>
public interface IDynamicContext public interface IDynamicContext
{ {
/// <summary>
/// 标识流程
/// </summary>
string Guid {get; }
/// <summary> /// <summary>
/// 运行环境包含IOC容器。 /// 运行环境包含IOC容器。
/// </summary> /// </summary>

View File

@@ -30,13 +30,13 @@ namespace Serein.Library.Api
/// </summary> /// </summary>
/// <param name="index"></param> /// <param name="index"></param>
/// <returns></returns> /// <returns></returns>
object GetDataOfParams(int index); object GetArgData(int index);
/// <summary> /// <summary>
/// 根据入参名称从入参数据获取数据 /// 根据入参名称从入参数据获取数据
/// </summary> /// </summary>
/// <param name="name"></param> /// <param name="name"></param>
/// <returns></returns> /// <returns></returns>
object GetDataOfParams(string name); // object GetDataOfParams(string name);
/// <summary> /// <summary>
/// 获取全局数据 /// 获取全局数据
/// </summary> /// </summary>

View File

@@ -393,7 +393,7 @@ namespace Serein.Library
md.ActingInstance = context.Env.IOC.Get(md.ActingInstanceType); md.ActingInstance = context.Env.IOC.Get(md.ActingInstanceType);
} }
object[] args = await GetParametersAsync(context, this, md); object[] args = await GetParametersAsync(context, this);
var result = await dd.InvokeAsync(md.ActingInstance, args); var result = await dd.InvokeAsync(md.ActingInstance, args);
return result; return result;
@@ -403,10 +403,10 @@ namespace Serein.Library
/// 获取对应的参数数组 /// 获取对应的参数数组
/// </summary> /// </summary>
public static async Task<object[]> GetParametersAsync(IDynamicContext context, public static async Task<object[]> GetParametersAsync(IDynamicContext context,
NodeModelBase nodeModel, NodeModelBase nodeModel)
MethodDetails md)
{ {
// 用正确的大小初始化参数数组 // 用正确的大小初始化参数数组
var md = nodeModel.MethodDetails;
if (md.ParameterDetailss.Length == 0) if (md.ParameterDetailss.Length == 0)
{ {
return null;// md.ActingInstance return null;// md.ActingInstance
@@ -670,8 +670,6 @@ namespace Serein.Library
// parameters[i] = result; // parameters[i] = result;
//} //}
} }

View File

@@ -65,11 +65,18 @@ namespace Serein.Library.Utils
} }
} }
/// <summary>
public static Type CreateTypeWithProperties(IDictionary<string, Type> properties, string typeName) /// 创建具有属性的类型
/// </summary>
/// <param name="properties">成员属性名称及类型</param>
/// <param name="typeName">类型名称</param>
/// <param name="isOverlay">是否覆盖</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static Type CreateTypeWithProperties(IDictionary<string, Type> properties, string typeName, bool isOverlay = false)
{ {
// 如果类型已经缓存,直接返回缓存的类型 // 如果类型已经缓存,且没有显示指定需要覆盖,直接返回缓存的类型
if (typeCache.ContainsKey(typeName)) if (typeCache.ContainsKey(typeName) && !isOverlay)
{ {
return typeCache[typeName]; return typeCache[typeName];
} }

View File

@@ -1026,6 +1026,10 @@ namespace Serein.NodeFlow.Env
#region #region
foreach (var toNode in NodeModels.Values) foreach (var toNode in NodeModels.Values)
{ {
if(toNode.MethodDetails.ParameterDetailss == null)
{
continue;
}
for (var i = 0; i < toNode.MethodDetails.ParameterDetailss.Length; i++) for (var i = 0; i < toNode.MethodDetails.ParameterDetailss.Length; i++)
{ {
var pd = toNode.MethodDetails.ParameterDetailss[i]; var pd = toNode.MethodDetails.ParameterDetailss[i];
@@ -1589,6 +1593,8 @@ namespace Serein.NodeFlow.Env
NodeConnectChangeEventArgs.ConnectChangeType.Remote // 是创建连接还是删除连接 NodeConnectChangeEventArgs.ConnectChangeType.Remote // 是创建连接还是删除连接
))); // 通知UI ))); // 通知UI
} }
for (int i = 0; i < argInfo.Length; i++) for (int i = 0; i < argInfo.Length; i++)
{ {
ParameterDetails? pd = nodeModel.MethodDetails.ParameterDetailss[i]; ParameterDetails? pd = nodeModel.MethodDetails.ParameterDetailss[i];

View File

@@ -22,7 +22,8 @@ namespace Serein.NodeFlow.Env
{ {
if(nodeControlType == NodeControlType.ExpCondition if(nodeControlType == NodeControlType.ExpCondition
|| nodeControlType == NodeControlType.ExpOp || nodeControlType == NodeControlType.ExpOp
|| nodeControlType == NodeControlType.GlobalData) || nodeControlType == NodeControlType.GlobalData
|| nodeControlType == NodeControlType.Script)
{ {
return true; return true;
} }
@@ -70,6 +71,8 @@ namespace Serein.NodeFlow.Env
} }
/// <summary> /// <summary>
/// 从节点信息读取节点类型 /// 从节点信息读取节点类型
/// </summary> /// </summary>

View File

@@ -41,7 +41,7 @@ namespace Serein.NodeFlow.Model
} }
object instance = md.ActingInstance; object instance = md.ActingInstance;
var args = await GetParametersAsync(context, this, md); var args = await GetParametersAsync(context, this);
// 因为这里会返回不确定的泛型 IFlipflopContext<TRsult> // 因为这里会返回不确定的泛型 IFlipflopContext<TRsult>
// 而我们只需要获取到 State 和 Value返回的数据 // 而我们只需要获取到 State 和 Value返回的数据
// 所以使用 dynamic 类型接收 // 所以使用 dynamic 类型接收

View File

@@ -5,7 +5,9 @@ using Serein.Script;
using Serein.Script.Node; using Serein.Script.Node;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Dynamic;
using System.Linq; using System.Linq;
using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -28,7 +30,7 @@ namespace Serein.NodeFlow.Model
private IScriptFlowApi ScriptFlowApi { get; } private IScriptFlowApi ScriptFlowApi { get; }
private ASTNode mainNode; private ASTNode mainNode;
private SereinScriptInterpreter ScriptInterpreter;
/// <summary> /// <summary>
/// 构建流程脚本节点 /// 构建流程脚本节点
/// </summary> /// </summary>
@@ -37,14 +39,11 @@ namespace Serein.NodeFlow.Model
{ {
//ScriptFlowApi = environment.IOC.Get<ScriptFlowApi>(); //ScriptFlowApi = environment.IOC.Get<ScriptFlowApi>();
ScriptFlowApi = new ScriptFlowApi(environment, this); ScriptFlowApi = new ScriptFlowApi(environment, this);
ScriptInterpreter = new SereinScriptInterpreter();
}
MethodInfo? method = this.GetType().GetMethod(nameof(GetFlowApi));
if (method != null)
{
SereinScriptInterpreter.AddFunction(nameof(GetFlowApi), method, () => this); // 挂载获取流程接口
}
static SingleScriptNode()
{
// 挂载静态方法 // 挂载静态方法
var tempMethods = typeof(BaseFunc).GetMethods().Where(method => var tempMethods = typeof(BaseFunc).GetMethods().Where(method =>
!(method.Name.Equals("GetHashCode") !(method.Name.Equals("GetHashCode")
@@ -52,21 +51,74 @@ namespace Serein.NodeFlow.Model
|| method.Name.Equals("ToString") || method.Name.Equals("ToString")
|| method.Name.Equals("GetType") || method.Name.Equals("GetType")
)).Select(method => (method.Name, method)).ToArray(); )).Select(method => (method.Name, method)).ToArray();
// 加载基础方法
foreach ((string name, MethodInfo method) item in tempMethods) foreach ((string name, MethodInfo method) item in tempMethods)
{ {
SereinScriptInterpreter.AddFunction(item.name, item.method); // 加载基础方法 SereinScriptInterpreter.AddStaticFunction(item.name, item.method);
} }
} }
public override void OnCreating()
{
MethodInfo? method = this.GetType().GetMethod(nameof(GetFlowApi));
if (method != null)
{
ScriptInterpreter.AddFunction(nameof(GetFlowApi), method, () => this); // 挂载获取流程接口
}
var md = MethodDetails;
var pd = md.ParameterDetailss ??= new ParameterDetails[1];
md.ParamsArgIndex = 0;
pd[0] = new ParameterDetails
{
Index = 0,
Name = "object",
IsExplicitData = true,
DataValue = string.Empty,
DataType = typeof(object),
ExplicitType = typeof(object),
ArgDataSourceNodeGuid = string.Empty,
ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData,
NodeModel = this,
ExplicitTypeName = "Value",
Items = null,
IsParams = true,
};
}
/// <summary> /// <summary>
/// 加载脚本代码 /// 导出脚本代码
/// </summary> /// </summary>
public void LoadScript() /// <param name="nodeInfo"></param>
/// <returns></returns>
public override NodeInfo SaveCustomData(NodeInfo nodeInfo)
{
dynamic data = new ExpandoObject();
data.Script = Script ?? "";
nodeInfo.CustomData = data;
return nodeInfo;
}
/// <summary>
/// 加载自定义数据
/// </summary>
/// <param name="nodeInfo"></param>
public override void LoadCustomData(NodeInfo nodeInfo)
{
this.Script = nodeInfo.CustomData?.Script ?? "";
}
/// <summary>
/// 重新加载脚本代码
/// </summary>
public void ReloadScript()
{ {
try try
{ {
mainNode = new SereinScriptParser(Script).Parse(); var p = new SereinScriptParser(Script);
mainNode = p.Parse();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -81,27 +133,33 @@ namespace Serein.NodeFlow.Model
/// <returns></returns> /// <returns></returns>
public override async Task<object?> ExecutingAsync(IDynamicContext context) public override async Task<object?> ExecutingAsync(IDynamicContext context)
{ {
var @params = await NodeModelBase.GetParametersAsync(context, this);
ScriptFlowApi.Context= context;
context.AddOrUpdate($"{context.Guid}_{this.Guid}_Params", @params[0]); // 后面再改
mainNode ??= new SereinScriptParser(Script).Parse(); mainNode ??= new SereinScriptParser(Script).Parse();
SereinScriptInterpreter scriptInterpreter = new SereinScriptInterpreter(); IScriptInvokeContext scriptContext = new ScriptInvokeContext();
var result = await scriptInterpreter.InterpretAsync(mainNode); // 从入口节点执行 var result = await ScriptInterpreter.InterpretAsync(scriptContext, mainNode); // 从入口节点执行
scriptInterpreter.ResetVar();
return result; return result;
} }
public IScriptFlowApi GetFlowApi() #region
{
return ScriptFlowApi; public IScriptFlowApi GetFlowApi()
{
return ScriptFlowApi;
} }
private static class BaseFunc private static class BaseFunc
{ {
public static DateTime GetNow() => DateTime.Now;
public static Type TypeOf(object type) public static Type TypeOf(object type)
{ {
return type.GetType(); return type.GetType();
} }
public static void Print(object value) public static void Print(object value)
{ {
@@ -120,7 +178,7 @@ namespace Serein.NodeFlow.Model
public static bool ToBool(object value) public static bool ToBool(object value)
{ {
return bool.Parse(value.ToString()); return bool.Parse(value.ToString());
} }
#endregion #endregion
public static async Task Delay(object value) public static async Task Delay(object value)
@@ -138,6 +196,7 @@ namespace Serein.NodeFlow.Model
} }
} }
} }
#endregion
} }
} }

View File

@@ -1,5 +1,6 @@
using Serein.Library; using Serein.Library;
using Serein.Library.Api; using Serein.Library.Api;
using Serein.Library.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -23,42 +24,47 @@ namespace Serein.NodeFlow
/// </summary> /// </summary>
public NodeModelBase NodeModel { get; private set; } public NodeModelBase NodeModel { get; private set; }
IDynamicContext IScriptFlowApi.Context { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public IDynamicContext? Context{ get; set; }
private string _paramsKey => $"{Context?.Guid}_{NodeModel.Guid}_Params";
/// <summary> /// <summary>
/// 创建流程脚本接口 /// 创建流程脚本接口
/// </summary> /// </summary>
/// <param name="environment">运行环境</param> /// <param name="environment">运行环境</param>
/// <param name="nodeModel">节点</param> /// <param name="nodeModel">节点</param>
public ScriptFlowApi(IFlowEnvironment environment, IDynamicContext dynamicContext, NodeModelBase nodeModel) public ScriptFlowApi(IFlowEnvironment environment, NodeModelBase nodeModel)
{ {
Env = environment; Env = environment;
NodeModel = nodeModel; NodeModel = nodeModel;
} }
Task<object> IScriptFlowApi.CallNode(string nodeGuid) public Task<object> CallNode(string nodeGuid)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
object IScriptFlowApi.GetDataOfParams(int index) public object? GetArgData(int index)
{ {
throw new NotImplementedException(); var obj = Context?.GetFlowData(_paramsKey);
if (obj is object[] @params && index < @params.Length)
{
return @params[index];
}
return null;
} }
object IScriptFlowApi.GetDataOfParams(string name)
public object? GetFlowData()
{ {
throw new NotImplementedException(); return Context?.GetFlowData(NodeModel.Guid);
} }
object IScriptFlowApi.GetFlowData() public object? GetGlobalData(string keyName)
{ {
throw new NotImplementedException(); return SereinEnv.GetFlowGlobalData(keyName);
}
object IScriptFlowApi.GetGlobalData(string keyName)
{
throw new NotImplementedException();
} }
} }

View File

@@ -11,13 +11,15 @@ namespace Serein.Script.Node
/// </summary> /// </summary>
public class ClassTypeDefinitionNode : ASTNode public class ClassTypeDefinitionNode : ASTNode
{ {
public bool IsOverlay { get; set; }
public string ClassName { get; } public string ClassName { get; }
public Dictionary<string, Type> Fields { get; } public Dictionary<string, Type> Fields { get; }
public ClassTypeDefinitionNode(Dictionary<string, Type> fields, string className) public ClassTypeDefinitionNode(Dictionary<string, Type> fields, string className, bool isOverlay)
{ {
this.Fields = fields; this.Fields = fields;
this.ClassName = className; this.ClassName = className;
IsOverlay = isOverlay;
} }
} }

View File

@@ -21,8 +21,45 @@ namespace Serein.Script
} }
} }
/// <summary>
/// 脚本运行上下文
/// </summary>
public interface IScriptInvokeContext
{
/// <summary>
/// 是否该退出了
/// </summary>
bool IsReturn { get; }
public class SereinScriptInterpreter /// <summary>
/// 是否严格检查 Null 值 (禁止使用 Null
/// </summary>
bool IsCheckNullValue { get; }
/// <summary>
/// 获取变量的值
/// </summary>
/// <param name="varName"></param>
/// <returns></returns>
object GetVarValue(string varName);
/// <summary>
/// 设置变量的值
/// </summary>
/// <param name="varName"></param>
/// <param name="value"></param>
/// <returns></returns>
bool SetVarValue(string varName, object value);
/// <summary>
/// 结束调用
/// </summary>
/// <returns></returns>
bool Exit();
}
public class ScriptInvokeContext : IScriptInvokeContext
{ {
/// <summary> /// <summary>
/// 定义的变量 /// 定义的变量
@@ -30,25 +67,37 @@ namespace Serein.Script
private Dictionary<string, object> _variables = new Dictionary<string, object>(); private Dictionary<string, object> _variables = new Dictionary<string, object>();
/// <summary> /// <summary>
/// 挂载的函数 /// 是否该退出了
/// </summary> /// </summary>
private static Dictionary<string, DelegateDetails> _functionTable = new Dictionary<string, DelegateDetails>(); public bool IsReturn { get; set; }
/// <summary> /// <summary>
/// 挂载的函数调用的对象(用于函数需要实例才能调用的场景 /// 是否严格检查 Null 值 (禁止使用 Null
/// </summary> /// </summary>
private static Dictionary<string, Func<object>> _callFuncOfGetObjects = new Dictionary<string, Func<object>>(); public bool IsCheckNullValue { get; set; }
/// <summary> object IScriptInvokeContext.GetVarValue(string varName)
/// 定义的类型
/// </summary>
private static Dictionary<string, Type> _classDefinition = new Dictionary<string, Type>();
/// <summary>
/// 重置的变量
/// </summary>
public void ResetVar()
{ {
_variables.TryGetValue(varName, out var value);
return value;
}
bool IScriptInvokeContext.SetVarValue(string varName, object? value)
{
if (!_variables.TryAdd(varName, value))
{
_variables[varName] = value;
}
return true;
}
bool IScriptInvokeContext.Exit()
{
// 清理脚本中加载的非托管资源
foreach (var nodeObj in _variables.Values) foreach (var nodeObj in _variables.Values)
{ {
if (nodeObj is not null) if (nodeObj is not null)
@@ -64,14 +113,47 @@ namespace Serein.Script
} }
} }
_variables.Clear(); _variables.Clear();
return true ;
} }
}
public class SereinScriptInterpreter
{
/// <summary>
/// 挂载的函数
/// </summary>
private static Dictionary<string, DelegateDetails> _functionTable = new Dictionary<string, DelegateDetails>();
/// <summary>
/// 挂载的函数调用的对象(用于函数需要实例才能调用的场景)
/// </summary>
private Dictionary<string, Func<object>> _callFuncOfGetObjects = new Dictionary<string, Func<object>>();
/// <summary>
/// 定义的类型
/// </summary>
private Dictionary<string, Type> _classDefinition = new Dictionary<string, Type>();
/// <summary>
/// 挂载静态函数
/// </summary>
/// <param name="functionName"></param>
/// <param name="methodInfo"></param>
public static void AddStaticFunction(string functionName, MethodInfo methodInfo)
{
_functionTable[functionName] = new DelegateDetails(methodInfo);
}
/// <summary> /// <summary>
/// 挂载函数 /// 挂载函数
/// </summary> /// </summary>
/// <param name="functionName">函数名称</param> /// <param name="functionName">函数名称</param>
/// <param name="methodInfo">方法信息</param> /// <param name="methodInfo">方法信息</param>
public static void AddFunction(string functionName, MethodInfo methodInfo, Func<object>? callObj = null) public void AddFunction(string functionName, MethodInfo methodInfo, Func<object>? callObj = null)
{ {
//if (!_functionTable.ContainsKey(functionName)) //if (!_functionTable.ContainsKey(functionName))
//{ //{
@@ -84,12 +166,16 @@ namespace Serein.Script
} }
if(!methodInfo.IsStatic && callObj is not null) if(!methodInfo.IsStatic && callObj is not null && !_callFuncOfGetObjects.ContainsKey(functionName))
{ {
// 静态函数不需要 // 静态函数不需要
_callFuncOfGetObjects.Add(functionName, callObj); _callFuncOfGetObjects.Add(functionName, callObj);
} }
_functionTable[functionName] = new DelegateDetails(methodInfo); if (!_functionTable.ContainsKey(functionName))
{
_functionTable[functionName] = new DelegateDetails(methodInfo);
}
//_functionTable[functionName] = new DelegateDetails(methodInfo);
} }
/// <summary> /// <summary>
@@ -97,7 +183,7 @@ namespace Serein.Script
/// </summary> /// </summary>
/// <param name="typeName">函数名称</param> /// <param name="typeName">函数名称</param>
/// <param name="type">方法信息</param> /// <param name="type">方法信息</param>
public static void AddClassType(Type type , string typeName = "") public void AddClassType(Type type , string typeName = "")
{ {
if (string.IsNullOrEmpty(typeName)) if (string.IsNullOrEmpty(typeName))
{ {
@@ -115,7 +201,7 @@ namespace Serein.Script
/// </summary> /// </summary>
/// <param name="programNode"></param> /// <param name="programNode"></param>
/// <returns></returns> /// <returns></returns>
private async Task<object?> ExecutionProgramNodeAsync(ProgramNode programNode) private async Task<object?> ExecutionProgramNodeAsync(IScriptInvokeContext context, ProgramNode programNode)
{ {
// 遍历 ProgramNode 中的所有语句并执行它们 // 遍历 ProgramNode 中的所有语句并执行它们
foreach (var statement in programNode.Statements) foreach (var statement in programNode.Statements)
@@ -123,11 +209,11 @@ namespace Serein.Script
// 直接退出 // 直接退出
if (statement is ReturnNode returnNode) // 遇到 Return 语句 提前退出 if (statement is ReturnNode returnNode) // 遇到 Return 语句 提前退出
{ {
return await EvaluateAsync(statement); return await EvaluateAsync(context, statement);
} }
else else
{ {
await InterpretAsync(statement); await InterpretAsync(context, statement);
} }
} }
@@ -141,12 +227,16 @@ namespace Serein.Script
/// <returns></returns> /// <returns></returns>
private void ExecutionClassTypeDefinitionNode(ClassTypeDefinitionNode classTypeDefinitionNode) private void ExecutionClassTypeDefinitionNode(ClassTypeDefinitionNode classTypeDefinitionNode)
{ {
if (_classDefinition.ContainsKey(classTypeDefinitionNode.ClassName)) if (_classDefinition.ContainsKey(classTypeDefinitionNode.ClassName) && !classTypeDefinitionNode.IsOverlay)
{ {
//SereinEnv.WriteLine(InfoType.WARN, $"异常信息 : 类型重复定义,代码在第{classTypeDefinitionNode.Row}行: {classTypeDefinitionNode.Code.Trim()}"); //SereinEnv.WriteLine(InfoType.WARN, $"异常信息 : 类型重复定义,代码在第{classTypeDefinitionNode.Row}行: {classTypeDefinitionNode.Code.Trim()}");
return; return;
} }
var type = DynamicObjectHelper.CreateTypeWithProperties(classTypeDefinitionNode.Fields, classTypeDefinitionNode.ClassName);
var isOverlay = true; // classTypeDefinitionNode.IsOverlay;
var type = DynamicObjectHelper.CreateTypeWithProperties(classTypeDefinitionNode.Fields, classTypeDefinitionNode.ClassName, isOverlay); // 覆盖
classTypeDefinitionNode.IsOverlay = false; // 已经加载过,则不再覆盖
_classDefinition[classTypeDefinitionNode.ClassName] = type; // 定义对象 _classDefinition[classTypeDefinitionNode.ClassName] = type; // 定义对象
} }
@@ -156,9 +246,9 @@ namespace Serein.Script
/// <param name="ifNode"></param> /// <param name="ifNode"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="Exception"></exception> /// <exception cref="Exception"></exception>
private async Task ExecutionIfNodeAsync(IfNode ifNode) private async Task ExecutionIfNodeAsync(IScriptInvokeContext context, IfNode ifNode)
{ {
var result = await EvaluateAsync(ifNode.Condition) ?? throw new SereinSciptException(ifNode, $"条件语句返回了 null"); var result = await EvaluateAsync(context, ifNode.Condition) ?? throw new SereinSciptException(ifNode, $"条件语句返回了 null");
if (result is not bool condition) if (result is not bool condition)
{ {
@@ -169,14 +259,14 @@ namespace Serein.Script
{ {
foreach (var trueNode in ifNode.TrueBranch) foreach (var trueNode in ifNode.TrueBranch)
{ {
await InterpretAsync(trueNode); await InterpretAsync(context, trueNode);
} }
} }
else else
{ {
foreach (var falseNode in ifNode.FalseBranch) foreach (var falseNode in ifNode.FalseBranch)
{ {
await InterpretAsync(falseNode); await InterpretAsync(context,falseNode);
} }
} }
} }
@@ -187,11 +277,11 @@ namespace Serein.Script
/// <param name="whileNode"></param> /// <param name="whileNode"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="Exception"></exception> /// <exception cref="Exception"></exception>
private async Task ExectutionWhileNodeAsync(WhileNode whileNode) private async Task ExectutionWhileNodeAsync(IScriptInvokeContext context, WhileNode whileNode)
{ {
while (true) while (true)
{ {
var result = await EvaluateAsync(whileNode.Condition) ?? throw new SereinSciptException(whileNode, $"条件语句返回了 null"); var result = await EvaluateAsync(context, whileNode.Condition) ?? throw new SereinSciptException(whileNode, $"条件语句返回了 null");
if (result is not bool condition) if (result is not bool condition)
{ {
throw new SereinSciptException(whileNode, $"条件语句返回值不为 bool 类型(当前返回值类型为 {result.GetType()})"); throw new SereinSciptException(whileNode, $"条件语句返回值不为 bool 类型(当前返回值类型为 {result.GetType()})");
@@ -202,7 +292,7 @@ namespace Serein.Script
} }
foreach(var node in whileNode.Body) foreach(var node in whileNode.Body)
{ {
await InterpretAsync(node); await InterpretAsync(context, node);
} }
} }
} }
@@ -212,19 +302,19 @@ namespace Serein.Script
/// </summary> /// </summary>
/// <param name="assignmentNode"></param> /// <param name="assignmentNode"></param>
/// <returns></returns> /// <returns></returns>
private async Task ExecutionAssignmentNodeAsync(AssignmentNode assignmentNode) private async Task ExecutionAssignmentNodeAsync(IScriptInvokeContext context, AssignmentNode assignmentNode)
{ {
var tmp = await EvaluateAsync(assignmentNode.Value); var tmp = await EvaluateAsync(context, assignmentNode.Value);
_variables[assignmentNode.Variable] = tmp; context.SetVarValue(assignmentNode.Variable, tmp);
} }
private async Task<object> InterpretFunctionCallAsync(FunctionCallNode functionCallNode) private async Task<object> InterpretFunctionCallAsync(IScriptInvokeContext context, FunctionCallNode functionCallNode)
{ {
// 评估函数参数 // 评估函数参数
var arguments = new object?[functionCallNode.Arguments.Count]; var arguments = new object?[functionCallNode.Arguments.Count];
for (int i = 0; i < functionCallNode.Arguments.Count; i++) for (int i = 0; i < functionCallNode.Arguments.Count; i++)
{ {
ASTNode? arg = functionCallNode.Arguments[i]; ASTNode? arg = functionCallNode.Arguments[i];
arguments[i] = await EvaluateAsync(arg); // 评估每个参数 arguments[i] = await EvaluateAsync(context, arg); // 评估每个参数
} }
var funcName = functionCallNode.FunctionName; var funcName = functionCallNode.FunctionName;
@@ -263,7 +353,7 @@ namespace Serein.Script
public async Task<object?> InterpretAsync(ASTNode node) public async Task<object?> InterpretAsync(IScriptInvokeContext context, ASTNode node)
{ {
if(node == null) if(node == null)
{ {
@@ -273,30 +363,29 @@ namespace Serein.Script
switch (node) switch (node)
{ {
case ProgramNode programNode: // AST树入口 case ProgramNode programNode: // AST树入口
var scritResult = await ExecutionProgramNodeAsync(programNode); var scritResult = await ExecutionProgramNodeAsync(context, programNode);
return scritResult; // 遍历 ProgramNode 中的所有语句并执行它们 return scritResult; // 遍历 ProgramNode 中的所有语句并执行它们
case ClassTypeDefinitionNode classTypeDefinitionNode: // 定义类型 case ClassTypeDefinitionNode classTypeDefinitionNode: // 定义类型
ExecutionClassTypeDefinitionNode(classTypeDefinitionNode); ExecutionClassTypeDefinitionNode(classTypeDefinitionNode);
break; break;
case AssignmentNode assignment: // 出现在 = 右侧的表达式 case AssignmentNode assignment: // 出现在 = 右侧的表达式
await ExecutionAssignmentNodeAsync(assignment); await ExecutionAssignmentNodeAsync(context, assignment);
break; break;
case MemberAssignmentNode memberAssignmentNode: // 设置对象属性 case MemberAssignmentNode memberAssignmentNode: // 设置对象属性
await SetMemberValue(memberAssignmentNode); await SetMemberValue(context, memberAssignmentNode);
break; break;
case MemberFunctionCallNode memberFunctionCallNode: case MemberFunctionCallNode memberFunctionCallNode:
return await CallMemberFunction(memberFunctionCallNode); return await CallMemberFunction(context, memberFunctionCallNode);
break;
case IfNode ifNode: // 执行 if...else... 语句块 case IfNode ifNode: // 执行 if...else... 语句块
await ExecutionIfNodeAsync(ifNode); await ExecutionIfNodeAsync(context, ifNode);
break; break;
case WhileNode whileNode: // 循环语句块 case WhileNode whileNode: // 循环语句块
await ExectutionWhileNodeAsync(whileNode); await ExectutionWhileNodeAsync(context, whileNode);
break; break;
case FunctionCallNode functionCallNode: // 方法调用节点 case FunctionCallNode functionCallNode: // 方法调用节点
return await InterpretFunctionCallAsync(functionCallNode); return await InterpretFunctionCallAsync(context, functionCallNode);
case ReturnNode returnNode: case ReturnNode returnNode:
return await EvaluateAsync(returnNode); return await EvaluateAsync(context, returnNode);
default: default:
throw new SereinSciptException(node, "解释器 InterpretAsync() 未实现节点行为"); throw new SereinSciptException(node, "解释器 InterpretAsync() 未实现节点行为");
} }
@@ -304,7 +393,7 @@ namespace Serein.Script
} }
private async Task<object?> EvaluateAsync(ASTNode node) private async Task<object?> EvaluateAsync(IScriptInvokeContext context, ASTNode node)
{ {
if(node == null) if(node == null)
{ {
@@ -321,27 +410,17 @@ namespace Serein.Script
case StringNode stringNode: case StringNode stringNode:
return stringNode.Value; // 返回字符串值 return stringNode.Value; // 返回字符串值
case IdentifierNode identifierNode: case IdentifierNode identifierNode:
if (_variables.TryGetValue(identifierNode.Name, out var result)) return context.GetVarValue(identifierNode.Name);
{ //throw new SereinSciptException(identifierNode, "尝试使用值为null的变量");
//if(result == null) //throw new SereinSciptException(identifierNode, "尝试使用未声明的变量");
//{
// throw new SereinSciptException(identifierNode, "尝试使用值为null的变量");
//}
return result; // 获取变量值
}
else
{
throw new SereinSciptException(identifierNode, "尝试使用未声明的变量");
}
case BinaryOperationNode binOpNode: case BinaryOperationNode binOpNode:
// 递归计算二元操作 // 递归计算二元操作
var left = await EvaluateAsync(binOpNode.Left); var left = await EvaluateAsync(context, binOpNode.Left);
//if (left == null ) //if (left == null )
//{ //{
// throw new SereinSciptException(binOpNode.Left, $"左值尝试使用 null"); // throw new SereinSciptException(binOpNode.Left, $"左值尝试使用 null");
//} //}
var right = await EvaluateAsync(context, binOpNode.Right);
var right = await EvaluateAsync(binOpNode.Right);
//if (right == null) //if (right == null)
//{ //{
// throw new SereinSciptException(binOpNode.Right, "右值尝试使用计算 null"); // throw new SereinSciptException(binOpNode.Right, "右值尝试使用计算 null");
@@ -354,7 +433,7 @@ namespace Serein.Script
for (int i = 0; i < objectInstantiationNode.Arguments.Count; i++) for (int i = 0; i < objectInstantiationNode.Arguments.Count; i++)
{ {
var argNode = objectInstantiationNode.Arguments[i]; var argNode = objectInstantiationNode.Arguments[i];
args[i] = await EvaluateAsync(argNode); args[i] = await EvaluateAsync(context, argNode);
} }
var obj = Activator.CreateInstance(type,args: args);// 创建对象 var obj = Activator.CreateInstance(type,args: args);// 创建对象
if (obj == null) if (obj == null)
@@ -370,11 +449,13 @@ namespace Serein.Script
} }
case FunctionCallNode callNode: case FunctionCallNode callNode:
return await InterpretFunctionCallAsync(callNode); // 调用方法返回函数的返回值 return await InterpretFunctionCallAsync(context, callNode); // 调用方法返回函数的返回值
case MemberFunctionCallNode memberFunctionCallNode:
return await CallMemberFunction(context, memberFunctionCallNode);
case MemberAccessNode memberAccessNode: case MemberAccessNode memberAccessNode:
return await GetValue(memberAccessNode); return await GetValue(context, memberAccessNode);
case ReturnNode returnNode: // case ReturnNode returnNode: //
return await EvaluateAsync(returnNode.Value); // 直接返回响应的内容 return await EvaluateAsync(context, returnNode.Value); // 直接返回响应的内容
default: default:
throw new SereinSciptException(node, "解释器 EvaluateAsync() 未实现节点行为"); throw new SereinSciptException(node, "解释器 EvaluateAsync() 未实现节点行为");
} }
@@ -440,10 +521,10 @@ namespace Serein.Script
/// <param name="memberAssignmentNode"></param> /// <param name="memberAssignmentNode"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="SereinSciptException"></exception> /// <exception cref="SereinSciptException"></exception>
public async Task SetMemberValue(MemberAssignmentNode memberAssignmentNode) public async Task SetMemberValue(IScriptInvokeContext context, MemberAssignmentNode memberAssignmentNode)
{ {
var target = await EvaluateAsync(memberAssignmentNode.Object); var target = await EvaluateAsync(context, memberAssignmentNode.Object);
var value = await EvaluateAsync(memberAssignmentNode.Value); var value = await EvaluateAsync(context, memberAssignmentNode.Value);
// 设置值 // 设置值
var lastMember = memberAssignmentNode.MemberName; var lastMember = memberAssignmentNode.MemberName;
@@ -474,9 +555,9 @@ namespace Serein.Script
/// <param name="memberAccessNode"></param> /// <param name="memberAccessNode"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="SereinSciptException"></exception> /// <exception cref="SereinSciptException"></exception>
public async Task<object?> GetValue(MemberAccessNode memberAccessNode) public async Task<object?> GetValue(IScriptInvokeContext context, MemberAccessNode memberAccessNode)
{ {
var target = await EvaluateAsync(memberAccessNode.Object); var target = await EvaluateAsync(context, memberAccessNode.Object);
var lastMember = memberAccessNode.MemberName; var lastMember = memberAccessNode.MemberName;
var lastProperty = target?.GetType().GetProperty(lastMember); var lastProperty = target?.GetType().GetProperty(lastMember);
@@ -503,9 +584,9 @@ namespace Serein.Script
/// </summary> /// </summary>
private Dictionary<string, DelegateDetails> MethodToDelegateCaches { get; } = new Dictionary<string, DelegateDetails>(); private Dictionary<string, DelegateDetails> MethodToDelegateCaches { get; } = new Dictionary<string, DelegateDetails>();
public async Task<object?> CallMemberFunction(MemberFunctionCallNode memberFunctionCallNode) public async Task<object?> CallMemberFunction(IScriptInvokeContext context, MemberFunctionCallNode memberFunctionCallNode)
{ {
var target = await EvaluateAsync(memberFunctionCallNode.Object); var target = await EvaluateAsync(context, memberFunctionCallNode.Object);
var lastMember = memberFunctionCallNode.FunctionName; var lastMember = memberFunctionCallNode.FunctionName;
var methodInfo = target?.GetType().GetMethod(lastMember) ?? throw new SereinSciptException(memberFunctionCallNode, $"对象没有方法\"{memberFunctionCallNode.FunctionName}\""); var methodInfo = target?.GetType().GetMethod(lastMember) ?? throw new SereinSciptException(memberFunctionCallNode, $"对象没有方法\"{memberFunctionCallNode.FunctionName}\"");
@@ -521,7 +602,7 @@ namespace Serein.Script
for (int i = 0; i < memberFunctionCallNode.Arguments.Count; i++) for (int i = 0; i < memberFunctionCallNode.Arguments.Count; i++)
{ {
ASTNode? arg = memberFunctionCallNode.Arguments[i]; ASTNode? arg = memberFunctionCallNode.Arguments[i];
arguments[i] = await EvaluateAsync(arg); // 评估每个参数 arguments[i] = await EvaluateAsync(context, arg); // 评估每个参数
} }
return await delegateDetails.InvokeAsync(target, arguments); return await delegateDetails.InvokeAsync(target, arguments);

View File

@@ -27,6 +27,10 @@ namespace Serein.Script
/// </summary> /// </summary>
String, String,
/// <summary> /// <summary>
/// 插值字符串
/// </summary>
InterpolatedString,
/// <summary>
/// 关键字 /// 关键字
/// </summary> /// </summary>
Keyword, Keyword,
@@ -273,6 +277,9 @@ namespace Serein.Script
} }
case '.': case '.':
return CreateToken(TokenType.Dot, "."); return CreateToken(TokenType.Dot, ".");
//case '$':
// return CreateToken(TokenType.InterpolatedString, "$");
} }
throw new Exception("Unexpected character: " + currentChar); throw new Exception("Unexpected character: " + currentChar);

View File

@@ -3,6 +3,7 @@ using Serein.Library;
using Serein.Script.Node; using Serein.Script.Node;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection;
namespace Serein.Script namespace Serein.Script
{ {
@@ -32,6 +33,7 @@ namespace Serein.Script
Statements.Clear(); Statements.Clear();
while (_currentToken.Type != TokenType.EOF) while (_currentToken.Type != TokenType.EOF)
{ {
var astNode = Statement(); var astNode = Statement();
if (astNode == null) if (astNode == null)
{ {
@@ -41,8 +43,7 @@ namespace Serein.Script
//if (astNode is ClassTypeDefinitionNode) //if (astNode is ClassTypeDefinitionNode)
//{ //{
// // 类型定义置顶 // statements = [astNode, ..statements]; // 类型定义置顶
// statements = [astNode, ..statements];
//} //}
//else //else
//{ //{
@@ -61,9 +62,17 @@ namespace Serein.Script
} }
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "class") if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "class")
{ {
return ParseClassDefinition(); return ParseClassDefinition(); // 加载类,如果已经加载过,则忽略
} }
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "new")
{
var _peekToken = _lexer.PeekToken();
if (_peekToken.Type == TokenType.Keyword && _peekToken.Value == "class")
{
return ParseClassDefinition(); // 重新加载类
}
}
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "if") if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "if")
{ {
return ParseIf(); return ParseIf();
@@ -134,20 +143,30 @@ namespace Serein.Script
private ASTNode ParseAssignment() private ASTNode ParseAssignment()
{ {
string variableName = _currentToken.Value.ToString(); string variableName = _currentToken.Value.ToString();
_currentToken = _lexer.NextToken(); // consume identifier
if(_currentToken.Type == TokenType.ParenthesisRight) var _peekToken = _lexer.PeekToken();
if (_peekToken.Type == TokenType.ParenthesisRight)
{ {
_currentToken = _lexer.NextToken(); // 消耗标识符
return new IdentifierNode(variableName).SetTokenInfo(_currentToken); return new IdentifierNode(variableName).SetTokenInfo(_currentToken);
} }
if(_currentToken.Type == TokenType.Operator && _currentToken.Value == "=") if(_peekToken.Type == TokenType.Operator && _peekToken.Value == "=")
{ {
// 赋值行为 // 赋值行为
_currentToken = _lexer.NextToken(); // consume "=" _currentToken = _lexer.NextToken(); // 消耗标识符
_currentToken = _lexer.NextToken(); // 消耗 "="
var _tempToken = _lexer.PeekToken(); var _tempToken = _lexer.PeekToken();
ASTNode valueNode; ASTNode valueNode;
if (_tempToken.Type == TokenType.ParenthesisLeft)
if(_tempToken.Type == TokenType.Operator && _tempToken.Value != "=")
{
//_currentToken = _lexer.NextToken(); // 消耗操作符
//_currentToken = _lexer.NextToken(); // 消耗操作符
valueNode = Expression();
}
else if (_tempToken.Type == TokenType.ParenthesisLeft)
{ {
// 解析赋值右边的表达式 // 解析赋值右边的表达式
// 是函数调用,解析函数调用 // 是函数调用,解析函数调用
@@ -160,10 +179,16 @@ namespace Serein.Script
} }
return new AssignmentNode(variableName, valueNode).SetTokenInfo(_currentToken); return new AssignmentNode(variableName, valueNode).SetTokenInfo(_currentToken);
} }
if (_currentToken.Type == TokenType.Dot) if (_peekToken.Type == TokenType.Dot)
{ {
// 可能是方法调用
return ParseMemberAccessOrAssignment(); return ParseMemberAccessOrAssignment();
} }
if(_peekToken.Type == TokenType.Operator)
{
return new IdentifierNode(variableName).SetTokenInfo(_currentToken);
}
@@ -204,13 +229,19 @@ namespace Serein.Script
private ASTNode ParseClassDefinition() private ASTNode ParseClassDefinition()
{ {
bool isOverlay = false;
if (_currentToken.Value == "new")
{
isOverlay = true; // 重新加载类
_currentToken = _lexer.NextToken(); // 消耗 new 关键字
}
_currentToken = _lexer.NextToken(); // 消耗 class 关键字 _currentToken = _lexer.NextToken(); // 消耗 class 关键字
var className = _currentToken.Value.ToString(); // 获取定义的类名 var className = _currentToken.Value.ToString(); // 获取定义的类名
_currentToken = _lexer.NextToken(); // 消耗括号 _currentToken = _lexer.NextToken(); // 消耗类名
if (_currentToken.Type != TokenType.BraceLeft || _currentToken.Value != "{") if (_currentToken.Type != TokenType.BraceLeft || _currentToken.Value != "{")
throw new Exception("Expected '{' after class definition"); throw new Exception("Expected '{' after class definition");
var classFields = new Dictionary<string, Type>(); var classFields = new Dictionary<string, Type>();
_currentToken = _lexer.NextToken(); _currentToken = _lexer.NextToken(); // 消耗括号
while (_currentToken.Type != TokenType.BraceRight) while (_currentToken.Type != TokenType.BraceRight)
{ {
var fieldType = _currentToken.Value.ToString().ToTypeOfString(); // 获取定义的类名 var fieldType = _currentToken.Value.ToString().ToTypeOfString(); // 获取定义的类名
@@ -231,7 +262,7 @@ namespace Serein.Script
_currentToken = _lexer.NextToken(); _currentToken = _lexer.NextToken();
_currentToken = _lexer.NextToken(); _currentToken = _lexer.NextToken();
return new ClassTypeDefinitionNode(classFields, className).SetTokenInfo(_currentToken); return new ClassTypeDefinitionNode(classFields, className, isOverlay).SetTokenInfo(_currentToken);
} }
public ASTNode ParseObjectInstantiation() public ASTNode ParseObjectInstantiation()
@@ -310,7 +341,6 @@ namespace Serein.Script
else else
{ {
if(_peekToken.Type == TokenType.ParenthesisLeft) if(_peekToken.Type == TokenType.ParenthesisLeft)
{ {
// 成员方法调用 obj.Member(xxx); // 成员方法调用 obj.Member(xxx);
@@ -323,6 +353,8 @@ namespace Serein.Script
} }
else else
{ {
_currentToken = _lexer.NextToken(); // 消耗 成员名称
// 成员获取 // 成员获取
return new MemberAccessNode(identifierNode, memberName).SetTokenInfo(_currentToken); return new MemberAccessNode(identifierNode, memberName).SetTokenInfo(_currentToken);
} }
@@ -361,7 +393,7 @@ namespace Serein.Script
} }
} }
_currentToken = _lexer.NextToken(); // consume ")" //_currentToken = _lexer.NextToken(); // consume ")"
return new MemberFunctionCallNode(targetNode, functionName, arguments).SetTokenInfo(_currentToken); return new MemberFunctionCallNode(targetNode, functionName, arguments).SetTokenInfo(_currentToken);
} }
@@ -548,6 +580,15 @@ namespace Serein.Script
_currentToken = _lexer.NextToken(); // 消耗数字 _currentToken = _lexer.NextToken(); // 消耗数字
return new StringNode(text.ToString()).SetTokenInfo(_currentToken); return new StringNode(text.ToString()).SetTokenInfo(_currentToken);
} }
if( _currentToken.Type == TokenType.InterpolatedString)
{
// 可能是插值字符串;
// let context = $"a{A}b{B}c";
// let context = "a" + A + "b" + B + c;
_currentToken = _lexer.NextToken(); // 消耗字符串
while (_currentToken.Type == TokenType.String) {
}
}
if (_currentToken.Type == TokenType.Number) if (_currentToken.Type == TokenType.Number)
{ {
@@ -589,51 +630,6 @@ namespace Serein.Script
if (_identifierPeekToken.Type == TokenType.Dot) if (_identifierPeekToken.Type == TokenType.Dot)
{ {
return ParseMemberAccessOrAssignment(); return ParseMemberAccessOrAssignment();
var identifierNode = new IdentifierNode(identifier).SetTokenInfo(_currentToken);
// 处理成员访问identifier.member
if (_currentToken.Type == TokenType.Dot)
{
_currentToken = _lexer.NextToken(); // 消耗 "."
if (_currentToken.Type != TokenType.Identifier)
{
throw new Exception("Expected member name after dot.");
}
var memberName = _currentToken.Value;
_currentToken = _lexer.NextToken(); // 消耗成员名
if (_currentToken.Type == TokenType.Operator && _currentToken.Value == "=")
{
// 成员赋值 obj.Member = xxx;
_currentToken = _lexer.NextToken(); // 消耗 "="
var valueNode = Expression(); // 解析右值
return new MemberAssignmentNode(identifierNode, memberName, valueNode).SetTokenInfo(_currentToken);
}
else
{
var _peekToken = _lexer.PeekToken();
if (_peekToken.Type == TokenType.ParenthesisLeft)
{
// 成员方法调用 obj.Member(xxx);
return ParseFunctionCall();
}
else if (_peekToken.Type == TokenType.SquareBracketsLeft)
{
// 数组 index; 字典 key obj.Member[xxx];
return ParseCollectionIndex();
}
else
{
// 成员获取
return new MemberAccessNode(identifierNode, memberName).SetTokenInfo(_currentToken);
}
}
}
return identifierNode;
} }
_currentToken = _lexer.NextToken(); // 消耗标识符 _currentToken = _lexer.NextToken(); // 消耗标识符
return new IdentifierNode(identifier.ToString()).SetTokenInfo(_currentToken); return new IdentifierNode(identifier.ToString()).SetTokenInfo(_currentToken);

View File

@@ -16,10 +16,10 @@
<ControlTemplate TargetType="{x:Type local:MethodDetailsControl}"> <ControlTemplate TargetType="{x:Type local:MethodDetailsControl}">
<!--根据方法入参数量生成相应的控件--> <!--根据方法入参数量生成相应的控件-->
<ItemsControl ItemsSource="{Binding MethodDetails.ParameterDetailss, RelativeSource={RelativeSource TemplatedParent}}"> <ItemsControl ItemsSource="{Binding MethodDetails.ParameterDetailss, RelativeSource={RelativeSource TemplatedParent}}" Background="#E3FDFD" >
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<Grid Background="#E3FDFD" > <Grid >
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/> <ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/> <ColumnDefinition Width="auto"/>

View File

@@ -44,6 +44,8 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
@@ -56,9 +58,25 @@
<Button Content="执行" Margin="3,0,1,0" Command="{Binding CommandExecuting}" Height="17.2"></Button> <Button Content="执行" Margin="3,0,1,0" Command="{Binding CommandExecuting}" Height="17.2"></Button>
<!--<Button Content="刷新 " Command="{Binding CommandCopyDataExp}" Height="17.2" Margin="2,0,0,0"></Button>--> <!--<Button Content="刷新 " Command="{Binding CommandCopyDataExp}" Height="17.2" Margin="2,0,0,0"></Button>-->
</StackPanel> </StackPanel>
<themes:MethodDetailsControl Grid.Row="1" x:Name="MethodDetailsControl" MethodDetails="{Binding NodeModel.MethodDetails}"/>
<TextBox Grid.Row="2" MinHeight="20" MinWidth="100" MaxWidth="270" TextWrapping="Wrap" AcceptsReturn="True" Text="{Binding Script}"></TextBox>
<Grid Grid.Row="3" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderThickness="1">
<TextBlock Text="result ->" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<Border Grid.Column="1" BorderThickness="1">
<TextBlock Text="{Binding NodeModel.MethodDetails.ReturnType.FullName, Mode=OneTime}" TextTrimming="CharacterEllipsis" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Border>
<TextBox Grid.Row="1" MinHeight="20" MinWidth="100" MaxWidth="270" TextWrapping="Wrap" AcceptsReturn="True" Text="{Binding Script}"></TextBox> <Border Grid.Column="2" BorderThickness="1">
<local:ResultJunctionControl Grid.Column="2" MyNode="{Binding NodeModel}" x:Name="ResultJunctionControl" HorizontalAlignment="Right"/>
</Border>
</Grid>
<!--<RichTextBox x:Name="richTextBox" VerticalScrollBarVisibility="Auto" <!--<RichTextBox x:Name="richTextBox" VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
@@ -69,7 +87,7 @@
<local:ResultJunctionControl Grid.Column="2" MyNode="{Binding NodelModel}" x:Name="ResultJunctionControl" HorizontalAlignment="Right"/> <local:ResultJunctionControl Grid.Column="2" MyNode="{Binding NodelModel}" x:Name="ResultJunctionControl" HorizontalAlignment="Right"/>
<TextBlock Text="设置数据源" Margin="2" HorizontalAlignment="Stretch" VerticalAlignment="Center"/> <TextBlock Text="设置数据源" Margin="2" HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
</StackPanel>--> </StackPanel>-->
</Grid> </Grid>
</Grid> </Grid>
</local:NodeControlBase> </local:NodeControlBase>

View File

@@ -21,7 +21,7 @@ namespace Serein.Workbench.Node.View
/// <summary> /// <summary>
/// ScriptNodeControl.xaml 的交互逻辑 /// ScriptNodeControl.xaml 的交互逻辑
/// </summary> /// </summary>
public partial class ScriptNodeControl : NodeControlBase public partial class ScriptNodeControl : NodeControlBase , INodeJunction
{ {
private ScriptNodeControlViewModel viewModel => (ScriptNodeControlViewModel)ViewModel; private ScriptNodeControlViewModel viewModel => (ScriptNodeControlViewModel)ViewModel;
private DispatcherTimer _debounceTimer; // 用于延迟更新 private DispatcherTimer _debounceTimer; // 用于延迟更新
@@ -45,6 +45,73 @@ namespace Serein.Workbench.Node.View
} }
/// <summary>
/// 入参控制点(可能有,可能没)
/// </summary>
JunctionControlBase INodeJunction.ExecuteJunction => this.ExecuteJunctionControl;
/// <summary>
/// 下一个调用方法控制点(可能有,可能没)
/// </summary>
JunctionControlBase INodeJunction.NextStepJunction => this.NextStepJunctionControl;
/// <summary>
/// 返回值控制点(可能有,可能没)
/// </summary>
JunctionControlBase INodeJunction.ReturnDataJunction => this.ResultJunctionControl;
/// <summary>
/// 方法入参控制点(可能有,可能没)
/// </summary>
JunctionControlBase[] INodeJunction.ArgDataJunction
{
get
{
// 获取 MethodDetailsControl 实例
var methodDetailsControl = this.MethodDetailsControl;
var itemsControl = FindVisualChild<ItemsControl>(methodDetailsControl); // 查找 ItemsControl
if (itemsControl != null)
{
var argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length];
var controls = new List<JunctionControlBase>();
for (int i = 0; i < itemsControl.Items.Count; i++)
{
var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
if (container != null)
{
var argControl = FindVisualChild<ArgJunctionControl>(container);
if (argControl != null)
{
controls.Add(argControl); // 收集 ArgJunctionControl 实例
}
}
}
return argDataJunction = controls.ToArray();
}
else
{
return [];
}
}
}
#if false #if false
// 每次输入时重置定时器 // 每次输入时重置定时器
private void RichTextBox_TextChanged(object sender, TextChangedEventArgs e) private void RichTextBox_TextChanged(object sender, TextChangedEventArgs e)

View File

@@ -41,7 +41,7 @@ namespace Serein.Workbench.Node.ViewModel
CommandLoadScript = new RelayCommand( o => CommandLoadScript = new RelayCommand( o =>
{ {
NodeModel.LoadScript(); NodeModel.ReloadScript();
}); });
} }