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