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));