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