using Convention.RScript.Parser; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using static Convention.RScript.RScriptContext; namespace Convention.RScript { public interface IRScriptEngine { IBasicRScriptContext context { get; } Dictionary Run(string script, RScriptImportClass import = null, RScriptVariables variables = null); IEnumerator RunAsync(string script, RScriptImportClass import = null, RScriptVariables variables = null); SerializableClass Compile(string script, RScriptImportClass import = null, RScriptVariables variables = null); Dictionary Run(SerializableClass data, RScriptImportClass import = null, RScriptVariables variables = null); IEnumerator RunAsync(SerializableClass data, RScriptImportClass import = null, RScriptVariables variables = null); } public sealed class RScriptEngine : IRScriptEngine { private ExpressionParser parser; public IBasicRScriptContext context { get; private set; } private IEnumerable SplitScript(string script) { StringBuilder builder = new(); List statements = new(); void PushBuilder() { if (builder.Length > 0) { statements.Add(builder.ToString().Trim()); 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) { // Skip single-line comment if (line[i + 1] == '/') { while (i < line.Length && line[i] != '\n') i++; } // Skip multi-line comment else if (line[i + 1] == '*') { i += 2; while (i + 1 < line.Length && !(line[i] == '*' && line[i + 1] == '/')) i++; i++; } else { builder.Append(c); } } else if (c == '#') { // Skip single-line comment while (i < line.Length && line[i] != '\n') i++; } else if (c == '\"') { builder.Append(c); for (i++; i < e; i++) { builder.Append(line[i]); if (line[i] == '\"') { break; } else if (line[i] == '\\') { i++; if (i < e) builder.Append(line[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, line, i, "namespace".Length) == 0) { Regex regex = new(@"^\s*namespace\s*\([a-zA-Z_][a-zA-Z0-9_]*\)"); var match = regex.Match(line); 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); } } } if (builder.Length > 0) { PushBuilder(); } return statements.Where(s => !string.IsNullOrWhiteSpace(s)); } private IBasicRScriptContext CreateContext(string[] statements, RScriptImportClass import = null, RScriptVariables variables = null) { return new RScriptContext(statements, import, variables); } private IBasicRScriptContext CreateContext(SerializableClass data, RScriptImportClass import = null, RScriptVariables variables = null) { return new RScriptContext(data, import, variables); } public Dictionary Run(string script, RScriptImportClass import = null, RScriptVariables variables = null) { parser = new(new()); context = CreateContext(SplitScript(script).ToArray(), import, variables); context.Run(parser); return context.GetCurrentVariables(); } public IEnumerator RunAsync(string script, RScriptImportClass import = null, RScriptVariables variables = null) { parser = new(new()); context = CreateContext(SplitScript(script).ToArray(), import, variables); return context.RunAsync(parser); } public SerializableClass Compile(string script, RScriptImportClass import = null, RScriptVariables variables = null) { parser = new(new()); context = CreateContext(SplitScript(script).ToArray(), import, variables); return context.Compile(parser); } public Dictionary Run(SerializableClass data, RScriptImportClass import = null, RScriptVariables variables = null) { parser = new(new()); //parser.Deserialize(data.CompileParser); context = CreateContext(data, import, variables); context.Run(parser); return context.GetCurrentVariables(); } public IEnumerator RunAsync(SerializableClass data, RScriptImportClass import = null, RScriptVariables variables = null) { parser = new(new()); //parser.Deserialize(data.CompileParser); context = CreateContext(data, import, variables); return context.RunAsync(parser); } } }