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 { /// /// 表达式, 格式: 任意合法表达式 /// Expression, /// /// 定义变量, 格式: 类型 变量名 [=Expression] /// 类型支持: string, int, double, float, bool, var /// 每层命名空间中不可重复定义变量, 不可使用未定义的变量, 不存在时会自动向上查找上级空间的变量 /// DefineVariable, /// /// 进入新的命名空间, 格式: { /// 命名空间是一对花括号包裹内容空间, 格式: {...} /// EnterNamespace, /// /// 退出当前命名空间, 格式: } /// 命名空间是一对花括号包裹内容空间, 格式: {...} /// ExitNamespace, /// /// 标签, 格式: label(labelname); /// Label, /// /// 跳转到指定标签, 格式: goto(boolean,labelname); /// 判断为真时跳转到labelname /// Goto, /// /// 跳转到当前命名空间的结束位置, 格式: break(boolean); /// Breakpoint, /// /// 跳转到上次跳转的位置的后一个位置, 格式: back(boolean); /// Backpoint, /// /// 命名空间命名, 格式: namespace(labelname){} /// NamedSpace, } public string content; public List 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 Labels = new(); internal readonly Dictionary NamespaceLayer = new(); internal readonly Dictionary NamespaceLabels = new(); public List SentenceParser = new() { new NamespaceMater(), new DefineVariableMatcher(), new LabelMatcher(), new GotoMatcher(), new BreakMatcher(), new BackMatcher(), }; public Dictionary 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 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 matcher = null, Dictionary 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 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 RuntimePointerStack = new(); internal readonly Stack GotoPointerStack = new(); public int CurrentRuntimePointer { get; internal set; } = 0; internal readonly Stack> CurrentLocalSpaceVariableNames = new(); public Dictionary GetCurrentVariables() { Dictionary result = new(); foreach (var (key, value) in Variables) { result[key] = value; } return result; } private void BeforeRun(ExpressionParser parser) { 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; } } public void Run(ExpressionParser parser) { BeforeRun(parser); 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) { BeforeRun(parser); 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); } } } }