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