From b8a87bae4ce04ee1373bf591b83c97818cb1637e Mon Sep 17 00:00:00 2001
From: ninemine <1371605831@qq.com>
Date: Wed, 15 Oct 2025 16:50:10 +0800
Subject: [PATCH] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E6=96=B0=E5=A2=9E=E5=91=BD?=
=?UTF-8?q?=E5=90=8D=E7=A9=BA=E9=97=B4=E7=9A=84=E5=91=BD=E5=90=8D,=20?=
=?UTF-8?q?=E7=94=A8=E4=BA=8E=E5=85=B7=E6=9C=89=E5=90=8D=E7=A7=B0=E7=9A=84?=
=?UTF-8?q?=E5=9D=97=E6=88=96=E6=98=AF=E5=87=BD=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Matcher/NamespaceMater.cs | 19 ++++-
PublicTypes/RScriptException.cs | 13 ++-
RScriptContext.cs | 32 +++++---
RScriptEngine.cs | 141 +++++++++++++++++++-------------
4 files changed, 130 insertions(+), 75 deletions(-)
diff --git a/Matcher/NamespaceMater.cs b/Matcher/NamespaceMater.cs
index 0059bed..56e2442 100644
--- a/Matcher/NamespaceMater.cs
+++ b/Matcher/NamespaceMater.cs
@@ -1,4 +1,6 @@
-namespace Convention.RScript.Matcher
+using System.Text.RegularExpressions;
+
+namespace Convention.RScript.Matcher
{
public class NamespaceMater : IRSentenceMatcher
{
@@ -14,6 +16,21 @@
sentence.mode = RScriptSentence.Mode.ExitNamespace;
return true;
}
+ else if (expression.StartsWith("namespace"))
+ {
+ sentence.mode = RScriptSentence.Mode.NamedSpace;
+ Regex regex = new(@"namespace\s*\(([a-zA-Z_][a-zA-Z0-9_]*)\)");
+ var match = regex.Match(expression);
+ if (match.Success)
+ {
+ sentence.content = match.Groups[1].Value;
+ return true;
+ }
+ else
+ {
+ throw new RScriptRuntimeException("Invalid namespace declaration", -1);
+ }
+ }
return false;
}
}
diff --git a/PublicTypes/RScriptException.cs b/PublicTypes/RScriptException.cs
index 35422f0..b925d9a 100644
--- a/PublicTypes/RScriptException.cs
+++ b/PublicTypes/RScriptException.cs
@@ -3,9 +3,16 @@
namespace Convention.RScript
{
[Serializable]
- public class RScriptException : Exception
+ public class RScriptRuntimeException : Exception
{
- public RScriptException(string message, int runtimePointer) : base($"when running {runtimePointer}, {message}") { }
- public RScriptException(string message, int runtimePointer, Exception inner) : base($"when running {runtimePointer}, {message}", inner) { }
+ public RScriptRuntimeException(string message, int runtimePointer) : base($"when running {runtimePointer}, {message}") { }
+ public RScriptRuntimeException(string message, int runtimePointer, Exception inner) : base($"when running {runtimePointer}, {message}", inner) { }
}
+
+ [Serializable]
+ public class RScriptCompileException : Exception
+ {
+ public RScriptCompileException(string message, int line, int chIndex) : base($"when compile on line {line} char {chIndex}, {message}") { }
+ public RScriptCompileException(string message, int line, int chIndex, Exception inner) : base($"when compile on line {line} char {chIndex}, {message}", inner) { }
+ }
}
diff --git a/RScriptContext.cs b/RScriptContext.cs
index ea0bf44..00ec14b 100644
--- a/RScriptContext.cs
+++ b/RScriptContext.cs
@@ -32,11 +32,11 @@ namespace Convention.RScript
///
ExitNamespace,
///
- /// 标签, 格式: label(labelname)
+ /// 标签, 格式: label(labelname);
///
Label,
///
- /// 跳转到指定标签, 格式: goto(boolean,labelname)
+ /// 跳转到指定标签, 格式: goto(boolean,labelname);
/// 判断为真时跳转到labelname
///
Goto,
@@ -48,6 +48,10 @@ namespace Convention.RScript
/// 跳转到上次跳转的位置的后一个位置, 格式: back(boolean);
///
Backpoint,
+ ///
+ /// 命名空间命名, 格式: namespace(labelname){}
+ ///
+ NamedSpace,
}
public string content;
@@ -66,7 +70,8 @@ namespace Convention.RScript
public readonly RScriptVariables Variables;
private readonly RScriptSentence[] Sentences;
private readonly Dictionary Labels;
- private readonly Dictionary Namespace;
+ private readonly Dictionary NamespaceLayer;
+ private readonly Dictionary NamespaceLabels;
public List SentenceParser = new()
{
@@ -86,8 +91,8 @@ namespace Convention.RScript
mode = RScriptSentence.Mode.Expression
};
expression = expression.Trim();
- expression.TrimEnd(';');
- SentenceParser.Any(matcher => matcher.Match(expression, ref result));
+ expression = expression.TrimEnd(';');
+ var _ = SentenceParser.Any(matcher => matcher.Match(expression, ref result));
return result;
}
@@ -107,14 +112,14 @@ namespace Convention.RScript
else if (Sentences[i].mode == RScriptSentence.Mode.ExitNamespace)
{
if (namespaceLayers.Count == 0)
- throw new RScriptException("Namespace exit without enter.", i);
+ throw new RScriptRuntimeException("Namespace exit without enter.", i);
var enterPointer = namespaceLayers.Pop();
namespaceIndicator[enterPointer] = i;
}
}
if (namespaceLayers.Count > 0)
{
- throw new RScriptException("Namespace enter without exit.", namespaceLayers.Peek());
+ throw new RScriptRuntimeException("Namespace enter without exit.", namespaceLayers.Peek());
}
}
@@ -124,8 +129,9 @@ namespace Convention.RScript
this.Variables = variables ?? new();
this.Sentences = (from item in expressions select ParseToSentence(item)).ToArray();
this.Labels = new();
- this.Namespace = new();
- BuildUpLabelsAndNamespace(ref this.Labels, ref this.Namespace);
+ this.NamespaceLayer = new();
+ this.NamespaceLabels = new();
+ BuildUpLabelsAndNamespace(ref this.Labels, ref this.NamespaceLayer);
}
@@ -171,7 +177,7 @@ namespace Convention.RScript
}
else
{
- throw new RScriptException($"Unsupported variable type '{varTypeName}'.", CurrentRuntimePointer);
+ throw new RScriptRuntimeException($"Unsupported variable type '{varTypeName}'.", CurrentRuntimePointer);
}
}
if (CurrentLocalSpaceVariableNames.Peek().Contains(varName) == false)
@@ -182,7 +188,7 @@ namespace Convention.RScript
}
else
{
- throw new RScriptException($"Variable '{varName}' already defined on this namespace.", CurrentRuntimePointer);
+ throw new RScriptRuntimeException($"Variable '{varName}' already defined on this namespace.", CurrentRuntimePointer);
}
}
@@ -252,7 +258,7 @@ namespace Convention.RScript
}
else
{
- throw new RScriptException($"Label '{sentence.content}' not found.", CurrentRuntimePointer);
+ throw new RScriptRuntimeException($"Label '{sentence.content}' not found.", CurrentRuntimePointer);
}
}
}
@@ -266,7 +272,7 @@ namespace Convention.RScript
{
CurrentRuntimePointer = Sentences.Length;
}
- else if (Namespace.TryGetValue(RuntimePointerStack.Peek(), out var exitPointer))
+ else if (NamespaceLayer.TryGetValue(RuntimePointerStack.Peek(), out var exitPointer))
{
CurrentRuntimePointer = exitPointer;
DoExitNamespace(parser);
diff --git a/RScriptEngine.cs b/RScriptEngine.cs
index 3862704..f3482be 100644
--- a/RScriptEngine.cs
+++ b/RScriptEngine.cs
@@ -19,82 +19,107 @@ namespace Convention.RScript
StringBuilder builder = new();
List statements = new();
- for (int i = 0, e = script.Length; i < e; i++)
+ void PushBuilder()
{
- char c = script[i];
- if (c == ';')
+ if (builder.Length > 0)
{
- if (builder.Length > 0)
- {
- statements.Add(builder.ToString().Trim());
- builder.Clear();
- }
+ statements.Add(builder.ToString().Trim());
+ builder.Clear();
}
- else if (c == '/' && i + 1 < e)
+ }
+
+ var lines = script.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None);
+ for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++)
+ {
+ string line = lines[lineIndex];
+ for (int i = 0, e = line.Length; i < e; i++)
{
- // Skip single-line comment
- if (script[i + 1] == '/')
+ char c = line[i];
+ if (c == ';')
{
+ PushBuilder();
+ }
+ 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++;
}
- // Skip multi-line comment
- else if (script[i + 1] == '*')
+ else if (c == '\"')
{
- i += 2;
- while (i + 1 < script.Length && !(script[i] == '*' && script[i + 1] == '/'))
- i++;
- i++;
+ 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 RScriptCompileException("Invalid escape sequence in string literal", lineIndex, i);
+ }
+ }
+ }
+ else if (c == '{' || c == '}')
+ {
+ PushBuilder();
+ statements.Add(c.ToString());
+ }
+ else if (string.Compare("namespace", 0, script, i, "namespace".Length) == 0)
+ {
+ builder.Append("namespace");
+ i += "namespace".Length;
+ if (i >= e)
+ throw new RScriptCompileException("Invalid namespace declaration", lineIndex, i);
+ Regex regex = new(@"^\s*\([a-zA-Z_][a-zA-Z0-9_]*\)");
+ var match = regex.Match(script, i);
+ if (match.Success)
+ {
+ builder.Append(match.Value);
+ PushBuilder();
+ i += match.Length;
+ }
+ else
+ {
+ throw new RScriptCompileException("Invalid namespace declaration", lineIndex, i);
+ }
+ // 前面的操作中已经完成位移, 此处是抵消循环中的i++
+ i -= 1;
}
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();
+ PushBuilder();
}
return statements.Where(s => !string.IsNullOrWhiteSpace(s));