EP RScript

This commit is contained in:
2025-10-13 19:39:36 +08:00
commit d42726a4dc
12 changed files with 977 additions and 0 deletions

20
Matcher/BackMatcher.cs Normal file
View File

@@ -0,0 +1,20 @@
using System.Text.RegularExpressions;
namespace Convention.RScript.Matcher
{
public class BackMatcher : IRSentenceMatcher
{
public bool Match(string expression, ref RScriptSentence sentence)
{
Regex LabelRegex = new(@"back\s*\(\s*(.+)\s*\)");
var LabelMatch = LabelRegex.Match(expression);
if (LabelMatch.Success)
{
sentence.mode = RScriptSentence.Mode.Backpoint;
sentence.content = LabelMatch.Groups[1].Value;
return true;
}
return false;
}
}
}

20
Matcher/BreakMatcher.cs Normal file
View File

@@ -0,0 +1,20 @@
using System.Text.RegularExpressions;
namespace Convention.RScript.Matcher
{
public class BreakMatcher : IRSentenceMatcher
{
public bool Match(string expression, ref RScriptSentence sentence)
{
Regex LabelRegex = new(@"break\s*\(\s*(.+)\s*\)");
var LabelMatch = LabelRegex.Match(expression);
if (LabelMatch.Success)
{
sentence.mode = RScriptSentence.Mode.Breakpoint;
sentence.content = LabelMatch.Groups[1].Value;
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,20 @@
using System.Text.RegularExpressions;
namespace Convention.RScript.Matcher
{
public class DefineVariableMatcher : IRSentenceMatcher
{
public bool Match(string expression, ref RScriptSentence sentence)
{
Regex DefineVariableRegex = new(@"(string|int|double|float|bool|var)\s+([a-zA-Z_][a-zA-Z0-9_]*)");
var DefineVariableMatch = DefineVariableRegex.Match(expression);
if (DefineVariableMatch.Success)
{
sentence.mode = RScriptSentence.Mode.DefineVariable;
sentence.info = new() { DefineVariableMatch.Groups[1].Value, DefineVariableMatch.Groups[2].Value };
return true;
}
return false;
}
}
}

22
Matcher/GotoMatcher.cs Normal file
View File

@@ -0,0 +1,22 @@
using System.Text.RegularExpressions;
namespace Convention.RScript.Matcher
{
public class GotoMatcher : IRSentenceMatcher
{
public bool Match(string expression, ref RScriptSentence sentence)
{
Regex GotoRegex = new(@"goto\s*\(\s*(.+)\s*,\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)");
var GotoMatch = GotoRegex.Match(expression);
if (GotoMatch.Success)
{
sentence.mode = RScriptSentence.Mode.Goto;
sentence.content = GotoMatch.Groups[2].Value;
sentence.info = new() { GotoMatch.Groups[1].Value, GotoMatch.Groups[2].Value };
return true;
}
return false;
}
}
}

20
Matcher/LabelMatcher.cs Normal file
View File

@@ -0,0 +1,20 @@
using System.Text.RegularExpressions;
namespace Convention.RScript.Matcher
{
public class LabelMatcher : IRSentenceMatcher
{
public bool Match(string expression, ref RScriptSentence sentence)
{
Regex LabelRegex = new(@"label\s*\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)");
var LabelMatch = LabelRegex.Match(expression);
if (LabelMatch.Success)
{
sentence.mode = RScriptSentence.Mode.Label;
sentence.content = LabelMatch.Groups[1].Value;
return true;
}
return false;
}
}
}

20
Matcher/NamespaceMater.cs Normal file
View File

@@ -0,0 +1,20 @@
namespace Convention.RScript.Matcher
{
public class NamespaceMater : IRSentenceMatcher
{
public bool Match(string expression, ref RScriptSentence sentence)
{
if (expression == "{")
{
sentence.mode = RScriptSentence.Mode.EnterNamespace;
return true;
}
else if (expression == "}")
{
sentence.mode = RScriptSentence.Mode.ExitNamespace;
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,76 @@
using Flee.PublicTypes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Convention.RScript.Parser
{
public static class ExpressionExtension
{
public const double DefaultDoubleAccuracy = 1e-7;
public static bool IsClose(this double value1, double value2, double maximumAbsoluteError = DefaultDoubleAccuracy)
{
if (double.IsInfinity(value1) || double.IsInfinity(value2))
{
return Equals(value1, value2);
}
if (double.IsNaN(value1) || double.IsNaN(value2))
{
return false;
}
var delta = value1 - value2;
return !(delta > maximumAbsoluteError || delta < -maximumAbsoluteError);
}
public static bool IsCloseToZero(this double value, double maximumAbsoluteError = DefaultDoubleAccuracy)
{
return !(double.IsInfinity(value) || double.IsNaN(value) || value > maximumAbsoluteError || value < -maximumAbsoluteError);
}
}
public class ExpressionParser
{
public readonly ExpressionContext context;
public ExpressionParser(ExpressionContext context)
{
this.context = context;
}
private readonly Dictionary<string, IExpression> CompileGenericExpression = new();
private readonly Dictionary<string, IDynamicExpression> CompileDynamicExpression = new();
public void ClearCache()
{
CompileGenericExpression.Clear();
CompileDynamicExpression.Clear();
}
public T Evaluate<T>(string expression)
{
if (CompileGenericExpression.TryGetValue(expression, out var result))
{
return (result as IGenericExpression<T>).Evaluate();
}
var compile = context.CompileGeneric<T>(expression);
CompileGenericExpression[expression] = compile;
return compile.Evaluate();
}
public object Evaluate(string expression)
{
if (CompileDynamicExpression.TryGetValue(expression, out var result))
{
return result.Evaluate();
}
var compile = context.CompileDynamic(expression);
CompileDynamicExpression[expression] = compile;
return compile.Evaluate();
}
}
}

View File

@@ -0,0 +1,11 @@
using System;
namespace Convention.RScript
{
[Serializable]
public class RScriptException : Exception
{
public RScriptException(string message, int runtimePointer) : base($"when running {runtimePointer}, {message}") { }
public RScriptException(string message, int runtimePointer, Exception inner) : base($"when running {runtimePointer}, {message}", inner) { }
}
}

View File

@@ -0,0 +1,138 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Convention.RScript
{
public class RScriptImportClass : ICollection<Type>
{
private readonly HashSet<Type> importedTypes = new();
private readonly Dictionary<string, List<MethodInfo>> cacheImportedFunctions = new();
public int Count => ((ICollection<Type>)importedTypes).Count;
public bool IsReadOnly => ((ICollection<Type>)importedTypes).IsReadOnly;
private void DoAdd(Type type)
{
foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public))
{
if (cacheImportedFunctions.ContainsKey(method.Name) == false)
cacheImportedFunctions.Add(method.Name, new());
cacheImportedFunctions[method.Name].Add(method);
}
}
public Type[] GetImports()
{
return importedTypes.ToArray();
}
public int CountMethod(string methodName)
{
return cacheImportedFunctions.TryGetValue(methodName, out var list) ? list.Count : 0;
}
public MethodInfo GetMethod(string methodName)
{
if (cacheImportedFunctions.TryGetValue(methodName, out var list))
{
if (list.Count != 1)
{
throw new AmbiguousMatchException($"Have more than one {methodName} is imported");
}
return list[0];
}
return null;
}
public MethodInfo GetMethodByReturn(string methodName, Type returnType)
{
if (cacheImportedFunctions.TryGetValue(methodName, out var list))
{
var query = from item in list where item.ReturnType == returnType select item;
if (query.Count() != 1)
{
throw new AmbiguousMatchException($"Have more than one {methodName} is imported");
}
return query.First();
}
return null;
}
public MethodInfo GetMethod(string methodName, params Type[] parameters)
{
static bool Pr(Type[] parameters1, Type[] parameters2)
{
if (parameters1.Length != parameters2.Length)
return false;
for (int i = 0, e = parameters1.Length; i != e; i++)
{
if (parameters1[i] != parameters2[i])
return false;
}
return true;
}
if (cacheImportedFunctions.TryGetValue(methodName, out var list))
{
var query = from item in list
where Pr((from _param in item.GetParameters() select _param.ParameterType).ToArray(), parameters)
select item;
if (query.Count() != 1)
{
throw new AmbiguousMatchException($"Have more than one {methodName} is imported");
}
return query.First();
}
return null;
}
public bool TryAdd(Type type)
{
var stats = importedTypes.Add(type);
if (stats)
{
DoAdd(type);
}
return stats;
}
public void Add(Type type)
{
TryAdd(type);
}
public IEnumerator<Type> GetEnumerator()
{
return ((IEnumerable<Type>)importedTypes).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)importedTypes).GetEnumerator();
}
public void Clear()
{
((ICollection<Type>)importedTypes).Clear();
}
public bool Contains(Type item)
{
return ((ICollection<Type>)importedTypes).Contains(item);
}
public void CopyTo(Type[] array, int arrayIndex)
{
((ICollection<Type>)importedTypes).CopyTo(array, arrayIndex);
}
public bool Remove(Type item)
{
return ((ICollection<Type>)importedTypes).Remove(item);
}
}
}

View File

@@ -0,0 +1,151 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Convention.RScript
{
public struct RScriptVariableEntry
{
public Type type;
public object data;
}
public class RScriptVariables : IDictionary<string, RScriptVariableEntry>
{
private readonly Dictionary<string, Stack<RScriptVariableEntry>> variables = new();
public RScriptVariableEntry this[string key]
{
get
{
return variables[key].Peek();
}
set
{
var current = variables[key].Peek();
if (current.type != value.type)
throw new ArgumentException($"current type is {current.type}, but setter.value is {value.type}");
variables[key].Pop();
variables[key].Push(value);
}
}
public ICollection<string> Keys => variables.Keys;
public ICollection<RScriptVariableEntry> Values => (from item in variables.Values select item.Peek()).ToArray();
public int Count => variables.Count;
public bool IsReadOnly => false;
public void Add(string key, RScriptVariableEntry value)
{
if (variables.ContainsKey(key) == false)
variables.Add(key, new());
variables[key].Push(value);
}
public void Add(KeyValuePair<string, RScriptVariableEntry> item)
{
Add(item.Key, item.Value);
}
public void ClearAllLayers()
{
variables.Clear();
}
/// <summary>
/// <see cref="ClearAllLayers"/>
/// </summary>
public void Clear()
{
ClearAllLayers();
}
public bool Contains(KeyValuePair<string, RScriptVariableEntry> item)
{
if (variables.TryGetValue(item.Key, out var items))
{
var current = items.Peek();
return current.data == item.Value.data;
}
return false;
}
public bool ContainsKey(string key)
{
return variables.ContainsKey(key);
}
public void CopyTo(KeyValuePair<string, RScriptVariableEntry>[] array, int arrayIndex)
{
foreach (var (key, items) in variables)
{
array[arrayIndex++] = new(key, items.Peek());
}
}
public IEnumerator<KeyValuePair<string, RScriptVariableEntry>> GetEnumerator()
{
return (from items in variables select new KeyValuePair<string, RScriptVariableEntry>(items.Key, items.Value.Peek())).GetEnumerator();
}
public bool Remove(string key)
{
if (variables.TryGetValue(key, out var items))
{
items.Pop();
if (items.Count == 0)
{
variables.Remove(key);
}
return true;
}
return false;
}
public bool Remove(KeyValuePair<string, RScriptVariableEntry> item)
{
if (variables.TryGetValue(item.Key, out var items))
{
if (item.Value.data == items.Peek().data)
{
items.Pop();
if (items.Count == 0)
{
variables.Remove(item.Key);
}
return true;
}
}
return false;
}
public bool TryGetValue(string key, [MaybeNullWhen(false)] out RScriptVariableEntry value)
{
if (variables.TryGetValue(key, out var items))
{
value = items.Peek();
return true;
}
value = default;
return false;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void SetValue(string varName, object value)
{
var top = variables[varName].Pop();
top.data = value;
variables[varName].Push(top);
}
}
}

368
RScriptContext.cs Normal file
View File

@@ -0,0 +1,368 @@
using Convention.RScript.Matcher;
using Convention.RScript.Parser;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Convention.RScript
{
public struct RScriptSentence
{
public enum Mode
{
/// <summary>
/// 表达式, 格式: 任意合法表达式
/// </summary>
Expression,
/// <summary>
/// 定义变量, 格式: 类型 变量名
/// <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,
}
public string content;
public List<string> info;
public Mode mode;
}
public interface IRSentenceMatcher
{
bool Match(string expression, ref RScriptSentence sentence);
}
public partial class RScriptContext
{
public readonly RScriptImportClass Import;
public readonly RScriptVariables Variables;
private readonly RScriptSentence[] Sentences;
private readonly Dictionary<string, int> Labels;
private readonly Dictionary<int, int> Namespace;
public List<IRSentenceMatcher> SentenceParser = new()
{
new NamespaceMater(),
new DefineVariableMatcher(),
new LabelMatcher(),
new GotoMatcher(),
new BreakMatcher(),
new BackMatcher(),
};
private RScriptSentence ParseToSentence(string expression)
{
RScriptSentence result = new()
{
content = expression,
mode = RScriptSentence.Mode.Expression
};
expression = expression.Trim();
expression.TrimEnd(';');
SentenceParser.Any(matcher => matcher.Match(expression, ref result));
return result;
}
private void BuildUpLabelsAndNamespace(ref Dictionary<string, int> labelIndicator, ref Dictionary<int, int> namespaceIndicator)
{
Stack<int> namespaceLayers = new();
for (int i = 0, e = Sentences.Length; i != e; i++)
{
if (Sentences[i].mode == RScriptSentence.Mode.Label)
{
labelIndicator[Sentences[i].content] = i;
}
else if (Sentences[i].mode == RScriptSentence.Mode.EnterNamespace)
{
namespaceLayers.Push(i);
}
else if (Sentences[i].mode == RScriptSentence.Mode.ExitNamespace)
{
if (namespaceLayers.Count == 0)
throw new RScriptException("Namespace exit without enter.", i);
var enterPointer = namespaceLayers.Pop();
namespaceIndicator[enterPointer] = i;
}
}
if (namespaceLayers.Count > 0)
{
throw new RScriptException("Namespace enter without exit.", namespaceLayers.Peek());
}
}
public RScriptContext(string[] expressions, RScriptImportClass import = null, RScriptVariables variables = null)
{
this.Import = import ?? new();
this.Variables = variables ?? new();
this.Sentences = (from item in expressions select ParseToSentence(item)).ToArray();
this.Labels = new();
this.Namespace = new();
BuildUpLabelsAndNamespace(ref this.Labels, ref this.Namespace);
}
public RScriptSentence CurrentSentence => Sentences[CurrentRuntimePointer];
private void DoDefineVariable(ExpressionParser parser, RScriptSentence sentence)
{
// 定义变量
var varTypeName = sentence.info[0];
var varName = sentence.info[1];
Type varType;
object varDefaultValue;
{
if (varTypeName == "string")
{
varType = typeof(string);
varDefaultValue = string.Empty;
}
else if (varTypeName == "int")
{
varType = typeof(int);
varDefaultValue = 0;
}
else if (varTypeName == "double")
{
varType = typeof(double);
varDefaultValue = 0.0;
}
else if (varTypeName == "float")
{
varType = typeof(float);
varDefaultValue = 0.0f;
}
else if (varTypeName == "bool")
{
varType = typeof(bool);
varDefaultValue = false;
}
else if (varTypeName == "var")
{
varType = typeof(object);
varDefaultValue = new object();
}
else
{
throw new RScriptException($"Unsupported variable type '{varTypeName}'.", CurrentRuntimePointer);
}
}
if (CurrentLocalSpaceVariableNames.Peek().Contains(varName) == false)
{
Variables.Add(varName, new() { type = varType, data = varDefaultValue });
parser.context.Variables[varName] = varDefaultValue;
CurrentLocalSpaceVariableNames.Peek().Add(varName);
}
else
{
throw new RScriptException($"Variable '{varName}' already defined on this namespace.", CurrentRuntimePointer);
}
}
private void DoEnterNamespace(ExpressionParser parser)
{
// 准备记录当前命名空间中定义的变量, 清空上层命名空间的变量
CurrentLocalSpaceVariableNames.Push(new());
// 更新变量值
foreach (var (varName, varValue) in parser.context.Variables)
{
Variables.SetValue(varName, varValue);
}
// 压栈
RuntimePointerStack.Push(CurrentRuntimePointer);
}
private void DoExitNamespace(ExpressionParser parser)
{
// 移除当前命名空间的变量
foreach (var local in CurrentLocalSpaceVariableNames.Peek())
{
Variables.Remove(local);
parser.context.Variables.Remove(local);
}
// 还原上层命名空间的变量
foreach (var local in CurrentLocalSpaceVariableNames.Peek())
{
parser.context.Variables[local] = Variables[local].data;
}
CurrentLocalSpaceVariableNames.Pop();
// 弹栈
RuntimePointerStack.Pop();
}
private void DoJumpRuntimePointer(ExpressionParser parser, int target)
{
bool isForwardMove = target > CurrentRuntimePointer;
int step = isForwardMove ? 1 : -1;
for (; CurrentRuntimePointer != target; CurrentRuntimePointer += step)
{
if (CurrentSentence.mode == RScriptSentence.Mode.ExitNamespace)
{
if (isForwardMove)
DoExitNamespace(parser);
else
DoEnterNamespace(parser);
}
else if (CurrentSentence.mode == RScriptSentence.Mode.EnterNamespace)
{
if (isForwardMove)
DoEnterNamespace(parser);
else
DoExitNamespace(parser);
}
}
}
private void DoGoto(ExpressionParser parser, RScriptSentence sentence)
{
// 检查并跳转到指定标签
if (parser.Evaluate<bool>(sentence.info[0]))
{
if (Labels.TryGetValue(sentence.content, out var labelPointer))
{
GotoPointerStack.Push(CurrentRuntimePointer);
DoJumpRuntimePointer(parser, labelPointer);
}
else
{
throw new RScriptException($"Label '{sentence.content}' not found.", CurrentRuntimePointer);
}
}
}
private void DoBreakpoint(ExpressionParser parser, RScriptSentence sentence)
{
// 检查并跳转到当前命名空间的结束位置
if (parser.Evaluate<bool>(sentence.content))
{
if (RuntimePointerStack.Count == 0)
{
CurrentRuntimePointer = Sentences.Length;
}
else if (Namespace.TryGetValue(RuntimePointerStack.Peek(), out var exitPointer))
{
CurrentRuntimePointer = exitPointer;
DoExitNamespace(parser);
}
else
{
throw new NotImplementedException($"No namespace to break.");
}
}
}
private void DoBackpoint(ExpressionParser parser, RScriptSentence sentence)
{
// 检查并跳转到上次跳转的位置
if (parser.Evaluate<bool>(sentence.content))
{
if (GotoPointerStack.Count == 0)
{
throw new NotImplementedException($"No position to back.");
}
else
{
DoJumpRuntimePointer(parser, GotoPointerStack.Pop());
}
}
}
private void RunNextStep(ExpressionParser parser)
{
var sentence = CurrentSentence;
switch (sentence.mode)
{
case RScriptSentence.Mode.Expression:
{
// 执行表达式
parser.Evaluate(sentence.content);
}
break;
case RScriptSentence.Mode.DefineVariable:
{
DoDefineVariable(parser, sentence);
}
break;
case RScriptSentence.Mode.EnterNamespace:
{
DoEnterNamespace(parser);
}
break;
case RScriptSentence.Mode.ExitNamespace:
{
DoExitNamespace(parser);
}
break;
case RScriptSentence.Mode.Goto:
{
DoGoto(parser, sentence);
}
break;
case RScriptSentence.Mode.Breakpoint:
{
DoBreakpoint(parser,sentence);
}
break;
case RScriptSentence.Mode.Backpoint:
{
DoBackpoint(parser, sentence);
}
break;
default:
// Do nothing
break;
}
}
private readonly Stack<int> RuntimePointerStack = new();
private readonly Stack<int> GotoPointerStack = new();
private int CurrentRuntimePointer = 0;
private readonly Stack<HashSet<string>> CurrentLocalSpaceVariableNames = new();
public Dictionary<string, RScriptVariableEntry> 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);
}
return Variables.ToDictionary();
}
}
}

111
RScriptEngine.cs Normal file
View File

@@ -0,0 +1,111 @@
using Convention.RScript.Parser;
using System;
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();
for (int i = 0, e = script.Length; i < e; i++)
{
char c = script[i];
if (c == ';')
{
if (builder.Length > 0)
{
statements.Add(builder.ToString().Trim());
builder.Clear();
}
}
else if (c == '/' && i + 1 < e)
{
// Skip single-line comment
if (script[i + 1] == '/')
{
while (i < script.Length && script[i] != '\n')
i++;
}
// Skip multi-line comment
else if (script[i + 1] == '*')
{
i += 2;
while (i + 1 < script.Length && !(script[i] == '*' && script[i + 1] == '/'))
i++;
i++;
}
else
{
builder.Append(c);
}
}
else if (c == '#')
{
// Skip single-line comment
while (i < script.Length && script[i] != '\n')
i++;
}
else if (c == '\"')
{
for (i++; i < e; i++)
{
builder.Append(script[i]);
if (script[i] == '\"')
{
break;
}
else if (script[i] == '\\')
{
i++;
if (i < e)
builder.Append(script[i]);
else
throw new RScriptException("Invalid escape sequence in string literal", -1);
}
}
}
else if (c == '{' || c == '}')
{
// Treat braces as statement separators
if (builder.Length > 0)
{
statements.Add(builder.ToString().Trim());
builder.Clear();
}
statements.Add(c.ToString());
}
else
{
builder.Append(c);
}
}
if (builder.Length > 0)
{
statements.Add(builder.ToString().Trim());
builder.Clear();
}
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);
foreach (var type in context.Import)
parser.context.Imports.AddType(type);
return context.Run(parser);
}
}
}