EP RScript
This commit is contained in:
325
Convention/[RScript]/ScriptContent.cs
Normal file
325
Convention/[RScript]/ScriptContent.cs
Normal file
@@ -0,0 +1,325 @@
|
||||
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<RuntimeContext, RuntimeContext> actor;
|
||||
}
|
||||
|
||||
private List<ScriptDataEntry> 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<int, bool> 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<int, ScriptDataEntry> CommandIndexs = new();
|
||||
private Dictionary<string, int> 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}\"<command iterator: {entry.iterator}> 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<Match>()
|
||||
.Select(m => m.Value.Trim())
|
||||
.Where(arg => !string.IsNullOrEmpty(arg))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
entry.parameters = ParseArguments(groups[2].Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 指令转化
|
||||
|
||||
private void LoopForTC2AC()
|
||||
{
|
||||
Dictionary<string, MethodInfo> cache = (from method in RuntimeBindingTarget.GetType().GetMethods()
|
||||
select new KeyValuePair<string, MethodInfo>(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();
|
||||
}
|
||||
}
|
||||
}
|
29
Convention/[RScript]/ScriptRunner.cs
Normal file
29
Convention/[RScript]/ScriptRunner.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,23 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Convention;
|
||||
using Convention.EasySave;
|
||||
using Convention.Symbolization;
|
||||
using Convention.Test;
|
||||
|
||||
public class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Convention-CSharp 测试程序");
|
||||
Console.WriteLine("==========================");
|
||||
|
||||
// 运行文件功能测试
|
||||
FileTest.RunTests();
|
||||
|
||||
Console.WriteLine("\n按任意键退出...");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user