diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..066313b
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "Convention/[FLEE]"]
+ path = Convention/[FLEE]
+ url = http://www.liubai.site:3000/ninemine/Flee.git
diff --git a/Convention-CSharp.csproj b/Convention-CSharp.csproj
index 77e5def..1bb1198 100644
--- a/Convention-CSharp.csproj
+++ b/Convention-CSharp.csproj
@@ -5,7 +5,6 @@
Exe
net8.0
Convention
- true
diff --git a/Convention/[FLEE] b/Convention/[FLEE]
new file mode 160000
index 0000000..c9e2493
--- /dev/null
+++ b/Convention/[FLEE]
@@ -0,0 +1 @@
+Subproject commit c9e249379698b92caeeb9486ce07237f664f48ac
diff --git a/Convention/[RScript]/Parser/ExpressionParser.cs b/Convention/[RScript]/Parser/ExpressionParser.cs
new file mode 100644
index 0000000..937bc95
--- /dev/null
+++ b/Convention/[RScript]/Parser/ExpressionParser.cs
@@ -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 CompileGenericExpression = new();
+ private readonly Dictionary CompileDynamicExpression = new();
+
+ public void ClearCache()
+ {
+ CompileGenericExpression.Clear();
+ CompileDynamicExpression.Clear();
+ }
+
+ public T Evaluate(string expression)
+ {
+ if (CompileGenericExpression.TryGetValue(expression, out var result))
+ {
+ return (result as IGenericExpression).Evaluate();
+ }
+ var compile = context.CompileGeneric(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();
+ }
+ }
+}
diff --git a/Convention/[RScript]/PublicTypes/RScriptException.cs b/Convention/[RScript]/PublicTypes/RScriptException.cs
new file mode 100644
index 0000000..0d865ec
--- /dev/null
+++ b/Convention/[RScript]/PublicTypes/RScriptException.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Convention.RScript
+{
+ [Serializable]
+ public class RScriptExceptionException : Exception
+ {
+ public RScriptExceptionException(string message, int runtimePointer) : base($"when running {runtimePointer}, {message}") { }
+ public RScriptExceptionException(string message, int runtimePointer, Exception inner) : base($"when running {runtimePointer}, {message}", inner) { }
+ }
+}
diff --git a/Convention/[RScript]/PublicTypes/RScriptImportClass.cs b/Convention/[RScript]/PublicTypes/RScriptImportClass.cs
new file mode 100644
index 0000000..1b0dee7
--- /dev/null
+++ b/Convention/[RScript]/PublicTypes/RScriptImportClass.cs
@@ -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
+ {
+ private readonly HashSet importedTypes = new();
+ private readonly Dictionary> cacheImportedFunctions = new();
+
+ public int Count => ((ICollection)importedTypes).Count;
+
+ public bool IsReadOnly => ((ICollection)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 GetEnumerator()
+ {
+ return ((IEnumerable)importedTypes).GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((IEnumerable)importedTypes).GetEnumerator();
+ }
+
+ public void Clear()
+ {
+ ((ICollection)importedTypes).Clear();
+ }
+
+ public bool Contains(Type item)
+ {
+ return ((ICollection)importedTypes).Contains(item);
+ }
+
+ public void CopyTo(Type[] array, int arrayIndex)
+ {
+ ((ICollection)importedTypes).CopyTo(array, arrayIndex);
+ }
+
+ public bool Remove(Type item)
+ {
+ return ((ICollection)importedTypes).Remove(item);
+ }
+ }
+}
diff --git a/Convention/[RScript]/PublicTypes/RScriptVariables.cs b/Convention/[RScript]/PublicTypes/RScriptVariables.cs
new file mode 100644
index 0000000..2502924
--- /dev/null
+++ b/Convention/[RScript]/PublicTypes/RScriptVariables.cs
@@ -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
+ {
+ private readonly Dictionary> 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 Keys => variables.Keys;
+
+ public ICollection 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 item)
+ {
+ Add(item.Key, item.Value);
+ }
+
+ public void ClearAllLayers()
+ {
+ variables.Clear();
+ }
+
+ ///
+ ///
+ ///
+ public void Clear()
+ {
+ ClearAllLayers();
+ }
+
+ public bool Contains(KeyValuePair 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[] array, int arrayIndex)
+ {
+ foreach (var (key, items) in variables)
+ {
+ array[arrayIndex++] = new(key, items.Peek());
+ }
+ }
+
+ public IEnumerator> GetEnumerator()
+ {
+ return (from items in variables select new KeyValuePair(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 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);
+ }
+ }
+}
diff --git a/Convention/[RScript]/RScriptContext.cs b/Convention/[RScript]/RScriptContext.cs
new file mode 100644
index 0000000..55fc4fb
--- /dev/null
+++ b/Convention/[RScript]/RScriptContext.cs
@@ -0,0 +1,310 @@
+using Convention.RScript.Parser;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace Convention.RScript
+{
+ public struct RScriptSentence
+ {
+ public enum Mode
+ {
+ ///
+ /// 表达式, 格式: 任意合法表达式
+ ///
+ Expression,
+ ///
+ /// 定义变量, 格式: 类型 变量名
+ /// 类型支持: string, int, double, float, bool, var
+ /// 每层命名空间中不可重复定义变量, 不可使用未定义的变量, 不存在时会自动向上查找上级空间的变量
+ ///
+ DefineVariable,
+ ///
+ /// 进入新的命名空间, 格式: {
+ /// 命名空间是一对花括号包裹内容空间, 格式: {...}
+ ///
+ EnterNamespace,
+ ///
+ /// 退出当前命名空间, 格式: }
+ /// 命名空间是一对花括号包裹内容空间, 格式: {...}
+ ///
+ ExitNamespace,
+ ///
+ /// 标签, 格式: label: 标签名
+ ///
+ Label,
+ ///
+ /// 跳转到指定标签, 格式: goto 标签名
+ ///
+ Goto,
+ ///
+ /// 条件判断, 格式: if (条件表达式)
+ ///
+ If
+ }
+
+ public string content;
+ public Mode mode;
+ }
+
+ public partial class RScriptContext
+ {
+ public readonly RScriptImportClass Import;
+ public readonly RScriptVariables Variables;
+ private readonly RScriptSentence[] Sentences;
+ private readonly Dictionary Labels;
+ private readonly Dictionary Namespace;
+
+ private static RScriptSentence ParseToSentence(string expression)
+ {
+ RScriptSentence result = new()
+ {
+ content = expression,
+ mode = RScriptSentence.Mode.Expression
+ };
+ expression = expression.Trim();
+ expression.TrimEnd(';');
+ if (expression == "{")
+ {
+ result.mode = RScriptSentence.Mode.EnterNamespace;
+ }
+ else if (expression == "}")
+ {
+ result.mode = RScriptSentence.Mode.ExitNamespace;
+ }
+
+ Regex DefineVariableRegex = new(@"^(string|int|double|float|bool|var) [a-zA-Z_][a-zA-Z0-9_]*$");
+ var DefineVariableMatch = DefineVariableRegex.Match(expression);
+ if (DefineVariableMatch.Success)
+ {
+ result.mode = RScriptSentence.Mode.DefineVariable;
+ return result;
+ }
+
+ Regex LabelRegex = new(@"^label:\s*([a-zA-Z_][a-zA-Z0-9_]*)$");
+ var LabelMatch = LabelRegex.Match(expression);
+ if (LabelMatch.Success)
+ {
+ result.mode = RScriptSentence.Mode.Label;
+ result.content = LabelMatch.Groups[1].Value;
+ return result;
+ }
+
+ Regex GotoRegex = new(@"^goto\s+([a-zA-Z_][a-zA-Z0-9_]*)$");
+ var GotoMatch = GotoRegex.Match(expression);
+ if (GotoMatch.Success)
+ {
+ result.mode = RScriptSentence.Mode.Goto;
+ result.content = GotoMatch.Groups[1].Value;
+ return result;
+ }
+
+ Regex IfRegex = new(@"^if\s*\((.*)\)$");
+ var IfMatch = IfRegex.Match(expression);
+ if (IfMatch.Success)
+ {
+ result.mode = RScriptSentence.Mode.If;
+ result.content = IfMatch.Groups[1].Value;
+ return result;
+ }
+
+ return result;
+ }
+ private void BuildUpLabelsAndNamespace(ref Dictionary labelIndicator, ref Dictionary namespaceIndicator)
+ {
+ Stack 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 RScriptExceptionException("Namespace exit without enter.", i);
+ var enterPointer = namespaceLayers.Pop();
+ namespaceIndicator[enterPointer] = i;
+ }
+ }
+ if (namespaceLayers.Count > 0)
+ {
+ throw new RScriptExceptionException("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 RunNextStep(ExpressionParser parser)
+ {
+ var sentence = CurrentSentence;
+ switch (sentence.mode)
+ {
+ case RScriptSentence.Mode.Expression:
+ {
+ // 执行表达式
+ parser.Evaluate(sentence.content);
+ }
+ break;
+ case RScriptSentence.Mode.DefineVariable:
+ {
+ // 定义变量
+ var content = sentence.content.Split(' ');
+ Type varType;
+ object varDefaultValue;
+ {
+ if (content[0] == "string")
+ {
+ varType = typeof(string);
+ varDefaultValue = string.Empty;
+ }
+ else if (content[0] == "int")
+ {
+ varType = typeof(int);
+ varDefaultValue = 0;
+ }
+ else if (content[0] == "double")
+ {
+ varType = typeof(double);
+ varDefaultValue = 0.0;
+ }
+ else if (content[0] == "float")
+ {
+ varType = typeof(float);
+ varDefaultValue = 0.0f;
+ }
+ else if (content[0] == "bool")
+ {
+ varType = typeof(bool);
+ varDefaultValue = false;
+ }
+ else if (content[0] == "var")
+ {
+ varType = typeof(object);
+ varDefaultValue = null;
+ }
+ else
+ {
+ throw new RScriptExceptionException($"Unsupported variable type '{content[0]}'.", CurrentRuntimePointer);
+ }
+ }
+ var varName = content[1];
+ if (CurrentLocalSpaceVariableNames.Contains(varName) == false)
+ {
+ Variables.Add(varName, new() { type = varType, data = varDefaultValue });
+ parser.context.Variables[varName] = varDefaultValue;
+ CurrentLocalSpaceVariableNames.Add(varName);
+ }
+ else
+ {
+ throw new RScriptExceptionException($"Variable '{varName}' already defined on this namespace.", CurrentRuntimePointer);
+ }
+ }
+ break;
+ case RScriptSentence.Mode.EnterNamespace:
+ {
+ // 准备记录当前命名空间中定义的变量, 清空上层命名空间的变量
+ CurrentLocalSpaceVariableNames.Clear();
+ // 更新变量值
+ foreach (var (varName, varValue) in parser.context.Variables)
+ {
+ Variables.SetValue(varName, varValue);
+ }
+ }
+ break;
+ case RScriptSentence.Mode.ExitNamespace:
+ {
+ // 移除在本命名空间中定义的变量
+ foreach (var local in CurrentLocalSpaceVariableNames)
+ {
+ Variables.Remove(local);
+ parser.context.Variables.Remove(local);
+ }
+ CurrentLocalSpaceVariableNames.Clear();
+ // 还原上层命名空间的变量
+ foreach (var local in Variables.Keys)
+ {
+ CurrentLocalSpaceVariableNames.Add(local);
+ parser.context.Variables[local] = Variables[local].data;
+ }
+ }
+ break;
+ case RScriptSentence.Mode.Goto:
+ {
+ // 跳转到指定标签
+ if (Labels.TryGetValue(sentence.content, out var labelPointer))
+ {
+ CurrentRuntimePointer = labelPointer;
+ }
+ else
+ {
+ throw new RScriptExceptionException($"Label '{sentence.content}' not found.", CurrentRuntimePointer);
+ }
+ }
+ break;
+ case RScriptSentence.Mode.If:
+ {
+ // 条件跳转
+ var conditionResult = parser.Evaluate(sentence.content);
+ if (conditionResult is bool b)
+ {
+ if (b == false)
+ {
+ if (Namespace.TryGetValue(CurrentRuntimePointer + 1, out var exitPointer) == false)
+ {
+ // 没有命名空间时只跳过下一句, +1后在外层循环末尾再+1, 最终结果为下一次循环开始时已经指向第二句
+ exitPointer = CurrentRuntimePointer + 1;
+ }
+ CurrentRuntimePointer = exitPointer;
+ }
+ }
+ else
+ {
+ throw new RScriptExceptionException($"If condition must be bool, but got {conditionResult?.GetType().ToString() ?? "null"}.", CurrentRuntimePointer);
+ }
+ }
+ break;
+ default:
+ // Do nothing
+ break;
+ }
+ }
+
+ private readonly Stack RuntimePointerStack = new();
+ private int CurrentRuntimePointer = 0;
+ private readonly HashSet CurrentLocalSpaceVariableNames = new();
+
+ public Dictionary Run(ExpressionParser parser)
+ {
+ CurrentLocalSpaceVariableNames.Clear();
+ RuntimePointerStack.Clear();
+ 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();
+ }
+ }
+}
diff --git a/Convention/[RScript]/RScriptEngine.cs b/Convention/[RScript]/RScriptEngine.cs
new file mode 100644
index 0000000..9e5f79c
--- /dev/null
+++ b/Convention/[RScript]/RScriptEngine.cs
@@ -0,0 +1,43 @@
+using Convention.RScript.Parser;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Convention.RScript
+{
+ public class RScriptEngine
+ {
+ private ExpressionParser parser;
+ private RScriptContext context;
+
+ public Dictionary Run(string script, RScriptImportClass import = null, RScriptVariables variables = null)
+ {
+ parser = new(new());
+ string newScript = "";
+ foreach (var item in script.Split('\n'))
+ {
+ var line = item.Trim();
+ if (string.IsNullOrEmpty(line))
+ continue;
+ if (line.StartsWith("//"))
+ continue;
+ if (line.StartsWith('#'))
+ continue;
+ newScript += line + ";"; // 添加分号分隔符
+ }
+
+ // 按分号分割并过滤空语句
+ var statements = newScript.Split(';', StringSplitOptions.RemoveEmptyEntries)
+ .Select(s => s.Trim())
+ .Where(s => !string.IsNullOrEmpty(s))
+ .ToArray();
+
+ context = new(statements, import, variables);
+ foreach (var type in context.Import)
+ parser.context.Imports.AddType(type);
+ return context.Run(parser);
+ }
+ }
+}
diff --git a/Convention/[RScript]/ScriptContent.cs b/Convention/[RScript]/ScriptContent.cs
deleted file mode 100644
index e499b0b..0000000
--- a/Convention/[RScript]/ScriptContent.cs
+++ /dev/null
@@ -1,325 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using System.Reflection;
-using System.Text.RegularExpressions;
-
-namespace Convention.RScript
-{
- public partial class ScriptContent
- {
- public object RuntimeBindingTarget;
-
- public struct RuntimeContext
- {
- public int iterator;
- public bool isResetIterator;
-
- public static RuntimeContext Create()
- {
- return new RuntimeContext()
- {
- iterator = 0,
- isResetIterator = false
- };
- }
-
- public RuntimeContext Clone()
- {
- return new()
- {
- iterator = iterator,
- isResetIterator = isResetIterator
- };
- }
- }
-
- public class ScriptDataEntry
- {
- public string text;
-
- public string command;
- public string[] parameters;
- public int iterator;
-
- public Func actor;
- }
-
- private List Commands = new();
-
- // 预处理工具
-
- public static int LastIndexOfNextTerminalTail(string text, int i, string terminal)
- {
- return text.IndexOf(terminal, i) + terminal.Length - 1;
- }
-
- public static bool StartWithSpecialTerminal(string text,int i)
- {
- switch (text[i])
- {
- case '{':
- case '}':
- return true;
- default:
- return false;
- }
- }
-
- public const string SingleLineTerminal = "//";
- public const string MultiLineBeginTerminal = "/*";
- public const string MultiLineEndTerminal = "*/";
- public const string TextTerminal = "\"";
-
- public static bool StartWithSingleLineTerminal(string text, int i)
- {
- int length = SingleLineTerminal.Length;
- if (text.Length - i < length)
- return false;
- return string.Compare(text, i, SingleLineTerminal, 0, length) == 0;
- }
-
- public static bool StartWithMultiLineTerminal(string text, int i)
- {
- int length = MultiLineBeginTerminal.Length;
- if (text.Length - i < length)
- return false;
- return string.Compare(text, i, MultiLineBeginTerminal, 0, length) == 0;
- }
-
- public static bool StartWithTextTerminal(string text, int i)
- {
- int length = TextTerminal.Length;
- if (text.Length - i < length)
- return false;
- return string.Compare(text, i, TextTerminal, 0, length) == 0;
- }
-
- public static int LastIndexOfNextTextTerminalTail(string text, int i,out string buffer)
- {
- char terminal = TextTerminal[0];
- int __head = i + 1;
- buffer = "";
- for (int __tail = text.Length; __head < __tail && text[__head] != terminal;)
- {
- if (text[__head] == '\\')
- {
- switch (text[__head+1])
- {
- case 'n':
- {
- buffer += '\n';
- }
- break;
- case 't':
- {
- buffer += '\t';
- }
- break;
- case 'r':
- {
- buffer += '\r';
- }
- break;
- case '0':
- {
- buffer += '\0';
- }
- break;
- default:
- {
- buffer += text[__head + 1];
- }
- break;
- }
- __head += 2;
- }
- else
- {
- buffer += text[__head];
- __head++;
- }
- }
- return __head;
- }
-
- // 文本预处理
- private void ScriptTextPreprocessing(string script)
- {
- string buffer = "";
- void PushBuffer()
- {
- if (string.IsNullOrEmpty(buffer) == false)
- {
- Commands.Add(new()
- {
- text = buffer.Trim(),
- iterator = Commands.Count
- });
- }
- buffer = "";
- }
- Func loopPr = x => x < script.Length;
- for (int i = 0; loopPr(i); i++)
- {
- // 切断语句\
- if (script[i] == ';')
- {
- PushBuffer();
- }
- // 读入特殊标记符
- else if (StartWithSpecialTerminal(script, i))
- {
- PushBuffer();
- buffer += script[i];
- PushBuffer();
- }
- // 跳过单行注释
- else if (StartWithSingleLineTerminal(script, i))
- {
- i = LastIndexOfNextTerminalTail(script, i, "\n");
- }
- // 跳过多行注释
- else if (StartWithMultiLineTerminal(script, i))
- {
- i = LastIndexOfNextTerminalTail(script, i, MultiLineEndTerminal);
- }
- // 读入文本
- else if (StartWithTextTerminal(script, i))
- {
- i = LastIndexOfNextTextTerminalTail(script, i, out var temp);
- buffer += temp;
- }
- // 读入
- else if (loopPr(i))
- buffer += script[i];
- }
- // 存在未完成的语句
- if (string.IsNullOrEmpty(buffer) == false)
- throw new ArgumentException("The script did not end with the correct terminator.");
- }
-
- // 指令预处理工具
-
- private Dictionary CommandIndexs = new();
- private Dictionary LabelIndexs = new();
-
- public const string GotoTerminal = "goto";
- public const string LabelTerminal = "label";
- public const string IfTerminal = "if";
-
- // 指令预处理
- private void CommandPreprocessing()
- {
- static GroupCollection Match(ScriptDataEntry entry, [StringSyntax(StringSyntaxAttribute.Regex)] string pattern)
- {
- Match match = Regex.Match(entry.text, pattern);
- if (!match.Success)
- throw new ArgumentException($"Script: \"{entry.text}\" is invalid statement");
- return match.Groups;
- }
-
- // 加入自由映射
- foreach (var entry in Commands)
- {
- CommandIndexs.Add(entry.iterator, entry);
- }
- // 匹配
- foreach (var entry in Commands)
- {
- if (
- // goto label-name;
- entry.text.StartsWith(GotoTerminal)||
- // label label-name;
- entry.text.StartsWith(LabelTerminal)
- )
- {
- var groups = Match(entry, @"^(\S*)\s+([^\s]+?)\s*;$");
- entry.command = groups[1].Value;
- entry.parameters = new string[] { groups[2].Value };
- // touch label
- if (entry.command == LabelTerminal)
- {
- LabelIndexs.Add(groups[2].Value, entry.iterator);
- }
- }
- else if (
- // if(expr)
- entry.text.StartsWith(IfTerminal)
- )
- {
- var groups = Match(entry, @"^(\S*)\s*(.*?)$");
- entry.command = groups[1].Value;
- entry.parameters = new string[] { groups[2].Value };
- }
- else
- {
- var groups = Match(entry, @"^(\w+)\s*\(\s*(.*?)\s*\)\s*;$");
- entry.command = groups[1].Value;
-
- static string[] ParseArguments(string argumentsString)
- {
- if (string.IsNullOrWhiteSpace(argumentsString))
- return new string[0];
-
- // 处理字符串字面量和普通参数的正则表达式
- string argPattern = @"""(?:[^""\\]|\\.)*""|[^,]+";
-
- return Regex.Matches(argumentsString, argPattern)
- .Cast()
- .Select(m => m.Value.Trim())
- .Where(arg => !string.IsNullOrEmpty(arg))
- .ToArray();
- }
-
- entry.parameters = ParseArguments(groups[2].Value);
- }
- }
- }
-
- // 指令转化
-
- private void LoopForTC2AC()
- {
- Dictionary cache = (from method in RuntimeBindingTarget.GetType().GetMethods()
- select new KeyValuePair(method.Name, method)).ToDictionary();
-
- for (int index = 0, e = Commands.Count; index < e; index++)
- {
- var entry = Commands[index];
- // Keywords
- if (entry.command == GotoTerminal)
- {
- entry.actor = context =>
- {
- var next = context.Clone();
- next.iterator = LabelIndexs[entry.parameters[0]];
- next.isResetIterator = true;
- return next;
- };
- }
- // Custom Binding
- else if(cache.TryGetValue(entry.command,out var methodInfo))
- {
- entry.actor = context =>
- {
-
- return context.Clone();
- };
- }
- else
- {
-
- }
- }
- }
-
- public void LoadScript(string script)
- {
- ScriptTextPreprocessing(script);
- CommandPreprocessing();
- LoopForTC2AC();
- }
- }
-}
diff --git a/Convention/[RScript]/ScriptRunner.cs b/Convention/[RScript]/ScriptRunner.cs
deleted file mode 100644
index 3a44379..0000000
--- a/Convention/[RScript]/ScriptRunner.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Convention.RScript
-{
- public class ScriptRunner
- {
- public ScriptContent BuildNewContent(object target)
- {
- return new()
- {
- RuntimeBindingTarget = target,
- };
- }
-
- public ScriptContent BuildNewContent()
- {
- return BuildNewContent(null);
- }
-
- public void RunScriptFromContent(ScriptContent content)
- {
-
- }
- }
-}
diff --git a/Convention/[Runtime]/Config.cs b/Convention/[Runtime]/Config.cs
index 9e09e71..ec3d497 100644
--- a/Convention/[Runtime]/Config.cs
+++ b/Convention/[Runtime]/Config.cs
@@ -66,7 +66,7 @@ namespace Convention
public static T ConvertValue(string str)
{
Type type = typeof(T);
- var parse_method = type.GetMethod("Parse");
+ var parse_method = type.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null);
if (parse_method != null &&
(parse_method.ReturnType.IsSubclassOf(type) || parse_method.ReturnType == type) &&
parse_method.GetParameters().Length == 1 &&
diff --git a/[Test]/FileTest.cs b/[Test]/FileTest.cs
deleted file mode 100644
index f03b2ee..0000000
--- a/[Test]/FileTest.cs
+++ /dev/null
@@ -1,214 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Convention;
-
-namespace Convention.Test
-{
- public class FileTest
- {
- public static void RunTests()
- {
- Console.WriteLine("=== ToolFile 功能测试 ===");
-
- // 测试基本文件操作
- TestBasicOperations();
-
- // 测试文件加载和保存
- TestLoadAndSave();
-
- // 测试压缩和解压
- TestCompression();
-
- // 测试加密和解密
- TestEncryption();
-
- // 测试哈希计算
- TestHash();
-
- // 测试备份功能
- TestBackup();
-
- // 测试权限管理
- TestPermissions();
-
- Console.WriteLine("=== 所有测试完成 ===");
- }
-
- private static void TestBasicOperations()
- {
- Console.WriteLine("\n--- 基本文件操作测试 ---");
-
- // 创建测试文件
- var testFile = new ToolFile("test_file.txt");
- testFile.SaveAsText("这是一个测试文件的内容");
-
- Console.WriteLine($"文件存在: {testFile.Exists()}");
- Console.WriteLine($"文件大小: {testFile.GetSize()} 字节");
- Console.WriteLine($"文件名: {testFile.GetFilename()}");
- Console.WriteLine($"文件扩展名: {testFile.GetExtension()}");
- Console.WriteLine($"文件目录: {testFile.GetDir()}");
- Console.WriteLine($"文件时间戳: {testFile.GetTimestamp()}");
-
- // 清理
- testFile.Remove();
- }
-
- private static void TestLoadAndSave()
- {
- Console.WriteLine("\n--- 文件加载和保存测试 ---");
-
- // 测试 JSON 保存和加载
- var jsonFile = new ToolFile("test_data.json");
- var testData = new { name = "测试", value = 123, items = new[] { "item1", "item2" } };
- jsonFile.SaveAsRawJson(testData);
-
- var loadedData = jsonFile.LoadAsRawJson();
- Console.WriteLine($"JSON 数据加载成功: {loadedData != null}");
-
- // 测试 CSV 保存和加载
- var csvFile = new ToolFile("test_data.csv");
- var csvData = new List
- {
- new[] { "姓名", "年龄", "城市" },
- new[] { "张三", "25", "北京" },
- new[] { "李四", "30", "上海" }
- };
- csvFile.SaveAsCsv(csvData);
-
- var loadedCsv = csvFile.LoadAsCsv();
- Console.WriteLine($"CSV 数据加载成功: {loadedCsv.Count} 行");
-
- // 清理
- jsonFile.Remove();
- csvFile.Remove();
- }
-
- private static void TestCompression()
- {
- Console.WriteLine("\n--- 压缩和解压测试 ---");
-
- // 创建测试文件
- var testFile = new ToolFile("test_compress.txt");
- testFile.SaveAsText("这是一个用于压缩测试的文件内容。".PadRight(1000, 'x'));
-
- // 压缩文件
- var compressedFile = testFile.Compress();
- Console.WriteLine($"压缩成功: {compressedFile.Exists()}");
- Console.WriteLine($"压缩后大小: {compressedFile.GetSize()} 字节");
-
- // 解压文件
- var decompressedDir = compressedFile.Decompress();
- Console.WriteLine($"解压成功: {decompressedDir.Exists()}");
-
- // 清理
- testFile.Remove();
- compressedFile.Remove();
- if (decompressedDir.Exists())
- decompressedDir.Remove();
- }
-
- private static void TestEncryption()
- {
- Console.WriteLine("\n--- 加密和解密测试 ---");
-
- // 创建测试文件
- var testFile = new ToolFile("test_encrypt.txt");
- testFile.SaveAsText("这是一个用于加密测试的敏感数据");
-
- // 加密文件
- testFile.Encrypt("mysecretkey123");
- Console.WriteLine($"加密成功: {testFile.Exists()}");
-
- // 解密文件
- testFile.Decrypt("mysecretkey123");
- var decryptedContent = testFile.LoadAsText();
- Console.WriteLine($"解密成功: {decryptedContent.Contains("敏感数据")}");
-
- // 清理
- testFile.Remove();
- }
-
- private static void TestHash()
- {
- Console.WriteLine("\n--- 哈希计算测试 ---");
-
- // 创建测试文件
- var testFile = new ToolFile("test_hash.txt");
- testFile.SaveAsText("这是一个用于哈希测试的文件内容");
-
- // 计算不同算法的哈希值
- var md5Hash = testFile.CalculateHash("MD5");
- var sha256Hash = testFile.CalculateHash("SHA256");
-
- Console.WriteLine($"MD5 哈希: {md5Hash}");
- Console.WriteLine($"SHA256 哈希: {sha256Hash}");
-
- // 验证哈希值
- var isValid = testFile.VerifyHash(md5Hash, "MD5");
- Console.WriteLine($"哈希验证: {isValid}");
-
- // 保存哈希值到文件
- var hashFile = testFile.SaveHash("MD5");
- Console.WriteLine($"哈希文件保存: {hashFile.Exists()}");
-
- // 清理
- testFile.Remove();
- hashFile.Remove();
- }
-
- private static void TestBackup()
- {
- Console.WriteLine("\n--- 备份功能测试 ---");
-
- // 创建测试文件
- var testFile = new ToolFile("test_backup.txt");
- testFile.SaveAsText("这是一个用于备份测试的文件内容");
-
- // 创建备份
- var backupFile = testFile.CreateBackup();
- Console.WriteLine($"备份创建成功: {backupFile.Exists()}");
-
- // 列出备份
- var backups = testFile.ListBackups();
- Console.WriteLine($"备份数量: {backups.Count}");
-
- // 恢复备份
- var restoredFile = testFile.RestoreBackup(backupFile.GetFullPath(), "restored_file.txt");
- Console.WriteLine($"备份恢复成功: {restoredFile.Exists()}");
-
- // 清理
- testFile.Remove();
- backupFile.Remove();
- restoredFile.Remove();
- }
-
- private static void TestPermissions()
- {
- Console.WriteLine("\n--- 权限管理测试 ---");
-
- // 创建测试文件
- var testFile = new ToolFile("test_permissions.txt");
- testFile.SaveAsText("这是一个用于权限测试的文件内容");
-
- // 获取权限
- var permissions = testFile.GetPermissions();
- Console.WriteLine($"读取权限: {permissions["read"]}");
- Console.WriteLine($"写入权限: {permissions["write"]}");
- Console.WriteLine($"隐藏属性: {permissions["hidden"]}");
-
- // 设置权限
- testFile.SetPermissions(hidden: true);
- var newPermissions = testFile.GetPermissions();
- Console.WriteLine($"设置隐藏后: {newPermissions["hidden"]}");
-
- // 检查权限
- Console.WriteLine($"可读: {testFile.IsReadable()}");
- Console.WriteLine($"可写: {testFile.IsWritable()}");
- Console.WriteLine($"隐藏: {testFile.IsHidden()}");
-
- // 清理
- testFile.Remove();
- }
- }
-}
\ No newline at end of file
diff --git a/[Test]/Program.cs b/[Test]/Program.cs
index 425a76a..102291a 100644
--- a/[Test]/Program.cs
+++ b/[Test]/Program.cs
@@ -1,10 +1,29 @@
-using System;
-using Convention.Test;
+using Convention.RScript;
+using Flee.PublicTypes;
+using System;
public class Program
{
static void Main(string[] args)
{
-
+ RScriptEngine engine = new();
+ RScriptImportClass import = new();
+ import.Add(typeof(Math));
+ var result = engine.Run(@"
+double x;
+double y;
+double z;
+double result;
+bool stats;
+stats = true;
+x = 10.0;
+y = 20.0;
+z = x + y;
+// This is a comment;
+# This is another comment;
+result = z * 2;
+result = Pow(result,2.0);
+", import);
+ Console.WriteLine($"Script executed successfully. Result: {result["result"].data}");
}
}
\ No newline at end of file