优化了脚本生成AST时的代码提示,增加了脚本运行时错误提示。

This commit is contained in:
fengjiayi
2025-07-09 21:49:26 +08:00
parent 4da8bf6b84
commit 70f674ca1b
32 changed files with 1218 additions and 266 deletions

View File

@@ -0,0 +1,163 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Script
{
public static class BinaryOperationEvaluator
{
public static object EvaluateValue(object left, string op, object right)
{
if (op == null) throw new ArgumentNullException(nameof(op));
// 特判字符串拼接
if (op == "+" && (left is string || right is string))
{
return (left?.ToString() ?? "") + (right?.ToString() ?? "");
}
// 支持 null 运算(可按需扩展)
if (left == null || right == null)
{
throw new InvalidOperationException($"无法对 null 执行操作:{op}");
}
// 尝试统一类型(浮点优先)
var leftType = left.GetType();
var rightType = right.GetType();
Type resultType = GetWiderType(leftType, rightType);
dynamic leftValue = Convert.ChangeType(left, resultType);
dynamic rightValue = Convert.ChangeType(right, resultType);
return op switch
{
"+" => leftValue + rightValue,
"-" => leftValue - rightValue,
"*" => leftValue * rightValue,
"/" => rightValue == 0 ? throw new DivideByZeroException() : leftValue / rightValue,
">" => leftValue > rightValue,
"<" => leftValue < rightValue,
">=" => leftValue >= rightValue,
"<=" => leftValue <= rightValue,
"==" => Equals(leftValue, rightValue),
"!=" => !Equals(leftValue, rightValue),
_ => throw new NotImplementedException($"未实现的操作符: {op}")
};
}
/// <summary>
/// 推导两个类型的“最通用类型”(类型提升)
/// </summary>
private static Type GetWiderType(Type t1, Type t2)
{
if (t1 == typeof(string) || t2 == typeof(string)) return typeof(string);
if (t1 == typeof(decimal) || t2 == typeof(decimal)) return typeof(decimal);
if (t1 == typeof(double) || t2 == typeof(double)) return typeof(double);
if (t1 == typeof(float) || t2 == typeof(float)) return typeof(float);
if (t1 == typeof(ulong) || t2 == typeof(ulong)) return typeof(ulong);
if (t1 == typeof(long) || t2 == typeof(long)) return typeof(long);
if (t1 == typeof(uint) || t2 == typeof(uint)) return typeof(uint);
if (t1 == typeof(int) || t2 == typeof(int)) return typeof(int);
// fallback
return typeof(object);
}
public static Type EvaluateType(Type leftType, string op, Type rightType)
{
if (leftType == null || rightType == null)
throw new ArgumentNullException("操作数类型不能为 null");
// 字符串拼接
if (op == "+" && (leftType == typeof(string) || rightType == typeof(string)))
return typeof(string);
// 比较操作总是返回 bool
if (op is ">" or "<" or ">=" or "<=" or "==" or "!=")
return typeof(bool);
// 数值类型推导
if (IsNumeric(leftType) && IsNumeric(rightType))
{
return PromoteNumericType(leftType, rightType);
}
// 逻辑操作
if (op is "&&" or "||")
{
if (leftType == typeof(bool) && rightType == typeof(bool))
return typeof(bool);
throw new InvalidOperationException($"逻辑操作 '{op}' 仅适用于 bool 类型");
}
throw new NotImplementedException($"不支持操作符 '{op}' 对 {leftType.Name} 和 {rightType.Name} 进行类型推导");
}
private static bool IsNumeric(Type type)
{
return type == typeof(byte) || type == typeof(sbyte) ||
type == typeof(short) || type == typeof(ushort) ||
type == typeof(int) || type == typeof(uint) ||
type == typeof(long) || type == typeof(ulong) ||
type == typeof(float) || type == typeof(double) ||
type == typeof(decimal);
}
// 提升到更高精度类型
private static Type[] types = new[]
{
typeof(decimal),
typeof(double),
typeof(float),
typeof(ulong),
typeof(long),
typeof(uint),
typeof(int),
typeof(ushort),
typeof(short),
typeof(byte),
typeof(sbyte)
};
private static Type PromoteNumericType(Type left, Type right)
{
var ranks = types;
foreach (var type in ranks)
{
if (type == left || type == right)
return type;
}
return typeof(object); // fallback
}
}
}

View File

