mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-05 07:46:35 +08:00
暂时实现了简陋的脚本AST分析解释,后面再绑定到控件上
This commit is contained in:
26
Serein.Script/Node/ASTNode.cs
Normal file
26
Serein.Script/Node/ASTNode.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
public abstract class ASTNode
|
||||
{
|
||||
public string Code { get; private set; }
|
||||
public int Row { get; private set; }
|
||||
public int StartIndex { get; private set; }
|
||||
public int Length { get; private set; }
|
||||
|
||||
internal ASTNode SetTokenInfo(Token token)
|
||||
{
|
||||
Row = token.Row;
|
||||
StartIndex = token.StartIndex;
|
||||
Length = token.Length;
|
||||
Code = token.Code;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
21
Serein.Script/Node/AssignmentNode.cs
Normal file
21
Serein.Script/Node/AssignmentNode.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 变量节点
|
||||
/// </summary>
|
||||
public class AssignmentNode : ASTNode
|
||||
{
|
||||
public string Variable { get; }
|
||||
public ASTNode Value { get; }
|
||||
public AssignmentNode(string variable, ASTNode value) => (Variable, Value) = (variable, value);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
26
Serein.Script/Node/BinaryOperationNode.cs
Normal file
26
Serein.Script/Node/BinaryOperationNode.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 二元表达式节点
|
||||
/// </summary>
|
||||
|
||||
public class BinaryOperationNode : ASTNode
|
||||
{
|
||||
public ASTNode Left { get; }
|
||||
public string Operator { get; }
|
||||
public ASTNode Right { get; }
|
||||
|
||||
public BinaryOperationNode(ASTNode left, string op, ASTNode right)
|
||||
{
|
||||
Left = left;
|
||||
Operator = op;
|
||||
Right = right;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Serein.Script/Node/BooleanNode.cs
Normal file
17
Serein.Script/Node/BooleanNode.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 布尔字面量
|
||||
/// </summary>
|
||||
public class BooleanNode : ASTNode
|
||||
{
|
||||
public bool Value { get; }
|
||||
public BooleanNode(bool value) => Value = value;
|
||||
}
|
||||
}
|
||||
24
Serein.Script/Node/ClassTypeDefinitionNode.cs
Normal file
24
Serein.Script/Node/ClassTypeDefinitionNode.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 动态类型定义
|
||||
/// </summary>
|
||||
public class ClassTypeDefinitionNode : ASTNode
|
||||
{
|
||||
public string ClassName { get; }
|
||||
public Dictionary<string, Type> Fields { get; }
|
||||
|
||||
public ClassTypeDefinitionNode(Dictionary<string, Type> fields, string className)
|
||||
{
|
||||
this.Fields = fields;
|
||||
this.ClassName = className;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
20
Serein.Script/Node/CollectionIndexNode.cs
Normal file
20
Serein.Script/Node/CollectionIndexNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 集合索引获取
|
||||
/// </summary>
|
||||
public class CollectionIndexNode : ASTNode
|
||||
{
|
||||
public ASTNode IndexValue { get; }
|
||||
public CollectionIndexNode(ASTNode indexValue)
|
||||
{
|
||||
this.IndexValue = indexValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Serein.Script/Node/FunctionCallNode.cs
Normal file
24
Serein.Script/Node/FunctionCallNode.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 挂载函数调用
|
||||
/// </summary>
|
||||
public class FunctionCallNode : ASTNode
|
||||
{
|
||||
public string FunctionName { get; }
|
||||
public List<ASTNode> Arguments { get; }
|
||||
|
||||
public FunctionCallNode(string functionName, List<ASTNode> arguments)
|
||||
{
|
||||
FunctionName = functionName;
|
||||
Arguments = arguments;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
17
Serein.Script/Node/IdentifierNode.cs
Normal file
17
Serein.Script/Node/IdentifierNode.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 标识符(变量)
|
||||
/// </summary>
|
||||
public class IdentifierNode : ASTNode
|
||||
{
|
||||
public string Name { get; }
|
||||
public IdentifierNode(string name) => Name = name;
|
||||
}
|
||||
}
|
||||
21
Serein.Script/Node/IfNode.cs
Normal file
21
Serein.Script/Node/IfNode.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 条件节点
|
||||
/// </summary>
|
||||
public class IfNode : ASTNode
|
||||
{
|
||||
public ASTNode Condition { get; }
|
||||
public List<ASTNode> TrueBranch { get; }
|
||||
public List<ASTNode> FalseBranch { get; }
|
||||
public IfNode(ASTNode condition, List<ASTNode> trueBranch, List<ASTNode> falseBranch)
|
||||
=> (Condition, TrueBranch, FalseBranch) = (condition, trueBranch, falseBranch);
|
||||
}
|
||||
|
||||
}
|
||||
23
Serein.Script/Node/MemberAccessNode.cs
Normal file
23
Serein.Script/Node/MemberAccessNode.cs
Normal 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
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示对象的成员访问
|
||||
/// </summary>
|
||||
public class MemberAccessNode : ASTNode
|
||||
{
|
||||
public ASTNode Object { get; }
|
||||
public string MemberName { get; }
|
||||
|
||||
public MemberAccessNode(ASTNode obj, string memberName)
|
||||
{
|
||||
Object = obj;
|
||||
MemberName = memberName;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Serein.Script/Node/MemberAssignmentNode.cs
Normal file
25
Serein.Script/Node/MemberAssignmentNode.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示对对象成员的赋值
|
||||
/// </summary>
|
||||
public class MemberAssignmentNode : ASTNode
|
||||
{
|
||||
public ASTNode Object { get; }
|
||||
public string MemberName { get; }
|
||||
public ASTNode Value { get; }
|
||||
|
||||
public MemberAssignmentNode(ASTNode obj, string memberName, ASTNode value)
|
||||
{
|
||||
Object = obj;
|
||||
MemberName = memberName;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Serein.Script/Node/MemberFunctionCallNode.cs
Normal file
25
Serein.Script/Node/MemberFunctionCallNode.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 对象成员方法调用
|
||||
/// </summary>
|
||||
public class MemberFunctionCallNode : ASTNode
|
||||
{
|
||||
public ASTNode Object { get; }
|
||||
public string FunctionName { get; }
|
||||
public List<ASTNode> Arguments { get; }
|
||||
|
||||
public MemberFunctionCallNode(ASTNode @object, string functionName, List<ASTNode> arguments)
|
||||
{
|
||||
Object = @object;
|
||||
FunctionName = functionName;
|
||||
Arguments = arguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Serein.Script/Node/NullNode.cs
Normal file
15
Serein.Script/Node/NullNode.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// Null节点
|
||||
/// </summary>
|
||||
public class NullNode : ASTNode
|
||||
{
|
||||
}
|
||||
}
|
||||
21
Serein.Script/Node/NumberNode.cs
Normal file
21
Serein.Script/Node/NumberNode.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
23
Serein.Script/Node/ObjectInstantiationNode.cs
Normal file
23
Serein.Script/Node/ObjectInstantiationNode.cs
Normal 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
|
||||
{
|
||||
/// <summary>
|
||||
/// 类型创建
|
||||
/// </summary>
|
||||
public class ObjectInstantiationNode : ASTNode
|
||||
{
|
||||
public string TypeName { get; }
|
||||
public List<ASTNode> Arguments { get; }
|
||||
public ObjectInstantiationNode(string typeName, List<ASTNode> arguments)
|
||||
{
|
||||
this.TypeName = typeName;
|
||||
this.Arguments = arguments;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
22
Serein.Script/Node/ProgramNode.cs
Normal file
22
Serein.Script/Node/ProgramNode.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 程序入口
|
||||
/// </summary>
|
||||
public class ProgramNode : ASTNode
|
||||
{
|
||||
public List<ASTNode> Statements { get; }
|
||||
|
||||
public ProgramNode(List<ASTNode> statements)
|
||||
{
|
||||
Statements = statements;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
25
Serein.Script/Node/ReturnNode.cs
Normal file
25
Serein.Script/Node/ReturnNode.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 返回值
|
||||
/// </summary>
|
||||
public class ReturnNode : ASTNode
|
||||
{
|
||||
public ASTNode Value { get; }
|
||||
|
||||
public ReturnNode(ASTNode returnNode)
|
||||
{
|
||||
Value = returnNode;
|
||||
}
|
||||
public ReturnNode()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
61
Serein.Script/Node/StringNode.cs
Normal file
61
Serein.Script/Node/StringNode.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 字符串字面量节点
|
||||
/// </summary>
|
||||
public class StringNode : ASTNode
|
||||
{
|
||||
public string Value { get; }
|
||||
|
||||
public StringNode(string input)
|
||||
{
|
||||
// 使用 StringBuilder 来构建输出
|
||||
StringBuilder output = new StringBuilder(input.Length);
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
if (i < input.Length - 1 && input[i] == '\\') // 找到反斜杠
|
||||
{
|
||||
char nextChar = input[i + 1];
|
||||
|
||||
// 处理转义符
|
||||
switch (nextChar)
|
||||
{
|
||||
case 'r':
|
||||
output.Append('\r');
|
||||
i++; // 跳过 'r'
|
||||
break;
|
||||
case 'n':
|
||||
output.Append('\n');
|
||||
i++; // 跳过 'n'
|
||||
break;
|
||||
case 't':
|
||||
output.Append('\t');
|
||||
i++; // 跳过 't'
|
||||
break;
|
||||
case '\\': // 字面量反斜杠
|
||||
output.Append('\\');
|
||||
i++; // 跳过第二个 '\\'
|
||||
break;
|
||||
default:
|
||||
output.Append(input[i]); // 不是转义符,保留反斜杠
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Append(input[i]); // 其他字符直接添加
|
||||
}
|
||||
}
|
||||
Value = output.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
19
Serein.Script/Node/WhileNode.cs
Normal file
19
Serein.Script/Node/WhileNode.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 循环条件节点
|
||||
/// </summary>
|
||||
public class WhileNode : ASTNode
|
||||
{
|
||||
public ASTNode Condition { get; }
|
||||
public List<ASTNode> Body { get; }
|
||||
public WhileNode(ASTNode condition, List<ASTNode> body) => (Condition, Body) = (condition, body);
|
||||
}
|
||||
|
||||
}
|
||||
19
Serein.Script/Serein.Script.csproj
Normal file
19
Serein.Script/Serein.Script.csproj
Normal file
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Tool\**" />
|
||||
<EmbeddedResource Remove="Tool\**" />
|
||||
<None Remove="Tool\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
532
Serein.Script/SereinScriptInterpreter.cs
Normal file
532
Serein.Script/SereinScriptInterpreter.cs
Normal file
@@ -0,0 +1,532 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Script.Node;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Serein.Script
|
||||
{
|
||||
public sealed class SereinSciptException : Exception
|
||||
{
|
||||
//public ASTNode Node { get; }
|
||||
public override string Message { get; }
|
||||
|
||||
public SereinSciptException(ASTNode node, string message)
|
||||
{
|
||||
//this.Node = node;
|
||||
Message = $"异常信息 : {message} ,代码在第{node.Row}行: {node.Code.Trim()}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SereinScriptInterpreter
|
||||
{
|
||||
/// <summary>
|
||||
/// 定义的变量
|
||||
/// </summary>
|
||||
private Dictionary<string, object> _variables = new Dictionary<string, object>();
|
||||
|
||||
/// <summary>
|
||||
/// 挂载的函数
|
||||
/// </summary>
|
||||
private static Dictionary<string, DelegateDetails> _functionTable = new Dictionary<string, DelegateDetails>();
|
||||
|
||||
/// <summary>
|
||||
/// 挂载的函数调用的对象(用于函数需要实例才能调用的场景)
|
||||
/// </summary>
|
||||
private static Dictionary<string, Func<object>> _callFuncOfGetObjects = new Dictionary<string, Func<object>>();
|
||||
|
||||
/// <summary>
|
||||
/// 定义的类型
|
||||
/// </summary>
|
||||
private static Dictionary<string, Type> _classDefinition = new Dictionary<string, Type>();
|
||||
|
||||
/// <summary>
|
||||
/// 重置的变量
|
||||
/// </summary>
|
||||
public void ResetVar()
|
||||
{
|
||||
foreach (var nodeObj in _variables.Values)
|
||||
{
|
||||
if (nodeObj is not null)
|
||||
{
|
||||
if (typeof(IDisposable).IsAssignableFrom(nodeObj?.GetType()) && nodeObj is IDisposable disposable)
|
||||
{
|
||||
disposable?.Dispose();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
_variables.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 挂载函数
|
||||
/// </summary>
|
||||
/// <param name="functionName">函数名称</param>
|
||||
/// <param name="methodInfo">方法信息</param>
|
||||
public static void AddFunction(string functionName, MethodInfo methodInfo, Func<object>? callObj = null)
|
||||
{
|
||||
//if (!_functionTable.ContainsKey(functionName))
|
||||
//{
|
||||
// _functionTable[functionName] = new DelegateDetails(methodInfo);
|
||||
//}
|
||||
if(!methodInfo.IsStatic && callObj is null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, "函数挂载失败:试图挂载非静态的函数,但没有传入相应的获取实例的方法。");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(!methodInfo.IsStatic && callObj is not null)
|
||||
{
|
||||
// 静态函数不需要
|
||||
_callFuncOfGetObjects.Add(functionName, callObj);
|
||||
}
|
||||
_functionTable[functionName] = new DelegateDetails(methodInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 挂载类型
|
||||
/// </summary>
|
||||
/// <param name="typeName">函数名称</param>
|
||||
/// <param name="type">方法信息</param>
|
||||
public static void AddClassType(Type type , string typeName = "")
|
||||
{
|
||||
if (string.IsNullOrEmpty(typeName))
|
||||
{
|
||||
typeName = type.Name;
|
||||
}
|
||||
if (!_classDefinition.ContainsKey(typeName))
|
||||
{
|
||||
_classDefinition[typeName] = type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 入口节点
|
||||
/// </summary>
|
||||
/// <param name="programNode"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<object?> ExecutionProgramNodeAsync(ProgramNode programNode)
|
||||
{
|
||||
// 遍历 ProgramNode 中的所有语句并执行它们
|
||||
foreach (var statement in programNode.Statements)
|
||||
{
|
||||
// 直接退出
|
||||
if (statement is ReturnNode returnNode) // 遇到 Return 语句 提前退出
|
||||
{
|
||||
return await EvaluateAsync(statement);
|
||||
}
|
||||
else
|
||||
{
|
||||
await InterpretAsync(statement);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 类型定义
|
||||
/// </summary>
|
||||
/// <param name="programNode"></param>
|
||||
/// <returns></returns>
|
||||
private void ExecutionClassTypeDefinitionNode(ClassTypeDefinitionNode classTypeDefinitionNode)
|
||||
{
|
||||
if (_classDefinition.ContainsKey(classTypeDefinitionNode.ClassName))
|
||||
{
|
||||
//SereinEnv.WriteLine(InfoType.WARN, $"异常信息 : 类型重复定义,代码在第{classTypeDefinitionNode.Row}行: {classTypeDefinitionNode.Code.Trim()}");
|
||||
return;
|
||||
}
|
||||
var type = DynamicObjectHelper.CreateTypeWithProperties(classTypeDefinitionNode.Fields, classTypeDefinitionNode.ClassName);
|
||||
_classDefinition[classTypeDefinitionNode.ClassName] = type; // 定义对象
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IF...ELSE... 语句块
|
||||
/// </summary>
|
||||
/// <param name="ifNode"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private async Task ExecutionIfNodeAsync(IfNode ifNode)
|
||||
{
|
||||
var result = await EvaluateAsync(ifNode.Condition) ?? throw new SereinSciptException(ifNode, $"条件语句返回了 null");
|
||||
|
||||
if (result is not bool condition)
|
||||
{
|
||||
throw new SereinSciptException(ifNode, "条件语句返回值不为 bool 类型");
|
||||
}
|
||||
|
||||
if (condition)
|
||||
{
|
||||
foreach (var trueNode in ifNode.TrueBranch)
|
||||
{
|
||||
await InterpretAsync(trueNode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var falseNode in ifNode.FalseBranch)
|
||||
{
|
||||
await InterpretAsync(falseNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WHILE(){...} 语句块
|
||||
/// </summary>
|
||||
/// <param name="whileNode"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private async Task ExectutionWhileNodeAsync(WhileNode whileNode)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var result = await EvaluateAsync(whileNode.Condition) ?? throw new SereinSciptException(whileNode, $"条件语句返回了 null");
|
||||
if (result is not bool condition)
|
||||
{
|
||||
throw new SereinSciptException(whileNode, $"条件语句返回值不为 bool 类型(当前返回值类型为 {result.GetType()})");
|
||||
}
|
||||
if (!condition)
|
||||
{
|
||||
break;
|
||||
}
|
||||
foreach(var node in whileNode.Body)
|
||||
{
|
||||
await InterpretAsync(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 操作节点
|
||||
/// </summary>
|
||||
/// <param name="assignmentNode"></param>
|
||||
/// <returns></returns>
|
||||
private async Task ExecutionAssignmentNodeAsync(AssignmentNode assignmentNode)
|
||||
{
|
||||
var tmp = await EvaluateAsync(assignmentNode.Value);
|
||||
_variables[assignmentNode.Variable] = tmp;
|
||||
}
|
||||
private async Task<object> InterpretFunctionCallAsync(FunctionCallNode functionCallNode)
|
||||
{
|
||||
// 评估函数参数
|
||||
var arguments = new object?[functionCallNode.Arguments.Count];
|
||||
for (int i = 0; i < functionCallNode.Arguments.Count; i++)
|
||||
{
|
||||
ASTNode? arg = functionCallNode.Arguments[i];
|
||||
arguments[i] = await EvaluateAsync(arg); // 评估每个参数
|
||||
}
|
||||
|
||||
var funcName = functionCallNode.FunctionName;
|
||||
|
||||
object? instance = null; // 静态方法不需要传入实例,所以可以传入null
|
||||
|
||||
// 查找并执行对应的函数
|
||||
if (_functionTable.TryGetValue(funcName, out DelegateDetails? function))
|
||||
{
|
||||
if (!function.EmitMethodInfo.IsStatic)
|
||||
{
|
||||
if(_callFuncOfGetObjects.TryGetValue(funcName, out var action))
|
||||
{
|
||||
instance = action.Invoke();// 非静态的方法需要获取相应的实例
|
||||
|
||||
if (instance is null)
|
||||
{
|
||||
throw new SereinSciptException(functionCallNode, $"函数 {funcName} 尝试获取实例时返回了 null ");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SereinSciptException(functionCallNode, $"挂载函数 {funcName} 时需要同时给定获取实例的 Func<object>");
|
||||
}
|
||||
}
|
||||
|
||||
var result = await function.InvokeAsync(instance,arguments);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Unknown function: {functionCallNode.FunctionName}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<object?> InterpretAsync(ASTNode node)
|
||||
{
|
||||
if(node == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (node)
|
||||
{
|
||||
case ProgramNode programNode: // AST树入口
|
||||
var scritResult = await ExecutionProgramNodeAsync(programNode);
|
||||
return scritResult; // 遍历 ProgramNode 中的所有语句并执行它们
|
||||
case ClassTypeDefinitionNode classTypeDefinitionNode: // 定义类型
|
||||
ExecutionClassTypeDefinitionNode(classTypeDefinitionNode);
|
||||
break;
|
||||
case AssignmentNode assignment: // 出现在 = 右侧的表达式
|
||||
await ExecutionAssignmentNodeAsync(assignment);
|
||||
break;
|
||||
case MemberAssignmentNode memberAssignmentNode: // 设置对象属性
|
||||
await SetMemberValue(memberAssignmentNode);
|
||||
break;
|
||||
case MemberFunctionCallNode memberFunctionCallNode:
|
||||
return await CallMemberFunction(memberFunctionCallNode);
|
||||
break;
|
||||
case IfNode ifNode: // 执行 if...else... 语句块
|
||||
await ExecutionIfNodeAsync(ifNode);
|
||||
break;
|
||||
case WhileNode whileNode: // 循环语句块
|
||||
await ExectutionWhileNodeAsync(whileNode);
|
||||
break;
|
||||
case FunctionCallNode functionCallNode: // 方法调用节点
|
||||
return await InterpretFunctionCallAsync(functionCallNode);
|
||||
case ReturnNode returnNode:
|
||||
return await EvaluateAsync(returnNode);
|
||||
default:
|
||||
throw new SereinSciptException(node, "解释器 InterpretAsync() 未实现节点行为");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private async Task<object?> EvaluateAsync(ASTNode node)
|
||||
{
|
||||
if(node == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
switch (node)
|
||||
{
|
||||
case NullNode nullNode:
|
||||
return null;
|
||||
case BooleanNode booleanNode:
|
||||
return booleanNode.Value; // 返回数值
|
||||
case NumberNode numberNode:
|
||||
return numberNode.Value; // 返回数值
|
||||
case StringNode stringNode:
|
||||
return stringNode.Value; // 返回字符串值
|
||||
case IdentifierNode identifierNode:
|
||||
if (_variables.TryGetValue(identifierNode.Name, out var result))
|
||||
{
|
||||
//if(result == null)
|
||||
//{
|
||||
// throw new SereinSciptException(identifierNode, "尝试使用值为null的变量");
|
||||
//}
|
||||
return result; // 获取变量值
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SereinSciptException(identifierNode, "尝试使用未声明的变量");
|
||||
}
|
||||
case BinaryOperationNode binOpNode:
|
||||
// 递归计算二元操作
|
||||
var left = await EvaluateAsync(binOpNode.Left);
|
||||
//if (left == null )
|
||||
//{
|
||||
// throw new SereinSciptException(binOpNode.Left, $"左值尝试使用 null");
|
||||
//}
|
||||
|
||||
var right = await EvaluateAsync(binOpNode.Right);
|
||||
//if (right == null)
|
||||
//{
|
||||
// throw new SereinSciptException(binOpNode.Right, "右值尝试使用计算 null");
|
||||
//}
|
||||
return EvaluateBinaryOperation(left, binOpNode.Operator, right);
|
||||
case ObjectInstantiationNode objectInstantiationNode:
|
||||
if (_classDefinition.TryGetValue(objectInstantiationNode.TypeName,out var type ))
|
||||
{
|
||||
object?[] args = new object[objectInstantiationNode.Arguments.Count];
|
||||
for (int i = 0; i < objectInstantiationNode.Arguments.Count; i++)
|
||||
{
|
||||
var argNode = objectInstantiationNode.Arguments[i];
|
||||
args[i] = await EvaluateAsync(argNode);
|
||||
}
|
||||
var obj = Activator.CreateInstance(type,args: args);// 创建对象
|
||||
if (obj == null)
|
||||
{
|
||||
throw new SereinSciptException(objectInstantiationNode, $"类型创建失败\"{objectInstantiationNode.TypeName}\"");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
throw new SereinSciptException(objectInstantiationNode, $"使用了未定义的类型\"{objectInstantiationNode.TypeName}\"");
|
||||
|
||||
}
|
||||
case FunctionCallNode callNode:
|
||||
return await InterpretFunctionCallAsync(callNode); // 调用方法返回函数的返回值
|
||||
case MemberAccessNode memberAccessNode:
|
||||
return await GetValue(memberAccessNode);
|
||||
case ReturnNode returnNode: //
|
||||
return await EvaluateAsync(returnNode.Value); // 直接返回响应的内容
|
||||
default:
|
||||
throw new SereinSciptException(node, "解释器 EvaluateAsync() 未实现节点行为");
|
||||
}
|
||||
}
|
||||
|
||||
private object EvaluateBinaryOperation(object left, string op, object right)
|
||||
{
|
||||
|
||||
|
||||
|
||||
// 根据运算符执行不同的运算
|
||||
switch (op)
|
||||
{
|
||||
case "+":
|
||||
if (left is string || right is string)
|
||||
{
|
||||
return left?.ToString() + right?.ToString(); // 字符串拼接
|
||||
}
|
||||
else if (left is int leftInt && right is int rightInt)
|
||||
{
|
||||
return leftInt + rightInt; // 整数加法
|
||||
}
|
||||
else if (left is long leftLong && right is long rightLong)
|
||||
{
|
||||
return leftLong + rightLong; // 整数加法
|
||||
}
|
||||
else if (left is double leftDouble && right is double rightDouble)
|
||||
{
|
||||
return leftDouble + rightDouble; // 整数加法
|
||||
}
|
||||
else
|
||||
{
|
||||
dynamic leftValue = Convert.ToDouble(left);
|
||||
dynamic rightValue = Convert.ToDouble(right);
|
||||
return leftValue + rightValue;
|
||||
}
|
||||
throw new Exception("Invalid types for + operator");
|
||||
case "-":
|
||||
return (int)left - (int)right;
|
||||
case "*":
|
||||
return (int)left * (int)right;
|
||||
case "/":
|
||||
return (int)left / (int)right;
|
||||
|
||||
case ">":
|
||||
return (int)left > (int)right;
|
||||
case "<":
|
||||
return (int)left < (int)right;
|
||||
case "==":
|
||||
return Equals(left, right);
|
||||
case "!=":
|
||||
return !Equals(left, right);
|
||||
|
||||
default:
|
||||
throw new NotImplementedException("未定义的操作符: " + op);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设置对象成员
|
||||
/// </summary>
|
||||
/// <param name="memberAssignmentNode"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="SereinSciptException"></exception>
|
||||
public async Task SetMemberValue(MemberAssignmentNode memberAssignmentNode)
|
||||
{
|
||||
var target = await EvaluateAsync(memberAssignmentNode.Object);
|
||||
var value = await EvaluateAsync(memberAssignmentNode.Value);
|
||||
// 设置值
|
||||
var lastMember = memberAssignmentNode.MemberName;
|
||||
|
||||
var lastProperty = target?.GetType().GetProperty(lastMember);
|
||||
if (lastProperty is null)
|
||||
{
|
||||
var lastField = target?.GetType().GetRuntimeField(lastMember);
|
||||
if (lastField is null)
|
||||
{
|
||||
throw new SereinSciptException(memberAssignmentNode, $"对象没有成员\"{memberAssignmentNode.MemberName}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
var convertedValue = Convert.ChangeType(value, lastField.FieldType);
|
||||
lastField.SetValue(target, convertedValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
|
||||
lastProperty.SetValue(target, convertedValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取对象成员
|
||||
/// </summary>
|
||||
/// <param name="memberAccessNode"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="SereinSciptException"></exception>
|
||||
public async Task<object?> GetValue(MemberAccessNode memberAccessNode)
|
||||
{
|
||||
var target = await EvaluateAsync(memberAccessNode.Object);
|
||||
var lastMember = memberAccessNode.MemberName;
|
||||
|
||||
var lastProperty = target?.GetType().GetProperty(lastMember);
|
||||
if (lastProperty is null)
|
||||
{
|
||||
var lastField = target?.GetType().GetRuntimeField(lastMember);
|
||||
if (lastField is null)
|
||||
{
|
||||
throw new SereinSciptException(memberAccessNode, $"对象没有成员\"{memberAccessNode.MemberName}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
return lastField.GetValue(target);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return lastProperty.GetValue(target);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缓存method委托
|
||||
/// </summary>
|
||||
private Dictionary<string, DelegateDetails> MethodToDelegateCaches { get; } = new Dictionary<string, DelegateDetails>();
|
||||
|
||||
public async Task<object?> CallMemberFunction(MemberFunctionCallNode memberFunctionCallNode)
|
||||
{
|
||||
var target = await EvaluateAsync(memberFunctionCallNode.Object);
|
||||
var lastMember = memberFunctionCallNode.FunctionName;
|
||||
|
||||
var methodInfo = target?.GetType().GetMethod(lastMember) ?? throw new SereinSciptException(memberFunctionCallNode, $"对象没有方法\"{memberFunctionCallNode.FunctionName}\"");
|
||||
if(!MethodToDelegateCaches.TryGetValue(methodInfo.Name, out DelegateDetails? delegateDetails))
|
||||
{
|
||||
delegateDetails = new DelegateDetails(methodInfo);
|
||||
MethodToDelegateCaches[methodInfo.Name] = delegateDetails;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var arguments = new object?[memberFunctionCallNode.Arguments.Count];
|
||||
for (int i = 0; i < memberFunctionCallNode.Arguments.Count; i++)
|
||||
{
|
||||
ASTNode? arg = memberFunctionCallNode.Arguments[i];
|
||||
arguments[i] = await EvaluateAsync(arg); // 评估每个参数
|
||||
}
|
||||
|
||||
return await delegateDetails.InvokeAsync(target, arguments);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
360
Serein.Script/SereinScriptLexer.cs
Normal file
360
Serein.Script/SereinScriptLexer.cs
Normal file
@@ -0,0 +1,360 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Serein.Script
|
||||
{
|
||||
internal enum TokenType
|
||||
{
|
||||
/// <summary>
|
||||
/// 预料之外的值
|
||||
/// </summary>
|
||||
Null,
|
||||
/// <summary>
|
||||
/// 标识符
|
||||
/// </summary>
|
||||
Identifier,
|
||||
/// <summary>
|
||||
/// 布尔
|
||||
/// </summary>
|
||||
Boolean,
|
||||
/// <summary>
|
||||
/// 数值
|
||||
/// </summary>
|
||||
Number,
|
||||
/// <summary>
|
||||
/// 字符串
|
||||
/// </summary>
|
||||
String,
|
||||
/// <summary>
|
||||
/// 关键字
|
||||
/// </summary>
|
||||
Keyword,
|
||||
/// <summary>
|
||||
/// 操作符
|
||||
/// </summary>
|
||||
Operator,
|
||||
/// <summary>
|
||||
/// 左小括号
|
||||
/// </summary>
|
||||
ParenthesisLeft,
|
||||
/// <summary>
|
||||
/// 右小括号
|
||||
/// </summary>
|
||||
ParenthesisRight,
|
||||
/// <summary>
|
||||
/// 左中括号
|
||||
/// </summary>
|
||||
SquareBracketsLeft,
|
||||
/// <summary>
|
||||
/// 右中括号
|
||||
/// </summary>
|
||||
SquareBracketsRight,
|
||||
/// <summary>
|
||||
/// 左大括号
|
||||
/// </summary>
|
||||
BraceLeft,
|
||||
/// <summary>
|
||||
/// 右大括号
|
||||
/// </summary>
|
||||
BraceRight,
|
||||
/// <summary>
|
||||
/// 点号
|
||||
/// </summary>
|
||||
Dot,
|
||||
/// <summary>
|
||||
/// 逗号
|
||||
/// </summary>
|
||||
Comma,
|
||||
|
||||
/// <summary>
|
||||
/// 分号
|
||||
/// </summary>
|
||||
Semicolon,
|
||||
|
||||
/// <summary>
|
||||
/// 行注释
|
||||
/// </summary>
|
||||
// RowComment,
|
||||
|
||||
/// <summary>
|
||||
/// 解析完成
|
||||
/// </summary>
|
||||
EOF
|
||||
}
|
||||
|
||||
internal ref struct Token
|
||||
{
|
||||
public TokenType Type { get; }
|
||||
public string Value { get; }
|
||||
|
||||
public int Row { get; set; }
|
||||
public string Code { get; set; }
|
||||
public int StartIndex { get; set; }
|
||||
public int Length { get; set; }
|
||||
|
||||
internal Token(TokenType type, string value)
|
||||
{
|
||||
Type = type;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal ref struct SereinScriptLexer
|
||||
{
|
||||
private readonly ReadOnlySpan<char> _input;
|
||||
private int _index;
|
||||
private int _row ;
|
||||
|
||||
|
||||
private string[] _keywords = [
|
||||
"let",
|
||||
"func",
|
||||
"if",
|
||||
"else",
|
||||
"return",
|
||||
"while",
|
||||
"new",
|
||||
"class",
|
||||
];
|
||||
|
||||
internal SereinScriptLexer(string input)
|
||||
{
|
||||
_input = input.AsSpan();
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
|
||||
internal Token PeekToken()
|
||||
{
|
||||
int currentIndex = _index; // 保存当前索引
|
||||
Token nextToken = NextToken(); // 获取下一个 token
|
||||
_index = currentIndex; // 恢复索引到当前位置
|
||||
return nextToken; // 返回下一个 token
|
||||
}
|
||||
|
||||
internal Token NextToken()
|
||||
{
|
||||
|
||||
// 跳过空白字符
|
||||
while (_index < _input.Length && char.IsWhiteSpace(_input[_index]))
|
||||
{
|
||||
if (_input[_index] == '\n')
|
||||
{
|
||||
_row++;
|
||||
}
|
||||
|
||||
_index++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (_index >= _input.Length) return new Token(TokenType.EOF, string.Empty);
|
||||
|
||||
char currentChar = _input[_index];
|
||||
|
||||
// 识别字符串字面量
|
||||
if (currentChar == '"')
|
||||
{
|
||||
return ReadString();
|
||||
}
|
||||
|
||||
// 跳过注释
|
||||
if (_input[_index] == '/' && _input[_index + 1] == '/')
|
||||
{
|
||||
// 一直识别到换行符的出现
|
||||
while (_index < _input.Length && _input[_index] != '\n')
|
||||
{
|
||||
_index++;
|
||||
}
|
||||
return NextToken(); // 跳过注释后,返回下一个识别token
|
||||
}
|
||||
|
||||
// 识别null字面量
|
||||
if (currentChar == 'n')
|
||||
{
|
||||
if (_input[_index + 1] == 'u'
|
||||
&& _input[_index + 2] == 'l'
|
||||
&& _input[_index + 3] == 'l')
|
||||
{
|
||||
var value = _input.Slice(_index, 4).ToString();
|
||||
|
||||
return CreateToken(TokenType.Null, "null");
|
||||
}
|
||||
}
|
||||
|
||||
// 识别布尔字面量
|
||||
if (currentChar == 't')
|
||||
{
|
||||
if (_input[_index + 1] == 'r'
|
||||
&& _input[_index + 2] == 'u'
|
||||
&& _input[_index + 3] == 'e')
|
||||
{
|
||||
return CreateToken(TokenType.Boolean, "true");
|
||||
}
|
||||
}
|
||||
else if (currentChar == 'f')
|
||||
{
|
||||
if (_input[_index + 1] == 'a'
|
||||
&& _input[_index + 2] == 'l'
|
||||
&& _input[_index + 3] == 's'
|
||||
&& _input[_index + 4] == 'e')
|
||||
{
|
||||
return CreateToken(TokenType.Boolean, "false");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 识别数字
|
||||
if (char.IsDigit(currentChar))
|
||||
{
|
||||
var start = _index;
|
||||
while (_index < _input.Length && char.IsDigit(_input[_index]))
|
||||
_index++;
|
||||
var value = _input.Slice(start, _index - start).ToString();
|
||||
_index = start; // 回退索引,索引必须只能在 CreateToken 方法内更新
|
||||
return CreateToken(TokenType.Number, value);
|
||||
}
|
||||
|
||||
// 识别标识符(变量名、关键字)
|
||||
if (char.IsLetter(currentChar))
|
||||
{
|
||||
var start = _index;
|
||||
while (_index < _input.Length && (char.IsLetterOrDigit(_input[_index]) || _input[_index] == '_'))
|
||||
_index++;
|
||||
var value = _input.Slice(start, _index - start).ToString();
|
||||
_index = start; // 回退索引,索引必须只能在 CreateToken 方法内更新
|
||||
return CreateToken(_keywords.Contains(value) ? TokenType.Keyword : TokenType.Identifier, value);
|
||||
|
||||
}
|
||||
|
||||
// 识别符号
|
||||
switch (currentChar)
|
||||
{
|
||||
case '(': return CreateToken(TokenType.ParenthesisLeft, "(");
|
||||
case ')': return CreateToken(TokenType.ParenthesisRight, ")");
|
||||
case '[': return CreateToken(TokenType.SquareBracketsLeft, "[");
|
||||
case ']': return CreateToken(TokenType.SquareBracketsRight, "]");
|
||||
case '{': return CreateToken(TokenType.BraceLeft, "{");
|
||||
case '}': return CreateToken(TokenType.BraceRight, "}");
|
||||
case ',': return CreateToken(TokenType.Comma, ",");
|
||||
case ';': return CreateToken(TokenType.Semicolon, ";");
|
||||
case '+':
|
||||
case '-':
|
||||
case '*':
|
||||
case '/':
|
||||
return CreateToken(TokenType.Operator, currentChar.ToString());
|
||||
case '>': // 识别 ">" 或 ">="
|
||||
if (_index + 1 < _input.Length && _input[_index + 1] == '=')
|
||||
{
|
||||
return CreateToken(TokenType.Operator, ">=");
|
||||
}
|
||||
return CreateToken(TokenType.Operator, ">");
|
||||
case '<': // 识别 "<" 或 "<="
|
||||
if (_index + 1 < _input.Length && _input[_index + 1] == '=')
|
||||
{
|
||||
return CreateToken(TokenType.Operator, "<=");
|
||||
}
|
||||
return CreateToken(TokenType.Operator, "<");
|
||||
case '!': // 识别 "!="
|
||||
if (_index + 1 < _input.Length && _input[_index + 1] == '=')
|
||||
{
|
||||
return CreateToken(TokenType.Operator, "!=");
|
||||
}
|
||||
break;
|
||||
case '=': // 识别 "=="
|
||||
if (_index + 1 < _input.Length && _input[_index + 1] == '=')
|
||||
{
|
||||
return CreateToken(TokenType.Operator, "==");
|
||||
}
|
||||
else
|
||||
{
|
||||
return CreateToken(TokenType.Operator, "=");
|
||||
}
|
||||
case '.':
|
||||
return CreateToken(TokenType.Dot, ".");
|
||||
}
|
||||
|
||||
throw new Exception("Unexpected character: " + currentChar);
|
||||
}
|
||||
|
||||
private Token CreateToken(TokenType tokenType, string value)
|
||||
{
|
||||
var code = GetLine(_row).ToString();
|
||||
var token = new Token(tokenType, value)
|
||||
{
|
||||
Row = _row,
|
||||
StartIndex = _index,
|
||||
Length = value.Length,
|
||||
Code = code,
|
||||
};
|
||||
_index += value.Length;
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 读取硬编码的文本
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private Token ReadString()
|
||||
{
|
||||
_index++; // 跳过开头的引号
|
||||
var start = _index;
|
||||
|
||||
while (_index < _input.Length && _input[_index] != '"')
|
||||
{
|
||||
if (_input[_index] == '\\' && _index + 1 < _input.Length && (_input[_index + 1] == '"' || _input[_index + 1] == '\\'))
|
||||
{
|
||||
// 处理转义字符
|
||||
_index++;
|
||||
}
|
||||
_index++;
|
||||
}
|
||||
|
||||
if (_index >= _input.Length) throw new Exception("Unterminated string literal");
|
||||
|
||||
var value = _input.Slice(start, _index - start).ToString();
|
||||
// var value = _input.Substring(start, _index - start);
|
||||
|
||||
_index = start + 1; // 跳过引号
|
||||
return CreateToken(TokenType.String, value);
|
||||
|
||||
// _index++; // 跳过结束的引号
|
||||
//return new Token(TokenType.String, value.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取对应行的代码文本
|
||||
/// </summary>
|
||||
/// <param name="lineNumber"></param>
|
||||
/// <returns></returns>
|
||||
private ReadOnlySpan<char> GetLine( int lineNumber)
|
||||
{
|
||||
ReadOnlySpan<char> text = _input;
|
||||
int currentLine = 0;
|
||||
int start = 0;
|
||||
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
if (text[i] == '\n') // 找到换行符
|
||||
{
|
||||
if (currentLine == lineNumber)
|
||||
{
|
||||
return text.Slice(start, i - start); // 返回从start到当前位置的行文本
|
||||
}
|
||||
currentLine++;
|
||||
start = i + 1; // 下一行的起始位置
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到指定行,返回空的Span
|
||||
return ReadOnlySpan<char>.Empty;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
647
Serein.Script/SereinScriptParser.cs
Normal file
647
Serein.Script/SereinScriptParser.cs
Normal file
@@ -0,0 +1,647 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serein.Library;
|
||||
using Serein.Script.Node;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Serein.Script
|
||||
{
|
||||
public ref struct SereinScriptParser
|
||||
{
|
||||
private SereinScriptLexer _lexer;
|
||||
private Token _currentToken;
|
||||
|
||||
|
||||
public SereinScriptParser(string script)
|
||||
{
|
||||
_lexer = new SereinScriptLexer(script); // 语法分析
|
||||
_currentToken = _lexer.NextToken();
|
||||
}
|
||||
|
||||
public ASTNode Parse()
|
||||
{
|
||||
return Program();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private List<ASTNode> Statements { get; } = new List<ASTNode>();
|
||||
|
||||
private ASTNode Program()
|
||||
{
|
||||
Statements.Clear();
|
||||
while (_currentToken.Type != TokenType.EOF)
|
||||
{
|
||||
var astNode = Statement();
|
||||
if (astNode == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Statements.Add(astNode);
|
||||
|
||||
//if (astNode is ClassTypeDefinitionNode)
|
||||
//{
|
||||
// // 类型定义置顶
|
||||
// statements = [astNode, ..statements];
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// statements.Add(astNode);
|
||||
//}
|
||||
|
||||
}
|
||||
return new ProgramNode(Statements).SetTokenInfo(_currentToken);
|
||||
}
|
||||
|
||||
private ASTNode Statement()
|
||||
{
|
||||
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "let")
|
||||
{
|
||||
return ParseLetAssignment();
|
||||
}
|
||||
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "class")
|
||||
{
|
||||
return ParseClassDefinition();
|
||||
}
|
||||
|
||||
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "if")
|
||||
{
|
||||
return ParseIf();
|
||||
}
|
||||
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "while")
|
||||
{
|
||||
return ParseWhile();
|
||||
}
|
||||
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "return")
|
||||
{
|
||||
return ParseReturn();
|
||||
}
|
||||
if (_currentToken.Type == TokenType.Identifier)
|
||||
{
|
||||
return ParseIdentifier();
|
||||
}
|
||||
if (_currentToken.Type == TokenType.Null)
|
||||
{
|
||||
return Expression();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 处理其他语句(如表达式语句等)
|
||||
if (_currentToken.Type == TokenType.Semicolon)
|
||||
{
|
||||
_currentToken = _lexer.NextToken();
|
||||
return null; // 表示空语句
|
||||
}
|
||||
|
||||
throw new Exception("Unexpected statement: " + _currentToken.Value.ToString());
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从标识符解析方法调用、变量赋值、获取对象成员行为。
|
||||
/// (非符号、关键字)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private ASTNode ParseIdentifier()
|
||||
{
|
||||
|
||||
// 检查标识符后是否跟有左圆括号
|
||||
var _tempToken = _lexer.PeekToken();
|
||||
if (_tempToken.Type == TokenType.ParenthesisLeft)
|
||||
{
|
||||
// 解析函数调用
|
||||
return ParseFunctionCall();
|
||||
}
|
||||
else if (_tempToken.Type == TokenType.Dot)
|
||||
{
|
||||
// 对象成员的获取
|
||||
return ParseMemberAccessOrAssignment();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 不是函数调用,是变量赋值或其他
|
||||
return ParseAssignment();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 解析赋值行为
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private ASTNode ParseAssignment()
|
||||
{
|
||||
string variableName = _currentToken.Value.ToString();
|
||||
_currentToken = _lexer.NextToken(); // consume identifier
|
||||
|
||||
if(_currentToken.Type == TokenType.ParenthesisRight)
|
||||
{
|
||||
return new IdentifierNode(variableName).SetTokenInfo(_currentToken);
|
||||
}
|
||||
|
||||
if(_currentToken.Type == TokenType.Operator && _currentToken.Value == "=")
|
||||
{
|
||||
// 赋值行为
|
||||
_currentToken = _lexer.NextToken(); // consume "="
|
||||
var _tempToken = _lexer.PeekToken();
|
||||
ASTNode valueNode;
|
||||
if (_tempToken.Type == TokenType.ParenthesisLeft)
|
||||
{
|
||||
// 解析赋值右边的表达式
|
||||
// 是函数调用,解析函数调用
|
||||
valueNode = ParseFunctionCall();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 解析赋值右边的字面量表达式
|
||||
valueNode = Expression();
|
||||
}
|
||||
return new AssignmentNode(variableName, valueNode).SetTokenInfo(_currentToken);
|
||||
}
|
||||
if (_currentToken.Type == TokenType.Dot)
|
||||
{
|
||||
return ParseMemberAccessOrAssignment();
|
||||
}
|
||||
|
||||
|
||||
|
||||
throw new Exception($"Expected '{_currentToken.Value}' after variable name");
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析 let 变量赋值行为
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private ASTNode ParseLetAssignment()
|
||||
{
|
||||
_currentToken = _lexer.NextToken(); // Consume "let"
|
||||
string variable = _currentToken.Value.ToString();
|
||||
_currentToken = _lexer.NextToken(); // Consume identifier
|
||||
ASTNode value;
|
||||
if (_currentToken.Type == TokenType.Semicolon)
|
||||
{
|
||||
// 定义一个变量,初始值为 null
|
||||
value = new NullNode();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_currentToken.Type != TokenType.Operator || _currentToken.Value != "=")
|
||||
throw new Exception("Expected '=' after variable name");
|
||||
_currentToken = _lexer.NextToken();
|
||||
value = Expression();
|
||||
_currentToken = _lexer.NextToken(); // Consume semicolon
|
||||
|
||||
}
|
||||
return new AssignmentNode(variable, value).SetTokenInfo(_currentToken);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private ASTNode ParseClassDefinition()
|
||||
{
|
||||
_currentToken = _lexer.NextToken(); // 消耗 class 关键字
|
||||
var className = _currentToken.Value.ToString(); // 获取定义的类名
|
||||
_currentToken = _lexer.NextToken(); // 消耗括号
|
||||
if (_currentToken.Type != TokenType.BraceLeft || _currentToken.Value != "{")
|
||||
throw new Exception("Expected '{' after class definition");
|
||||
var classFields = new Dictionary<string, Type>();
|
||||
_currentToken = _lexer.NextToken();
|
||||
while (_currentToken.Type != TokenType.BraceRight)
|
||||
{
|
||||
var fieldType = _currentToken.Value.ToString().ToTypeOfString(); // 获取定义的类名
|
||||
_currentToken = _lexer.NextToken();
|
||||
var fieldName = _currentToken.Value.ToString(); // 获取定义的类名
|
||||
_currentToken = _lexer.NextToken();
|
||||
classFields.Add(fieldName,fieldType);
|
||||
if (_currentToken.Type == TokenType.Semicolon && _lexer.PeekToken().Type == TokenType.BraceRight)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentToken = _lexer.NextToken();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_currentToken = _lexer.NextToken();
|
||||
_currentToken = _lexer.NextToken();
|
||||
return new ClassTypeDefinitionNode(classFields, className).SetTokenInfo(_currentToken);
|
||||
}
|
||||
|
||||
public ASTNode ParseObjectInstantiation()
|
||||
{
|
||||
_currentToken = _lexer.NextToken(); // Consume "new"
|
||||
string typeName = _currentToken.Value.ToString(); // Get type name
|
||||
_currentToken = _lexer.NextToken();
|
||||
if (_currentToken.Type != TokenType.ParenthesisLeft)
|
||||
throw new Exception("Expected '(' after function name");
|
||||
|
||||
_currentToken = _lexer.NextToken(); // consume "("
|
||||
|
||||
var arguments = new List<ASTNode>();
|
||||
while (_currentToken.Type != TokenType.ParenthesisRight)
|
||||
{
|
||||
arguments.Add(Expression());
|
||||
if (_currentToken.Type == TokenType.Comma)
|
||||
{
|
||||
_currentToken = _lexer.NextToken(); // consume ","
|
||||
}
|
||||
}
|
||||
|
||||
_currentToken = _lexer.NextToken(); // consume ")"
|
||||
return new ObjectInstantiationNode(typeName, arguments).SetTokenInfo(_currentToken);
|
||||
}
|
||||
|
||||
|
||||
public ASTNode ParseCollectionIndex()
|
||||
{
|
||||
string functionName = _currentToken.Value.ToString();
|
||||
_currentToken = _lexer.NextToken(); // consume identifier
|
||||
|
||||
if (_currentToken.Type != TokenType.ParenthesisLeft)
|
||||
throw new Exception("Expected '[' after function name");
|
||||
|
||||
_currentToken = _lexer.NextToken(); // consume "["
|
||||
|
||||
ASTNode indexValue = Expression(); // get index value
|
||||
|
||||
_currentToken = _lexer.NextToken(); // consume "]"
|
||||
return new CollectionIndexNode(indexValue).SetTokenInfo(_currentToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取对象成员
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private ASTNode ParseMemberAccessOrAssignment()
|
||||
{
|
||||
var identifierNode = new IdentifierNode(_currentToken.Value).SetTokenInfo(_currentToken);
|
||||
_currentToken = _lexer.NextToken(); // 消耗当前标识符
|
||||
|
||||
|
||||
// 处理成员访问:identifier.member
|
||||
if (_currentToken.Type == TokenType.Dot)
|
||||
{
|
||||
_currentToken = _lexer.NextToken(); // 消耗 "."
|
||||
if (_currentToken.Type != TokenType.Identifier)
|
||||
{
|
||||
throw new Exception("Expected member name after dot.");
|
||||
}
|
||||
|
||||
var memberName = _currentToken.Value;
|
||||
//_currentToken = _lexer.NextToken(); // 消耗成员名
|
||||
|
||||
var _peekToken = _lexer.PeekToken();
|
||||
if (_peekToken.Type == TokenType.Operator && _peekToken.Value == "=")
|
||||
{
|
||||
// 成员赋值 obj.Member = xxx;
|
||||
_currentToken = _lexer.NextToken(); // 消耗 "="
|
||||
_currentToken = _lexer.NextToken(); // 消耗 "="
|
||||
var valueNode = Expression(); // 解析右值
|
||||
return new MemberAssignmentNode(identifierNode, memberName, valueNode).SetTokenInfo(_peekToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
if(_peekToken.Type == TokenType.ParenthesisLeft)
|
||||
{
|
||||
// 成员方法调用 obj.Member(xxx);
|
||||
return ParseMemberFunctionCall(identifierNode);
|
||||
}
|
||||
else if (_peekToken.Type == TokenType.SquareBracketsLeft)
|
||||
{
|
||||
// 数组 index; 字典 key obj.Member[xxx];
|
||||
return ParseCollectionIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 成员获取
|
||||
return new MemberAccessNode(identifierNode, memberName).SetTokenInfo(_currentToken);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return identifierNode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private ASTNode ParseMemberFunctionCall(ASTNode targetNode)
|
||||
{
|
||||
string functionName = _currentToken.Value.ToString();
|
||||
_currentToken = _lexer.NextToken(); // consume identifier
|
||||
|
||||
if (_currentToken.Type != TokenType.ParenthesisLeft)
|
||||
throw new Exception("Expected '(' after function name");
|
||||
|
||||
_currentToken = _lexer.NextToken(); // consume "("
|
||||
|
||||
var arguments = new List<ASTNode>();
|
||||
while (_currentToken.Type != TokenType.ParenthesisRight)
|
||||
{
|
||||
var arg = Expression();
|
||||
_currentToken = _lexer.NextToken(); // consume arg
|
||||
arguments.Add(arg);
|
||||
if (_currentToken.Type == TokenType.Comma)
|
||||
{
|
||||
_currentToken = _lexer.NextToken(); // consume ","
|
||||
}
|
||||
if (_currentToken.Type == TokenType.Semicolon)
|
||||
{
|
||||
break; // consume ";"
|
||||
}
|
||||
}
|
||||
|
||||
_currentToken = _lexer.NextToken(); // consume ")"
|
||||
|
||||
return new MemberFunctionCallNode(targetNode, functionName, arguments).SetTokenInfo(_currentToken);
|
||||
}
|
||||
|
||||
|
||||
private ASTNode ParseFunctionCall()
|
||||
{
|
||||
string functionName = _currentToken.Value.ToString();
|
||||
_currentToken = _lexer.NextToken(); // consume identifier
|
||||
|
||||
if (_currentToken.Type != TokenType.ParenthesisLeft)
|
||||
throw new Exception("Expected '(' after function name");
|
||||
|
||||
_currentToken = _lexer.NextToken(); // consume "("
|
||||
|
||||
var arguments = new List<ASTNode>();
|
||||
while (_currentToken.Type != TokenType.ParenthesisRight)
|
||||
{
|
||||
var arg = Expression();
|
||||
_currentToken = _lexer.NextToken(); // consume arg
|
||||
arguments.Add(arg);
|
||||
if (_currentToken.Type == TokenType.Comma)
|
||||
{
|
||||
_currentToken = _lexer.NextToken(); // consume ","
|
||||
}
|
||||
if (_currentToken.Type == TokenType.Semicolon)
|
||||
{
|
||||
break; // consume ";"
|
||||
}
|
||||
}
|
||||
|
||||
_currentToken = _lexer.NextToken(); // consume ")"
|
||||
|
||||
|
||||
//var node = Statements[^1];
|
||||
//if (node is MemberAccessNode memberAccessNode)
|
||||
//{
|
||||
// // 上一个是对象
|
||||
// return new MemberFunctionCallNode(memberAccessNode, functionName, arguments).SetTokenInfo(_currentToken);
|
||||
//}
|
||||
//if (node is IdentifierNode identifierNode)
|
||||
//{
|
||||
// return new MemberFunctionCallNode(identifierNode, functionName, arguments).SetTokenInfo(_currentToken);
|
||||
//}
|
||||
|
||||
// 从挂载的函数表寻找对应的函数,尝试调用
|
||||
return new FunctionCallNode(functionName, arguments).SetTokenInfo(_currentToken);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public ASTNode ParseReturn()
|
||||
{
|
||||
_currentToken = _lexer.NextToken();
|
||||
if(_currentToken.Type == TokenType.Semicolon)
|
||||
{
|
||||
return new ReturnNode().SetTokenInfo(_currentToken);
|
||||
}
|
||||
var resultValue = Expression();
|
||||
_currentToken = _lexer.NextToken();
|
||||
return new ReturnNode(resultValue).SetTokenInfo(_currentToken);
|
||||
}
|
||||
|
||||
private ASTNode ParseIf()
|
||||
{
|
||||
_currentToken = _lexer.NextToken(); // Consume "if"
|
||||
_currentToken = _lexer.NextToken(); // Consume "("
|
||||
ASTNode condition = Expression();
|
||||
_currentToken = _lexer.NextToken(); // Consume ")"
|
||||
|
||||
// 确保遇到左大括号 { 后进入代码块解析
|
||||
if (_currentToken.Type != TokenType.BraceLeft)
|
||||
{
|
||||
throw new Exception("Expected '{' after if condition");
|
||||
}
|
||||
_currentToken = _lexer.NextToken(); // Consume "{"
|
||||
|
||||
// 解析大括号中的语句
|
||||
List<ASTNode> trueBranch = new List<ASTNode>();
|
||||
List<ASTNode> falseBranch = new List<ASTNode>();
|
||||
while (_currentToken.Type != TokenType.BraceRight && _currentToken.Type != TokenType.EOF)
|
||||
{
|
||||
var astNode = Statement();
|
||||
if (astNode != null)
|
||||
{
|
||||
trueBranch.Add(astNode);
|
||||
}
|
||||
}
|
||||
// 确保匹配右大括号 }
|
||||
if (_currentToken.Type != TokenType.BraceRight)
|
||||
{
|
||||
throw new Exception("Expected '}' after if block");
|
||||
}
|
||||
_currentToken = _lexer.NextToken(); // Consume "}"
|
||||
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "else")
|
||||
{
|
||||
_currentToken = _lexer.NextToken(); // Consume "{"
|
||||
_currentToken = _lexer.NextToken(); // Consume "{"
|
||||
while (_currentToken.Type != TokenType.BraceRight && _currentToken.Type != TokenType.EOF)
|
||||
{
|
||||
var astNode = Statement();
|
||||
if (astNode != null)
|
||||
{
|
||||
falseBranch.Add(astNode);
|
||||
}
|
||||
}
|
||||
// 确保匹配右大括号 }
|
||||
if (_currentToken.Type != TokenType.BraceRight)
|
||||
{
|
||||
throw new Exception("Expected '}' after if block");
|
||||
}
|
||||
_currentToken = _lexer.NextToken(); // Consume "}"
|
||||
}
|
||||
|
||||
|
||||
return new IfNode(condition, trueBranch, falseBranch).SetTokenInfo(_currentToken);
|
||||
}
|
||||
|
||||
private ASTNode ParseWhile()
|
||||
{
|
||||
_currentToken = _lexer.NextToken(); // Consume "while"
|
||||
_currentToken = _lexer.NextToken(); // Consume "("
|
||||
ASTNode condition = Expression();
|
||||
_currentToken = _lexer.NextToken(); // Consume ")"
|
||||
_currentToken = _lexer.NextToken(); // Consume "{"
|
||||
List<ASTNode> body = new List<ASTNode>();
|
||||
while (_currentToken.Type != TokenType.BraceRight)
|
||||
{
|
||||
body.Add(Statement());
|
||||
}
|
||||
_currentToken = _lexer.NextToken(); // Consume "}"
|
||||
return new WhileNode(condition, body).SetTokenInfo(_currentToken);
|
||||
}
|
||||
|
||||
|
||||
private ASTNode Expression()
|
||||
{
|
||||
ASTNode left = Term();
|
||||
while (_currentToken.Type == TokenType.Operator && (
|
||||
_currentToken.Value == "+" || _currentToken.Value == "-" ||
|
||||
_currentToken.Value == "*" || _currentToken.Value == "/"))
|
||||
{
|
||||
string op = _currentToken.Value.ToString();
|
||||
_currentToken = _lexer.NextToken();
|
||||
ASTNode right = Term();
|
||||
left = new BinaryOperationNode(left, op, right).SetTokenInfo(_currentToken);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
private ASTNode Term()
|
||||
{
|
||||
ASTNode left = Factor();
|
||||
while (_currentToken.Type == TokenType.Operator &&
|
||||
(_currentToken.Value == "<" || _currentToken.Value == ">" ||
|
||||
_currentToken.Value == "<=" || _currentToken.Value == ">=" ||
|
||||
_currentToken.Value == "==" || _currentToken.Value == "!="))
|
||||
{
|
||||
string op = _currentToken.Value.ToString();
|
||||
_currentToken = _lexer.NextToken();
|
||||
ASTNode right = Factor();
|
||||
left = new BinaryOperationNode(left, op, right).SetTokenInfo(_currentToken);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
private ASTNode Factor()
|
||||
{
|
||||
#region 返回字面量
|
||||
if (_currentToken.Type == TokenType.Null)
|
||||
{
|
||||
_currentToken = _lexer.NextToken(); // 消耗 null
|
||||
return new NullNode().SetTokenInfo(_currentToken);
|
||||
}
|
||||
if (_currentToken.Type == TokenType.Boolean)
|
||||
{
|
||||
var value = bool.Parse(_currentToken.Value);
|
||||
_currentToken = _lexer.NextToken(); // 消耗布尔量
|
||||
return new BooleanNode(value).SetTokenInfo(_currentToken);
|
||||
}
|
||||
if (_currentToken.Type == TokenType.String)
|
||||
{
|
||||
var text = _currentToken.Value;
|
||||
_currentToken = _lexer.NextToken(); // 消耗数字
|
||||
return new StringNode(text.ToString()).SetTokenInfo(_currentToken);
|
||||
}
|
||||
|
||||
if (_currentToken.Type == TokenType.Number)
|
||||
{
|
||||
var value = int.Parse(_currentToken.Value);
|
||||
_currentToken = _lexer.NextToken(); // 消耗数字
|
||||
return new NumberNode(value).SetTokenInfo(_currentToken);
|
||||
}
|
||||
#endregion
|
||||
|
||||
// 方法调用
|
||||
if (_currentToken.Type == TokenType.ParenthesisLeft)
|
||||
{
|
||||
_currentToken = _lexer.NextToken(); // 消耗 "("
|
||||
var expr = Expression();
|
||||
if (_currentToken.Type != TokenType.ParenthesisRight)
|
||||
throw new Exception("非预期的符号,预期符号为\")\"。");
|
||||
_currentToken = _lexer.NextToken(); // 消耗 ")"
|
||||
return expr;
|
||||
}
|
||||
|
||||
// 创建对象
|
||||
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "new")
|
||||
{
|
||||
return ParseObjectInstantiation();
|
||||
}
|
||||
|
||||
// 标识符节点
|
||||
if (_currentToken.Type == TokenType.Identifier)
|
||||
{
|
||||
var identifier = _currentToken.Value; // 标识符字面量
|
||||
var _identifierPeekToken = _lexer.PeekToken();
|
||||
// 该标识符是方法调用
|
||||
if (_identifierPeekToken.Type == TokenType.ParenthesisLeft)
|
||||
{
|
||||
return ParseFunctionCall();
|
||||
}
|
||||
|
||||
// 需要从该标识符调用另一个标识符
|
||||
if (_identifierPeekToken.Type == TokenType.Dot)
|
||||
{
|
||||
return ParseMemberAccessOrAssignment();
|
||||
|
||||
var identifierNode = new IdentifierNode(identifier).SetTokenInfo(_currentToken);
|
||||
// 处理成员访问:identifier.member
|
||||
if (_currentToken.Type == TokenType.Dot)
|
||||
{
|
||||
_currentToken = _lexer.NextToken(); // 消耗 "."
|
||||
if (_currentToken.Type != TokenType.Identifier)
|
||||
{
|
||||
throw new Exception("Expected member name after dot.");
|
||||
}
|
||||
|
||||
var memberName = _currentToken.Value;
|
||||
_currentToken = _lexer.NextToken(); // 消耗成员名
|
||||
if (_currentToken.Type == TokenType.Operator && _currentToken.Value == "=")
|
||||
{
|
||||
// 成员赋值 obj.Member = xxx;
|
||||
_currentToken = _lexer.NextToken(); // 消耗 "="
|
||||
var valueNode = Expression(); // 解析右值
|
||||
return new MemberAssignmentNode(identifierNode, memberName, valueNode).SetTokenInfo(_currentToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
var _peekToken = _lexer.PeekToken();
|
||||
if (_peekToken.Type == TokenType.ParenthesisLeft)
|
||||
{
|
||||
// 成员方法调用 obj.Member(xxx);
|
||||
return ParseFunctionCall();
|
||||
}
|
||||
else if (_peekToken.Type == TokenType.SquareBracketsLeft)
|
||||
{
|
||||
// 数组 index; 字典 key obj.Member[xxx];
|
||||
return ParseCollectionIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 成员获取
|
||||
return new MemberAccessNode(identifierNode, memberName).SetTokenInfo(_currentToken);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return identifierNode;
|
||||
|
||||
}
|
||||
_currentToken = _lexer.NextToken(); // 消耗标识符
|
||||
return new IdentifierNode(identifier.ToString()).SetTokenInfo(_currentToken);
|
||||
}
|
||||
|
||||
|
||||
throw new Exception("Unexpected factor: " + _currentToken.Value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
119
Serein.Script/Tool/DelegateDetails.cs
Normal file
119
Serein.Script/Tool/DelegateDetails.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static Serein.Library.Utils.EmitHelper;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// Emit创建的委托描述,用于WebApi、WebSocket、NodeFlow动态调用方法的场景。
|
||||
/// 一般情况下你无须关注内部细节,只需要调用 Invoke() 方法即可。
|
||||
/// </summary>
|
||||
public class DelegateDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// 根据方法信息构建Emit委托
|
||||
/// </summary>
|
||||
/// <param name="methodInfo"></param>
|
||||
public DelegateDetails(MethodInfo methodInfo)
|
||||
{
|
||||
var emitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out var emitDelegate);
|
||||
_emitMethodInfo = emitMethodType;
|
||||
_emitDelegate = emitDelegate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*/// <summary>
|
||||
/// 更新委托方法
|
||||
/// </summary>
|
||||
/// <param name="EmitMethodType"></param>
|
||||
/// <param name="EmitDelegate"></param>
|
||||
public void Upload(EmitMethodType EmitMethodType, Delegate EmitDelegate)
|
||||
{
|
||||
_emitMethodType = EmitMethodType;
|
||||
_emitDelegate = EmitDelegate;
|
||||
}*/
|
||||
|
||||
private Delegate _emitDelegate;
|
||||
private EmitMethodInfo _emitMethodInfo;
|
||||
|
||||
///// <summary>
|
||||
///// <para>普通方法:Func<object,object[],object></para>
|
||||
///// <para>异步方法:Func<object,object[],Task></para>
|
||||
///// <para>异步有返回值方法:Func<object,object[],Task<object>></para>
|
||||
///// </summary>
|
||||
//public Delegate EmitDelegate { get => _emitDelegate; }
|
||||
///// <summary>
|
||||
///// 表示Emit构造的委托类型
|
||||
///// </summary>
|
||||
//public EmitMethodType EmitMethodType { get => _emitMethodType; }
|
||||
|
||||
|
||||
|
||||
public async Task<object> AutoInvokeAsync(object[] args)
|
||||
{
|
||||
if (_emitMethodInfo.IsStatic)
|
||||
{
|
||||
return await InvokeAsync(null, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
var obj = Activator.CreateInstance(_emitMethodInfo.DeclaringType);
|
||||
return await InvokeAsync(obj, args);
|
||||
}
|
||||
throw new Exception("Not static method");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>使用的实例必须能够正确调用该委托,传入的参数也必须符合方法入参信息。</para>
|
||||
/// </summary>
|
||||
/// <param name="instance">拥有符合委托签名的方法信息的实例</param>
|
||||
/// <param name="args">如果方法没有入参,也需要传入一个空数组</param>
|
||||
/// <returns>void方法自动返回null</returns>
|
||||
public async Task<object> InvokeAsync(object instance, object[] args)
|
||||
{
|
||||
if (args is null)
|
||||
{
|
||||
args = Array.Empty<object>();
|
||||
}
|
||||
if(_emitMethodInfo.IsStatic)
|
||||
{
|
||||
instance = null;
|
||||
}
|
||||
object result = null;
|
||||
if (_emitDelegate is Func<object, object[], Task<object>> hasResultTask)
|
||||
{
|
||||
result = await hasResultTask(instance, args);
|
||||
}
|
||||
else if (_emitDelegate is Func<object, object[], Task> task)
|
||||
{
|
||||
await task.Invoke(instance, args);
|
||||
}
|
||||
else if (_emitDelegate is Func<object, object[], object> func)
|
||||
{
|
||||
result = func.Invoke(instance, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException("创建了非预期委托(应该不会出现)");
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
|
||||
//try
|
||||
//{
|
||||
|
||||
//}
|
||||
//catch
|
||||
//{
|
||||
// throw;
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
203
Serein.Script/Tool/EmitHelper.cs
Normal file
203
Serein.Script/Tool/EmitHelper.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Serein.Library.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Emit创建委托工具类
|
||||
/// </summary>
|
||||
public class EmitHelper
|
||||
{
|
||||
|
||||
public class EmitMethodInfo
|
||||
{
|
||||
public Type DeclaringType { get; set; }
|
||||
/// <summary>
|
||||
/// 是异步方法
|
||||
/// </summary>
|
||||
public bool IsTask { get; set; }
|
||||
/// <summary>
|
||||
/// 是静态的
|
||||
/// </summary>
|
||||
public bool IsStatic { get; set; }
|
||||
}
|
||||
|
||||
public enum EmitMethodType
|
||||
{
|
||||
/// <summary>
|
||||
/// 普通的方法。如果方法返回void时,将会返回null。
|
||||
/// </summary>
|
||||
Func,
|
||||
/// <summary>
|
||||
/// 无返回值的异步方法
|
||||
/// </summary>
|
||||
Task,
|
||||
/// <summary>
|
||||
/// 有返回值的异步方法
|
||||
/// </summary>
|
||||
HasResultTask,
|
||||
|
||||
/// <summary>
|
||||
/// 普通的方法。如果方法返回void时,将会返回null。
|
||||
/// </summary>
|
||||
StaticFunc,
|
||||
/// <summary>
|
||||
/// 无返回值的异步方法
|
||||
/// </summary>
|
||||
StaticTask,
|
||||
}
|
||||
|
||||
public static bool IsGenericTask(Type returnType, out Type taskResult)
|
||||
{
|
||||
// 判断是否为 Task 类型或泛型 Task<T>
|
||||
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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据方法信息创建动态调用的委托,返回方法类型,以及传出一个委托
|
||||
/// </summary>
|
||||
/// <param name="methodInfo"></param>
|
||||
/// <param name="delegate"></param>
|
||||
/// <returns></returns>
|
||||
public static EmitMethodInfo CreateDynamicMethod(MethodInfo methodInfo,out Delegate @delegate)
|
||||
{
|
||||
EmitMethodInfo emitMethodInfo = new EmitMethodInfo();
|
||||
bool IsTask = IsGenericTask(methodInfo.ReturnType, out var taskGenericsType);
|
||||
bool IsTaskGenerics = taskGenericsType != null;
|
||||
DynamicMethod dynamicMethod;
|
||||
|
||||
Type returnType;
|
||||
if (!IsTask)
|
||||
{
|
||||
// 普通方法
|
||||
returnType = typeof(object);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// 异步方法
|
||||
if (IsTaskGenerics)
|
||||
{
|
||||
returnType = typeof(Task<object>);
|
||||
}
|
||||
else
|
||||
{
|
||||
returnType = typeof(Task);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
dynamicMethod = new DynamicMethod(
|
||||
name: methodInfo.Name + "_DynamicEmitMethod",
|
||||
returnType: returnType,
|
||||
parameterTypes: new[] { typeof(object), typeof(object[]) }, // 方法实例、方法入参
|
||||
restrictedSkipVisibility: true // 跳过私有方法访问限制
|
||||
);
|
||||
|
||||
var il = dynamicMethod.GetILGenerator();
|
||||
|
||||
// 判断是否为静态方法
|
||||
bool isStatic = methodInfo.IsStatic;
|
||||
|
||||
if (isStatic)
|
||||
{
|
||||
// 如果是静态方法,直接跳过实例(不加载Ldarg_0)
|
||||
}
|
||||
else
|
||||
{
|
||||
// 加载实例 (this) 对于非静态方法
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Castclass, methodInfo.DeclaringType); // 将 ISocketControlBase 转换为目标类类型
|
||||
}
|
||||
// 加载方法参数
|
||||
var methodParams = methodInfo.GetParameters();
|
||||
for (int i = 0; i < methodParams.Length; i++)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_1); // 加载参数数组
|
||||
il.Emit(OpCodes.Ldc_I4, i); // 加载当前参数索引
|
||||
il.Emit(OpCodes.Ldelem_Ref); // 取出数组元素
|
||||
|
||||
var paramType = methodParams[i].ParameterType;
|
||||
if (paramType.IsValueType) // 如果参数是值类型,拆箱
|
||||
{
|
||||
il.Emit(OpCodes.Unbox_Any, paramType);
|
||||
}
|
||||
//else if (paramType.IsGenericParameter) // 如果是泛型参数,直接转换
|
||||
//{
|
||||
// il.Emit(OpCodes.Castclass, paramType);
|
||||
//}
|
||||
else // 如果是引用类型,直接转换
|
||||
{
|
||||
il.Emit(OpCodes.Castclass, paramType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 调用方法:静态方法使用 Call,实例方法使用 Callvirt
|
||||
if (isStatic)
|
||||
{
|
||||
il.Emit(OpCodes.Call, methodInfo); // 对于静态方法,使用 Call
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Callvirt, methodInfo); // 对于实例方法,使用 Callvirt
|
||||
}
|
||||
|
||||
//// 处理返回值,如果没有返回值,则返回null
|
||||
if (methodInfo.ReturnType == typeof(void))
|
||||
{
|
||||
il.Emit(OpCodes.Ldnull);
|
||||
}
|
||||
else if (methodInfo.ReturnType.IsValueType)
|
||||
{
|
||||
il.Emit(OpCodes.Box, methodInfo.ReturnType); // 如果是值类型,将其装箱
|
||||
}
|
||||
// 处理返回值,如果没有返回值,则返回null
|
||||
il.Emit(OpCodes.Ret); // 返回
|
||||
if (IsTask)
|
||||
{
|
||||
if (IsTaskGenerics)
|
||||
{
|
||||
@delegate = dynamicMethod.CreateDelegate(typeof(Func<object, object[], Task<object>>));
|
||||
}
|
||||
else
|
||||
{
|
||||
@delegate = dynamicMethod.CreateDelegate(typeof(Func<object, object[], Task>));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@delegate = dynamicMethod.CreateDelegate(typeof(Func<object, object[], object>));
|
||||
|
||||
}
|
||||
return new EmitMethodInfo
|
||||
{
|
||||
DeclaringType = methodInfo.DeclaringType,
|
||||
IsTask = IsTask,
|
||||
IsStatic = isStatic
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user