Files
RScript/RScriptContext.cs

268 lines
9.9 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;
namespace Convention.RScript
{
public struct RScriptSentence
{
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 List<string> info;
public Mode mode;
public override string ToString()
{
return $"{mode.ToString()}/: {content}";
}
}
public interface IRSentenceMatcher
{
bool Match(string expression, ref RScriptSentence sentence);
}
public interface IRSentenceRunner
{
[return: MaybeNull] object Run(ExpressionParser parser, RScriptSentence sentence, RScriptContext context);
}
public partial class RScriptContext
{
public readonly RScriptImportClass Import;
public readonly RScriptVariables Variables;
internal readonly RScriptSentence[] Sentences;
internal readonly Dictionary<string, int> Labels = new();
internal readonly Dictionary<int, int> NamespaceLayer = new();
internal readonly Dictionary<string, int> NamespaceLabels = new();
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 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.Sentences = (from item in expressions select ParseToSentence(item)).ToArray();
if (matcher != null)
this.SentenceParser = matcher;
if (sentenceRunners != null)
this.SentenceRunners = sentenceRunners;
BuildUpLabelsAndNamespace();
}
public RScriptSentence CurrentSentence => Sentences[CurrentRuntimePointer];
internal object RunNextStep(ExpressionParser parser)
{
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;
}
public void Run(ExpressionParser parser)
{
CurrentLocalSpaceVariableNames.Clear();
RuntimePointerStack.Clear();
GotoPointerStack.Clear();
CurrentLocalSpaceVariableNames.Clear();
CurrentLocalSpaceVariableNames.Push(new());
for (CurrentRuntimePointer = 0; CurrentRuntimePointer < Sentences.Length; CurrentRuntimePointer++)
{
RunNextStep(parser);
}
// 更新上下文变量
foreach (var (varName, varValue) in parser.context.Variables)
{
if (Variables.ContainsKey(varName))
Variables.SetValue(varName, varValue);
}
}
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);
}
}
}
}