refactot(script) : 调整了脚本执行相关代码(但后续这个脚本会新的DSL被代替),故不再进行调整

This commit is contained in:
fengjiayi
2026-01-27 17:25:20 +08:00
parent dddc3b3b53
commit f93d46565f
9 changed files with 169 additions and 35 deletions

View File

@@ -8,15 +8,10 @@ namespace Serein.Script
public interface IScriptInvokeContext public interface IScriptInvokeContext
{ {
/// <summary>
/// 是否该退出了(由 TokenSource 控制,用于响应外部发出停止信号)
/// </summary>
bool IsReturn { get; }
/// <summary> /// <summary>
/// 是否需要提前返回(用于脚本中提前结束) /// 是否需要提前返回(用于脚本中提前结束)
/// </summary> /// </summary>
bool IsNeedReturn { get; set; } bool IsReturn { get; set; }
/// <summary> /// <summary>
/// 是否严格检查 Null 值 (禁止使用 Null /// 是否严格检查 Null 值 (禁止使用 Null

View File

@@ -11,6 +11,9 @@ namespace Serein.Script.Node
/// </summary> /// </summary>
public class TypeNode : ASTNode public class TypeNode : ASTNode
{ {
/// <summary>
/// 类型名称
/// </summary>
public string TypeName { get; } public string TypeName { get; }
public TypeNode(string typeName) public TypeNode(string typeName)

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Script.Node
{
internal class UsingNode : ASTNode
{
public string Namespace { get; }
public UsingNode(string @namespace)
{
Namespace = @namespace;
}
public override string ToString()
{
return $"using Namespace";
}
}
}

View File

@@ -21,11 +21,6 @@ namespace Serein.Script
/// </summary> /// </summary>
private CancellationTokenSource _tokenSource = new CancellationTokenSource(); private CancellationTokenSource _tokenSource = new CancellationTokenSource();
/// <summary>
/// 是否该退出了
/// </summary>
public bool IsReturn => _tokenSource.IsCancellationRequested;
/// <summary> /// <summary>
/// 是否严格检查 Null 值 (禁止使用 Null /// 是否严格检查 Null 值 (禁止使用 Null
/// </summary> /// </summary>
@@ -34,7 +29,7 @@ namespace Serein.Script
/// <summary> /// <summary>
/// 是否需要提前返回(用于脚本中提前结束) /// 是否需要提前返回(用于脚本中提前结束)
/// </summary> /// </summary>
public bool IsNeedReturn { get; set; } public bool IsReturn { get; set; }
/// <summary> /// <summary>

View File

@@ -5,7 +5,9 @@ using System.Reflection;
namespace Serein.Script namespace Serein.Script
{ {
/// <summary>
/// Serein 脚本引擎
/// </summary>
public class SereinScript public class SereinScript
{ {
/// <summary> /// <summary>
@@ -14,10 +16,17 @@ namespace Serein.Script
public SereinScriptTypeAnalysis TypeAnalysis { get; } = new SereinScriptTypeAnalysis(); public SereinScriptTypeAnalysis TypeAnalysis { get; } = new SereinScriptTypeAnalysis();
/// <summary>
/// 程序起始节点
/// </summary>
private ProgramNode? programNode; private ProgramNode? programNode;
/// <summary>
/// 执行脚本(静态方法)
/// </summary>
/// <param name="script"></param>
/// <param name="argTypes"></param>
/// <returns></returns>
public static Task<object?> ExecuteAsync(string script, Dictionary<string, Type>? argTypes = null) public static Task<object?> ExecuteAsync(string script, Dictionary<string, Type>? argTypes = null)
{ {
SereinScriptParser parser = new SereinScriptParser(); SereinScriptParser parser = new SereinScriptParser();
@@ -50,7 +59,22 @@ namespace Serein.Script
return returnType; // 脚本返回类型 return returnType; // 脚本返回类型
} }
/// <summary>
/// 执行脚本
/// </summary>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public async Task<object?> InterpreterAsync()
{
IScriptInvokeContext context = new ScriptInvokeContext();
if (programNode is null)
{
throw new ArgumentNullException(nameof(programNode));
}
Dictionary<ASTNode, Type> symbolInfos = TypeAnalysis.NodeSymbolInfos.ToDictionary();
SereinScriptInterpreter Interpreter = new SereinScriptInterpreter(symbolInfos);
return await Interpreter.InterpreterAsync(context, programNode);
}
/// <summary> /// <summary>
/// 执行脚本 /// 执行脚本
@@ -73,7 +97,7 @@ namespace Serein.Script
/// <summary> /// <summary>
/// 转换为 C# 代码,并且附带方法信息 /// 转换为 C# 代码,并且附带方法信息
/// </summary> /// </summary>
/// <param name="script">脚本</param> /// <param name="mehtodName">脚本</param>
/// <param name="argTypes">挂载的变量</param> /// <param name="argTypes">挂载的变量</param>
/// <returns></returns> /// <returns></returns>
public SereinScriptMethodInfo? ConvertCSharpCode(string mehtodName, Dictionary<string, Type>? argTypes = null) public SereinScriptMethodInfo? ConvertCSharpCode(string mehtodName, Dictionary<string, Type>? argTypes = null)
@@ -163,7 +187,7 @@ namespace Serein.Script
/// <summary> /// <summary>
/// 挂载类型 /// 挂载类型
/// </summary> /// </summary>
/// <param name="typeName">函数名称</param> /// <param name="type">类型</param>
/// <param name="typeName">指定类型名称</param> /// <param name="typeName">指定类型名称</param>
public static void AddClassType(Type type, string typeName = "") public static void AddClassType(Type type, string typeName = "")
{ {

View File

@@ -32,7 +32,6 @@ namespace Serein.Script
foreach (var node in nodes) foreach (var node in nodes)
{ {
result = await InterpretAsync(context, node); // 解释每个节点 result = await InterpretAsync(context, node); // 解释每个节点
if (context.IsReturn) break; // 如果需要提前返回,则停止执行
} }
return result; // 返回最后一个节点的结果 return result; // 返回最后一个节点的结果
} }
@@ -41,14 +40,25 @@ namespace Serein.Script
{ {
switch (node) switch (node)
{ {
case UsingNode usingNode: // 程序开始节点
return null;
case ProgramNode programNode: // 程序开始节点 case ProgramNode programNode: // 程序开始节点
throw new Exception(); throw new Exception();
case ReturnNode returnNode: // 程序退出节点 case ReturnNode returnNode: // 程序退出节点
async Task<object?> InterpreterReturnNodeAsync(IScriptInvokeContext context, ReturnNode returnNode) async Task<object?> InterpreterReturnNodeAsync(IScriptInvokeContext context, ReturnNode returnNode)
{ {
object? returnValue = await InterpretAsync(context, returnNode.Value); var node = returnNode.Value;
context.IsNeedReturn = true; context.IsReturn = true;
return returnValue; if (node is null)
{
return null;
}
else
{
object? returnValue = await InterpretAsync(context, node);
return returnValue;
}
} }
return await InterpreterReturnNodeAsync(context, returnNode); return await InterpreterReturnNodeAsync(context, returnNode);
#region #region
@@ -86,7 +96,7 @@ namespace Serein.Script
data = await InterpretAsync(context, branchNode); data = await InterpretAsync(context, branchNode);
if (branchNode is ReturnNode) // 遇到 Return 语句 提前退出 if (branchNode is ReturnNode) // 遇到 Return 语句 提前退出
{ {
context.IsNeedReturn = true; context.IsReturn = true;
break; break;
} }
} }
@@ -109,7 +119,7 @@ namespace Serein.Script
data = await InterpretAsync(context, node); data = await InterpretAsync(context, node);
if (node is ReturnNode) // 遇到 Return 语句 提前退出 if (node is ReturnNode) // 遇到 Return 语句 提前退出
{ {
context.IsNeedReturn = true; context.IsReturn = true;
break ; break ;
} }
} }

View File

@@ -156,6 +156,7 @@ namespace Serein.Script
"while", "while",
"new", "new",
"class", "class",
"using",
]; ];
internal SereinScriptLexer(string input) internal SereinScriptLexer(string input)

View File

@@ -14,7 +14,6 @@ namespace Serein.Script
private SereinScriptLexer _lexer; private SereinScriptLexer _lexer;
private Token _currentToken; private Token _currentToken;
public SereinScriptParser() public SereinScriptParser()
{ {
@@ -32,7 +31,16 @@ namespace Serein.Script
return Program(); return Program();
} }
private List<ASTNode> Statements { get; } = new List<ASTNode>();
/// <summary>
/// 命名空间
/// </summary>
public List<string> UsingNamespaces { get; } = new List<string>();
/// <summary>
/// 程序节点
/// </summary>
public List<ASTNode> Statements { get; } = new List<ASTNode>();
/// <summary> /// <summary>
/// 解析整个程序直到遇到文件结尾EOF为止。 /// 解析整个程序直到遇到文件结尾EOF为止。
@@ -122,7 +130,6 @@ namespace Serein.Script
#region /ASTNode #region /ASTNode
if (isAssignment) if (isAssignment)
{ {
// 以赋值语句的形式进行处理 // 以赋值语句的形式进行处理
var assignmentNode = ParseAssignmentNode(); // 解析复制表达式 var assignmentNode = ParseAssignmentNode(); // 解析复制表达式
//if(_currentToken.Type == TokenType.Semicolon) //if(_currentToken.Type == TokenType.Semicolon)
@@ -140,6 +147,12 @@ namespace Serein.Script
#endregion #endregion
} }
else if (JudgmentKeyword("using")) // 定义对象
{
var usingNode = ParseUsingNode();
UsingNamespaces.Add(usingNode.Namespace);
return usingNode;
}
else if (JudgmentKeyword("class")) // 定义对象 else if (JudgmentKeyword("class")) // 定义对象
{ {
var classDefinitionNode = ParseClassDefinitionNode(); var classDefinitionNode = ParseClassDefinitionNode();
@@ -175,6 +188,27 @@ namespace Serein.Script
}*/ }*/
} }
/// <summary>
/// 解析表达式根节点
/// </summary>
/// <returns></returns>
private ASTNode ParserExpRootNode()
{
var name = _currentToken.Value;
foreach(var nsp in UsingNamespaces)
{
var fullName = $"{nsp}.{name}";
var type = SereinScriptTypeAnalysis.GetTypeOfString(fullName);
if (type is null)
{
continue;
}
var typeNode = new TypeNode(fullName).SetTokenInfo(_currentToken);
return typeNode;
}
return new IdentifierNode(_currentToken.Value).SetTokenInfo(_currentToken);
}
/// <summary> /// <summary>
/// 解析赋值语句 /// 解析赋值语句
/// </summary> /// </summary>
@@ -213,8 +247,10 @@ namespace Serein.Script
* */ * */
//if (JudgmentOperator(_currentToken, "=")) break; // 退出 //if (JudgmentOperator(_currentToken, "=")) break; // 退出
//var tempPeekToken = _lexer.PeekToken(); //var tempPeekToken = _lexer.PeekToken();
var backupToken = _currentToken;
var targetNode = new IdentifierNode(_currentToken.Value).SetTokenInfo(_currentToken); // 生成 Tagget 标记节点
var targetNode = ParserExpRootNode(); // 生成 Tagget 标记节点
//var targetNode = new IdentifierNode(_currentToken.Value).SetTokenInfo(_currentToken); // 生成 Tagget 标记节点
List<ASTNode> nodes = [targetNode]; List<ASTNode> nodes = [targetNode];
ASTNode? source; ASTNode? source;
@@ -473,6 +509,34 @@ namespace Serein.Script
throw new Exception($"解析参数节点后当前Token类型不符合预期当前类型为 {_currentToken.Type}。"); throw new Exception($"解析参数节点后当前Token类型不符合预期当前类型为 {_currentToken.Type}。");
} }
/// <summary>
/// (不处理分号)
/// 1. 引入命名空间
/// </summary>
/// <returns></returns>
private UsingNode ParseUsingNode()
{
var startIndex = _lexer.GetIndex(); // 从“using”开始锚定代码范围
NextToken(); // 消耗“using”关键字获取类名
List<string> namespaceItems = [];
while (true) // 遇到 ";" 时结束
{
string className = _currentToken.Value; // 命名空间
namespaceItems.Add(className);
if (_currentToken.Type == TokenType.Semicolon)
{
break;
}
NextToken(); // 命名空间
}
var nsp = string.Join('.', namespaceItems);
UsingNode usingNode = new UsingNode(nsp);
var usingToken = _currentToken;
usingToken.Code = _lexer.GetCoreContent(startIndex); // 收集类型定义的代码。
usingNode.SetTokenInfo(usingToken);
return usingNode;
}
/// <summary> /// <summary>
/// (没有分号)解析类定义 /// (没有分号)解析类定义
/// </summary> /// </summary>

View File

@@ -23,7 +23,6 @@ namespace Serein.Script
/// </summary> /// </summary>
public class SereinScriptTypeAnalysis public class SereinScriptTypeAnalysis
{ {
/// <summary> /// <summary>
/// 符号表 /// 符号表
/// </summary> /// </summary>
@@ -100,6 +99,10 @@ namespace Serein.Script
{ {
switch (node) switch (node)
{ {
case UsingNode usingNode:
// 引用命名空间节点,不产生类型
NodeSymbolInfos[usingNode] = typeof(void);
return typeof(void);
case ProgramNode programNode: // 程序开始节点 case ProgramNode programNode: // 程序开始节点
NodeSymbolInfos[programNode] = typeof(void); NodeSymbolInfos[programNode] = typeof(void);
return typeof(void); return typeof(void);
@@ -666,21 +669,37 @@ namespace Serein.Script
} }
} }
/// <summary>
public static Type GetTypeOfString(string typeName) /// 跳过名称搜索类型
/// </summary>
/// <param name="typeFullnameName"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public static Type GetTypeOfString(string typeFullnameName)
{ {
Type? resultType = null; Type? resultType = null;
resultType = DynamicObjectHelper.GetCacheType(typeName); // 从自定义类型查询类型 resultType = DynamicObjectHelper.GetCacheType(typeFullnameName); // 从自定义类型查询类型
if (resultType != null) if (resultType != null)
{ {
return resultType; return resultType;
} }
resultType = Type.GetType(typeName); // 从命名空间查询类型
resultType = Type.GetType(typeFullnameName); // 从命名空间查询类型
if (resultType != null) if (resultType != null)
{ {
return resultType; return resultType;
} }
throw new InvalidOperationException($"无法匹配类型 {typeName}");
if(SereinEnv.Environment is not null && SereinEnv.Environment.FlowLibraryService is not null)
{
resultType = SereinEnv.Environment.FlowLibraryService.GetType(typeFullnameName);
if (resultType != null)
{
return resultType;
}
}
throw new InvalidOperationException($"无法匹配类型 {typeFullnameName}");
} }