EP RScript 完成控制流, 基本完成
This commit is contained in:
@@ -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) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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,53 +222,35 @@ 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:
|
||||||
{
|
{
|
||||||
// 跳转到指定标签
|
// 检查并跳转到指定标签
|
||||||
|
var leftValue = parser.Evaluate<double>(sentence.info[0]);
|
||||||
|
var rightValue = parser.Evaluate<double>(sentence.info[1]);
|
||||||
|
if (leftValue > rightValue)
|
||||||
|
{
|
||||||
if (Labels.TryGetValue(sentence.content, out var labelPointer))
|
if (Labels.TryGetValue(sentence.content, out var labelPointer))
|
||||||
{
|
{
|
||||||
CurrentRuntimePointer = labelPointer;
|
CurrentRuntimePointer = labelPointer;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new RScriptExceptionException($"Label '{sentence.content}' not found.", CurrentRuntimePointer);
|
throw new RScriptException($"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)
|
|
||||||
{
|
|
||||||
// 没有命名空间时只跳过下一句, +1后在外层循环末尾再+1, 最终结果为下一次循环开始时已经指向第二句
|
|
||||||
exitPointer = CurrentRuntimePointer + 1;
|
|
||||||
}
|
|
||||||
CurrentRuntimePointer = exitPointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new RScriptExceptionException($"If condition must be bool, but got {conditionResult?.GetType().ToString() ?? "null"}.", CurrentRuntimePointer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -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);
|
||||||
|
@@ -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);
|
||||||
|
@@ -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}");
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user