141 lines
5.0 KiB
C#
141 lines
5.0 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;
|
|
|
|
namespace Convention.RScript
|
|
{
|
|
public class RScriptEngine
|
|
{
|
|
private ExpressionParser parser;
|
|
private RScriptContext context;
|
|
|
|
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));
|
|
}
|
|
|
|
public Dictionary<string, RScriptVariableEntry> Run(string script, RScriptImportClass import = null, RScriptVariables variables = null)
|
|
{
|
|
parser = new(new());
|
|
context = new(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 = new(SplitScript(script).ToArray(), import, variables);
|
|
return context.RunAsync(parser);
|
|
}
|
|
}
|
|
}
|