mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-03 00:00:49 +08:00
优化了脚本生成AST时的代码提示,增加了脚本运行时错误提示。
This commit is contained in:
163
Serein.Script/BinaryOperationEvaluator.cs
Normal file
163
Serein.Script/BinaryOperationEvaluator.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script
|
||||
{
|
||||
public static class BinaryOperationEvaluator
|
||||
{
|
||||
public static object EvaluateValue(object left, string op, object right)
|
||||
{
|
||||
if (op == null) throw new ArgumentNullException(nameof(op));
|
||||
|
||||
// 特判字符串拼接
|
||||
if (op == "+" && (left is string || right is string))
|
||||
{
|
||||
return (left?.ToString() ?? "") + (right?.ToString() ?? "");
|
||||
}
|
||||
|
||||
// 支持 null 运算(可按需扩展)
|
||||
if (left == null || right == null)
|
||||
{
|
||||
throw new InvalidOperationException($"无法对 null 执行操作:{op}");
|
||||
}
|
||||
|
||||
// 尝试统一类型(浮点优先)
|
||||
var leftType = left.GetType();
|
||||
var rightType = right.GetType();
|
||||
|
||||
Type resultType = GetWiderType(leftType, rightType);
|
||||
dynamic leftValue = Convert.ChangeType(left, resultType);
|
||||
dynamic rightValue = Convert.ChangeType(right, resultType);
|
||||
|
||||
return op switch
|
||||
{
|
||||
"+" => leftValue + rightValue,
|
||||
"-" => leftValue - rightValue,
|
||||
"*" => leftValue * rightValue,
|
||||
"/" => rightValue == 0 ? throw new DivideByZeroException() : leftValue / rightValue,
|
||||
|
||||
">" => leftValue > rightValue,
|
||||
"<" => leftValue < rightValue,
|
||||
">=" => leftValue >= rightValue,
|
||||
"<=" => leftValue <= rightValue,
|
||||
"==" => Equals(leftValue, rightValue),
|
||||
"!=" => !Equals(leftValue, rightValue),
|
||||
|
||||
_ => throw new NotImplementedException($"未实现的操作符: {op}")
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 推导两个类型的“最通用类型”(类型提升)
|
||||
/// </summary>
|
||||
private static Type GetWiderType(Type t1, Type t2)
|
||||
{
|
||||
if (t1 == typeof(string) || t2 == typeof(string)) return typeof(string);
|
||||
if (t1 == typeof(decimal) || t2 == typeof(decimal)) return typeof(decimal);
|
||||
if (t1 == typeof(double) || t2 == typeof(double)) return typeof(double);
|
||||
if (t1 == typeof(float) || t2 == typeof(float)) return typeof(float);
|
||||
if (t1 == typeof(ulong) || t2 == typeof(ulong)) return typeof(ulong);
|
||||
if (t1 == typeof(long) || t2 == typeof(long)) return typeof(long);
|
||||
if (t1 == typeof(uint) || t2 == typeof(uint)) return typeof(uint);
|
||||
if (t1 == typeof(int) || t2 == typeof(int)) return typeof(int);
|
||||
|
||||
// fallback
|
||||
return typeof(object);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static Type EvaluateType(Type leftType, string op, Type rightType)
|
||||
{
|
||||
if (leftType == null || rightType == null)
|
||||
throw new ArgumentNullException("操作数类型不能为 null");
|
||||
|
||||
// 字符串拼接
|
||||
if (op == "+" && (leftType == typeof(string) || rightType == typeof(string)))
|
||||
return typeof(string);
|
||||
|
||||
// 比较操作总是返回 bool
|
||||
if (op is ">" or "<" or ">=" or "<=" or "==" or "!=")
|
||||
return typeof(bool);
|
||||
|
||||
// 数值类型推导
|
||||
if (IsNumeric(leftType) && IsNumeric(rightType))
|
||||
{
|
||||
return PromoteNumericType(leftType, rightType);
|
||||
}
|
||||
|
||||
// 逻辑操作
|
||||
if (op is "&&" or "||")
|
||||
{
|
||||
if (leftType == typeof(bool) && rightType == typeof(bool))
|
||||
return typeof(bool);
|
||||
throw new InvalidOperationException($"逻辑操作 '{op}' 仅适用于 bool 类型");
|
||||
}
|
||||
|
||||
throw new NotImplementedException($"不支持操作符 '{op}' 对 {leftType.Name} 和 {rightType.Name} 进行类型推导");
|
||||
}
|
||||
|
||||
private static bool IsNumeric(Type type)
|
||||
{
|
||||
return type == typeof(byte) || type == typeof(sbyte) ||
|
||||
type == typeof(short) || type == typeof(ushort) ||
|
||||
type == typeof(int) || type == typeof(uint) ||
|
||||
type == typeof(long) || type == typeof(ulong) ||
|
||||
type == typeof(float) || type == typeof(double) ||
|
||||
type == typeof(decimal);
|
||||
}
|
||||
|
||||
// 提升到更高精度类型
|
||||
private static Type[] types = new[]
|
||||
{
|
||||
typeof(decimal),
|
||||
typeof(double),
|
||||
typeof(float),
|
||||
typeof(ulong),
|
||||
typeof(long),
|
||||
typeof(uint),
|
||||
typeof(int),
|
||||
typeof(ushort),
|
||||
typeof(short),
|
||||
typeof(byte),
|
||||
typeof(sbyte)
|
||||
};
|
||||
|
||||
private static Type PromoteNumericType(Type left, Type right)
|
||||
{
|
||||
var ranks = types;
|
||||
foreach (var type in ranks)
|
||||
{
|
||||
if (type == left || type == right)
|
||||
return type;
|
||||
}
|
||||
|
||||
return typeof(object); // fallback
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,14 @@ namespace Serein.Script.Node
|
||||
/// </summary>
|
||||
public class MemberAccessNode : ASTNode
|
||||
{
|
||||
/// <summary>
|
||||
/// 对象token
|
||||
/// </summary>
|
||||
public ASTNode Object { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 成员名称
|
||||
/// </summary>
|
||||
public string MemberName { get; }
|
||||
|
||||
public MemberAccessNode(ASTNode obj, string memberName)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
90
Serein.Script/Node/NumberIntNode.cs
Normal file
90
Serein.Script/Node/NumberIntNode.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
/// <summary>
|
||||
/// 数值型节点
|
||||
/// </summary>
|
||||
public abstract class NumberNode<T> : ASTNode where T : struct, IComparable<T>
|
||||
{
|
||||
public T Value { get; }
|
||||
public NumberNode(T value) => Value = value;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// int 整数型字面量
|
||||
/// </summary>
|
||||
public class NumberIntNode(int vlaue) : NumberNode<int>(vlaue)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// int 整数型字面量
|
||||
/// </summary>
|
||||
public class NumberLongNode(long vlaue) : NumberNode<long>(vlaue)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// int 整数型字面量
|
||||
/// </summary>
|
||||
public class NumberFloatNode(float vlaue) : NumberNode<float>(vlaue)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// int 整数型字面量
|
||||
/// </summary>
|
||||
public class NumberDoubleNode(double vlaue) : NumberNode<double>(vlaue)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*/// <summary>
|
||||
/// int 整数型字面量
|
||||
/// </summary>
|
||||
public class NumberIntNode : ASTNode
|
||||
{
|
||||
public int Value { get; }
|
||||
public NumberIntNode(int value) => Value = value;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// int 整数型字面量
|
||||
/// </summary>
|
||||
public class NumberLongNode : ASTNode
|
||||
{
|
||||
public long Value { get; }
|
||||
public NumberLongNode(long value) => Value = value;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// int 整数型字面量
|
||||
/// </summary>
|
||||
public class NumberFloatNode : ASTNode
|
||||
{
|
||||
public float Value { get; }
|
||||
public NumberFloatNode(float value) => Value = value;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// int 整数型字面量
|
||||
/// </summary>
|
||||
public class NumberDoubleNode : ASTNode
|
||||
{
|
||||
public double Value { get; }
|
||||
public NumberDoubleNode(double value) => Value = value;
|
||||
}*/
|
||||
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Node
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 整数型字面量
|
||||
/// </summary>
|
||||
public class NumberNode : ASTNode
|
||||
{
|
||||
public int Value { get; }
|
||||
public NumberNode(int value) => Value = value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -30,6 +30,9 @@ namespace Serein.Script
|
||||
/// </summary>
|
||||
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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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(); // 二元操作中获取对象成员
|
||||
}
|
||||
|
||||
|
||||
|
||||
173
Serein.Script/SereinScriptTypeAnalysis.cs
Normal file
173
Serein.Script/SereinScriptTypeAnalysis.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Script.Node;
|
||||
using Serein.Script.Symbol;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Serein.Script
|
||||
{
|
||||
public class SereinScriptTypeAnalysis
|
||||
{
|
||||
private Dictionary<string, SymbolInfo> SymbolInfos = new Dictionary<string, SymbolInfo>();
|
||||
|
||||
public SereinScriptTypeAnalysis(ProgramNode programNode)
|
||||
{
|
||||
SymbolInfos.Clear(); // 清空符号表
|
||||
foreach (ASTNode astNode in programNode.Statements)
|
||||
{
|
||||
var type = Trace(astNode);
|
||||
if (type is null) continue;
|
||||
var info = Analyse(astNode, type);
|
||||
if(info != null)
|
||||
{
|
||||
SymbolInfos[info.Name] = info;
|
||||
}
|
||||
/*if(astNode is AssignmentNode assignmentNode)
|
||||
{
|
||||
var name = assignmentNode.Variable;
|
||||
var node = assignmentNode.Value;
|
||||
var type = Analyse(node);
|
||||
if(type is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var symbolInfo = new SymbolInfo
|
||||
{
|
||||
Type = type,
|
||||
Node = node,
|
||||
Name = name,
|
||||
};
|
||||
SymbolInfos[name] = symbolInfo;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 追踪类型
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
/// <returns></returns>
|
||||
private Type Trace(ASTNode node)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
switch (node)
|
||||
{
|
||||
case NullNode nullNode: // 返回null
|
||||
return typeof(object);
|
||||
case BooleanNode booleanNode: // 返回布尔
|
||||
return typeof(bool);
|
||||
case NumberIntNode numberNode: // 数值
|
||||
return typeof(int);
|
||||
case StringNode stringNode: // 字符串
|
||||
return typeof(string);
|
||||
case CharNode charNode: // char
|
||||
return typeof(char);
|
||||
case IdentifierNode identifierNode: // 定义变量
|
||||
return typeof(object);
|
||||
case AssignmentNode assignmentNode: // 赋值行为
|
||||
var type = Trace(assignmentNode.Value);
|
||||
return type;
|
||||
//throw new SereinSciptException(identifierNode, "尝试使用值为null的变量");
|
||||
//throw new SereinSciptException(identifierNode, "尝试使用未声明的变量");
|
||||
case BinaryOperationNode binOpNode: // 递归计算二元操作
|
||||
var leftType = Trace(binOpNode.Left);
|
||||
var op = binOpNode.Operator;
|
||||
var rightType = Trace(binOpNode.Right);
|
||||
var resultType = BinaryOperationEvaluator.EvaluateType(leftType, op, rightType);
|
||||
return resultType;
|
||||
case ClassTypeDefinitionNode classTypeDefinitionNode:
|
||||
var definitionType = DynamicObjectHelper.CreateTypeWithProperties(classTypeDefinitionNode.Fields, classTypeDefinitionNode.ClassName, true);
|
||||
return definitionType;
|
||||
case ObjectInstantiationNode objectInstantiationNode: // 创建对象
|
||||
|
||||
var typeName = objectInstantiationNode.TypeName;
|
||||
var objectType = Type.GetType(typeName);
|
||||
objectType ??= DynamicObjectHelper.GetCacheType(typeName);
|
||||
return objectType;
|
||||
case FunctionCallNode callNode: // 调用方法
|
||||
return null;
|
||||
case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用
|
||||
return null;
|
||||
case MemberAccessNode memberAccessNode: // 对象成员访问
|
||||
var memberType = memberAccessNode.MemberName;
|
||||
return null;
|
||||
case CollectionIndexNode collectionIndexNode:
|
||||
case ReturnNode returnNode: // 返回内容
|
||||
|
||||
default:
|
||||
break;
|
||||
//throw new SereinSciptException(node, $"解释器 EvaluateAsync() 未实现{node}节点行为");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private SymbolInfo Analyse(ASTNode node, Type type)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
switch (node)
|
||||
{
|
||||
case IdentifierNode identifierNode: // 定义变量
|
||||
return new SymbolInfo
|
||||
{
|
||||
Name = identifierNode.Name,
|
||||
Node = node,
|
||||
Type = type,
|
||||
};
|
||||
case AssignmentNode assignmentNode: // 赋值行为
|
||||
return new SymbolInfo
|
||||
{
|
||||
Name = assignmentNode.Variable,
|
||||
Node = node,
|
||||
Type = type,
|
||||
};
|
||||
case BinaryOperationNode binOpNode: // 递归计算二元操作
|
||||
//case ClassTypeDefinitionNode classTypeDefinitionNode
|
||||
case ObjectInstantiationNode objectInstantiationNode: // 创建对象
|
||||
case FunctionCallNode callNode: // 调用方法
|
||||
case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用
|
||||
case MemberAccessNode memberAccessNode: // 对象成员访问
|
||||
case CollectionIndexNode collectionIndexNode:
|
||||
case ReturnNode returnNode: // 返回内容
|
||||
default:
|
||||
break;
|
||||
//throw new SereinSciptException(node, $"解释器 EvaluateAsync() 未实现{node}节点行为");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
case NullNode nullNode: // 返回null
|
||||
case BooleanNode booleanNode: // 返回布尔
|
||||
case NumberIntNode numberNode: // 数值
|
||||
case StringNode stringNode: // 字符串
|
||||
case CharNode charNode: // char
|
||||
case IdentifierNode identifierNode: // 定义变量
|
||||
case AssignmentNode assignmentNode: // 赋值行为
|
||||
case BinaryOperationNode binOpNode: // 递归计算二元操作
|
||||
case ObjectInstantiationNode objectInstantiationNode: // 创建对象
|
||||
case FunctionCallNode callNode: // 调用方法
|
||||
case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用
|
||||
case MemberAccessNode memberAccessNode: // 对象成员访问
|
||||
case CollectionIndexNode collectionIndexNode:
|
||||
case ReturnNode returnNode: // 返回内容
|
||||
default:
|
||||
break;
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
37
Serein.Script/Symbol/SymbolInfo.cs
Normal file
37
Serein.Script/Symbol/SymbolInfo.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Serein.Script.Node;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Script.Symbol
|
||||
{
|
||||
public enum SymbolType
|
||||
{
|
||||
Identifier,
|
||||
FunctionReturn,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 符号信息
|
||||
/// </summary>
|
||||
internal class SymbolInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 符号名称
|
||||
/// </summary>
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// 对应类型
|
||||
/// </summary>
|
||||
public Type Type;
|
||||
|
||||
/// <summary>
|
||||
/// 节点
|
||||
/// </summary>
|
||||
public ASTNode Node;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user