@@ -11,7 +11,14 @@ namespace Serein.Script.Node
/// </summary>
public class MemberAccessNode : ASTNode
{
/// <summary>
/// 对象token
/// </summary>
public ASTNode Object { get; }
/// <summary>
/// 成员名称
/// </summary>
public string MemberName { get; }
public MemberAccessNode(ASTNode obj, string memberName)

View File

@@ -11,8 +11,17 @@ namespace Serein.Script.Node
/// </summary>
public class MemberAssignmentNode : ASTNode
{
/// <summary>
/// 作用的对象
/// </summary>
public ASTNode Object { get; }
/// <summary>
/// 被赋值的成员(属性/字段)名称
/// </summary>
public string MemberName { get; }
/// <summary>
/// 值来源
/// </summary>
public ASTNode Value { get; }
public MemberAssignmentNode(ASTNode obj, string memberName, ASTNode value)

View File

@@ -11,8 +11,19 @@ namespace Serein.Script.Node
/// </summary>
public class MemberFunctionCallNode : ASTNode
{
/// <summary>
/// 需要被调用的对象
/// </summary>
public ASTNode Object { get; }
/// <summary>
/// 被调用的方法名称
/// </summary>
public string FunctionName { get; }
/// <summary>
/// 方法参数
/// </summary>
public List<ASTNode> Arguments { get; }
public MemberFunctionCallNode(ASTNode @object, string functionName, List<ASTNode> arguments)

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Script.Node
{
/// <summary>
/// 数值型节点
/// </summary>
public abstract class NumberNode<T> : ASTNode where T : struct, IComparable<T>
{
public T Value { get; }
public NumberNode(T value) => Value = value;
}
/// <summary>
/// int 整数型字面量
/// </summary>
public class NumberIntNode(int vlaue) : NumberNode<int>(vlaue)
{
}
/// <summary>
/// int 整数型字面量
/// </summary>
public class NumberLongNode(long vlaue) : NumberNode<long>(vlaue)
{
}
/// <summary>
/// int 整数型字面量
/// </summary>
public class NumberFloatNode(float vlaue) : NumberNode<float>(vlaue)
{
}
/// <summary>
/// int 整数型字面量
/// </summary>
public class NumberDoubleNode(double vlaue) : NumberNode<double>(vlaue)
{
}
/*/// <summary>
/// int 整数型字面量
/// </summary>
public class NumberIntNode : ASTNode
{
public int Value { get; }
public NumberIntNode(int value) => Value = value;
}
/// <summary>
/// int 整数型字面量
/// </summary>
public class NumberLongNode : ASTNode
{
public long Value { get; }
public NumberLongNode(long value) => Value = value;
}
/// <summary>
/// int 整数型字面量
/// </summary>
public class NumberFloatNode : ASTNode
{
public float Value { get; }
public NumberFloatNode(float value) => Value = value;
}
/// <summary>
/// int 整数型字面量
/// </summary>
public class NumberDoubleNode : ASTNode
{
public double Value { get; }
public NumberDoubleNode(double value) => Value = value;
}*/
}

View File

@@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Script.Node
{
/// <summary>
/// 整数型字面量
/// </summary>
public class NumberNode : ASTNode
{
public int Value { get; }
public NumberNode(int value) => Value = value;
}
}

View File

@@ -30,6 +30,9 @@ namespace Serein.Script
/// </summary>
public interface IScriptInvokeContext
{
/// <summary>
/// 脚本运行的流程上下文,包含了流程上下文和变量等信息
/// </summary>
IDynamicContext FlowContext { get; }
/// <summary>
@@ -64,7 +67,6 @@ namespace Serein.Script
void OnExit();
}
public class ScriptInvokeContext : IScriptInvokeContext
{
public ScriptInvokeContext(IDynamicContext dynamicContext)
@@ -78,7 +80,10 @@ namespace Serein.Script
/// 定义的变量
/// </summary>
private Dictionary<string, object> _variables = new Dictionary<string, object>();
/// <summary>
/// 取消令牌源,用于控制脚本的执行
/// </summary>
private CancellationTokenSource _tokenSource = new CancellationTokenSource();
/// <summary>
@@ -109,8 +114,6 @@ namespace Serein.Script
}
void IScriptInvokeContext.OnExit()
{
// 清理脚本中加载的非托管资源
@@ -134,7 +137,9 @@ namespace Serein.Script
}
/// <summary>
/// 脚本解释器,负责解析和执行 Serein 脚本
/// </summary>
public class SereinScriptInterpreter
{
@@ -220,23 +225,34 @@ namespace Serein.Script
private async Task<object?> ExecutionProgramNodeAsync(IScriptInvokeContext context, ProgramNode programNode)
{
// 加载变量
// 遍历 ProgramNode 中的所有语句并执行它们
foreach (var statement in programNode.Statements)
ASTNode statement = null;
try
{
// 直接退出
if (statement is ReturnNode returnNode) // 遇到 Return 语句 提前退出
{
return await EvaluateAsync(context, statement);
}
else
{
await InterpretAsync(context, statement);
}
}
return null;
// 遍历 ProgramNode 中的所有语句并执行它们
for (int index = 0; index < programNode.Statements.Count; index++)
{
statement = programNode.Statements[index];
// 直接退出
if (statement is ReturnNode returnNode) // 遇到 Return 语句 提前退出
{
return await EvaluateAsync(context, statement);
}
else
{
await InterpretAsync(context, statement);
}
}
return null;
}
catch (Exception ex )
{
if(statement is not null)
{
SereinEnv.WriteLine(InfoType.ERROR, $"脚本异常发生在[行{statement.Row}]{ex.Message}{Environment.NewLine}\t{statement.Code}");
}
throw;
}
}
/// <summary>
@@ -385,6 +401,13 @@ namespace Serein.Script
}
/// <summary>
/// 解释操作
/// </summary>
/// <param name="context"></param>
/// <param name="node"></param>
/// <returns></returns>
/// <exception cref="SereinSciptException"></exception>
public async Task<object?> InterpretAsync(IScriptInvokeContext context, ASTNode node)
{
if(node == null)
@@ -407,10 +430,10 @@ namespace Serein.Script
case MemberAssignmentNode memberAssignmentNode: // 设置对象属性
await SetMemberValue(context, memberAssignmentNode);
break;
case MemberFunctionCallNode memberFunctionCallNode:
case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用
return await CallMemberFunction(context, memberFunctionCallNode);
case IfNode ifNode: // 执行 if...else... 语句块
await ExecutionIfNodeAsync(context, ifNode);
await ExecutionIfNodeAsync(context, ifNode);
break;
case WhileNode whileNode: // 循环语句块
await ExectutionWhileNodeAsync(context, whileNode);
@@ -425,7 +448,13 @@ namespace Serein.Script
return null;
}
/// <summary>
/// 评估
/// </summary>
/// <param name="context"></param>
/// <param name="node"></param>
/// <returns></returns>
/// <exception cref="SereinSciptException"></exception>
private async Task<object?> EvaluateAsync(IScriptInvokeContext context, ASTNode node)
{
if(node == null)
@@ -438,8 +467,14 @@ namespace Serein.Script
return null;
case BooleanNode booleanNode:
return booleanNode.Value; // 返回数值
case NumberNode numberNode:
return numberNode.Value; // 返回数
case NumberIntNode numberNode:
return numberNode.Value; // 返回 int 整型
case NumberLongNode numberNode:
return numberNode.Value; // 返回 long 整型数
case NumberFloatNode numberNode:
return numberNode.Value; // 返回 float 浮点型
case NumberDoubleNode numberNode:
return numberNode.Value; // 返回 double 浮点型
case StringNode stringNode:
return stringNode.Value; // 返回字符串值
case CharNode charNode:
@@ -451,17 +486,11 @@ namespace Serein.Script
case BinaryOperationNode binOpNode:
// 递归计算二元操作
var left = await EvaluateAsync(context, binOpNode.Left);
//if (left == null )
//{
// throw new SereinSciptException(binOpNode.Left, $"左值尝试使用 null");
//}
//if (left == null ) throw new SereinSciptException(binOpNode.Left, $"左值尝试使用 null");
var right = await EvaluateAsync(context, binOpNode.Right);
//if (right == null)
//{
// throw new SereinSciptException(binOpNode.Right, "右值尝试使用计算 null");
//}
//if (right == null) throw new SereinSciptException(binOpNode.Right, "右值尝试使用计算 null");
return EvaluateBinaryOperation(left, binOpNode.Operator, right);
case ObjectInstantiationNode objectInstantiationNode:
case ObjectInstantiationNode objectInstantiationNode: // 对象实例化
if (_classDefinition.TryGetValue(objectInstantiationNode.TypeName,out var type ))
{
object?[] args = new object[objectInstantiationNode.Arguments.Count];
@@ -493,6 +522,8 @@ namespace Serein.Script
return await GetCollectionValue(context, collectionIndexNode);
case ReturnNode returnNode: // 返回内容
return await EvaluateAsync(context, returnNode.Value); // 直接返回响应的内容
//case ObjectInstantiationNode objectInstantiationNode: // 返回内容
default:
throw new SereinSciptException(node, $"解释器 EvaluateAsync() 未实现{node}节点行为");
}
@@ -500,8 +531,7 @@ namespace Serein.Script
private object EvaluateBinaryOperation(object left, string op, object right)
{
return BinaryOperationEvaluator.EvaluateValue(left, op, right);
// 根据运算符执行不同的运算
switch (op)

View File

@@ -1,9 +1,13 @@
using Newtonsoft.Json.Linq;
using System.Runtime.CompilerServices;
using System.Xml.Linq;
using static System.Net.Mime.MediaTypeNames;
namespace Serein.Script
{
/// <summary>
/// Serein脚本词法分析器的Token类型
/// </summary>
internal enum TokenType
{
/// <summary>
@@ -19,9 +23,21 @@ namespace Serein.Script
/// </summary>
Boolean,
/// <summary>
/// 数
/// int 整
/// </summary>
Number,
NumberInt,
/// <summary>
/// long 整数
/// </summary>
NumberLong,
/// <summary>
/// float 浮点数
/// </summary>
NumberFloat,
/// <summary>
/// double 浮点数
/// </summary>
NumberDouble,
/// <summary>
/// 字符串
/// </summary>
@@ -91,6 +107,9 @@ namespace Serein.Script
EOF
}
/// <summary>
/// Serein脚本词法分析器的Token结构体
/// </summary>
internal ref struct Token
{
public TokenType Type { get; }
@@ -108,18 +127,26 @@ namespace Serein.Script
}
}
/// <summary>
/// Serein脚本词法分析器
/// </summary>
internal ref struct SereinScriptLexer
{
private readonly ReadOnlySpan<char> _input;
private int _index;
private int _row ;
private int coreRangeStartIndex = 0;
/// <summary>
/// 关键字,防止声明为变量
/// </summary>
private string[] _keywords = [
"let",
"func",
"if",
"else",
"func",
"if",
"else",
"return",
"while",
"new",
@@ -136,8 +163,10 @@ namespace Serein.Script
internal Token PeekToken()
{
int currentIndex = _index; // 保存当前索引
var currentRow = _row; // 保存当前行数
Token nextToken = NextToken(); // 获取下一个 token
_index = currentIndex; // 恢复索引到当前位置
_row = currentRow; // 恢复到当前行数
return nextToken; // 返回下一个 token
}
@@ -156,8 +185,7 @@ namespace Serein.Script
}
if (_index >= _input.Length) return new Token(TokenType.EOF, string.Empty);
if (_index >= _input.Length) return new Token(TokenType.EOF, string.Empty); // 程序结束
char currentChar = _input[_index];
@@ -230,12 +258,77 @@ namespace Serein.Script
// 识别数字
if (char.IsDigit(currentChar))
{
var start = _index;
while (_index < _input.Length && char.IsDigit(_input[_index]))
_index++;
var value = _input.Slice(start, _index - start).ToString();
_index = start; // 回退索引,索引必须只能在 CreateToken 方法内更新
return CreateToken(TokenType.Number, value);
#region
if (char.IsDigit(currentChar))
{
var start = _index;
bool hasDot = false;
bool hasSuffix = false;
while (_index < _input.Length)
{
var ch = _input[_index];
if (char.IsDigit(ch))
{
_index++;
}
else if (ch == '.' && !hasDot)
{
hasDot = true;
_index++;
}
else if (ch is 'f' or 'F' or 'd' or 'D' or 'l' or 'L')
{
hasSuffix = true;
_index++;
break; // 后缀后应结束
}
else
{
break;
}
}
var raw = _input.Slice(start, _index - start).ToString();
_index = start; // 回退索引,仅 CreateToken 负责推进
TokenType type;
// 判断类型
if (hasDot)
{
if (raw.EndsWith("f", StringComparison.OrdinalIgnoreCase))
type = TokenType.NumberFloat;
else if (raw.EndsWith("d", StringComparison.OrdinalIgnoreCase))
type = TokenType.NumberDouble;
else
type = TokenType.NumberDouble; // 默认小数为 double
}
else
{
if (raw.EndsWith("l", StringComparison.OrdinalIgnoreCase))
type = TokenType.NumberLong;
else
{
// 自动根据位数判断 int 或 long
if (long.TryParse(raw, out var val))
{
if (val >= int.MinValue && val <= int.MaxValue)
type = TokenType.NumberInt;
else
type = TokenType.NumberLong;
}
else
{
type = TokenType.NumberLong; // 超出 long 会出错,默认成 long
}
}
}
return CreateToken(type, raw);
}
#endregion
}
// 识别标识符(变量名、关键字)
@@ -303,6 +396,12 @@ namespace Serein.Script
throw new Exception("Unexpected character: " + currentChar);
}
/// <summary>
/// 创建一个新的Token实例
/// </summary>
/// <param name="tokenType"></param>
/// <param name="value"></param>
/// <returns></returns>
private Token CreateToken(TokenType tokenType, string value)
{
var code = GetLine(_row).ToString();
@@ -314,6 +413,7 @@ namespace Serein.Script
Code = code,
};
_index += value.Length;
return token;
}
@@ -396,6 +496,16 @@ namespace Serein.Script
}
public int GetIndex()
{
return _index;
}
public string GetCoreContent(int index)
{
ReadOnlySpan<char> text = _input;
var content = text.Slice(index, _index - index); // 返回从start到当前位置的行文本
return content.ToString();
}
}

View File

@@ -5,9 +5,14 @@ using Serein.Script.Node;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
namespace Serein.Script
{
/// <summary>
/// SereinScriptParser 用于解析 Serein 脚本语言的语法。
/// </summary>
public ref struct SereinScriptParser
{
private SereinScriptLexer _lexer;
@@ -20,6 +25,11 @@ namespace Serein.Script
_currentToken = _lexer.NextToken();
}
/// <summary>
/// 解析脚本并返回 AST抽象语法树根节点。
/// </summary>
/// <returns></returns>
public ASTNode Parse()
{
return Program();
@@ -29,40 +39,67 @@ namespace Serein.Script
private List<ASTNode> Statements { get; } = new List<ASTNode>();
/// <summary>
/// 解析整个程序直到遇到文件结尾EOF为止。
/// </summary>
/// <returns></returns>
private ASTNode Program()
{
Statements.Clear();
while (_currentToken.Type != TokenType.EOF)
{
var astNode = Statement();
var astNode = Statement(); // 解析单个语句
if (astNode == null)
{
continue;
}
Statements.Add(astNode);
//if (astNode is ClassTypeDefinitionNode)
//{
// statements = [astNode, ..statements]; // 类型定义置顶
//}
//else
//{
// statements.Add(astNode);
//}
Statements.Add(astNode); // 将解析得到的 AST 节点添加到语句列表中
}
return new ProgramNode(Statements).SetTokenInfo(_currentToken);
var programNode = new ProgramNode(Statements);
programNode.SetTokenInfo(_currentToken); // 程序节点,包含所有解析的语句列表
SereinScriptTypeAnalysis typeAnalysis = new SereinScriptTypeAnalysis(programNode);
return programNode;
/*if (astNode is ClassTypeDefinitionNode)
{
statements = [astNode, ..statements]; // 类型定义置顶
}
else
{
statements.Add(astNode);
}*/
}
/// <summary>
/// 解析单个语句。
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private ASTNode Statement()
{
// 处理其他语句(如表达式语句等)
while (_currentToken.Type == TokenType.Semicolon)
{
_currentToken = _lexer.NextToken();
}
if(_currentToken.Type == TokenType.EOF)
{
return null;
}
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "let")
{
// 处理 let 变量赋值语句
return ParseLetAssignment();
}
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "class")
{
// 加载类定义
return ParseClassDefinition(); // 加载类,如果已经加载过,则忽略
}
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "new")
@@ -70,39 +107,44 @@ namespace Serein.Script
var _peekToken = _lexer.PeekToken();
if (_peekToken.Type == TokenType.Keyword && _peekToken.Value == "class")
{
// 重新加载类定义
return ParseClassDefinition(); // 重新加载类
}
}
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "if")
{
// 处理 if 语句
return ParseIf();
}
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "while")
{
// 处理 while 循环语句
return ParseWhile();
}
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "return")
{
// 处理 return 语句
return ParseReturn();
}
if (_currentToken.Type == TokenType.Identifier)
{
// 处理标识符,可能是函数调用、变量赋值或对象成员访问等行为
return ParseIdentifier();
}
if (_currentToken.Type == TokenType.Null)
{
// 处理 null 语句
return Expression();
}
// 处理其他语句(如表达式语句等)
if (_currentToken.Type == TokenType.Semicolon)
/*if (_currentToken.Type == TokenType.Semicolon)
{
_currentToken = _lexer.NextToken();
return null; // 表示空语句
}
}*/
throw new Exception("Unexpected statement: " + _currentToken.Value.ToString());
}
@@ -126,12 +168,13 @@ namespace Serein.Script
else if (_tempToken.Type == TokenType.Dot)
{
// 对象成员的获取
return ParseMemberAccessOrAssignment();
return ParseMemberAccessOrAssignment();
}
else if (_tempToken.Type == TokenType.SquareBracketsLeft)
{
// 数组 index; 字典 key obj.Member[xxx];
return ParseCollectionIndex();
}
else
{
@@ -197,7 +240,6 @@ namespace Serein.Script
}
throw new Exception($"Expected '{_currentToken.Value}' after variable name");
}
@@ -209,33 +251,55 @@ namespace Serein.Script
/// <exception cref="Exception"></exception>
private ASTNode ParseLetAssignment()
{
_currentToken = _lexer.NextToken(); // Consume "let"
string variable = _currentToken.Value.ToString(); // 变量名称
_currentToken = _lexer.NextToken(); // Consume identifier
ASTNode value;
AssignmentNode assignmentNode;
if (_currentToken.Type == TokenType.Semicolon)
{
// 定义一个变量,初始值为 null
value = new NullNode();
assignmentNode = new AssignmentNode(variable, value); // 生成node
assignmentNode.SetTokenInfo(_currentToken); // 设置token信息
}
else
{
// 如果定义了变量,后面紧跟着操作符,且操作符不是“=”话视为异常
// let value = obj;
if (_currentToken.Type != TokenType.Operator || _currentToken.Value != "=")
throw new Exception("Expected '=' after variable name");
_currentToken = _lexer.NextToken();
value = Expression();
_currentToken = _lexer.NextToken(); // Consume semicolon
_currentToken = _lexer.NextToken(); // 消耗操作符(“=”)
var nodeToken = _currentToken;
value = Expression(); // 解析获取赋值表达式
assignmentNode = new AssignmentNode(variable, value); // 生成node
assignmentNode.SetTokenInfo(nodeToken); // 设置token信息
_currentToken = _lexer.NextToken(); // 消耗分号
}
return new AssignmentNode(variable, value).SetTokenInfo(_currentToken);
return assignmentNode;
}
/// <summary>
/// 解析类定义
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private ASTNode ParseClassDefinition()
{
bool isOverlay = false;
var sb = new StringBuilder(); // 收集代码信息
/*
* 有两种定义类型的方式:
* 1. class MyClass{}
* 2. new class MyClass{}
* 解析执行时第二种方式定义的类会顶掉其他地方创建的“MyClass”同名类型
*/
var coreStartRangeIndex = _lexer.GetIndex() - "class".Length; // 从“class”开始锚定代码范围
bool isOverlay = false; // 指示是否覆盖缓存中创建过的同名其它类型
if (_currentToken.Value == "new")
{
isOverlay = true; // 重新加载类
@@ -248,15 +312,19 @@ namespace Serein.Script
throw new Exception("Expected '{' after class definition");
var classFields = new Dictionary<string, Type>();
_currentToken = _lexer.NextToken(); // 消耗括号
while (_currentToken.Type != TokenType.BraceRight)
{
var fieldType = _currentToken.Value.ToString().ToTypeOfString(); // 获取定义的类名
_currentToken = _lexer.NextToken();
// 获取类字段定义
var fieldType = _currentToken.Value.ToString().ToTypeOfString(); // 获取字段的类型
_currentToken = _lexer.NextToken(); // 消耗类型
var fieldName = _currentToken.Value.ToString(); // 获取定义的类名
_currentToken = _lexer.NextToken();
classFields.Add(fieldName,fieldType);
if (_currentToken.Type == TokenType.Semicolon && _lexer.PeekToken().Type == TokenType.BraceRight)
_currentToken = _lexer.NextToken(); // 消耗字段名称
classFields.Add(fieldName,fieldType); // 添加字段
if (_currentToken.Type == TokenType.Semicolon
&& _lexer.PeekToken().Type == TokenType.BraceRight)
{
// 如果遇到分号、大括号,退出字段定义。
break;
}
else
@@ -265,37 +333,50 @@ namespace Serein.Script
}
}
_currentToken = _lexer.NextToken();
_currentToken = _lexer.NextToken();
return new ClassTypeDefinitionNode(classFields, className, isOverlay).SetTokenInfo(_currentToken);
_currentToken = _lexer.NextToken(); // 消耗类型定义 } 括号
var typeDefinitionCode = _lexer.GetCoreContent(coreStartRangeIndex); // 收集类型定义的代码。在Statement方法中开始收集的
var node = new ClassTypeDefinitionNode(classFields, className, isOverlay);
_currentToken.Code = typeDefinitionCode;
node.SetTokenInfo(_currentToken);
// _currentToken = _lexer.NextToken();
_currentToken = _lexer.NextToken();
return node;
}
/// <summary>
/// 解析对象实例化行为
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public ASTNode ParseObjectInstantiation()
{
_currentToken = _lexer.NextToken(); // Consume "new"
string typeName = _currentToken.Value.ToString(); // Get type name
_currentToken = _lexer.NextToken(); // 消耗 new 关键字
string typeName = _currentToken.Value.ToString(); // 获取类型名称
_currentToken = _lexer.NextToken();
if (_currentToken.Type != TokenType.ParenthesisLeft)
throw new Exception("Expected '(' after function name");
_currentToken = _lexer.NextToken(); // consume "("
_currentToken = _lexer.NextToken(); // 消耗 "("
var arguments = new List<ASTNode>();
while (_currentToken.Type != TokenType.ParenthesisRight)
{
arguments.Add(Expression());
arguments.Add(Expression()); // 获取参数表达式
if (_currentToken.Type == TokenType.Comma)
{
_currentToken = _lexer.NextToken(); // consume ","
}
}
_currentToken = _lexer.NextToken(); // consume ")"
_currentToken = _lexer.NextToken(); // 消耗 ")"
return new ObjectInstantiationNode(typeName, arguments).SetTokenInfo(_currentToken);
}
/// <summary>
/// 解析集合索引行为(数组或字典)
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public ASTNode ParseCollectionIndex()
{
var identifierNode = new IdentifierNode(_currentToken.Value.ToString()).SetTokenInfo(_currentToken);
@@ -375,10 +456,15 @@ namespace Serein.Script
}
/// <summary>
/// 解析成员函数调用行为
/// </summary>
/// <param name="targetNode"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private ASTNode ParseMemberFunctionCall(ASTNode targetNode)
{
string functionName = _currentToken.Value.ToString();
string functionName = _currentToken.Value.ToString(); // 函数名称
_currentToken = _lexer.NextToken(); // consume identifier
if (_currentToken.Type != TokenType.ParenthesisLeft)
@@ -389,9 +475,10 @@ namespace Serein.Script
var arguments = new List<ASTNode>();
while (_currentToken.Type != TokenType.ParenthesisRight)
{
// 获取参数表达式
var arg = Expression();
_currentToken = _lexer.NextToken(); // consume arg
arguments.Add(arg);
arguments.Add(arg); // 添加到参数列表
if (_currentToken.Type == TokenType.Comma)
{
_currentToken = _lexer.NextToken(); // consume ","
@@ -408,10 +495,15 @@ namespace Serein.Script
}
/// <summary>
/// 解析函数调用行为
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private ASTNode ParseFunctionCall()
{
string functionName = _currentToken.Value.ToString();
_currentToken = _lexer.NextToken(); // consume identifier
_currentToken = _lexer.NextToken(); // consume identifier
if (_currentToken.Type != TokenType.ParenthesisLeft)
throw new Exception("Expected '(' after function name");
@@ -422,7 +514,7 @@ namespace Serein.Script
bool isBreak = false;
while (_currentToken.Type != TokenType.ParenthesisRight)
{
var arg = Expression();
var arg = Expression(); // 获取参数表达式
_currentToken = _lexer.NextToken(); // consume arg
arguments.Add(arg);
if (_currentToken.Type == TokenType.Comma)
@@ -456,18 +548,28 @@ namespace Serein.Script
}
/// <summary>
/// 解析 return 语句。
/// </summary>
/// <returns></returns>
public ASTNode ParseReturn()
{
_currentToken = _lexer.NextToken();
if(_currentToken.Type == TokenType.Semicolon)
{
return new ReturnNode().SetTokenInfo(_currentToken);
return new ReturnNode().SetTokenInfo(_currentToken); // 返回空的 ReturnNode
}
var resultValue = Expression();
_currentToken = _lexer.NextToken();
return new ReturnNode(resultValue).SetTokenInfo(_currentToken);
var resultValue = Expression(); // 获取返回值表达式
_currentToken = _lexer.NextToken();
return new ReturnNode(resultValue).SetTokenInfo(_currentToken);
}
/// <summary>
/// 解析 if 语句。
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private ASTNode ParseIf()
{
_currentToken = _lexer.NextToken(); // Consume "if"
@@ -487,10 +589,10 @@ namespace Serein.Script
List<ASTNode> falseBranch = new List<ASTNode>();
while (_currentToken.Type != TokenType.BraceRight && _currentToken.Type != TokenType.EOF)
{
var astNode = Statement();
var astNode = Statement(); // 解析 if 分支中的语句
if (astNode != null)
{
trueBranch.Add(astNode);
trueBranch.Add(astNode); // 将 if 分支的语句添加到 trueBranch 中
}
}
// 确保匹配右大括号 }
@@ -505,10 +607,10 @@ namespace Serein.Script
_currentToken = _lexer.NextToken(); // Consume "{"
while (_currentToken.Type != TokenType.BraceRight && _currentToken.Type != TokenType.EOF)
{
var astNode = Statement();
var astNode = Statement(); // 解析 else 分支中的语句
if (astNode != null)
{
falseBranch.Add(astNode);
falseBranch.Add(astNode); // 将 else 分支的语句添加到 falseBranch 中
}
}
// 确保匹配右大括号 }
@@ -523,6 +625,10 @@ namespace Serein.Script
return new IfNode(condition, trueBranch, falseBranch).SetTokenInfo(_currentToken);
}
/// <summary>
/// 解析 while 循环语句。
/// </summary>
/// <returns></returns>
private ASTNode ParseWhile()
{
_currentToken = _lexer.NextToken(); // Consume "while"
@@ -533,13 +639,16 @@ namespace Serein.Script
List<ASTNode> body = new List<ASTNode>();
while (_currentToken.Type != TokenType.BraceRight)
{
body.Add(Statement());
body.Add(Statement()); // 解析循环体中的语句
}
_currentToken = _lexer.NextToken(); // Consume "}"
return new WhileNode(condition, body).SetTokenInfo(_currentToken);
}
/// <summary>
/// 解析表达式。
/// </summary>
/// <returns></returns>
private ASTNode Expression()
{
ASTNode left = Term();
@@ -555,6 +664,11 @@ namespace Serein.Script
return left;
}
/// <summary>
/// 解析项Term用于处理加减乘除等运算符。
/// </summary>
/// <returns></returns>
private ASTNode Term()
{
ASTNode left = Factor();
@@ -571,6 +685,11 @@ namespace Serein.Script
return left;
}
/// <summary>
/// 解析因子Factor用于处理基本的字面量、标识符、括号表达式等。
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private ASTNode Factor()
{
#region
@@ -588,8 +707,9 @@ namespace Serein.Script
if (_currentToken.Type == TokenType.String)
{
var text = _currentToken.Value;
var node = new StringNode(text).SetTokenInfo(_currentToken);
_currentToken = _lexer.NextToken(); // 消耗字符串
return new StringNode(text).SetTokenInfo(_currentToken);
return node;
}
if (_currentToken.Type == TokenType.Char)
{
@@ -599,6 +719,7 @@ namespace Serein.Script
}
if( _currentToken.Type == TokenType.InterpolatedString)
{
// 暂未实现插值字符串
// 可能是插值字符串;
// let context = $"a{A}b{B}c";
// let context = "a" + A + "b" + B + c;
@@ -607,11 +728,32 @@ namespace Serein.Script
}
}
if (_currentToken.Type == TokenType.Number)
if (_currentToken.Type == TokenType.NumberInt)
{
var value = int.Parse(_currentToken.Value);
_currentToken = _lexer.NextToken(); // 消耗 int 整型
return new NumberIntNode(value).SetTokenInfo(_currentToken);
}
if (_currentToken.Type == TokenType.NumberLong)
{
var value = long.Parse(_currentToken.Value);
_currentToken = _lexer.NextToken(); // 消耗
return new NumberLongNode(value).SetTokenInfo(_currentToken);
}
if (_currentToken.Type == TokenType.NumberFloat)
{
var value = float.Parse(_currentToken.Value);
_currentToken = _lexer.NextToken(); // 消耗数字
return new NumberNode(value).SetTokenInfo(_currentToken);
return new NumberFloatNode(value).SetTokenInfo(_currentToken);
}
if (_currentToken.Type == TokenType.NumberDouble)
{
var value = float.Parse(_currentToken.Value);
_currentToken = _lexer.NextToken(); // 消耗数字
return new NumberDoubleNode(value).SetTokenInfo(_currentToken);
}
#endregion
@@ -629,6 +771,7 @@ namespace Serein.Script
// 创建对象
if (_currentToken.Type == TokenType.Keyword && _currentToken.Value == "new")
{
// 可能是对象实例化
return ParseObjectInstantiation();
}
@@ -640,13 +783,15 @@ namespace Serein.Script
// 该标识符是方法调用
if (_identifierPeekToken.Type == TokenType.ParenthesisLeft)
{
// 可能是函数调用
return ParseFunctionCall();
}
// 需要从该标识符调用另一个标识符
if (_identifierPeekToken.Type == TokenType.Dot)
{
return ParseMemberAccessOrAssignment();
// 可能是成员访问或成员赋值
return ParseMemberAccessOrAssignment(); // 二元操作中获取对象成员
}

View File

@@ -0,0 +1,173 @@
using Serein.Library.Utils;
using Serein.Script.Node;
using Serein.Script.Symbol;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Serein.Script
{
public class SereinScriptTypeAnalysis
{
private Dictionary<string, SymbolInfo> SymbolInfos = new Dictionary<string, SymbolInfo>();
public SereinScriptTypeAnalysis(ProgramNode programNode)
{
SymbolInfos.Clear(); // 清空符号表
foreach (ASTNode astNode in programNode.Statements)
{
var type = Trace(astNode);
if (type is null) continue;
var info = Analyse(astNode, type);
if(info != null)
{
SymbolInfos[info.Name] = info;
}
/*if(astNode is AssignmentNode assignmentNode)
{
var name = assignmentNode.Variable;
var node = assignmentNode.Value;
var type = Analyse(node);
if(type is null)
{
continue;
}
var symbolInfo = new SymbolInfo
{
Type = type,
Node = node,
Name = name,
};
SymbolInfos[name] = symbolInfo;
}*/
}
}
/// <summary>
/// 追踪类型
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
private Type Trace(ASTNode node)
{
if (node == null)
{
return null;
}
switch (node)
{
case NullNode nullNode: // 返回null
return typeof(object);
case BooleanNode booleanNode: // 返回布尔
return typeof(bool);
case NumberIntNode numberNode: // 数值
return typeof(int);
case StringNode stringNode: // 字符串
return typeof(string);
case CharNode charNode: // char
return typeof(char);
case IdentifierNode identifierNode: // 定义变量
return typeof(object);
case AssignmentNode assignmentNode: // 赋值行为
var type = Trace(assignmentNode.Value);
return type;
//throw new SereinSciptException(identifierNode, "尝试使用值为null的变量");
//throw new SereinSciptException(identifierNode, "尝试使用未声明的变量");
case BinaryOperationNode binOpNode: // 递归计算二元操作
var leftType = Trace(binOpNode.Left);
var op = binOpNode.Operator;
var rightType = Trace(binOpNode.Right);
var resultType = BinaryOperationEvaluator.EvaluateType(leftType, op, rightType);
return resultType;
case ClassTypeDefinitionNode classTypeDefinitionNode:
var definitionType = DynamicObjectHelper.CreateTypeWithProperties(classTypeDefinitionNode.Fields, classTypeDefinitionNode.ClassName, true);
return definitionType;
case ObjectInstantiationNode objectInstantiationNode: // 创建对象
var typeName = objectInstantiationNode.TypeName;
var objectType = Type.GetType(typeName);
objectType ??= DynamicObjectHelper.GetCacheType(typeName);
return objectType;
case FunctionCallNode callNode: // 调用方法
return null;
case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用
return null;
case MemberAccessNode memberAccessNode: // 对象成员访问
var memberType = memberAccessNode.MemberName;
return null;
case CollectionIndexNode collectionIndexNode:
case ReturnNode returnNode: // 返回内容
default:
break;
//throw new SereinSciptException(node, $"解释器 EvaluateAsync() 未实现{node}节点行为");
}
return null;
}
private SymbolInfo Analyse(ASTNode node, Type type)
{
if (node == null)
{
return null;
}
switch (node)
{
case IdentifierNode identifierNode: // 定义变量
return new SymbolInfo
{
Name = identifierNode.Name,
Node = node,
Type = type,
};
case AssignmentNode assignmentNode: // 赋值行为
return new SymbolInfo
{
Name = assignmentNode.Variable,
Node = node,
Type = type,
};
case BinaryOperationNode binOpNode: // 递归计算二元操作
//case ClassTypeDefinitionNode classTypeDefinitionNode
case ObjectInstantiationNode objectInstantiationNode: // 创建对象
case FunctionCallNode callNode: // 调用方法
case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用
case MemberAccessNode memberAccessNode: // 对象成员访问
case CollectionIndexNode collectionIndexNode:
case ReturnNode returnNode: // 返回内容
default:
break;
//throw new SereinSciptException(node, $"解释器 EvaluateAsync() 未实现{node}节点行为");
}
return null;
}
/*
case NullNode nullNode: // 返回null
case BooleanNode booleanNode: // 返回布尔
case NumberIntNode numberNode: // 数值
case StringNode stringNode: // 字符串
case CharNode charNode: // char
case IdentifierNode identifierNode: // 定义变量
case AssignmentNode assignmentNode: // 赋值行为
case BinaryOperationNode binOpNode: // 递归计算二元操作
case ObjectInstantiationNode objectInstantiationNode: // 创建对象
case FunctionCallNode callNode: // 调用方法
case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用
case MemberAccessNode memberAccessNode: // 对象成员访问
case CollectionIndexNode collectionIndexNode:
case ReturnNode returnNode: // 返回内容
default:
break;
*/
}
}

View File

@@ -0,0 +1,37 @@
using Serein.Script.Node;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Script.Symbol
{
public enum SymbolType
{
Identifier,
FunctionReturn,
}
/// <summary>
/// 符号信息
/// </summary>
internal class SymbolInfo
{
/// <summary>
/// 符号名称
/// </summary>
public string Name;
/// <summary>
/// 对应类型
/// </summary>
public Type Type;
/// <summary>
/// 节点
/// </summary>
public ASTNode Node;
}
}