Files
RScript/RScriptEngine.cs

185 lines
7.4 KiB
C#

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<string, RScriptVariableEntry> 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<string, RScriptVariableEntry> 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<string> SplitScript(string script)
{
StringBuilder builder = new();
List<string> 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<string, RScriptVariableEntry> 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<string, RScriptVariableEntry> 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);
}
}
}