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;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading;
|
|
||||||
using Convention;
|
|
||||||
using Convention.EasySave;
|
|
||||||
using Convention.Symbolization;
|
|
||||||
using Convention.Test;
|
using Convention.Test;
|
||||||
|
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
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