mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-05-04 06:11:28 +08:00
修改了脚本执行的一些BUG。
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
/// 运行环境
|
/// 运行环境
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 类型接收
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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"/>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace Serein.Workbench.Node.ViewModel
|
|||||||
|
|
||||||
CommandLoadScript = new RelayCommand( o =>
|
CommandLoadScript = new RelayCommand( o =>
|
||||||
{
|
{
|
||||||
NodeModel.LoadScript();
|
NodeModel.ReloadScript();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user