366 lines
14 KiB
C#
366 lines
14 KiB
C#
using Convention.RScript.Matcher;
|
|
using Convention.RScript.Parser;
|
|
using Convention.RScript.Runner;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using static Convention.RScript.RScriptContext;
|
|
|
|
namespace Convention.RScript
|
|
{
|
|
[Serializable]
|
|
public struct RScriptSentence
|
|
{
|
|
[Serializable]
|
|
public enum Mode
|
|
{
|
|
/// <summary>
|
|
/// 表达式, 格式: 任意合法表达式
|
|
/// </summary>
|
|
Expression,
|
|
/// <summary>
|
|
/// 定义变量, 格式: 类型 变量名 [=Expression]
|
|
/// <para>类型支持: string, int, double, float, bool, var</para>
|
|
/// <para>每层命名空间中不可重复定义变量, 不可使用未定义的变量, 不存在时会自动向上查找上级空间的变量</para>
|
|
/// </summary>
|
|
DefineVariable,
|
|
/// <summary>
|
|
/// 进入新的命名空间, 格式: {
|
|
/// <para>命名空间是一对花括号包裹内容空间, 格式: {...}</para>
|
|
/// </summary>
|
|
EnterNamespace,
|
|
/// <summary>
|
|
/// 退出当前命名空间, 格式: }
|
|
/// <para>命名空间是一对花括号包裹内容空间, 格式: {...}</para>
|
|
/// </summary>
|
|
ExitNamespace,
|
|
/// <summary>
|
|
/// 标签, 格式: label(labelname);
|
|
/// </summary>
|
|
Label,
|
|
/// <summary>
|
|
/// 跳转到指定标签, 格式: goto(boolean,labelname);
|
|
/// <para>判断为真时跳转到labelname</para>
|
|
/// </summary>
|
|
Goto,
|
|
/// <summary>
|
|
/// 跳转到当前命名空间的结束位置, 格式: break(boolean);
|
|
/// </summary>
|
|
Breakpoint,
|
|
/// <summary>
|
|
/// 跳转到上次跳转的位置的后一个位置, 格式: back(boolean);
|
|
/// </summary>
|
|
Backpoint,
|
|
/// <summary>
|
|
/// 命名空间命名, 格式: namespace(labelname){}
|
|
/// </summary>
|
|
NamedSpace,
|
|
}
|
|
|
|
public string content;
|
|
public string[] info;
|
|
public Mode mode;
|
|
|
|
public override readonly string ToString()
|
|
{
|
|
return $"{mode}: {content}";
|
|
}
|
|
}
|
|
|
|
public interface IRSentenceMatcher
|
|
{
|
|
bool Match(string expression, ref RScriptSentence sentence);
|
|
}
|
|
|
|
public interface IRSentenceRunner
|
|
{
|
|
[return: MaybeNull] object Run(ExpressionParser parser, RScriptSentence sentence, RScriptContext context);
|
|
void Compile(ExpressionParser parser, RScriptSentence sentence, RScriptContext context);
|
|
}
|
|
|
|
public interface IBasicRScriptContext
|
|
{
|
|
RScriptImportClass Import { get; }
|
|
RScriptVariables Variables { get; }
|
|
RScriptSentence[] Sentences { get; }
|
|
int CurrentRuntimePointer { get; }
|
|
RScriptSentence CurrentSentence { get; }
|
|
|
|
Dictionary<string, RScriptVariableEntry> GetCurrentVariables();
|
|
void Run(ExpressionParser parser);
|
|
IEnumerator RunAsync(ExpressionParser parser);
|
|
SerializableClass Compile(ExpressionParser parser);
|
|
}
|
|
|
|
public partial class RScriptContext : IBasicRScriptContext
|
|
{
|
|
public RScriptImportClass Import { get;private set; }
|
|
public RScriptVariables Variables { get;private set; }
|
|
public RScriptSentence[] Sentences { get; private set; }
|
|
internal readonly Dictionary<string, int> Labels = new();
|
|
internal readonly Dictionary<int, int> NamespaceLayer = new();
|
|
internal readonly Dictionary<string, int> NamespaceLabels = new();
|
|
|
|
[Serializable]
|
|
public struct SerializableClass
|
|
{
|
|
public RScriptSentence[] Sentences;
|
|
public Tuple<string, int>[] Labels;
|
|
public Tuple<int, int>[] NamespaceLayer;
|
|
public Tuple<string, int>[] NamespaceLabels;
|
|
public ExpressionParser.SerializableParser CompileParser;
|
|
}
|
|
|
|
public List<IRSentenceMatcher> SentenceParser = new()
|
|
{
|
|
new NamespaceMater(),
|
|
new DefineVariableMatcher(),
|
|
new LabelMatcher(),
|
|
new GotoMatcher(),
|
|
new BreakMatcher(),
|
|
new BackMatcher(),
|
|
};
|
|
|
|
public Dictionary<RScriptSentence.Mode, IRSentenceRunner> SentenceRunners = new()
|
|
{
|
|
{ RScriptSentence.Mode.DefineVariable, new DefineVariableRunner() },
|
|
{ RScriptSentence.Mode.EnterNamespace, new EnterNamespaceRunner() },
|
|
{ RScriptSentence.Mode.ExitNamespace, new ExitNamespaceRunner() },
|
|
{ RScriptSentence.Mode.Goto, new GoToRunner() },
|
|
{ RScriptSentence.Mode.Breakpoint, new BreakpointRunner() },
|
|
{ RScriptSentence.Mode.Backpoint, new BackpointRunner() },
|
|
{ RScriptSentence.Mode.Expression, new ExpressionRunner() },
|
|
{ RScriptSentence.Mode.NamedSpace, new EnterNamedSpaceRunner() },
|
|
};
|
|
|
|
private RScriptSentence ParseToSentence(string expression)
|
|
{
|
|
RScriptSentence result = new()
|
|
{
|
|
content = expression,
|
|
mode = RScriptSentence.Mode.Expression
|
|
};
|
|
expression = expression.Trim();
|
|
expression = expression.TrimEnd(';');
|
|
var _ = SentenceParser.Any(matcher => matcher.Match(expression, ref result));
|
|
return result;
|
|
}
|
|
|
|
private void BuildUpLabelsAndNamespace()
|
|
{
|
|
Stack<int> namespaceLayers = new();
|
|
string namespaceName = "";
|
|
for (int i = 0, e = Sentences.Length; i != e; i++)
|
|
{
|
|
var sentence = Sentences[i];
|
|
if (string.IsNullOrEmpty(namespaceName))
|
|
{
|
|
if (sentence.mode == RScriptSentence.Mode.Label)
|
|
{
|
|
this.Labels[Sentences[i].content] = i;
|
|
}
|
|
else if (sentence.mode == RScriptSentence.Mode.EnterNamespace)
|
|
{
|
|
namespaceLayers.Push(i);
|
|
}
|
|
else if (sentence.mode == RScriptSentence.Mode.ExitNamespace)
|
|
{
|
|
if (namespaceLayers.Count == 0)
|
|
throw new RScriptRuntimeException("Namespace exit without enter.", i);
|
|
var enterPointer = namespaceLayers.Pop();
|
|
this.NamespaceLayer[enterPointer] = i;
|
|
}
|
|
else if (sentence.mode == RScriptSentence.Mode.NamedSpace)
|
|
{
|
|
namespaceName = sentence.content;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (sentence.mode == RScriptSentence.Mode.EnterNamespace)
|
|
{
|
|
namespaceLayers.Push(i);
|
|
this.NamespaceLabels[namespaceName] = i;
|
|
namespaceName = "";
|
|
}
|
|
else
|
|
{
|
|
throw new RScriptRuntimeException($"Namespace is invalid", i);
|
|
}
|
|
}
|
|
}
|
|
if (namespaceLayers.Count > 0)
|
|
{
|
|
throw new RScriptRuntimeException("Namespace enter without exit.", namespaceLayers.Peek());
|
|
}
|
|
}
|
|
|
|
public class BuildInContext
|
|
{
|
|
private RScriptContext context;
|
|
public BuildInContext(RScriptContext context)
|
|
{
|
|
this.context = context;
|
|
}
|
|
|
|
public bool ExistVar(string name)
|
|
{
|
|
return context.Variables.ContainsKey(name);
|
|
}
|
|
|
|
public bool ExistNamespace(string name)
|
|
{
|
|
return context.NamespaceLabels.ContainsKey(name);
|
|
}
|
|
|
|
public bool ExistLabel(string name)
|
|
{
|
|
return context.Labels.ContainsKey(name);
|
|
}
|
|
}
|
|
|
|
public RScriptContext(string[] expressions,
|
|
RScriptImportClass import = null,
|
|
RScriptVariables variables = null,
|
|
List<IRSentenceMatcher> matcher = null,
|
|
Dictionary<RScriptSentence.Mode, IRSentenceRunner> sentenceRunners = null)
|
|
{
|
|
this.Import = import ?? new();
|
|
this.Variables = variables ?? new();
|
|
this.Variables.Add("context", new(typeof(object), new BuildInContext(this)));
|
|
this.Sentences = (from item in expressions select ParseToSentence(item)).ToArray();
|
|
if (matcher != null)
|
|
this.SentenceParser = matcher;
|
|
if (sentenceRunners != null)
|
|
this.SentenceRunners = sentenceRunners;
|
|
|
|
BuildUpLabelsAndNamespace();
|
|
}
|
|
|
|
public RScriptContext(SerializableClass data,
|
|
RScriptImportClass import = null,
|
|
RScriptVariables variables = null,
|
|
List<IRSentenceMatcher> matcher = null,
|
|
Dictionary<RScriptSentence.Mode, IRSentenceRunner> sentenceRunners = null)
|
|
{
|
|
this.Import = import ?? new();
|
|
this.Variables = variables ?? new();
|
|
this.Variables.Add("context", new(typeof(object), new BuildInContext(this)));
|
|
|
|
this.Sentences = data.Sentences;
|
|
this.Labels = (from item in data.Labels select item).ToDictionary(t => t.Item1, t => t.Item2);
|
|
this.NamespaceLayer = (from item in data.NamespaceLayer select item).ToDictionary(t => t.Item1, t => t.Item2);
|
|
this.NamespaceLabels = (from item in data.NamespaceLabels select item).ToDictionary(t => t.Item1, t => t.Item2);
|
|
}
|
|
|
|
public RScriptSentence CurrentSentence => Sentences[CurrentRuntimePointer];
|
|
|
|
public int StepCount { get; private set; }
|
|
|
|
internal object RunNextStep(ExpressionParser parser)
|
|
{
|
|
StepCount++;
|
|
var sentence = CurrentSentence;
|
|
try
|
|
{
|
|
return SentenceRunners.TryGetValue(sentence.mode, out var runner) ? runner.Run(parser, sentence, this) : null;
|
|
}
|
|
catch (RScriptException)
|
|
{
|
|
throw;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new RScriptRuntimeException($"Runtime error: {ex.Message}", CurrentRuntimePointer, ex);
|
|
}
|
|
}
|
|
|
|
internal readonly Stack<int> RuntimePointerStack = new();
|
|
internal readonly Stack<int> GotoPointerStack = new();
|
|
public int CurrentRuntimePointer { get; internal set; } = 0;
|
|
internal readonly Stack<HashSet<string>> CurrentLocalSpaceVariableNames = new();
|
|
|
|
public Dictionary<string, RScriptVariableEntry> GetCurrentVariables()
|
|
{
|
|
Dictionary<string, RScriptVariableEntry> result = new();
|
|
foreach (var (key, value) in Variables)
|
|
{
|
|
result[key] = value;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private void BeforeRun(ExpressionParser parser)
|
|
{
|
|
StepCount = 0;
|
|
CurrentLocalSpaceVariableNames.Clear();
|
|
RuntimePointerStack.Clear();
|
|
GotoPointerStack.Clear();
|
|
CurrentLocalSpaceVariableNames.Clear();
|
|
CurrentLocalSpaceVariableNames.Push(new());
|
|
foreach (var staticType in Import)
|
|
{
|
|
parser.context.Imports.AddType(staticType);
|
|
}
|
|
foreach (var (name, varObject) in Variables)
|
|
{
|
|
parser.context.Variables[name] = varObject.data;
|
|
}
|
|
}
|
|
|
|
private void AfterRun(ExpressionParser parser)
|
|
{
|
|
foreach (var (varName, varValue) in parser.context.Variables)
|
|
{
|
|
if (Variables.ContainsKey(varName))
|
|
Variables.SetValue(varName, varValue);
|
|
}
|
|
}
|
|
|
|
public void Run(ExpressionParser parser)
|
|
{
|
|
BeforeRun(parser);
|
|
for (CurrentRuntimePointer = 0; CurrentRuntimePointer < Sentences.Length; CurrentRuntimePointer++)
|
|
{
|
|
RunNextStep(parser);
|
|
}
|
|
AfterRun(parser);
|
|
}
|
|
|
|
public IEnumerator RunAsync(ExpressionParser parser)
|
|
{
|
|
BeforeRun(parser);
|
|
for (CurrentRuntimePointer = 0; CurrentRuntimePointer < Sentences.Length; CurrentRuntimePointer++)
|
|
{
|
|
var ret = RunNextStep(parser);
|
|
if (ret is IEnumerator ir)
|
|
{
|
|
yield return ir;
|
|
}
|
|
yield return null;
|
|
}
|
|
AfterRun(parser);
|
|
}
|
|
|
|
public SerializableClass Compile(ExpressionParser parser)
|
|
{
|
|
BeforeRun(parser);
|
|
foreach (var item in Sentences)
|
|
{
|
|
if (SentenceRunners.TryGetValue(item.mode, out var runner))
|
|
runner.Compile(parser, item, this);
|
|
}
|
|
return new SerializableClass()
|
|
{
|
|
CompileParser = parser.Serialize(),
|
|
Labels = (from item in Labels select Tuple.Create(item.Key, item.Value)).ToArray(),
|
|
NamespaceLayer = (from item in NamespaceLayer select Tuple.Create(item.Key, item.Value)).ToArray(),
|
|
NamespaceLabels = (from item in NamespaceLabels select Tuple.Create(item.Key, item.Value)).ToArray(),
|
|
Sentences = Sentences,
|
|
};
|
|
}
|
|
}
|
|
}
|