From 4e20e816aef39fdd6c98b144acf10f5fdb374f53 Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Wed, 23 Jul 2025 15:57:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86http=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=E6=97=A0=E6=B3=95=E6=AD=A3=E7=A1=AE=E5=A4=84?= =?UTF-8?q?=E7=90=86post=E8=AF=B7=E6=B1=82=E5=85=A5=E5=8F=82=EF=BC=9B?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86modbus=20tcp=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E6=94=AF=E6=8C=81=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library/Network/Http/ApiHandleConfig.cs | 2 +- Library/Network/Http/Attribute.cs | 4 +- Library/Network/Http/Router.cs | 4 +- Library/Network/Http/WebApiServer.cs | 14 +- Library/Network/Modbus/ModbusFunctionCode.cs | 47 + Library/Network/Modbus/ModbusTcpClient.cs | 403 ++++++ Library/Network/Modbus/ModbusTcpRequest.cs | 32 + Library/ScriptBaseFunc.cs | 110 ++ Library/Serein.Library.csproj | 2 +- .../Utils/FlowTrigger/ChannelFlowInterrupt.cs | 23 - Net462DllTest/Enums/FromValue.cs | 7 +- Net462DllTest/Web/FlowController.cs | 2 +- NodeFlow/Model/Node/SingleScriptNode.cs | 134 +- NodeFlow/Serein.NodeFlow.csproj | 2 +- NodeFlow/Services/FlowCoreGenerateService.cs | 13 + .../Serein.Library.NodeGenerator.csproj | 2 +- Serein.Script/Node/CollectionIndexNode.cs | 2 +- Serein.Script/Serein.Script.csproj | 4 + Serein.Script/SereinScript.cs | 52 +- Serein.Script/SereinScriptExtension.cs | 38 + Serein.Script/SereinScriptILCompiler.cs | 1215 +++++++++++++++++ Serein.Script/SereinScriptInterpreter.cs | 15 +- Serein.Script/SereinScriptToCsharpScript.cs | 464 +++++++ Serein.Script/SereinScriptTypeAnalysis.cs | 64 +- 24 files changed, 2466 insertions(+), 189 deletions(-) create mode 100644 Library/Network/Modbus/ModbusFunctionCode.cs create mode 100644 Library/Network/Modbus/ModbusTcpClient.cs create mode 100644 Library/Network/Modbus/ModbusTcpRequest.cs create mode 100644 Library/ScriptBaseFunc.cs delete mode 100644 Library/Utils/FlowTrigger/ChannelFlowInterrupt.cs create mode 100644 Serein.Script/SereinScriptExtension.cs create mode 100644 Serein.Script/SereinScriptILCompiler.cs create mode 100644 Serein.Script/SereinScriptToCsharpScript.cs diff --git a/Library/Network/Http/ApiHandleConfig.cs b/Library/Network/Http/ApiHandleConfig.cs index c621fe5..099fe8d 100644 --- a/Library/Network/Http/ApiHandleConfig.cs +++ b/Library/Network/Http/ApiHandleConfig.cs @@ -130,7 +130,7 @@ namespace Serein.Library.Network } else if (jsonObject != null && PostArgTypes[i] == PostArgType.IsBobyData) { - args[i] = jsonObject; + args[i] = jsonObject.ToObject(type); } else if (jsonObject != null) { diff --git a/Library/Network/Http/Attribute.cs b/Library/Network/Http/Attribute.cs index 485b3f3..4c618ec 100644 --- a/Library/Network/Http/Attribute.cs +++ b/Library/Network/Http/Attribute.cs @@ -36,10 +36,10 @@ namespace Serein.Library.Web /// /// 标记该类为 Web Api 处理类 /// - public class AutoHostingAttribute : Attribute + public class WebApiControllerAttribute : Attribute { public string Url { get; } - public AutoHostingAttribute(string url = "") + public WebApiControllerAttribute(string url = "") { this.Url = url; } diff --git a/Library/Network/Http/Router.cs b/Library/Network/Http/Router.cs index 515189e..632e059 100644 --- a/Library/Network/Http/Router.cs +++ b/Library/Network/Http/Router.cs @@ -92,7 +92,7 @@ namespace Serein.Library.Web { if (!controllerType.IsClass || controllerType.IsAbstract) return; // 如果不是类或者是抽象类,则直接返回 - var autoHostingAttribute = controllerType.GetCustomAttribute(); + var autoHostingAttribute = controllerType.GetCustomAttribute(); var methods = controllerType.GetMethods().Where(m => m.GetCustomAttribute() != null).ToArray(); @@ -264,7 +264,7 @@ namespace Serein.Library.Web /// 控制器类型 /// 方法信息 /// 方法对应的urk - private string AddRoutesUrl(AutoHostingAttribute autoHostingAttribute, WebApiAttribute webAttribute, Type controllerType, MethodInfo method) + private string AddRoutesUrl(WebApiControllerAttribute autoHostingAttribute, WebApiAttribute webAttribute, Type controllerType, MethodInfo method) { string controllerName; if (string.IsNullOrWhiteSpace(autoHostingAttribute.Url)) diff --git a/Library/Network/Http/WebApiServer.cs b/Library/Network/Http/WebApiServer.cs index 24e70ed..6303d49 100644 --- a/Library/Network/Http/WebApiServer.cs +++ b/Library/Network/Http/WebApiServer.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Net; using System.Threading.Tasks; @@ -47,8 +48,15 @@ namespace Serein.Library.Web { while (listener.IsListening) { - var context = await listener.GetContextAsync(); // 获取请求上下文 - ProcessRequestAsync(context); // 处理请求 + try + { + var context = await listener.GetContextAsync(); // 获取请求上下文 + await ProcessRequestAsync(context); // 处理请求 + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + } } }); } @@ -84,7 +92,7 @@ namespace Serein.Library.Web /// /// /// - private async void ProcessRequestAsync(HttpListenerContext context) + private async Task ProcessRequestAsync(HttpListenerContext context) { // 添加CORS头部 context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); diff --git a/Library/Network/Modbus/ModbusFunctionCode.cs b/Library/Network/Modbus/ModbusFunctionCode.cs new file mode 100644 index 0000000..7320644 --- /dev/null +++ b/Library/Network/Modbus/ModbusFunctionCode.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Network.Modbus +{ + /// + /// Modbus 功能码枚举 + /// + public enum ModbusFunctionCode + { + /// + /// 读线圈 + /// + ReadCoils = 0x01, + /// + /// 读离散输入 + /// + ReadDiscreteInputs = 0x02, + /// + /// 读保持寄存器 + /// + ReadHoldingRegisters = 0x03, + /// + /// 读输入寄存器 + /// + ReadInputRegisters = 0x04, + /// + /// 写单个线圈 + /// + WriteSingleCoil = 0x05, + /// + /// 写单个寄存器 + /// + WriteSingleRegister = 0x06, + /// + /// 写多个线圈 + /// + WriteMultipleCoils = 0x0F, + /// + /// 写多个寄存器 + /// + WriteMultipleRegister = 0x10, + } +} diff --git a/Library/Network/Modbus/ModbusTcpClient.cs b/Library/Network/Modbus/ModbusTcpClient.cs new file mode 100644 index 0000000..d0a4d23 --- /dev/null +++ b/Library/Network/Modbus/ModbusTcpClient.cs @@ -0,0 +1,403 @@ +using System; +using System.Buffers.Binary; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using System.Threading.Channels; +using System.Threading; +using System.Threading.Tasks; +using System.ComponentModel.DataAnnotations; + +namespace Serein.Library.Network.Modbus +{ + /// + /// Modbus TCP 客户端 + /// + public class ModbusTcpClient : IDisposable + { + /// + /// 消息通道 + /// + private readonly Channel _channel = Channel.CreateUnbounded(); + /// + /// TCP客户端 + /// + private readonly TcpClient _tcpClient; + /// + /// TCP客户端的网络流,用于发送和接收数据 + /// + private readonly NetworkStream _stream; + /// + /// 存储未完成请求的字典,键为事务ID,值为任务完成源 + /// + private readonly ConcurrentDictionary> _pendingRequests = new(); + /// + /// 事务ID计数器,用于生成唯一的事务ID + /// + private int _transactionId = 0; + + public ModbusTcpClient(string host, int port = 502) + { + _tcpClient = new TcpClient(); + _tcpClient.Connect(host, port); + _stream = _tcpClient.GetStream(); + + _ = ProcessQueueAsync(); // 启动后台消费者 + _ = ReceiveLoopAsync(); // 启动接收响应线程 + } + + #region 功能码封装 + + /// + /// 读取线圈状态 + /// + /// + /// + /// + public async Task ReadCoils(ushort startAddress, ushort quantity) + { + var pdu = BuildReadPdu(startAddress, quantity); + var responsePdu = await SendAsync(ModbusFunctionCode.ReadCoils, pdu); + return ParseDiscreteBits(responsePdu, quantity); + } + + /// + /// 读取离散输入状态 + /// + /// + /// + /// + public async Task ReadDiscreteInputs(ushort startAddress, ushort quantity) + { + var pdu = BuildReadPdu(startAddress, quantity); + var responsePdu = await SendAsync(ModbusFunctionCode.ReadDiscreteInputs, pdu); + return ParseDiscreteBits(responsePdu, quantity); + } + + /// + /// 读取保持寄存器 + /// + /// + /// + /// + public async Task ReadHoldingRegisters(ushort startAddress, ushort quantity) + { + var pdu = BuildReadPdu(startAddress, quantity); + var responsePdu = await SendAsync(ModbusFunctionCode.ReadHoldingRegisters, pdu); + return ParseRegisters(responsePdu, quantity); + } + + /// + /// 读取输入寄存器 + /// + /// + /// + /// + public async Task ReadInputRegisters(ushort startAddress, ushort quantity) + { + var pdu = BuildReadPdu(startAddress, quantity); + var responsePdu = await SendAsync(ModbusFunctionCode.ReadInputRegisters, pdu); + return ParseRegisters(responsePdu, quantity); + } + + /// + /// 写单个线圈 + /// + /// + /// + /// + public async Task WriteSingleCoil(ushort address, bool value) + { + var pdu = new byte[] + { + (byte)(address >> 8), // 地址高字节 + (byte)(address & 0xFF), // 地址低字节 + value ? (byte)0xFF : (byte)0x00, // 线圈值高字节,高电平为0xFF,低电平为00 + 0x00 // 线圈值低字节 + }; + await SendAsync(ModbusFunctionCode.WriteSingleCoil, pdu); + } + + /// + /// 写单个寄存器 + /// + /// + /// + /// + public async Task WriteSingleRegister(ushort address, ushort value) + { + var pdu = new byte[] + { + (byte)(address >> 8), // 地址高字节 + (byte)(address & 0xFF), // 地址低字节 + (byte)(value >> 8), // 寄存器值高字节 + (byte)(value & 0xFF) // 寄存器值低字节 + }; + await SendAsync(ModbusFunctionCode.WriteSingleRegister, pdu); + } + + /// + /// 写多个线圈 + /// + /// + /// + /// + public async Task WriteMultipleCoils(ushort startAddress, bool[] values) + { + int byteCount = (values.Length + 7) / 8; // 计算需要的字节数 + byte[] data = new byte[byteCount]; + + for (int i = 0; i < values.Length; i++) + { + if (values[i]) + data[i / 8] |= (byte)(1 << (i % 8)); // 设置对应位 + } + + var pdu = new List + { + (byte)(startAddress >> 8), // 地址高字节 + (byte)(startAddress & 0xFF), // 地址低字节 + (byte)(values.Length >> 8), // 数量高字节 + (byte)(values.Length & 0xFF), // 数量低字节 + (byte)data.Length // 字节数 + }; + + pdu.AddRange(data); + + await SendAsync(ModbusFunctionCode.WriteMultipleCoils, pdu.ToArray()); + } + + /// + /// 写多个寄存器 + /// + /// + /// + /// + public async Task WriteMultipleRegisters(ushort startAddress, ushort[] values) + { + var pdu = new List + { + (byte)(startAddress >> 8), // 地址高字节 + (byte)(startAddress & 0xFF), // 地址低字节 + (byte)(values.Length >> 8), // 数量高字节 + (byte)(values.Length & 0xFF), // 数量低字节 + (byte)(values.Length * 2) // 字节数 + }; + + foreach (var val in values) + { + pdu.Add((byte)(val >> 8)); // 寄存器值高字节 + pdu.Add((byte)(val & 0xFF)); // 寄存器值低字节 + } + + await SendAsync(ModbusFunctionCode.WriteMultipleRegister, pdu.ToArray()); + } + + /// + /// 构建读取PDU数据 + /// + /// + /// + /// + private byte[] BuildReadPdu(ushort startAddress, ushort quantity) + { + byte[] buffer = new byte[4]; + BinaryPrimitives.WriteUInt16BigEndian(buffer.AsSpan(0, 2), startAddress); // 起始地址高低字节 + BinaryPrimitives.WriteUInt16BigEndian(buffer.AsSpan(2, 2), quantity); // 读取数量高低字节 + return buffer; + } + + + /// + /// 解析离散位数据 + /// + /// + /// + /// + private bool[] ParseDiscreteBits(byte[] pdu, ushort count) + { + int byteCount = pdu[2]; // 第2字节是后续的字节数量 + int dataIndex = 3; // 数据从第3字节开始(0-based) + + var result = new bool[count]; + + for (int i = 0, bytePos = 0, bitPos = 0; i < count; i++, bitPos++) + { + if (bitPos == 8) + { + bitPos = 0; + bytePos++; + } + result[i] = ((pdu[dataIndex + bytePos] >> bitPos) & 0x01) != 0; + } + return result; + } + + /// + /// 解析寄存器数据 + /// + /// + /// + /// + private ushort[] ParseRegisters(byte[] pdu, ushort count) + { + var result = new ushort[count]; + int dataStart = 3; // 数据从第3字节开始 + + for (int i = 0; i < count; i++) + { + int offset = dataStart + i * 2; + result[i] = (ushort)((pdu[offset] << 8) | pdu[offset + 1]); + } + + return result; + } + + + #endregion + + /// + /// 处理消息队列,发送请求到服务器 + /// + /// + private async Task ProcessQueueAsync() + { + var request = await _channel.Reader.ReadAsync(); + byte[] packet = BuildPacket(request.TransactionId, 0x01, (byte)request.FunctionCode, request.PDU); + await _stream.WriteAsync(packet, 0, packet.Length); + await _stream.FlushAsync(); + } + + /// + /// 发送请求并等待响应 + /// + /// 功能码 + /// 内容 + /// + /// + public Task SendAsync(ModbusFunctionCode functionCode, byte[] pdu) + { + int id = Interlocked.Increment(ref _transactionId); + var transactionId = (ushort)(id % ushort.MaxValue); // 0~65535 循环 + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + var request = new ModbusTcpRequest + { + TransactionId = transactionId, + FunctionCode = functionCode, + PDU = pdu, + Completion = tcs + }; + _pendingRequests[transactionId] = tcs; + _channel.Writer.TryWrite(request); + + return tcs.Task; + } + + /// + /// 接收数据循环 + /// + /// + private async Task ReceiveLoopAsync() + { + var buffer = new byte[1024]; + while (true) + { +#if NET462 + int len = await _stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); +#else + int len = await _stream.ReadAsync(buffer.AsMemory(0, buffer.Length)).ConfigureAwait(false); +#endif + + if (len == 0) return; // 连接关闭 + + if (len < 6) + { + Console.WriteLine("接收到的数据长度不足"); + return; + } + + + ushort protocolId = BinaryPrimitives.ReadUInt16BigEndian(buffer.AsSpan(2, 2)); + if (protocolId != 0x0000) + { + Console.WriteLine($"协议不匹配: {protocolId:X4}"); + return; + } + + ushort dataLength = BinaryPrimitives.ReadUInt16BigEndian(buffer.AsSpan(4, 2)); + // 检查数据长度是否合法 + if (dataLength > 253 || len < 6 + dataLength) + { + Console.WriteLine($"数据长度异常: dataLength={dataLength}, 实际接收={len}"); + return; + } + + ushort transactionId = BinaryPrimitives.ReadUInt16BigEndian(buffer.AsSpan(0, 2)); + if (_pendingRequests.TryRemove(transactionId, out var tcs)) + { + var responsePdu = new ReadOnlySpan(buffer, 6, dataLength).ToArray(); + tcs.SetResult(responsePdu); // 如需 byte[] 则 ToArray + } + else + { + Console.WriteLine($"未匹配到 TransactionId={transactionId} 的请求"); + } + + } + } + + /// + /// 构造 Modbus Tcp 报文 + /// + /// + /// + /// + /// + /// + private byte[] BuildPacket(ushort transactionId, byte unitId, byte functionCode, byte[] pduData) + { + int pduLength = 1 + pduData.Length; // PDU 长度 = 功能码1字节 + 数据长度 + int totalLength = 7 + pduLength; // MBAP头长度 = 7字节 + PDU长度 + + Span packet = totalLength <= 256 ? stackalloc byte[totalLength] : new byte[totalLength]; + + // 写入事务ID(大端序) + packet[0] = (byte)(transactionId >> 8); + packet[1] = (byte)(transactionId); + + // 协议ID(固定0x0000) + packet[2] = 0; + packet[3] = 0; + + // 长度(PDU长度 + 1字节UnitID) + ushort length = (ushort)(pduLength + 1); + packet[4] = (byte)(length >> 8); + packet[5] = (byte)(length); + + // UnitID & 功能码 + packet[6] = unitId; + packet[7] = functionCode; + + // 复制PDU数据 + pduData.AsSpan().CopyTo(packet.Slice(8)); + + return packet.ToArray(); + } + + + + public void Dispose() + { + var tcs = _pendingRequests.Values.ToArray(); + foreach (var pending in tcs) + { + pending.TrySetCanceled(); + } + _stream?.Close(); + _tcpClient?.Close(); + } + } +} diff --git a/Library/Network/Modbus/ModbusTcpRequest.cs b/Library/Network/Modbus/ModbusTcpRequest.cs new file mode 100644 index 0000000..969e0e9 --- /dev/null +++ b/Library/Network/Modbus/ModbusTcpRequest.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Network.Modbus +{ + /// + /// Modbus TCP 请求实体 + /// + public class ModbusTcpRequest + { + /// + /// 事务ID + /// + public ushort TransactionId { get; set; } + /// + /// 功能码 + /// + public ModbusFunctionCode FunctionCode { get; set; } + /// + /// PDU 数据 + /// + public byte[] PDU { get; set; } + /// + /// 请求的完成源,用于异步等待响应 + /// + public TaskCompletionSource Completion { get; set; } + } + +} diff --git a/Library/ScriptBaseFunc.cs b/Library/ScriptBaseFunc.cs new file mode 100644 index 0000000..120e855 --- /dev/null +++ b/Library/ScriptBaseFunc.cs @@ -0,0 +1,110 @@ +using Serein.Library; +using Serein.Library.Utils; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Serein.Library +{ + + + public static class ScriptBaseFunc + { + public static DateTime now() => DateTime.Now; + + #region 常用的类型转换 + public static bool @bool(object value) + { + return ConvertHelper.ValueParse(value); + } + public static byte @byte(object value) + { + return ConvertHelper.ValueParse(value); + } + public static decimal @decimal(object value) + { + return ConvertHelper.ValueParse(value); + } + public static float @float(object value) + { + return ConvertHelper.ValueParse(value); + } + public static double @double(object value) + { + return ConvertHelper.ValueParse(value); + } + public static int @int(object value) + { + return ConvertHelper.ValueParse(value); + } + public static int @long(object value) + { + return ConvertHelper.ValueParse(value); + } + + #endregion + + public static int len(object target) + { + // 获取数组或集合对象 + // 访问数组或集合中的指定索引 + if (target is Array array) + { + return array.Length; + } + else if (target is string chars) + { + return chars.Length; + } + else if (target is IDictionary dict) + { + return dict.Count; + } + else if (target is IList list) + { + return list.Count; + } + else + { + throw new ArgumentException($"并非有效集合"); + } + } + + + public static string str(object obj) + { + return obj?.ToString() ?? string.Empty; + } + + + public static object global(string name) + { + return SereinEnv.GetFlowGlobalData(name); + } + + public static Type type(object type) + { + return type.GetType(); + } + + public static void log(object value) + { + SereinEnv.WriteLine(InfoType.INFO, value?.ToString()); + } + + public static async Task sleep(object value) + { + if (value is int @int) + { + Console.WriteLine($"等待{@int}ms"); + await Task.Delay(@int); + } + else if (value is TimeSpan timeSpan) + { + Console.WriteLine($"等待{timeSpan}"); + await Task.Delay(timeSpan); + } + + } + } +} diff --git a/Library/Serein.Library.csproj b/Library/Serein.Library.csproj index a17a143..19cf836 100644 --- a/Library/Serein.Library.csproj +++ b/Library/Serein.Library.csproj @@ -1,7 +1,7 @@  - 1.2.0 + 1.2.1.1 net8.0;net462 ..\.\.Output True diff --git a/Library/Utils/FlowTrigger/ChannelFlowInterrupt.cs b/Library/Utils/FlowTrigger/ChannelFlowInterrupt.cs deleted file mode 100644 index d14778b..0000000 --- a/Library/Utils/FlowTrigger/ChannelFlowInterrupt.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Newtonsoft.Json.Linq; -using Serein.Library.Api; -using Serein.Library.Utils; -using System; -using System.Collections.Concurrent; -using System.Reactive.Linq; -using System.Reactive.Subjects; -using System.Threading; -using System.Threading.Channels; -using System.Threading.Tasks; -using System.Transactions; - -namespace Serein.Library.Utils -{ - - - - - - - - -} diff --git a/Net462DllTest/Enums/FromValue.cs b/Net462DllTest/Enums/FromValue.cs index c503862..2c0a3f9 100644 --- a/Net462DllTest/Enums/FromValue.cs +++ b/Net462DllTest/Enums/FromValue.cs @@ -1,13 +1,12 @@ -using Net462DllTest.View; -using Serein.Library; +using Serein.Library; namespace Net462DllTest.Signal { - public enum FromValue + /*public enum FromValue { [BindValue(typeof(FromWorkBenchView))] FromWorkBenchView, [BindValue(typeof(TestFormView))] TestFormView, - } + }*/ } diff --git a/Net462DllTest/Web/FlowController.cs b/Net462DllTest/Web/FlowController.cs index f51ffc4..d550c6a 100644 --- a/Net462DllTest/Web/FlowController.cs +++ b/Net462DllTest/Web/FlowController.cs @@ -9,7 +9,7 @@ using System; namespace Net462DllTest.Web { - [AutoHosting] + [WebApiController] public class FlowController : ControllerBase { private readonly SiemensPlcDevice plcDevice; diff --git a/NodeFlow/Model/Node/SingleScriptNode.cs b/NodeFlow/Model/Node/SingleScriptNode.cs index 143f1b3..49d44eb 100644 --- a/NodeFlow/Model/Node/SingleScriptNode.cs +++ b/NodeFlow/Model/Node/SingleScriptNode.cs @@ -1,10 +1,10 @@ using Serein.Library; using Serein.Library.Api; -using Serein.Library.Utils; using Serein.Script; using Serein.Script.Node.FlowControl; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Dynamic; using System.Linq; using System.Linq.Expressions; @@ -52,7 +52,8 @@ namespace Serein.NodeFlow.Model static SingleScriptNode() { // 挂载静态方法 - var tempMethods = typeof(ScriptBaseFunc).GetMethods().Where(method => + var tempMethods = typeof(Serein.Library.ScriptBaseFunc).GetMethods().Where(method => + method.IsStatic && !(method.Name.Equals("GetHashCode") || method.Name.Equals("Equals") || method.Name.Equals("ToString") @@ -86,7 +87,7 @@ namespace Serein.NodeFlow.Model pd[0] = new ParameterDetails { Index = 0, - Name = "string", + Name = "arg", IsExplicitData = true, DataValue = string.Empty, DataType = typeof(string), @@ -156,25 +157,35 @@ namespace Serein.NodeFlow.Model var argTypes = MethodDetails.ParameterDetailss .Select(pd => { + if(pd.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData) + { + var Type = pd.DataType; + return (pd.Name, Type); + } if (Env.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var node) && node.MethodDetails?.ReturnType is not null) { pd.DataType = node.MethodDetails.ReturnType; - return (pd.Name, node.MethodDetails.ReturnType); + var Type = node.MethodDetails.ReturnType; + return (pd.Name, Type); } return default; }) .Where(x => x != default) - .ToDictionary(x => x.Name, x => x.ReturnType); // 准备预定义类型 + .ToDictionary(x => x.Name, x => x.Type); // 准备预定义类型 var returnType = sereinScript.ParserScript(Script, argTypes); // 开始解析获取程序主节点 + MethodDetails.ReturnType = returnType; + + var code = sereinScript.ConvertCSharpCode("Test", argTypes); + Debug.WriteLine(code); return true; } catch (Exception ex) { - SereinEnv.WriteLine(ex); + SereinEnv.WriteLine(InfoType.WARN, ex.Message); return false; // 解析失败 } } @@ -244,116 +255,5 @@ namespace Serein.NodeFlow.Model return new FlowResult(this.Guid, context, result); } - #region 挂载的方法 - - /*public IScriptFlowApi GetFlowApi() - { - return ScriptFlowApi; - }*/ - - private static class ScriptBaseFunc - { - public static DateTime now() => DateTime.Now; - - #region 常用的类型转换 - public static bool @bool(object value) - { - return ConvertHelper.ValueParse(value); - } - public static decimal @decimal(object value) - { - return ConvertHelper.ValueParse(value); - } - public static float @float(object value) - { - return ConvertHelper.ValueParse(value); - } - public static double @double(object value) - { - return ConvertHelper.ValueParse(value); - } - public static int @int(object value) - { - return ConvertHelper.ValueParse(value); - } - public static int @long(object value) - { - return ConvertHelper.ValueParse(value); - } - - #endregion - - public static int len(object target) - { - // 获取数组或集合对象 - // 访问数组或集合中的指定索引 - if (target is Array array) - { - return array.Length; - } - else if (target is string chars) - { - return chars.Length; - } - else if (target is IDictionary dict) - { - return dict.Count; - } - else if (target is IList list) - { - return list.Count; - } - else - { - throw new ArgumentException($"并非有效集合"); - } - } - - public static void regType(Type type, string name = "") - { - SereinScript.AddClassType(type, name); - } - - public static string str(object obj) - { - return obj?.ToString() ?? string.Empty; - } - - public static object obj(Type type) - { - return Activator.CreateInstance(type); - } - - public static object global(string name) - { - return SereinEnv.GetFlowGlobalData(name); - } - - public static Type type(object type) - { - return type.GetType(); - } - - public static void log(object value) - { - SereinEnv.WriteLine(InfoType.INFO, value?.ToString()); - } - - public static async Task sleep(object value) - { - if (value is int @int) - { - Console.WriteLine($"等待{@int}ms"); - await Task.Delay(@int); - } - else if (value is TimeSpan timeSpan) - { - Console.WriteLine($"等待{timeSpan}"); - await Task.Delay(timeSpan); - } - - } - } - #endregion } } diff --git a/NodeFlow/Serein.NodeFlow.csproj b/NodeFlow/Serein.NodeFlow.csproj index 881dca4..219547f 100644 --- a/NodeFlow/Serein.NodeFlow.csproj +++ b/NodeFlow/Serein.NodeFlow.csproj @@ -1,7 +1,7 @@  - 1.2.0 + 1.2.1 net8.0 enable enable diff --git a/NodeFlow/Services/FlowCoreGenerateService.cs b/NodeFlow/Services/FlowCoreGenerateService.cs index 9c55642..3a19532 100644 --- a/NodeFlow/Services/FlowCoreGenerateService.cs +++ b/NodeFlow/Services/FlowCoreGenerateService.cs @@ -651,6 +651,10 @@ namespace Serein.NodeFlow.Services private Dictionary flowApiMethodInfos = []; + /// + /// 生成流程接口方法信息 + /// + /// private void GenerateFlowApi_InitFlowApiMethodInfos(SingleFlowCallNode[] flowCallNodes) { @@ -667,6 +671,11 @@ namespace Serein.NodeFlow.Services } } } + + /// + /// 生成流程接口模板类 + /// + /// private void GenerateFlowApi_InterfaceAndImpleClass(StringBuilder sb) { /* @@ -703,6 +712,10 @@ namespace Serein.NodeFlow.Services } + /// + /// 生成流程接口参数 + /// + /// private void GenerateFlowApi_ApiParamClass(StringBuilder sb) { var infos = flowApiMethodInfos.Values.ToArray(); diff --git a/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj b/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj index a7bb569..0505845 100644 --- a/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj +++ b/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 1.2.0 + 1.2.1 true ..\.\.Output True diff --git a/Serein.Script/Node/CollectionIndexNode.cs b/Serein.Script/Node/CollectionIndexNode.cs index ba6b100..b6e9898 100644 --- a/Serein.Script/Node/CollectionIndexNode.cs +++ b/Serein.Script/Node/CollectionIndexNode.cs @@ -39,7 +39,7 @@ namespace Serein.Script.Node public CollectionIndexNode Collection { get; } /// - /// 索引来源 + /// 赋值值来源 /// public ASTNode Value { get; } diff --git a/Serein.Script/Serein.Script.csproj b/Serein.Script/Serein.Script.csproj index d44f15f..6fcbdec 100644 --- a/Serein.Script/Serein.Script.csproj +++ b/Serein.Script/Serein.Script.csproj @@ -19,6 +19,10 @@ + + + + diff --git a/Serein.Script/SereinScript.cs b/Serein.Script/SereinScript.cs index dd8dbe2..2384bee 100644 --- a/Serein.Script/SereinScript.cs +++ b/Serein.Script/SereinScript.cs @@ -1,4 +1,5 @@ -using Serein.Library; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Serein.Library; using Serein.Library.Api; using Serein.Script.Node; using Serein.Script.Node.FlowControl; @@ -22,7 +23,7 @@ namespace Serein.Script /// /// 类型分析 /// - public SereinScriptTypeAnalysis TypeAnalysis { get; set; } = new SereinScriptTypeAnalysis(); + public SereinScriptTypeAnalysis TypeAnalysis { get; } = new SereinScriptTypeAnalysis(); @@ -34,7 +35,7 @@ namespace Serein.Script SereinScriptParser parser = new SereinScriptParser(); SereinScriptTypeAnalysis analysis = new SereinScriptTypeAnalysis(); var programNode = parser.Parse(script); - analysis.NodeSymbolInfos.Clear(); // 清空符号表 + analysis.Reset(); if (argTypes is not null) analysis.LoadSymbol(argTypes); // 提前加载脚本节点定义的符号 analysis.Analysis(programNode); // 分析节点类型 SereinScriptInterpreter Interpreter = new SereinScriptInterpreter(analysis.NodeSymbolInfos); @@ -61,6 +62,14 @@ namespace Serein.Script return returnType; // 脚本返回类型 } + + + /// + /// 执行脚本 + /// + /// + /// + /// public async Task InterpreterAsync(IScriptInvokeContext context) { if(programNode is null) @@ -73,6 +82,35 @@ namespace Serein.Script } + /// + /// 转换为c#代码 + /// + /// 脚本 + /// 挂载的变量 + /// + public string ConvertCSharpCode(string mehtodName, Dictionary? argTypes = null) + { + if (string.IsNullOrWhiteSpace(mehtodName)) return string.Empty; + if (programNode is null) return string.Empty; + SereinScriptToCsharpScript tool = new SereinScriptToCsharpScript(TypeAnalysis); + return tool.CompileToCSharp(mehtodName, programNode, argTypes); + } + + + + /// + /// 编译为 IL 代码 + /// + /// 脚本 + /// 挂载的变量 + /// + [Obsolete("因为暂未想到如何支持异步方法,所以暂时废弃生成", true)] + private Delegate CompilerIL(string dynamicMethodName, ProgramNode programNode, Dictionary? argTypes = null) + { + SereinScriptILCompiler compiler = new SereinScriptILCompiler(TypeAnalysis.NodeSymbolInfos); + var @delegate = compiler.Compiler(dynamicMethodName, programNode, argTypes); // 编译脚本 + return @delegate; + } @@ -96,10 +134,10 @@ namespace Serein.Script /// /// 挂载的函数调用的对象(用于解决函数需要实例才能调用的场景) /// - public static Dictionary> DelegateInstances = new Dictionary>(); + // public static Dictionary> DelegateInstances = new Dictionary>(); /// - /// 挂载静态函数 + /// 挂载函数 /// /// /// @@ -115,7 +153,7 @@ namespace Serein.Script /// /// 函数名称 /// 方法信息 - public static void AddFunction(string functionName, MethodInfo methodInfo, Func? callObj = null) + /*public static void AddFunction(string functionName, MethodInfo methodInfo, Func? callObj = null) { if (!methodInfo.IsStatic && callObj is null) { @@ -132,7 +170,7 @@ namespace Serein.Script { FunctionDelegates[functionName] = new DelegateDetails(methodInfo); } - } + }*/ /// /// 挂载类型 diff --git a/Serein.Script/SereinScriptExtension.cs b/Serein.Script/SereinScriptExtension.cs new file mode 100644 index 0000000..3693091 --- /dev/null +++ b/Serein.Script/SereinScriptExtension.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Script +{ + internal static class SereinScriptExtension + { /// + /// 添加代码 + /// + /// 字符串构建器 + /// 缩进次数(4个空格) + /// 要添加的代码 + /// 字符串构建器本身 + public static StringBuilder AppendCode(this StringBuilder sb, + int retractCount = 0, + string code = null, + bool isWrapping = true) + { + if (!string.IsNullOrWhiteSpace(code)) + { + string retract = new string(' ', retractCount * 4); + sb.Append(retract); + if (isWrapping) + { + sb.AppendLine(code); + } + else + { + sb.Append(code); + } + } + return sb; + } + } +} diff --git a/Serein.Script/SereinScriptILCompiler.cs b/Serein.Script/SereinScriptILCompiler.cs new file mode 100644 index 0000000..9e8b050 --- /dev/null +++ b/Serein.Script/SereinScriptILCompiler.cs @@ -0,0 +1,1215 @@ +using Serein.Library.Api; +using Serein.Script.Node; +using Serein.Script.Node.FlowControl; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Linq; + +namespace Serein.Script +{ + +/* + 暂未想到如何编译出具备 await / async 功能的IL代码,暂时放弃 + */ + + internal record ILResult + { + public Type ReturnType { get; set; } + public LocalBuilder? TempVar { get; set; } // 可用于缓存 + public Action Emit { get; set; } // 用于推入值的代码 + } + + + /// + /// 脚本编译IL代码 + /// + internal class SereinScriptILCompiler + { + /// + /// 符号表 + /// + private readonly Dictionary symbolInfos; + + /// + /// 发射IL + /// + //private ILGenerator _il; + + /// + /// 创建的对应方法 + /// + private DynamicMethod _method; + + /// + /// 临时变量绑定 + /// + private readonly Dictionary _locals = new(); + + /// + /// 节点对应的委托缓存,避免重复编译同一节点 + /// + private readonly Dictionary _nodeCache = new(); + + /// + /// IL映射 + /// + Dictionary _ilResults = new Dictionary(); + + private Label _methodExit; + + public SereinScriptILCompiler(Dictionary symbolInfos) + { + this.symbolInfos = symbolInfos; + } + + /// + /// 是否调用了异步方法 + /// + private bool _isUseAwait = false; + + private Dictionary _parameterIndexes = new Dictionary(); + + public Delegate Compiler(string compilerMethodName, ProgramNode programNode, Dictionary? argTypes = null) + { + argTypes ??= new Dictionary(); + + var parameterTypes = argTypes.Values.ToArray(); + var parameterNames = argTypes.Keys.ToArray(); + + // 构建参数索引映射,方便 EmitNode 访问参数 + _parameterIndexes.Clear(); + for (int i = 0; i < parameterNames.Length; i++) + { + _parameterIndexes[parameterNames[i]] = i; + } + + _method = new DynamicMethod( + compilerMethodName, + typeof(object), + parameterTypes, + typeof(SereinScriptILCompiler).Module, + skipVisibility: true); + + var il = _method.GetILGenerator(); + + _ilResults.Clear(); + + var resultType = symbolInfos[programNode]; + + _methodExit = il.DefineLabel(); + + foreach (var node in programNode.Statements) + { + EmitNode(il, node); + } + + il.MarkLabel(_methodExit); + + if (resultType == typeof(void)) + { + il.Emit(OpCodes.Ldnull); + } + else if (resultType.IsValueType) + { + il.Emit(OpCodes.Box, resultType); + } + + il.Emit(OpCodes.Ret); + + Type delegateType; + if (parameterTypes.Length == 0) + { + delegateType = typeof(Func); + } + else + { + var funcGenericTypeArgs = parameterTypes.Concat(new[] { typeof(object) }).ToArray(); + delegateType = Expression.GetFuncType(funcGenericTypeArgs); + } + + var @delegate = _method.CreateDelegate(delegateType); + return @delegate; + } + + /// + /// 用于将 GenerateIL() 返回的 ILResult 缓存到 _ilResults 中,避免在 GenerateIL() 中混乱生成逻辑 + /// + /// + /// + /// + private ILResult EmitNode(ILGenerator il, ASTNode node) + { + ILResult result = GenerateIL(il, node); + _ilResults[node] = result; + return result; + } + + private ILResult GenerateIL(ILGenerator il, ASTNode node) + { + switch (node) + { + case ProgramNode programNode: // 程序开始节点,不用分析IL + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => { } + }; + case ReturnNode returnNode: // 程序退出节点 + ILResult GenerateReturnNodeIL(ReturnNode returnNode) + { + if (returnNode.Value is null) // 没有返回值 + { + return new ILResult + { + ReturnType = typeof(void), + Emit = il => + { + il.Emit(OpCodes.Ldnull); // 返回 null + il.Emit(OpCodes.Br, _methodExit); // 跳转到程序退出 + } + }; + } + else // 有返回值 + { + var valueResult = EmitNode(il, returnNode.Value); + return new ILResult + { + ReturnType = valueResult.ReturnType, + Emit = il => + { + valueResult.Emit(il); // 推入返回值 + il.Emit(OpCodes.Br, _methodExit); // 跳转到程序退出 + } + }; + } + } + return GenerateReturnNodeIL(returnNode); + #region 字面量IL生成 + case NullNode nullNode: // null + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => il.Emit(OpCodes.Ldnull) + }; + case CharNode charNode: // char字面量 加载 char(本质为 ushort / int) + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => + { + il.Emit(OpCodes.Ldc_I4, (int)charNode.Value); + il.Emit(OpCodes.Box, typeof(char)); + } + }; + case StringNode stringNode: // 字符串字面量 + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => il.Emit(OpCodes.Ldstr, stringNode.Value) + }; + case BooleanNode booleanNode: // 布尔值字面量 + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => + { + il.Emit(booleanNode.Value ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Box, typeof(bool)); + } + }; + case NumberIntNode numberIntNode: // int整型数值字面量 + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => + { + il.Emit(OpCodes.Ldc_I4, numberIntNode.Value); + il.Emit(OpCodes.Box, typeof(int)); + } + }; + case NumberLongNode numberLongNode: // long整型数值字面量 + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => + { + il.Emit(OpCodes.Ldc_I8, numberLongNode.Value); + il.Emit(OpCodes.Box, typeof(long)); + } + }; + case NumberFloatNode numberFloatNode: // float浮点数值字面量 + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => + { + il.Emit(OpCodes.Ldc_R4, numberFloatNode.Value); + il.Emit(OpCodes.Box, typeof(float)); + } + }; + case NumberDoubleNode numberDoubleNode: // double浮点数值字面量 + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => + { + il.Emit(OpCodes.Ldc_R8, numberDoubleNode.Value); + il.Emit(OpCodes.Box, typeof(double)); + } + }; + + #endregion + case IdentifierNode identifierNode: + ILResult GenerateIdentifierNodeIL(IdentifierNode identifierNode) + { + if (_parameterIndexes.TryGetValue(identifierNode.Name, out int paramIndex)) + { + // 变量是方法参数,直接加载参数 + return new ILResult + { + ReturnType = symbolInfos[identifierNode], + Emit = il => + { + switch (paramIndex) + { + case 0: il.Emit(OpCodes.Ldarg_0); break; + case 1: il.Emit(OpCodes.Ldarg_1); break; + case 2: il.Emit(OpCodes.Ldarg_2); break; + case 3: il.Emit(OpCodes.Ldarg_3); break; + default: il.Emit(OpCodes.Ldarg, paramIndex); break; + } + } + }; + } + else + { + // 变量是本地变量,声明并访问 + if (!_locals.TryGetValue(identifierNode.Name, out var local)) + { + var varType = symbolInfos.TryGetValue(identifierNode, out var t) ? t : typeof(object); + local = il.DeclareLocal(varType); + _locals[identifierNode.Name] = local; + } + + return new ILResult + { + ReturnType = symbolInfos[identifierNode], + Emit = il => + { + il.Emit(OpCodes.Ldloc, local); + } + }; + } + } + return GenerateIdentifierNodeIL(identifierNode); + case IfNode ifNode: // if语句结构 + ILResult GenerateIfNodeIL(IfNode ifNode) + { + var elseLabel = il.DefineLabel(); + var endLabel = il.DefineLabel(); + // 计算条件表达式 + var conditionResult = EmitNode(il, ifNode.Condition); + conditionResult.Emit(il); // 条件结果入栈 + // 弹出object装箱,转bool + il.Emit(OpCodes.Unbox_Any, typeof(bool)); + // 条件为 false,跳转 else + il.Emit(OpCodes.Brfalse, elseLabel); + // 执行 TrueBranch + foreach (var stmt in ifNode.TrueBranch) + { + var stmtResult = EmitNode(il, stmt); + stmtResult.Emit(il); + if (stmt is ReturnNode) // 如果是返回语句,直接跳到结束标签 + { + il.Emit(OpCodes.Br, endLabel); + } + } + // 跳转到 if 结束 + il.Emit(OpCodes.Br, endLabel); + // 处理 else 分支 + il.MarkLabel(elseLabel); + foreach (var stmt in ifNode.FalseBranch) + { + var stmtResult = EmitNode(il, stmt); + stmtResult.Emit(il); + if (stmt is ReturnNode) // 如果是返回语句,直接跳到结束标签 + { + il.Emit(OpCodes.Br, endLabel); + } + } + // 结束标签 + il.MarkLabel(endLabel); + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => { } // 无需额外操作,已在各分支处理 + }; + } + return GenerateIfNodeIL(ifNode); + case WhileNode whileNode: // while语句结构 + ILResult GenerateWhileNode(WhileNode whileNode) + { + // 帮我实现处理逻辑: + var startLabel = il.DefineLabel(); // 循环开始标签 + var endLabel = il.DefineLabel(); // 循环结束标签 + il.MarkLabel(startLabel); // 标记循环开始 + // 计算条件表达式 + var conditionResult = EmitNode(il, whileNode.Condition); + conditionResult.Emit(il); // 条件结果入栈 + // 弹出object装箱,转bool + il.Emit(OpCodes.Unbox_Any, typeof(bool)); + // 条件为 false,跳转到循环结束 + il.Emit(OpCodes.Brfalse, endLabel); + foreach (var stmt in whileNode.Body) + { + var stmtResult = EmitNode(il, stmt); + stmtResult.Emit(il); + if (stmt is ReturnNode) // 如果是返回语句,直接跳到结束标签 + { + il.Emit(OpCodes.Br, endLabel); + } + } + il.Emit(OpCodes.Br, startLabel); + il.MarkLabel(endLabel); + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => { } // 无需额外操作,已在各分支处理 + }; + + } + return GenerateWhileNode(whileNode); + case AssignmentNode assignmentNode: // 赋值语句 + ILResult GenerateAssignmentNodeIL(AssignmentNode assignmentNode) + { + if (assignmentNode.Target is not IdentifierNode identifierNode) + throw new Exception("AssignmentNode.Target 必须是 IdentifierNode"); + + if (!_locals.TryGetValue(identifierNode.Name, out var local)) + { + var varType = symbolInfos.TryGetValue(identifierNode, out var t) ? t : typeof(object); + local = il.DeclareLocal(varType); + _locals[identifierNode.Name] = local; + } + + var valueResult = EmitNode(il, assignmentNode.Value); + + return new ILResult + { + ReturnType = valueResult.ReturnType, + Emit = il => + { + valueResult.Emit(il); // 先把赋值值推入栈顶 + + var targetType = local.LocalType; + var sourceType = valueResult.ReturnType; + if (targetType != sourceType) + { + EmitConvert(il, sourceType, targetType); // 尝试类型转换 + } + + il.Emit(OpCodes.Stloc, local); // 然后保存到局部变量 + } + }; + } + return GenerateAssignmentNodeIL(assignmentNode); + case BinaryOperationNode binaryOperationNode: // 二元运算操作 + ILResult GenerateBinaryOperationNodeIL(BinaryOperationNode binaryOperationNode) + { + void EmitShortCircuit(ILGenerator il, string op, ILResult left, ILResult right) + { + var labelEnd = il.DefineLabel(); + var labelFalse = il.DefineLabel(); + var labelTrue = il.DefineLabel(); + + if (op == "&&") + { + left.Emit(il); + il.Emit(OpCodes.Brfalse, labelFalse); + right.Emit(il); + il.Emit(OpCodes.Br, labelEnd); + + il.MarkLabel(labelFalse); + il.Emit(OpCodes.Ldc_I4_0); + + il.MarkLabel(labelEnd); + } + else if (op == "||") + { + left.Emit(il); + il.Emit(OpCodes.Brtrue, labelTrue); + right.Emit(il); + il.Emit(OpCodes.Br, labelEnd); + + il.MarkLabel(labelTrue); + il.Emit(OpCodes.Ldc_I4_1); + + il.MarkLabel(labelEnd); + } + } + + var left = EmitNode(il, binaryOperationNode.Left); + var right = EmitNode(il, binaryOperationNode.Right); + + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => + { + switch (binaryOperationNode.Operator) + { + case "&&": + case "||": + EmitShortCircuit(il, binaryOperationNode.Operator, left, right); + break; + + default: + left.Emit(il); + right.Emit(il); + switch (binaryOperationNode.Operator) + { + case "+": + il.Emit(OpCodes.Add); + break; + case "-": + il.Emit(OpCodes.Sub); + break; + case "*": + il.Emit(OpCodes.Mul); + break; + case "/": + il.Emit(OpCodes.Div); + break; + case "==": + il.Emit(OpCodes.Ceq); + break; + case "!=": + il.Emit(OpCodes.Ceq); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); // 取反 + break; + case ">": + il.Emit(OpCodes.Cgt); + break; + case "<": + il.Emit(OpCodes.Clt); + break; + case ">=": + il.Emit(OpCodes.Clt); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); // !(a < b) + break; + case "<=": + il.Emit(OpCodes.Cgt); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); // !(a > b) + break; + default: + throw new NotSupportedException($"未知的操作符: {binaryOperationNode.Operator}"); + } + break; + } + } + }; + } + return GenerateBinaryOperationNodeIL(binaryOperationNode); + case CollectionAssignmentNode collectionAssignmentNode: // 集合赋值 + ILResult GenerateCollectionAssignmentNodeIL(CollectionAssignmentNode collectionAssignmentNode) + { + // 加载集合、索引和值 + var collectionIL = EmitNode(il, collectionAssignmentNode.Collection.Collection); + var indexIL = EmitNode(il, collectionAssignmentNode.Collection.Index); + var valueIL = EmitNode(il, collectionAssignmentNode.Value); + return new ILResult + { + ReturnType = typeof(void), + Emit = il => + { + collectionIL.Emit(il); // 加载集合 + indexIL.Emit(il); // 加载索引 + valueIL.Emit(il); // 加载值 + var collectionType = collectionIL.ReturnType; + var valueType = valueIL.ReturnType; + if (collectionType.IsArray && collectionType.GetArrayRank() == 1) + { + var elementType = collectionType.GetElementType(); + + // 如果值类型不匹配,尝试强转(可能需要扩展) + if (elementType != valueType) + { + EmitConvert(il, valueType, elementType); + } + + // 发射 Stelem 指令 + EmitStoreElement(il, elementType); + } + else + { + // 对象集合,调用 set_Item 方法 + var indexerSetter = collectionType + .GetMethods() + .FirstOrDefault(m => m.Name == "set_Item" && m.GetParameters().Length == 2); + + if (indexerSetter == null) + throw new InvalidOperationException($"集合类型 {collectionType} 不支持索引赋值"); + + if (collectionType.IsValueType) + { + il.Emit(OpCodes.Constrained, collectionType); + } + + il.EmitCall(OpCodes.Callvirt, indexerSetter, null); + } + } + }; + } + return GenerateCollectionAssignmentNodeIL(collectionAssignmentNode); + case CollectionIndexNode collectionIndexNode: // 集合取值 + ILResult GenerateCollectionIndexNodeIL(CollectionIndexNode collectionIndexNode) + { + var collectionIL = EmitNode(il, collectionIndexNode.Collection); + var indexIL = EmitNode(il, collectionIndexNode.Index); + var resultType = symbolInfos[collectionIndexNode]; + + return new ILResult + { + ReturnType = resultType, + Emit = il => + { + collectionIL.Emit(il); + indexIL.Emit(il); + + var collectionType = collectionIL.ReturnType; + + if (collectionType.IsArray) + { + var elementType = collectionType.GetElementType(); + EmitLoadElement(il, elementType); + } + else + { + // 尝试获取索引器 get_Item 方法(匹配参数类型) + var indexerMethod = collectionType.GetMethod("get_Item", new[] { indexIL.ReturnType }); + if (indexerMethod == null) + { + // 退而求其次:只要名字匹配且参数个数相等的第一个 + indexerMethod = collectionType.GetMethods() + .FirstOrDefault(m => m.Name == "get_Item" && m.GetParameters().Length == 1); + if (indexerMethod == null) + throw new InvalidOperationException($"集合类型 {collectionType} 没有索引器方法"); + } + + if (collectionType.IsValueType) + { + il.Emit(OpCodes.Constrained, collectionType); + } + il.EmitCall(OpCodes.Callvirt, indexerMethod, null); + } + } + }; + } + + return GenerateCollectionIndexNodeIL(collectionIndexNode); + case ClassTypeDefinitionNode classTypeDefinitionNode: // 类型定义,在 Parser. 阶段已经处理,不生成 IL + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => { } + }; + case TypeNode typeNode:// 指示类型的 FullName 由 ClassTypeDefinitionNode 实际使用,不生成 IL + return new ILResult + { + ReturnType = symbolInfos[node], + Emit = il => { } + }; + case ObjectInstantiationNode objectInstantiationNode: // 类型实例化 + ILResult GenerateObjectInstantiationNodeIL(ObjectInstantiationNode objectInstantiationNode) + { + var type = symbolInfos[objectInstantiationNode.Type] ?? throw new Exception($"未找到类型 {objectInstantiationNode.Type.TypeName}"); + + var ctorArgTypes = objectInstantiationNode.Arguments + .Select(arg => symbolInfos[arg] ?? typeof(object)) + .ToArray(); + + var ctor = type.GetConstructor(ctorArgTypes) + ?? throw new Exception($"类型 {type} 找不到匹配的构造函数"); + + ILResult[] argIlResult = objectInstantiationNode.Arguments.Select(arg => EmitNode(il, arg)) .ToArray(); + + return new ILResult + { + ReturnType = type, + Emit = il => + { + // 构造函数参数压栈 + foreach (var argIL in argIlResult) + { + argIL.Emit(il); + } + + il.Emit(OpCodes.Newobj, ctor); // 调用构造函数,新对象入栈 + + if (objectInstantiationNode.CtorAssignments.Count == 0) + return; + + // 需要成员赋值,存入局部变量 + var local = il.DeclareLocal(type); + il.Emit(OpCodes.Stloc, local); // 将对象保存到局部变量 + il.Emit(OpCodes.Ldloc, local); // 再加载对象,保证栈顶仍是实例 + + // 遍历成员赋值 + foreach (var assignmentNode in objectInstantiationNode.CtorAssignments) + { + il.Emit(OpCodes.Ldloc, local); // 载入对象引用 + + var valueIL = EmitNode(il, assignmentNode.Value); + valueIL.Emit(il); // 载入赋值的值 + + // 先查属性 + var prop = type.GetProperty(assignmentNode.MemberName, BindingFlags.Public | BindingFlags.Instance); + if (prop != null && prop.CanWrite) + { + var setMethod = prop.GetSetMethod(); + il.EmitCall(OpCodes.Callvirt, setMethod, null); + } + else + { + // 查字段 + var field = type.GetField(assignmentNode.MemberName, BindingFlags.Public | BindingFlags.Instance); + if (field != null) + { + il.Emit(OpCodes.Stfld, field); + } + else + { + throw new Exception($"类型 {type} 没有成员 {assignmentNode.MemberName}"); + } + } + } + + // 最终栈顶是实例对象,供后续调用 + il.Emit(OpCodes.Ldloc, local); + } + }; + } + return GenerateObjectInstantiationNodeIL(objectInstantiationNode); + case CtorAssignmentNode ctorAssignmentNode: // 构造器赋值 + ILResult GenerateCtorAssignmentNodeIL(CtorAssignmentNode ctorAssignmentNode) + { + var classType = symbolInfos[ctorAssignmentNode.Class] ?? throw new Exception($"未找到类型 {ctorAssignmentNode.Class.TypeName}"); + + return new ILResult + { + ReturnType = typeof(void), + Emit = il => + { + // 栈顶已有对象引用,先发射赋值值 + var valueIL = EmitNode(il, ctorAssignmentNode.Value); + valueIL.Emit(il); + + // 查找成员 + var prop = classType.GetProperty(ctorAssignmentNode.MemberName, BindingFlags.Public | BindingFlags.Instance); + var field = prop == null ? classType.GetField(ctorAssignmentNode.MemberName, BindingFlags.Public | BindingFlags.Instance) : null; + + Type memberType = null; + if (prop != null && prop.CanWrite) + { + memberType = prop.PropertyType; + } + else if (field != null) + { + memberType = field.FieldType; + } + else + { + throw new Exception($"类型 {classType} 没有成员 {ctorAssignmentNode.MemberName}"); + } + + // 类型转换 + if (memberType != null && valueIL.ReturnType != memberType) + { + EmitConvert(il, valueIL.ReturnType, memberType); + } + + // 赋值 + if (prop != null && prop.CanWrite) + { + var setMethod = prop.GetSetMethod(); + il.EmitCall(OpCodes.Callvirt, setMethod, null); + } + else if (field != null) + { + il.Emit(OpCodes.Stfld, field); + } + } + }; + } + return GenerateCtorAssignmentNodeIL(ctorAssignmentNode); + case MemberAccessNode memberAccessNode: // 对象成员访问 + ILResult GenerateMemberAccessNodeIL(MemberAccessNode memberAccessNode) + { + var targetIL = EmitNode(il, memberAccessNode.Object); + + var targetType = targetIL.ReturnType; + var propInfo = targetType.GetProperty(memberAccessNode.MemberName, + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + ?? throw new InvalidOperationException($"属性 {memberAccessNode.MemberName} 不存在"); + + if (propInfo.GetMethod is null) + throw new InvalidOperationException($"属性 {memberAccessNode.MemberName} 无法获取 MethodInfo"); + + return new ILResult + { + ReturnType = propInfo.PropertyType, + Emit = il => + { + targetIL.Emit(il); + if (targetType.IsValueType) + { + il.Emit(OpCodes.Constrained, targetType); + il.EmitCall(OpCodes.Callvirt, propInfo.GetMethod, null); + } + else + { + il.EmitCall(OpCodes.Callvirt, propInfo.GetMethod, null); + } + } + }; + } + + return GenerateMemberAccessNodeIL(memberAccessNode); + case MemberAssignmentNode memberAssignmentNode: + ILResult GenerateMemberAssignmentNodeIL(MemberAssignmentNode memberAssignmentNode) + { + var objectResult = EmitNode(il, memberAssignmentNode.Object); + var classType = objectResult.ReturnType; + + return new ILResult + { + ReturnType = typeof(void), + Emit = il => + { + objectResult.Emit(il); // 先入栈对象实例 + var valueResult = EmitNode(il, memberAssignmentNode.Value); + valueResult.Emit(il); // 再入栈赋值值 + + var prop = classType.GetProperty(memberAssignmentNode.MemberName, BindingFlags.Public | BindingFlags.Instance); + var field = prop == null ? classType.GetField(memberAssignmentNode.MemberName, BindingFlags.Public | BindingFlags.Instance) : null; + + Type memberType = prop != null ? prop.PropertyType : field?.FieldType; + if (memberType != null && valueResult.ReturnType != memberType) + { + EmitConvert(il, valueResult.ReturnType, memberType); + } + + if (prop != null && prop.CanWrite) + { + if (classType.IsValueType) + { + il.Emit(OpCodes.Constrained, classType); + } + var setMethod = prop.GetSetMethod(); + il.EmitCall(OpCodes.Callvirt, setMethod, null); + } + else if (field != null) + { + il.Emit(OpCodes.Stfld, field); + } + else + { + throw new Exception($"类型 {classType} 没有成员 {memberAssignmentNode.MemberName}"); + } + } + }; + } + + return GenerateMemberAssignmentNodeIL(memberAssignmentNode); + case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用 + ILResult GenerateMemberFunctionCallNodeIL(MemberFunctionCallNode memberFunctionCallNode) + { + var targetIL = EmitNode(il, memberFunctionCallNode.Object); + + var argTypes = memberFunctionCallNode.Arguments + .Select(arg => symbolInfos[arg] ?? typeof(object)) + .ToArray(); + + var method = targetIL.ReturnType.GetMethod(memberFunctionCallNode.FunctionName, argTypes) + ?? throw new InvalidOperationException($"方法 {memberFunctionCallNode.FunctionName} 找不到匹配的重载"); + + var argResults = memberFunctionCallNode.Arguments.Select(arg => EmitNode(il, arg)).ToList(); + + return new ILResult + { + ReturnType = method.ReturnType, + Emit = il => + { + targetIL.Emit(il); + for (int i = 0; i < argResults.Count; i++) + { + argResults[i].Emit(il); + var paramType = method.GetParameters()[i].ParameterType; + var argType = argResults[i].ReturnType; + if (paramType != argType) + { + EmitConvert(il, argType, paramType); + } + } + + if (targetIL.ReturnType.IsValueType) + { + il.Emit(OpCodes.Constrained, targetIL.ReturnType); + il.EmitCall(OpCodes.Callvirt, method, null); + } + else + { + il.EmitCall(OpCodes.Callvirt, method, null); + } + } + }; + } + + return GenerateMemberFunctionCallNodeIL(memberFunctionCallNode); + case FunctionCallNode functionCallNode: // 外部挂载的函数调用 + ILResult GenerateFunctionCallNode(FunctionCallNode functionCallNode) + { + if (!SereinScript.FunctionInfos.TryGetValue(functionCallNode.FunctionName, out var methodInfo)) + throw new InvalidOperationException($"没有挂载 {functionCallNode.FunctionName} 方法信息"); + + var argResults = functionCallNode.Arguments.Select(arg => EmitNode(il, arg)).ToList(); + + return new ILResult + { + ReturnType = methodInfo.ReturnType, + Emit = il => + { + // 挂载函数为静态方法,不推入实例 + + for (int i = 0; i < argResults.Count; i++) + { + argResults[i].Emit(il); + var paramType = methodInfo.GetParameters()[i].ParameterType; + var argType = argResults[i].ReturnType; + if (paramType != argType) + { + EmitConvert(il, argType, paramType); + } + } + + if (methodInfo.IsStatic) + il.EmitCall(OpCodes.Call, methodInfo, null); + else + il.EmitCall(OpCodes.Callvirt, methodInfo, null); + } + }; + } + + return GenerateFunctionCallNode(functionCallNode); + default: // 未定义的节点类型 + break; + } + throw new InvalidOperationException($""); + } + + /// + /// 元素读取指令 + /// + /// + /// + void EmitLoadElement(ILGenerator il, Type elementType) + { + if (elementType.IsValueType) + { + if (elementType == typeof(int)) + il.Emit(OpCodes.Ldelem_I4); + else if (elementType == typeof(float)) + il.Emit(OpCodes.Ldelem_R4); + else if (elementType == typeof(double)) + il.Emit(OpCodes.Ldelem_R8); + else if (elementType == typeof(bool)) + il.Emit(OpCodes.Ldelem_I1); + else if (elementType == typeof(char)) + il.Emit(OpCodes.Ldelem_U2); + else + il.Emit(OpCodes.Ldelem, elementType); // 对于其它值类型,通用形式 + } + else + { + // 引用类型 + il.Emit(OpCodes.Ldelem_Ref); + } + } + + /// + /// 元素发射指令 + /// + /// + /// + void EmitStoreElement(ILGenerator il, Type elementType) + { + if (!elementType.IsValueType) + { + il.Emit(OpCodes.Stelem_Ref); + } + else if (elementType == typeof(int)) + { + il.Emit(OpCodes.Stelem_I4); + } + else if (elementType == typeof(long)) + { + il.Emit(OpCodes.Stelem_I8); + } + else if (elementType == typeof(float)) + { + il.Emit(OpCodes.Stelem_R4); + } + else if (elementType == typeof(double)) + { + il.Emit(OpCodes.Stelem_R8); + } + else if (elementType == typeof(short)) + { + il.Emit(OpCodes.Stelem_I2); + } + else if (elementType == typeof(byte)) + { + il.Emit(OpCodes.Stelem_I1); + } + else + { + // 泛型结构体类型 + il.Emit(OpCodes.Stelem, elementType); + } + } + + /// + /// 类型转换指令 + /// + /// + /// + /// + void EmitConvert(ILGenerator il, Type fromType, Type toType) + { + if (toType.IsAssignableFrom(fromType)) + return; + + if (toType.IsValueType) + il.Emit(OpCodes.Unbox_Any, toType); + else + il.Emit(OpCodes.Castclass, toType); + } + + + /*private void EmitIfNode(IScriptInvokeContext context, ILGenerator il, IfNode ifNode) + { + // Labels + var elseLabel = il.DefineLabel(); + var endLabel = il.DefineLabel(); + var loopBreakLabel = il.DefineLabel(); + + // 1. 计算 condition,栈顶为 object + EmitNode(il, ifNode.Condition); + + // 弹出object装箱,转bool + il.Emit(OpCodes.Unbox_Any, typeof(bool)); + + // 条件为 false,跳转 else + il.Emit(OpCodes.Brfalse, elseLabel); + + // 2. TrueBranch执行 + foreach (var stmt in ifNode.TrueBranch) + { + EmitNode(il, stmt); + + if (stmt is ReturnNode) + { + // 设置 context.IsNeedReturn = true + il.Emit(OpCodes.Ldarg_0); // context 参数 + il.Emit(OpCodes.Ldc_I4_1); + var isNeedReturnSetter = typeof(IScriptInvokeContext).GetProperty("IsNeedReturn").GetSetMethod(); + il.Emit(OpCodes.Callvirt, isNeedReturnSetter); + + // 跳出循环(直接跳到endLabel) + il.Emit(OpCodes.Br, endLabel); + } + } + + // 跳转到 if 结束 + il.Emit(OpCodes.Br, endLabel); + + // 3. else branch + il.MarkLabel(elseLabel); + foreach (var stmt in ifNode.FalseBranch) + { + EmitNode( il, stmt); + + if (stmt is ReturnNode) + { + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldc_I4_1); + var setter = typeof(IScriptInvokeContext).GetProperty("IsNeedReturn").GetSetMethod(); + il.Emit(OpCodes.Callvirt, setter); + il.Emit(OpCodes.Br, endLabel); + } + } + + // 4. 结束标签 + il.MarkLabel(endLabel); + + // 如果方法签名需要返回值,这里可放置默认 return 语句或用局部变量存储返回值 + }*/ + + private static bool IsGenericTask(Type returnType, out Type taskResult) + { + // 判断是否为 Task 类型或泛型 Task + if (returnType == typeof(Task)) + { + taskResult = null; + return true; + } + else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) + { + // 获取泛型参数类型 + Type genericArgument = returnType.GetGenericArguments()[0]; + taskResult = genericArgument; + return true; + } + else + { + taskResult = null; + return false; + + } + } + + + } +} + + +/* + +一、压入 IL 栈顶指令。 + 指令 含义 示例值(压栈) + ldc.i4 加载 4 字节整数 ldc.i4 123 + ldc.i4.s 加载 1 字节小整数(-128~127) ldc.i4.s 42 + ldc.i8 加载 8 字节整数 ldc.i8 123456789L + ldc.r4 加载 4 字节浮点数(float) ldc.r4 3.14f + ldc.r8 加载 8 字节浮点数(double) ldc.r8 3.14159 + ldstr 加载字符串 ldstr "hello" + ldnull 加载 null ldnull + ldarg, ldloc 加载参数或局部变量 ldarg.0, ldloc.1 + ldfld 加载实例字段 ldfld int32 MyField + ldsfld 加载静态字段 ldsfld string StaticVal + call, callvirt 方法调用的返回值会压栈 call int32 MyFunc() + newobj 创建对象实例,压入对象引用 newobj instance class MyClass::.ctor() + +二、取栈指令(Pop 或消耗栈顶),这些指令从栈中取出值(或多个值)进行使用或丢弃。 + 指令 含义 示例用途 + pop 弹出并丢弃栈顶元素 pop + stloc, starg 从栈顶弹出值,存入变量或参数 stloc.0, starg.1 + stfld 设置对象字段,先 pop 对象,再 pop 值 stfld int32 MyField + stsfld 设置静态字段 stsfld string StaticVal + stelem.* 设置数组元素 stelem.i4 等 + call, callvirt 方法调用会消费入参数,栈中应提前压好所有参数 + ret 从方法返回,会 pop 返回值(如有) ret + +一、算术运算指令(Arithmetic) +对栈顶两个数进行操作,结果压回栈顶: + + 指令 含义 示例(计算 a + b) + add 加法 ldloc.0, ldloc.1, add + sub 减法 ldloc.0, ldloc.1, sub + mul 乘法 ldloc.0, ldloc.1, mul + div 除法(整除) ldloc.0, ldloc.1, div + div.un 无符号除法 用于无符号整数 + rem 取模 ldloc.0, ldloc.1, rem + rem.un 无符号取模 + neg 取负数 ldloc.0, neg + inc 不存在,需自己实现 ldloc.0, ldc.i4.1, add + +二、逻辑/位运算指令(Bitwise & Logical) + 指令 含义 示例 + and 位与(&) ldloc.0, ldloc.1, and + or 位或(|) ldloc.0, ldloc.1, or + xor 位异或(^) + not 没有,需要自己实现 一般使用 ldc -1 xor 实现 + shl 左移(<<) ldloc.0, ldc.i4.2, shl + shr 右移(>>) + shr.un 无符号右移 + +三、比较指令(Comparison) +栈顶两个值比较,结果为 int32(0=false,1=true),通常用于判断表达式,配合 brtrue, brfalse 使用: + + 指令 含义 + ceq 相等比较(==) + cgt 大于比较(signed) + cgt.un 大于比较(unsigned) + clt 小于比较(signed) + clt.un 小于比较(unsigned) + + +四、条件跳转指令(Branch) + 指令 含义 + br label 无条件跳转 + brtrue label 若栈顶为 true(非0)则跳转 + brfalse label 若栈顶为 false(0)则跳转 + beq label 相等时跳转(会 pop 两个值) + bne.un label 不等时跳转 + bgt, blt, bge, ble 等 常见关系跳转 + +五、类型转换指令(Cast / Convert) + 指令 含义 + conv.i4 转换为 int32 + conv.r8 转为 double + box 装箱(值类型 -> object) + unbox.any 拆箱为值类型(object -> T) + castclass 强转引用类型 + isinst 类型判断(返回非 null 代表是该类型) + +六、对象相关指令(Object / Field / Property) + 指令 含义 + newobj 构造对象 + call 调用静态或实例方法(非虚拟) + callvirt 调用虚拟方法(或接口方法) + ldfld 加载字段值 + stfld 设置字段值 + ldsfld 加载静态字段值 + stsfld 设置静态字段值 + ldftn 加载函数指针 + ldvirtftn 加载虚拟函数指针 + constrained 修饰后续 callvirt 以支持值类型调用 + +七、数组操作指令 + 指令 含义 + newarr 创建一维数组 + ldlen 获取数组长度 + ldelem.* 读取数组元素 + stelem.* 写入数组元素 + +八、局部变量和参数指令 + 指令 含义 + ldloc.* 加载局部变量 + stloc.* 设置局部变量 + ldarg.* 加载参数 + starg.* 设置参数 + +九、异常处理(配合 .try/.catch/.finally) + 指令 含义 + .try, catch, finally 定义块 + throw 抛出异常 + rethrow 重新抛出当前异常 + leave 离开 try 块 + endfinally finally 块结束 + +十、调试与无操作 + 指令 含义 + nop 无操作,占位 + break 触发调试中断 + */ \ No newline at end of file diff --git a/Serein.Script/SereinScriptInterpreter.cs b/Serein.Script/SereinScriptInterpreter.cs index edfbf32..2dbefec 100644 --- a/Serein.Script/SereinScriptInterpreter.cs +++ b/Serein.Script/SereinScriptInterpreter.cs @@ -42,13 +42,6 @@ namespace Serein.Script return result; // 返回最后一个节点的结果 } - /* - async Task InterpreterAsync(IScriptInvokeContext context, ProgramNode node) - { - return null; - } - return await InterpreterAsync(context, node); - */ private async Task InterpretAsync(IScriptInvokeContext context, ASTNode node) { switch (node) @@ -243,8 +236,8 @@ namespace Serein.Script return await InterpreterObjectInstantiationNodeAsync(context, objectInstantiationNode); case CtorAssignmentNode ctorAssignmentNode: return default; - case ExpressionNode expressionNode: // 类型表达式(链式调用) - return await InterpretAsync(context, expressionNode.Value); // 直接计算表达式的值 + /*case ExpressionNode expressionNode: // 类型表达式(链式调用) + return await InterpretAsync(context, expressionNode.Value); // 直接计算表达式的值*/ case MemberAccessNode memberAccessNode: // 对象成员访问 async Task InterpreterMemberAccessNodeAsync(IScriptInvokeContext context, MemberAccessNode memberAccessNode) { @@ -319,7 +312,7 @@ namespace Serein.Script if (!SereinScript.FunctionDelegates.TryGetValue(funcName, out DelegateDetails? function)) throw new SereinSciptException(functionCallNode, $"没有挂载方法\"{functionCallNode.FunctionName}\""); - if (!function.EmitMethodInfo.IsStatic) + /* if (!function.EmitMethodInfo.IsStatic) { if (!SereinScript.DelegateInstances.TryGetValue(funcName, out var action)) { @@ -331,7 +324,7 @@ namespace Serein.Script { throw new SereinSciptException(functionCallNode, $"函数 {funcName} 尝试获取实例时返回了 null "); } - } + }*/ var result = await function.InvokeAsync(instance, arguments); return result; diff --git a/Serein.Script/SereinScriptToCsharpScript.cs b/Serein.Script/SereinScriptToCsharpScript.cs new file mode 100644 index 0000000..9c7a869 --- /dev/null +++ b/Serein.Script/SereinScriptToCsharpScript.cs @@ -0,0 +1,464 @@ +using Newtonsoft.Json.Linq; +using Serein.Library; +using Serein.Script.Node; +using Serein.Script.Node.FlowControl; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using System.Net.Security; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Serein.Script +{ + /// + /// 将 Serein 脚本转换为 C# 脚本的类 + /// + internal class SereinScriptToCsharpScript + { + /// + /// 符号表 + /// + private readonly Dictionary _symbolInfos; + + /// + /// 记录方法节点是否需要异步调用 + /// + private readonly Dictionary _asyncMethods; + + /// + /// 临时变量表 + /// + private readonly Dictionary _local = []; + + private bool _isTaskMain = false; + + public SereinScriptToCsharpScript(SereinScriptTypeAnalysis analysis) + { + _symbolInfos = analysis.NodeSymbolInfos; + _asyncMethods = analysis.AsyncMethods; + _isTaskMain = _asyncMethods.Any(kvp => kvp.Value); + } + + private readonly StringBuilder _codeBuilder = new StringBuilder(); + private int _indentLevel = 0; + + private void AppendLine(string code) + { + _codeBuilder.AppendLine(new string(' ', _indentLevel * 4) + code); + } + + private void Append(string code) + { + _codeBuilder.Append(code); + } + + private void Indent() => _indentLevel++; + private void Unindent() => _indentLevel--; + + private List> _classDefinitions = new List>(); + + public string CompileToCSharp(string mehtodName, ProgramNode programNode, Dictionary? param) + { + _codeBuilder.Clear(); + var sb = _codeBuilder; + var methodResultType = _symbolInfos[programNode]; + if(methodResultType == typeof(void)) + { + methodResultType = typeof(object); + } + var taskFullName = typeof(Task).FullName; + var returnContent = _isTaskMain ? $"global::{taskFullName}" : $"global::{methodResultType.FullName}"; + + AppendLine("public class SereinScriptToCsharp"); + AppendLine( "{"); + Indent(); + if(param is null || param.Count == 0) + { + AppendLine($"public static {returnContent} {mehtodName}()"); + } + else + { + AppendLine($"public static {returnContent} {mehtodName}({GetMethodParamster(param)})"); + } + AppendLine( "{"); + Indent(); + foreach (var stmt in programNode.Statements) + { + ConvertCode(stmt); // 递归遍历 + Append(";"); + } + if(!_symbolInfos.Keys.Any(node => node is ReturnNode)) + { + AppendLine("return null;"); + } + Unindent(); + AppendLine("}"); + Unindent(); + AppendLine("}"); + + foreach (var cd in _classDefinitions) + { + cd.Invoke(sb); + } + + return sb.ToString(); + } + + private string GetMethodParamster(Dictionary param) + { + var values = param.Select(kvp => + { + _local[kvp.Key] = kvp.Value; + return $"global::{kvp.Value.FullName} {kvp.Key}"; + }); + return string.Join(',', values); + } + + private void ConvertCode( ASTNode node) + { + switch (node) + { + case ProgramNode programNode: // 程序开始节点 + break; + case ReturnNode returnNode: // 程序退出节点 + void ConvertCodeOfReturnNode(ReturnNode returnNode) + { + if (returnNode.Value is not null) + { + Append($"return "); + ConvertCode(returnNode.Value); + Append(";"); + AppendLine(string.Empty); + } + else + { + AppendLine("return defult;"); + } + } + ConvertCodeOfReturnNode(returnNode); + break; + #region 字面量解析 + case NullNode nullNode: // null + Append("null"); + break; + case CharNode charNode: // char字面量 + Append($"'{charNode.Value}'"); + break; + case StringNode stringNode: // 字符串字面量 + Append($"\"{stringNode.Value}\""); + break; + case BooleanNode booleanNode: // 布尔值字面量 + Append($"{(booleanNode.Value ? "true" : "false")}"); + break; + case NumberIntNode numberIntNode: // int整型数值字面量 + Append($"{numberIntNode.Value}"); + break; + case NumberLongNode numberLongNode: // long整型数值字面量 + Append($"{numberLongNode.Value}"); + break; + case NumberFloatNode numberFloatNode: // float浮点数值字面量 + Append($"{numberFloatNode.Value}f"); + break; + case NumberDoubleNode numberDoubleNode: // double浮点数值字面量 + Append($"{numberDoubleNode.Value}d"); + break; + #endregion + case IdentifierNode identifierNode: // 变量定义 + void ConvertCodeOfIdentifierNode(IdentifierNode identifierNode) + { + var varName = identifierNode.Name; + + if(_local.TryGetValue(varName, out var type)) + { + // 定义过,需要使用变量 + Append(varName); + } + else + { + if (_symbolInfos.TryGetValue(identifierNode, out type)) + { + // 不存在,定义变量 + Append($"global::{type.FullName} {varName}"); + _local[varName] = type; + //Append(varName); + } + else + { + throw new Exception($"加载符号表时,无法匹配 IdentifierNode 节点的类型。 name : {varName}"); + } + } + } + ConvertCodeOfIdentifierNode(identifierNode); + break; + case IfNode ifNode: // if语句结构 + void ConvertCodeOfIfNode(IfNode ifNOde) + { + AppendLine(""); + Append($"if("); + ConvertCode(ifNOde.Condition); // 解析条件 + Append($" == true)"); + AppendLine("{"); + Indent(); + foreach(var item in ifNOde.TrueBranch) + { + ConvertCode(item); + //Append(";"); + AppendLine(string.Empty); + } + Unindent(); + AppendLine("}"); + AppendLine("else"); + AppendLine("{"); + Indent(); + foreach (var item in ifNOde.TrueBranch) + { + ConvertCode(item); + //Append(";"); + AppendLine(string.Empty); + } + Unindent(); + AppendLine("}"); + } + ConvertCodeOfIfNode(ifNode); + break; + case WhileNode whileNode: // while语句结构 + void ConvertCodeOfWhileNode(WhileNode whileNode) + { + AppendLine(""); + Append($"while("); + ConvertCode(whileNode.Condition); // 解析条件 + Append($" == {true})"); + AppendLine("{"); + Indent(); + foreach (var item in whileNode.Body) + { + ConvertCode(item); + Append(";"); + AppendLine(string.Empty); + } + Unindent(); + AppendLine("}"); + } + ConvertCodeOfWhileNode(whileNode); + break; + case AssignmentNode assignmentNode: // 变量赋值语句 + void ConvertCodeOfAssignmentNode(AssignmentNode assignmentNode) + { + ConvertCode(assignmentNode.Target); + Append(" = "); + if (assignmentNode.Value is null) + { + Append("null"); + } + else + { + ConvertCode(assignmentNode.Value); + } + Append(";"); + AppendLine(string.Empty); + } + ConvertCodeOfAssignmentNode(assignmentNode); + break; + case BinaryOperationNode binaryOperationNode: // 二元运算操作 + void ConvertCodeOfBinaryOperationNode(BinaryOperationNode binaryOperationNode) + { + Append("("); + ConvertCode(binaryOperationNode.Left); // 左操作数 + Append(binaryOperationNode.Operator); // 操作符 + ConvertCode(binaryOperationNode.Right); // 左操作数 + Append(")"); + } + ConvertCodeOfBinaryOperationNode(binaryOperationNode); + break; + case CollectionAssignmentNode collectionAssignmentNode: + void ConvertCodeOfCollectionAssignmentNode(CollectionAssignmentNode collectionAssignmentNode) + { + ConvertCode(collectionAssignmentNode.Collection); + Append(" = "); + ConvertCode(collectionAssignmentNode.Value); + Append(";"); + AppendLine(string.Empty); + } + ConvertCodeOfCollectionAssignmentNode(collectionAssignmentNode); + break; + case CollectionIndexNode collectionIndexNode: // 集合类型操作 + void ConvertCodeOfCollectionIndexNode(CollectionIndexNode collectionIndexNode) + { + ConvertCode(collectionIndexNode.Collection); + Append("["); + ConvertCode(collectionIndexNode.Index); + Append("]"); + } + ConvertCodeOfCollectionIndexNode(collectionIndexNode); + break; + case ClassTypeDefinitionNode classTypeDefinitionNode: // 类型定义 + void ConvertCodeOfClassTypeDefinitionNode(ClassTypeDefinitionNode classTypeDefinitionNode) + { + _classDefinitions.Add((sb) => + { + var type = _symbolInfos[classTypeDefinitionNode.ClassType]; + AppendLine($"public class {type.FullName}"); + AppendLine("{"); + Indent(); + foreach (var property in classTypeDefinitionNode.Propertys) + { + var propertyName = property.Key; + var propertyType = _symbolInfos[property.Value]; + AppendLine($"public global::{propertyType.FullName} {propertyName} {{ get; set; }}"); + } + Unindent(); + AppendLine("}"); + }); + } + ConvertCodeOfClassTypeDefinitionNode(classTypeDefinitionNode); + break; + case TypeNode typeNode: // 类型 + break; + case ObjectInstantiationNode objectInstantiationNode: // 类型实例化 + void ConvertCodeOfObjectInstantiationNode(ObjectInstantiationNode objectInstantiationNode) + { + var type = _symbolInfos[objectInstantiationNode.Type]; + Append($"new global::{type}"); + if (objectInstantiationNode.Arguments.Count > 0) + { + Append("("); + for (int i = 0; i < objectInstantiationNode.Arguments.Count; i++) + { + ConvertCode(objectInstantiationNode.Arguments[i]); + if (i < objectInstantiationNode.Arguments.Count - 1) + { + Append(", "); + } + } + Append(")"); + } + else + { + Append("()"); + } + if (objectInstantiationNode.CtorAssignments.Count > 0) + { + AppendLine("{"); + Indent(); + foreach (var assignment in objectInstantiationNode.CtorAssignments) + { + ConvertCode(assignment); + } + Unindent(); + AppendLine("}"); + } + } + ConvertCodeOfObjectInstantiationNode(objectInstantiationNode); + break; + case CtorAssignmentNode ctorAssignmentNode: // 构造器赋值 + void ConvertCodeOfCtorAssignmentNode(CtorAssignmentNode ctorAssignmentNode) + { + var propertyName = ctorAssignmentNode.MemberName; + var value = ctorAssignmentNode.Value; + Append($"{propertyName} = "); + ConvertCode(value); + AppendLine(","); + } + ConvertCodeOfCtorAssignmentNode(ctorAssignmentNode); + break; + case MemberAccessNode memberAccessNode: // 对象成员访问 + void ConvertCodeOfMemberAccessNode(MemberAccessNode memberAccessNode) + { + ConvertCode(memberAccessNode.Object); + Append($".{memberAccessNode.MemberName}"); + } + ConvertCodeOfMemberAccessNode(memberAccessNode); + break; + case MemberAssignmentNode memberAssignmentNode: // 对象成员赋值 + void ConvertCodeOfMemberAssignmentNode(MemberAssignmentNode memberAssignmentNode) + { + ConvertCode(memberAssignmentNode.Object); + Append($".{memberAssignmentNode.MemberName} = "); + ConvertCode(memberAssignmentNode.Value); + Append($";"); + AppendLine(string.Empty); + } + ConvertCodeOfMemberAssignmentNode(memberAssignmentNode); + break; + case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用 + void ConvertCodeOfMemberFunctionCallNode(MemberFunctionCallNode memberFunctionCallNode) + { + var isAsync = _asyncMethods.TryGetValue(memberFunctionCallNode, out var isAsyncValue) && isAsyncValue; + if (isAsync) + { + Append($"(await "); + } + + ConvertCode(memberFunctionCallNode.Object); + Append($".{memberFunctionCallNode.FunctionName}("); + for (int i = 0; i < memberFunctionCallNode.Arguments.Count; i++) + { + ASTNode? argNode = memberFunctionCallNode.Arguments[i]; + ConvertCode(argNode); + if (i < memberFunctionCallNode.Arguments.Count - 1) + { + Append(", "); + } + } + Append($")"); + if (isAsync) + { + Append($")"); + } + + + } + ConvertCodeOfMemberFunctionCallNode(memberFunctionCallNode); + break; + case FunctionCallNode functionCallNode: // 外部挂载的函数调用 + void ConvertCodeOfFunctionCallNode(FunctionCallNode functionCallNode) + { + var isAsync = _asyncMethods.TryGetValue(functionCallNode, out var isAsyncValue) && isAsyncValue; + if (isAsync) + { + Append($"(await "); + } + var funcName = functionCallNode.FunctionName switch + { + "int" => "@int" , + "bool" => "@bool" , + "double" => "@double" , + "long" => "@long" , + "decimal" => "@decimal" , + "float" => "@float" , + "byte" => "@byte" , + + _ => functionCallNode.FunctionName, + }; + Append($"global::Serein.Library.ScriptBaseFunc.{funcName}("); + for (int i = 0; i < functionCallNode.Arguments.Count; i++) + { + ASTNode? argNode = functionCallNode.Arguments[i]; + ConvertCode(argNode); + if (i < functionCallNode.Arguments.Count - 1) + { + Append(", "); + } + } + Append($")"); + if (isAsync) + { + Append($")"); + } + } + ConvertCodeOfFunctionCallNode(functionCallNode); + break; + default: // 未定义的节点类型 + break; + } + } + + } + + +} diff --git a/Serein.Script/SereinScriptTypeAnalysis.cs b/Serein.Script/SereinScriptTypeAnalysis.cs index 15d5810..fde6898 100644 --- a/Serein.Script/SereinScriptTypeAnalysis.cs +++ b/Serein.Script/SereinScriptTypeAnalysis.cs @@ -5,6 +5,7 @@ using Serein.Script.Node.FlowControl; using Serein.Script.Symbol; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reactive; using System.Reflection; @@ -27,6 +28,17 @@ namespace Serein.Script /// public Dictionary NodeSymbolInfos { get; } = new Dictionary(); + /// + /// 记录方法节点是否需要进行异步调用 + /// + public Dictionary AsyncMethods { get; } = new Dictionary(); + + public void Reset() + { + NodeSymbolInfos.Clear(); // 清空符号表 + AsyncMethods.Clear(); + } + public void LoadSymbol(Dictionary identifierNodes) { foreach(var kvp in identifierNodes) @@ -74,6 +86,7 @@ namespace Serein.Script } } + /// /// 类型获取 @@ -293,7 +306,7 @@ namespace Serein.Script return valueType; } return AnalysisCtorAssignmentNode(ctorAssignmentNode); - case ExpressionNode expressionNode: // 类型表达式(链式调用) + /*case ExpressionNode expressionNode: // 类型表达式(链式调用) Type AnalysisObjectMemberExpressionNode(ExpressionNode expressionNode) { // 1. 对象成员获取 MemberAccessNode @@ -304,7 +317,7 @@ namespace Serein.Script NodeSymbolInfos[expressionNode] = resultType; return resultType; } - return AnalysisObjectMemberExpressionNode(expressionNode); + return AnalysisObjectMemberExpressionNode(expressionNode);*/ case MemberAccessNode memberAccessNode: // 对象成员访问 Type AnalysisMemberAccessNode(MemberAccessNode memberAccessNode) { @@ -365,9 +378,12 @@ namespace Serein.Script Type argType = types[index]; NodeSymbolInfos[argNode] = argType; } + var isAsync = IsGenericTask(methodInfo.ReturnType, out var taskResult); + var methodReturnType = isAsync ? taskResult : methodInfo.ReturnType; + AsyncMethods[memberFunctionCallNode] = isAsync; NodeSymbolInfos[memberFunctionCallNode.Object] = objectType; - NodeSymbolInfos[memberFunctionCallNode] = methodInfo.ReturnType; - return methodInfo.ReturnType; + NodeSymbolInfos[memberFunctionCallNode] = methodReturnType; + return methodReturnType; } @@ -386,8 +402,11 @@ namespace Serein.Script Type argType = types[index]; NodeSymbolInfos[argNode] = argType; } - NodeSymbolInfos[functionCallNode] = methodInfo.ReturnType; - return methodInfo.ReturnType; + var isAsync = IsGenericTask(methodInfo.ReturnType, out var taskResult); + var methodReturnType = isAsync ? taskResult : methodInfo.ReturnType; + AsyncMethods[functionCallNode] = isAsync; + NodeSymbolInfos[functionCallNode] = methodReturnType; + return methodReturnType; } return AnalysisFunctionCallNode(functionCallNode); default: // 未定义的节点类型 @@ -398,7 +417,6 @@ namespace Serein.Script - /// /// 类型分析 /// @@ -472,9 +490,9 @@ namespace Serein.Script case ObjectInstantiationNode objectInstantiationNode: // 类型实例化 Analysis(objectInstantiationNode); break; - case ExpressionNode expressionNode: // 类型表达式(链式调用) + /* case ExpressionNode expressionNode: // 类型表达式(链式调用) Analysis(expressionNode.Value); - break; + break;*/ case MemberAccessNode memberAccessNode: // 对象成员访问 Analysis(memberAccessNode); break; @@ -497,7 +515,7 @@ namespace Serein.Script } - private void Analysis2(ASTNode node) + private void ToILCompiler(ASTNode node) { switch (node) { @@ -541,9 +559,7 @@ namespace Serein.Script break; case ObjectInstantiationNode objectInstantiationNode: // 类型实例化 break; - case CtorAssignmentNode ctorAssignmentNode: - break; - case ExpressionNode expressionNode: // 类型表达式(链式调用) + case CtorAssignmentNode ctorAssignmentNode: // 构造器赋值 break; case MemberAccessNode memberAccessNode: // 对象成员访问 break; @@ -582,8 +598,28 @@ namespace Serein.Script } } + private static bool IsGenericTask(Type returnType, out Type taskResult) + { + // 判断是否为 Task 类型或泛型 Task + if (returnType == typeof(Task)) + { + taskResult = typeof(void); + return true; + } + else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) + { + // 获取泛型参数类型 + Type genericArgument = returnType.GetGenericArguments()[0]; + taskResult = genericArgument; + return true; + } + else + { + taskResult = null; + return false; - + } + } /// /// 获取某个集合类型支持的索引参数类型