EP RScript 完成控制流, 基本完成

This commit is contained in:
2025-10-10 11:04:43 +08:00
parent f8d81d9198
commit c07c64be1e
4 changed files with 145 additions and 104 deletions

View File

@@ -3,9 +3,9 @@
namespace Convention.RScript namespace Convention.RScript
{ {
[Serializable] [Serializable]
public class RScriptExceptionException : Exception public class RScriptException : Exception
{ {
public RScriptExceptionException(string message, int runtimePointer) : base($"when running {runtimePointer}, {message}") { } public RScriptException(string message, int runtimePointer) : base($"when running {runtimePointer}, {message}") { }
public RScriptExceptionException(string message, int runtimePointer, Exception inner) : base($"when running {runtimePointer}, {message}", inner) { } public RScriptException(string message, int runtimePointer, Exception inner) : base($"when running {runtimePointer}, {message}", inner) { }
} }
} }

View File

@@ -31,20 +31,18 @@ namespace Convention.RScript
/// </summary> /// </summary>
ExitNamespace, ExitNamespace,
/// <summary> /// <summary>
/// 标签, 格式: label: 标签名 /// 标签, 格式: label(labelname)
/// </summary> /// </summary>
Label, Label,
/// <summary> /// <summary>
/// 跳转到指定标签, 格式: goto 标签名 /// 跳转到指定标签, 格式: goto(a,b,labelname)
/// <para>当a大于b时跳转到labelname</para>
/// </summary> /// </summary>
Goto, Goto,
/// <summary>
/// 条件判断, 格式: if (条件表达式)
/// </summary>
If
} }
public string content; public string content;
public List<string> info;
public Mode mode; public Mode mode;
} }
@@ -74,15 +72,16 @@ namespace Convention.RScript
result.mode = RScriptSentence.Mode.ExitNamespace; result.mode = RScriptSentence.Mode.ExitNamespace;
} }
Regex DefineVariableRegex = new(@"^(string|int|double|float|bool|var) [a-zA-Z_][a-zA-Z0-9_]*$"); Regex DefineVariableRegex = new(@"^(string|int|double|float|bool|var)\s+([a-zA-Z_][a-zA-Z0-9_]*)$");
var DefineVariableMatch = DefineVariableRegex.Match(expression); var DefineVariableMatch = DefineVariableRegex.Match(expression);
if (DefineVariableMatch.Success) if (DefineVariableMatch.Success)
{ {
result.mode = RScriptSentence.Mode.DefineVariable; result.mode = RScriptSentence.Mode.DefineVariable;
result.info = new() { DefineVariableMatch.Groups[1].Value, DefineVariableMatch.Groups[2].Value };
return result; return result;
} }
Regex LabelRegex = new(@"^label:\s*([a-zA-Z_][a-zA-Z0-9_]*)$"); Regex LabelRegex = new(@"^label\s*\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)$");
var LabelMatch = LabelRegex.Match(expression); var LabelMatch = LabelRegex.Match(expression);
if (LabelMatch.Success) if (LabelMatch.Success)
{ {
@@ -91,21 +90,13 @@ namespace Convention.RScript
return result; return result;
} }
Regex GotoRegex = new(@"^goto\s+([a-zA-Z_][a-zA-Z0-9_]*)$"); Regex GotoRegex = new(@"^goto\s*\(\s*(.+)\s*,\s*(.*)\s*,([a-zA-Z_][a-zA-Z0-9_]*)\s*\)$");
var GotoMatch = GotoRegex.Match(expression); var GotoMatch = GotoRegex.Match(expression);
if (GotoMatch.Success) if (GotoMatch.Success)
{ {
result.mode = RScriptSentence.Mode.Goto; result.mode = RScriptSentence.Mode.Goto;
result.content = GotoMatch.Groups[1].Value; result.content = GotoMatch.Groups[3].Value;
return result; result.info = new() { GotoMatch.Groups[1].Value, GotoMatch.Groups[2].Value, GotoMatch.Groups[3].Value };
}
Regex IfRegex = new(@"^if\s*\((.*)\)$");
var IfMatch = IfRegex.Match(expression);
if (IfMatch.Success)
{
result.mode = RScriptSentence.Mode.If;
result.content = IfMatch.Groups[1].Value;
return result; return result;
} }
@@ -127,14 +118,14 @@ namespace Convention.RScript
else if (Sentences[i].mode == RScriptSentence.Mode.ExitNamespace) else if (Sentences[i].mode == RScriptSentence.Mode.ExitNamespace)
{ {
if (namespaceLayers.Count == 0) if (namespaceLayers.Count == 0)
throw new RScriptExceptionException("Namespace exit without enter.", i); throw new RScriptException("Namespace exit without enter.", i);
var enterPointer = namespaceLayers.Pop(); var enterPointer = namespaceLayers.Pop();
namespaceIndicator[enterPointer] = i; namespaceIndicator[enterPointer] = i;
} }
} }
if (namespaceLayers.Count > 0) if (namespaceLayers.Count > 0)
{ {
throw new RScriptExceptionException("Namespace enter without exit.", namespaceLayers.Peek()); throw new RScriptException("Namespace enter without exit.", namespaceLayers.Peek());
} }
} }
@@ -165,62 +156,62 @@ namespace Convention.RScript
case RScriptSentence.Mode.DefineVariable: case RScriptSentence.Mode.DefineVariable:
{ {
// 定义变量 // 定义变量
var content = sentence.content.Split(' '); var varTypeName = sentence.info[0];
var varName = sentence.info[1];
Type varType; Type varType;
object varDefaultValue; object varDefaultValue;
{ {
if (content[0] == "string") if (varTypeName == "string")
{ {
varType = typeof(string); varType = typeof(string);
varDefaultValue = string.Empty; varDefaultValue = string.Empty;
} }
else if (content[0] == "int") else if (varTypeName == "int")
{ {
varType = typeof(int); varType = typeof(int);
varDefaultValue = 0; varDefaultValue = 0;
} }
else if (content[0] == "double") else if (varTypeName == "double")
{ {
varType = typeof(double); varType = typeof(double);
varDefaultValue = 0.0; varDefaultValue = 0.0;
} }
else if (content[0] == "float") else if (varTypeName == "float")
{ {
varType = typeof(float); varType = typeof(float);
varDefaultValue = 0.0f; varDefaultValue = 0.0f;
} }
else if (content[0] == "bool") else if (varTypeName == "bool")
{ {
varType = typeof(bool); varType = typeof(bool);
varDefaultValue = false; varDefaultValue = false;
} }
else if (content[0] == "var") else if (varTypeName == "var")
{ {
varType = typeof(object); varType = typeof(object);
varDefaultValue = null; varDefaultValue = null;
} }
else else
{ {
throw new RScriptExceptionException($"Unsupported variable type '{content[0]}'.", CurrentRuntimePointer); throw new RScriptException($"Unsupported variable type '{varTypeName}'.", CurrentRuntimePointer);
} }
} }
var varName = content[1]; if (CurrentLocalSpaceVariableNames.Peek().Contains(varName) == false)
if (CurrentLocalSpaceVariableNames.Contains(varName) == false)
{ {
Variables.Add(varName, new() { type = varType, data = varDefaultValue }); Variables.Add(varName, new() { type = varType, data = varDefaultValue });
parser.context.Variables[varName] = varDefaultValue; parser.context.Variables[varName] = varDefaultValue;
CurrentLocalSpaceVariableNames.Add(varName); CurrentLocalSpaceVariableNames.Peek().Add(varName);
} }
else else
{ {
throw new RScriptExceptionException($"Variable '{varName}' already defined on this namespace.", CurrentRuntimePointer); throw new RScriptException($"Variable '{varName}' already defined on this namespace.", CurrentRuntimePointer);
} }
} }
break; break;
case RScriptSentence.Mode.EnterNamespace: case RScriptSentence.Mode.EnterNamespace:
{ {
// 准备记录当前命名空间中定义的变量, 清空上层命名空间的变量 // 准备记录当前命名空间中定义的变量, 清空上层命名空间的变量
CurrentLocalSpaceVariableNames.Clear(); CurrentLocalSpaceVariableNames.Push(new());
// 更新变量值 // 更新变量值
foreach (var (varName, varValue) in parser.context.Variables) foreach (var (varName, varValue) in parser.context.Variables)
{ {
@@ -231,52 +222,34 @@ namespace Convention.RScript
case RScriptSentence.Mode.ExitNamespace: case RScriptSentence.Mode.ExitNamespace:
{ {
// 移除在本命名空间中定义的变量 // 移除在本命名空间中定义的变量
foreach (var local in CurrentLocalSpaceVariableNames) foreach (var local in CurrentLocalSpaceVariableNames.Peek())
{ {
Variables.Remove(local); Variables.Remove(local);
parser.context.Variables.Remove(local); parser.context.Variables.Remove(local);
} }
CurrentLocalSpaceVariableNames.Clear();
// 还原上层命名空间的变量 // 还原上层命名空间的变量
foreach (var local in Variables.Keys) foreach (var local in CurrentLocalSpaceVariableNames.Peek())
{ {
CurrentLocalSpaceVariableNames.Add(local);
parser.context.Variables[local] = Variables[local].data; parser.context.Variables[local] = Variables[local].data;
} }
CurrentLocalSpaceVariableNames.Pop();
} }
break; break;
case RScriptSentence.Mode.Goto: case RScriptSentence.Mode.Goto:
{ {
// 跳转到指定标签 // 检查并跳转到指定标签
if (Labels.TryGetValue(sentence.content, out var labelPointer)) var leftValue = parser.Evaluate<double>(sentence.info[0]);
var rightValue = parser.Evaluate<double>(sentence.info[1]);
if (leftValue > rightValue)
{ {
CurrentRuntimePointer = labelPointer; if (Labels.TryGetValue(sentence.content, out var labelPointer))
}
else
{
throw new RScriptExceptionException($"Label '{sentence.content}' not found.", CurrentRuntimePointer);
}
}
break;
case RScriptSentence.Mode.If:
{
// 条件跳转
var conditionResult = parser.Evaluate(sentence.content);
if (conditionResult is bool b)
{
if (b == false)
{ {
if (Namespace.TryGetValue(CurrentRuntimePointer + 1, out var exitPointer) == false) CurrentRuntimePointer = labelPointer;
{ }
// 没有命名空间时只跳过下一句, +1后在外层循环末尾再+1, 最终结果为下一次循环开始时已经指向第二句 else
exitPointer = CurrentRuntimePointer + 1; {
} throw new RScriptException($"Label '{sentence.content}' not found.", CurrentRuntimePointer);
CurrentRuntimePointer = exitPointer;
} }
}
else
{
throw new RScriptExceptionException($"If condition must be bool, but got {conditionResult?.GetType().ToString() ?? "null"}.", CurrentRuntimePointer);
} }
} }
break; break;
@@ -288,12 +261,14 @@ namespace Convention.RScript
private readonly Stack<int> RuntimePointerStack = new(); private readonly Stack<int> RuntimePointerStack = new();
private int CurrentRuntimePointer = 0; private int CurrentRuntimePointer = 0;
private readonly HashSet<string> CurrentLocalSpaceVariableNames = new(); private readonly Stack<HashSet<string>> CurrentLocalSpaceVariableNames = new();
public Dictionary<string, RScriptVariableEntry> Run(ExpressionParser parser) public Dictionary<string, RScriptVariableEntry> Run(ExpressionParser parser)
{ {
CurrentLocalSpaceVariableNames.Clear(); CurrentLocalSpaceVariableNames.Clear();
RuntimePointerStack.Clear(); RuntimePointerStack.Clear();
CurrentLocalSpaceVariableNames.Clear();
CurrentLocalSpaceVariableNames.Push(new());
for (CurrentRuntimePointer = 0; CurrentRuntimePointer < Sentences.Length; CurrentRuntimePointer++) for (CurrentRuntimePointer = 0; CurrentRuntimePointer < Sentences.Length; CurrentRuntimePointer++)
{ {
RunNextStep(parser); RunNextStep(parser);

View File

@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Convention.RScript namespace Convention.RScript
@@ -12,29 +13,96 @@ namespace Convention.RScript
private ExpressionParser parser; private ExpressionParser parser;
private RScriptContext context; private RScriptContext context;
private IEnumerable<string> SplitScript(string script)
{
StringBuilder builder = new();
List<string> statements = new();
for (int i = 0, e = script.Length; i < e; i++)
{
char c = script[i];
if (c == ';')
{
if (builder.Length > 0)
{
statements.Add(builder.ToString().Trim());
builder.Clear();
}
}
else if (c == '/' && i + 1 < e)
{
// Skip single-line comment
if (script[i + 1] == '/')
{
while (i < script.Length && script[i] != '\n')
i++;
}
// Skip multi-line comment
else if (script[i + 1] == '*')
{
i += 2;
while (i + 1 < script.Length && !(script[i] == '*' && script[i + 1] == '/'))
i++;
i++;
}
else
{
builder.Append(c);
}
}
else if (c == '#')
{
// Skip single-line comment
while (i < script.Length && script[i] != '\n')
i++;
}
else if (c == '\"')
{
for (i++; i < e; i++)
{
builder.Append(script[i]);
if (script[i] == '\"')
{
break;
}
else if (script[i] == '\\')
{
i++;
if (i < e)
builder.Append(script[i]);
else
throw new RScriptException("Invalid escape sequence in string literal", -1);
}
}
}
else if (c == '{' || c == '}')
{
// Treat braces as statement separators
if (builder.Length > 0)
{
statements.Add(builder.ToString().Trim());
builder.Clear();
}
statements.Add(c.ToString());
}
else
{
builder.Append(c);
}
}
if (builder.Length > 0)
{
statements.Add(builder.ToString().Trim());
builder.Clear();
}
return statements.Where(s => !string.IsNullOrWhiteSpace(s));
}
public Dictionary<string, RScriptVariableEntry> Run(string script, RScriptImportClass import = null, RScriptVariables variables = null) public Dictionary<string, RScriptVariableEntry> Run(string script, RScriptImportClass import = null, RScriptVariables variables = null)
{ {
parser = new(new()); parser = new(new());
string newScript = ""; context = new(SplitScript(script).ToArray(), import, variables);
foreach (var item in script.Split('\n'))
{
var line = item.Trim();
if (string.IsNullOrEmpty(line))
continue;
if (line.StartsWith("//"))
continue;
if (line.StartsWith('#'))
continue;
newScript += line + ";"; // 添加分号分隔符
}
// 按分号分割并过滤空语句
var statements = newScript.Split(';', StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim())
.Where(s => !string.IsNullOrEmpty(s))
.ToArray();
context = new(statements, import, variables);
foreach (var type in context.Import) foreach (var type in context.Import)
parser.context.Imports.AddType(type); parser.context.Imports.AddType(type);
return context.Run(parser); return context.Run(parser);

View File

@@ -7,22 +7,20 @@ public class Program
static void Main(string[] args) static void Main(string[] args)
{ {
RScriptEngine engine = new(); RScriptEngine engine = new();
RScriptImportClass import = new(); RScriptImportClass import = new()
import.Add(typeof(Math)); {
typeof(Math)
};
var result = engine.Run(@" var result = engine.Run(@"
double x; double i;
double y; i = 2.0;
double z; {
double result; label(test);
bool stats; i = Pow(i,2.0);
stats = true; goto(100,i,test);
x = 10.0; }
y = 20.0; string result;
z = x + y; result = i.ToString()+i.ToString();
// This is a comment;
# This is another comment;
result = z * 2;
result = Pow(result,2.0);
", import); ", import);
Console.WriteLine($"Script executed successfully. Result: {result["result"].data}"); Console.WriteLine($"Script executed successfully. Result: {result["result"].data}");
} }