Compare commits
3 Commits
e3589cf1ec
...
b8a87bae4c
Author | SHA1 | Date | |
---|---|---|---|
b8a87bae4c | |||
4f358c9664 | |||
90ae999ba2 |
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.meta
|
@@ -1,4 +1,6 @@
|
|||||||
namespace Convention.RScript.Matcher
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Convention.RScript.Matcher
|
||||||
{
|
{
|
||||||
public class NamespaceMater : IRSentenceMatcher
|
public class NamespaceMater : IRSentenceMatcher
|
||||||
{
|
{
|
||||||
@@ -14,6 +16,21 @@
|
|||||||
sentence.mode = RScriptSentence.Mode.ExitNamespace;
|
sentence.mode = RScriptSentence.Mode.ExitNamespace;
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,9 +3,16 @@
|
|||||||
namespace Convention.RScript
|
namespace Convention.RScript
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class RScriptException : Exception
|
public class RScriptRuntimeException : Exception
|
||||||
{
|
{
|
||||||
public RScriptException(string message, int runtimePointer) : base($"when running {runtimePointer}, {message}") { }
|
public RScriptRuntimeException(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, 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) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using Convention.RScript.Matcher;
|
using Convention.RScript.Matcher;
|
||||||
using Convention.RScript.Parser;
|
using Convention.RScript.Parser;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@@ -31,11 +32,11 @@ namespace Convention.RScript
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
ExitNamespace,
|
ExitNamespace,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 标签, 格式: label(labelname)
|
/// 标签, 格式: label(labelname);
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Label,
|
Label,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 跳转到指定标签, 格式: goto(boolean,labelname)
|
/// 跳转到指定标签, 格式: goto(boolean,labelname);
|
||||||
/// <para>判断为真时跳转到labelname</para>
|
/// <para>判断为真时跳转到labelname</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Goto,
|
Goto,
|
||||||
@@ -47,6 +48,10 @@ namespace Convention.RScript
|
|||||||
/// 跳转到上次跳转的位置的后一个位置, 格式: back(boolean);
|
/// 跳转到上次跳转的位置的后一个位置, 格式: back(boolean);
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Backpoint,
|
Backpoint,
|
||||||
|
/// <summary>
|
||||||
|
/// 命名空间命名, 格式: namespace(labelname){}
|
||||||
|
/// </summary>
|
||||||
|
NamedSpace,
|
||||||
}
|
}
|
||||||
|
|
||||||
public string content;
|
public string content;
|
||||||
@@ -65,7 +70,8 @@ namespace Convention.RScript
|
|||||||
public readonly RScriptVariables Variables;
|
public readonly RScriptVariables Variables;
|
||||||
private readonly RScriptSentence[] Sentences;
|
private readonly RScriptSentence[] Sentences;
|
||||||
private readonly Dictionary<string, int> Labels;
|
private readonly Dictionary<string, int> Labels;
|
||||||
private readonly Dictionary<int, int> Namespace;
|
private readonly Dictionary<int, int> NamespaceLayer;
|
||||||
|
private readonly Dictionary<string, int> NamespaceLabels;
|
||||||
|
|
||||||
public List<IRSentenceMatcher> SentenceParser = new()
|
public List<IRSentenceMatcher> SentenceParser = new()
|
||||||
{
|
{
|
||||||
@@ -85,8 +91,8 @@ namespace Convention.RScript
|
|||||||
mode = RScriptSentence.Mode.Expression
|
mode = RScriptSentence.Mode.Expression
|
||||||
};
|
};
|
||||||
expression = expression.Trim();
|
expression = expression.Trim();
|
||||||
expression.TrimEnd(';');
|
expression = expression.TrimEnd(';');
|
||||||
SentenceParser.Any(matcher => matcher.Match(expression, ref result));
|
var _ = SentenceParser.Any(matcher => matcher.Match(expression, ref result));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,14 +112,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 RScriptException("Namespace exit without enter.", i);
|
throw new RScriptRuntimeException("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 RScriptException("Namespace enter without exit.", namespaceLayers.Peek());
|
throw new RScriptRuntimeException("Namespace enter without exit.", namespaceLayers.Peek());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,8 +129,9 @@ namespace Convention.RScript
|
|||||||
this.Variables = variables ?? new();
|
this.Variables = variables ?? new();
|
||||||
this.Sentences = (from item in expressions select ParseToSentence(item)).ToArray();
|
this.Sentences = (from item in expressions select ParseToSentence(item)).ToArray();
|
||||||
this.Labels = new();
|
this.Labels = new();
|
||||||
this.Namespace = new();
|
this.NamespaceLayer = new();
|
||||||
BuildUpLabelsAndNamespace(ref this.Labels, ref this.Namespace);
|
this.NamespaceLabels = new();
|
||||||
|
BuildUpLabelsAndNamespace(ref this.Labels, ref this.NamespaceLayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -170,7 +177,7 @@ namespace Convention.RScript
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new RScriptException($"Unsupported variable type '{varTypeName}'.", CurrentRuntimePointer);
|
throw new RScriptRuntimeException($"Unsupported variable type '{varTypeName}'.", CurrentRuntimePointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (CurrentLocalSpaceVariableNames.Peek().Contains(varName) == false)
|
if (CurrentLocalSpaceVariableNames.Peek().Contains(varName) == false)
|
||||||
@@ -181,7 +188,7 @@ namespace Convention.RScript
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new RScriptException($"Variable '{varName}' already defined on this namespace.", CurrentRuntimePointer);
|
throw new RScriptRuntimeException($"Variable '{varName}' already defined on this namespace.", CurrentRuntimePointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +258,7 @@ namespace Convention.RScript
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new RScriptException($"Label '{sentence.content}' not found.", CurrentRuntimePointer);
|
throw new RScriptRuntimeException($"Label '{sentence.content}' not found.", CurrentRuntimePointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,7 +272,7 @@ namespace Convention.RScript
|
|||||||
{
|
{
|
||||||
CurrentRuntimePointer = Sentences.Length;
|
CurrentRuntimePointer = Sentences.Length;
|
||||||
}
|
}
|
||||||
else if (Namespace.TryGetValue(RuntimePointerStack.Peek(), out var exitPointer))
|
else if (NamespaceLayer.TryGetValue(RuntimePointerStack.Peek(), out var exitPointer))
|
||||||
{
|
{
|
||||||
CurrentRuntimePointer = exitPointer;
|
CurrentRuntimePointer = exitPointer;
|
||||||
DoExitNamespace(parser);
|
DoExitNamespace(parser);
|
||||||
@@ -293,17 +300,13 @@ namespace Convention.RScript
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RunNextStep(ExpressionParser parser)
|
private object RunNextStep(ExpressionParser parser)
|
||||||
{
|
{
|
||||||
var sentence = CurrentSentence;
|
var sentence = CurrentSentence;
|
||||||
switch (sentence.mode)
|
switch (sentence.mode)
|
||||||
{
|
{
|
||||||
case RScriptSentence.Mode.Expression:
|
case RScriptSentence.Mode.Expression:
|
||||||
{
|
return parser.Evaluate(sentence.content);
|
||||||
// 执行表达式
|
|
||||||
parser.Evaluate(sentence.content);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RScriptSentence.Mode.DefineVariable:
|
case RScriptSentence.Mode.DefineVariable:
|
||||||
{
|
{
|
||||||
DoDefineVariable(parser, sentence);
|
DoDefineVariable(parser, sentence);
|
||||||
@@ -326,7 +329,7 @@ namespace Convention.RScript
|
|||||||
break;
|
break;
|
||||||
case RScriptSentence.Mode.Breakpoint:
|
case RScriptSentence.Mode.Breakpoint:
|
||||||
{
|
{
|
||||||
DoBreakpoint(parser,sentence);
|
DoBreakpoint(parser, sentence);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RScriptSentence.Mode.Backpoint:
|
case RScriptSentence.Mode.Backpoint:
|
||||||
@@ -338,6 +341,7 @@ namespace Convention.RScript
|
|||||||
// Do nothing
|
// Do nothing
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Stack<int> RuntimePointerStack = new();
|
private readonly Stack<int> RuntimePointerStack = new();
|
||||||
@@ -345,7 +349,17 @@ namespace Convention.RScript
|
|||||||
private int CurrentRuntimePointer = 0;
|
private int CurrentRuntimePointer = 0;
|
||||||
private readonly Stack<HashSet<string>> CurrentLocalSpaceVariableNames = new();
|
private readonly Stack<HashSet<string>> CurrentLocalSpaceVariableNames = new();
|
||||||
|
|
||||||
public Dictionary<string, RScriptVariableEntry> Run(ExpressionParser parser)
|
public Dictionary<string, RScriptVariableEntry> GetCurrentVariables()
|
||||||
|
{
|
||||||
|
Dictionary<string, RScriptVariableEntry> result = new();
|
||||||
|
foreach (var (key, value) in Variables)
|
||||||
|
{
|
||||||
|
result[key] = value;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run(ExpressionParser parser)
|
||||||
{
|
{
|
||||||
CurrentLocalSpaceVariableNames.Clear();
|
CurrentLocalSpaceVariableNames.Clear();
|
||||||
RuntimePointerStack.Clear();
|
RuntimePointerStack.Clear();
|
||||||
@@ -362,7 +376,30 @@ namespace Convention.RScript
|
|||||||
if (Variables.ContainsKey(varName))
|
if (Variables.ContainsKey(varName))
|
||||||
Variables.SetValue(varName, varValue);
|
Variables.SetValue(varName, varValue);
|
||||||
}
|
}
|
||||||
return Variables.ToDictionary();
|
}
|
||||||
|
|
||||||
|
public IEnumerator RunAsync(ExpressionParser parser)
|
||||||
|
{
|
||||||
|
CurrentLocalSpaceVariableNames.Clear();
|
||||||
|
RuntimePointerStack.Clear();
|
||||||
|
GotoPointerStack.Clear();
|
||||||
|
CurrentLocalSpaceVariableNames.Clear();
|
||||||
|
CurrentLocalSpaceVariableNames.Push(new());
|
||||||
|
for (CurrentRuntimePointer = 0; CurrentRuntimePointer < Sentences.Length; CurrentRuntimePointer++)
|
||||||
|
{
|
||||||
|
var ret = RunNextStep(parser);
|
||||||
|
if (ret is IEnumerator ir)
|
||||||
|
{
|
||||||
|
yield return ir;
|
||||||
|
}
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
// 更新上下文变量
|
||||||
|
foreach (var (varName, varValue) in parser.context.Variables)
|
||||||
|
{
|
||||||
|
if (Variables.ContainsKey(varName))
|
||||||
|
Variables.SetValue(varName, varValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using Convention.RScript.Parser;
|
using Convention.RScript.Parser;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -18,10 +19,7 @@ namespace Convention.RScript
|
|||||||
StringBuilder builder = new();
|
StringBuilder builder = new();
|
||||||
List<string> statements = new();
|
List<string> 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)
|
||||||
{
|
{
|
||||||
@@ -29,6 +27,18 @@ namespace Convention.RScript
|
|||||||
builder.Clear();
|
builder.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
char c = line[i];
|
||||||
|
if (c == ';')
|
||||||
|
{
|
||||||
|
PushBuilder();
|
||||||
|
}
|
||||||
else if (c == '/' && i + 1 < e)
|
else if (c == '/' && i + 1 < e)
|
||||||
{
|
{
|
||||||
// Skip single-line comment
|
// Skip single-line comment
|
||||||
@@ -71,29 +81,45 @@ namespace Convention.RScript
|
|||||||
if (i < e)
|
if (i < e)
|
||||||
builder.Append(script[i]);
|
builder.Append(script[i]);
|
||||||
else
|
else
|
||||||
throw new RScriptException("Invalid escape sequence in string literal", -1);
|
throw new RScriptCompileException("Invalid escape sequence in string literal", lineIndex, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (c == '{' || c == '}')
|
else if (c == '{' || c == '}')
|
||||||
{
|
{
|
||||||
// Treat braces as statement separators
|
PushBuilder();
|
||||||
if (builder.Length > 0)
|
|
||||||
{
|
|
||||||
statements.Add(builder.ToString().Trim());
|
|
||||||
builder.Clear();
|
|
||||||
}
|
|
||||||
statements.Add(c.ToString());
|
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
|
else
|
||||||
{
|
{
|
||||||
builder.Append(c);
|
builder.Append(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (builder.Length > 0)
|
if (builder.Length > 0)
|
||||||
{
|
{
|
||||||
statements.Add(builder.ToString().Trim());
|
PushBuilder();
|
||||||
builder.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return statements.Where(s => !string.IsNullOrWhiteSpace(s));
|
return statements.Where(s => !string.IsNullOrWhiteSpace(s));
|
||||||
@@ -105,7 +131,17 @@ namespace Convention.RScript
|
|||||||
context = new(SplitScript(script).ToArray(), import, variables);
|
context = new(SplitScript(script).ToArray(), 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);
|
context.Run(parser);
|
||||||
|
return context.GetCurrentVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator RunAsync(string script, RScriptImportClass import = null, RScriptVariables variables = null)
|
||||||
|
{
|
||||||
|
parser = new(new());
|
||||||
|
context = new(SplitScript(script).ToArray(), import, variables);
|
||||||
|
foreach (var type in context.Import)
|
||||||
|
parser.context.Imports.AddType(type);
|
||||||
|
return context.RunAsync(parser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user