Add project files.
This commit is contained in:
232
Assets/Cryville/Common/Pdt/PdtInterpreter.cs
Normal file
232
Assets/Cryville/Common/Pdt/PdtInterpreter.cs
Normal file
@@ -0,0 +1,232 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Cryville.Common.Pdt {
|
||||
/// <summary>
|
||||
/// Interpreter for Property Definition Tree (PDT) file format.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The object type represented by the PDT.</typeparam>
|
||||
public partial class PdtInterpreter<T> {
|
||||
/// <summary>
|
||||
/// The character category map.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <list type="bullet">
|
||||
/// <item><term><c>0x0001</c></term><description>White Space</description></item>
|
||||
/// <item><term><c>0x0010</c></term><description>Identifier</description></item>
|
||||
/// <item><term><c>0x0020</c></term><description>Identifier Begin</description></item>
|
||||
/// <item><term><c>0x0040</c></term><description>Digit</description></item>
|
||||
/// <item><term><c>0x0080</c></term><description>Operator</description></item>
|
||||
/// <item><term><c>0x0100</c></term><description>String</description></item>
|
||||
/// <item><term><c>0x0200</c></term><description>Opening Bracket</description></item>
|
||||
/// <item><term><c>0x0400</c></term><description>Closing Bracket</description></item>
|
||||
/// <item><term><c>0x0800</c></term><description>End of Expression</description></item>
|
||||
/// <item><term><c>0x1000</c></term><description>End of Key</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
readonly static int[] cm = new int[] {
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0001, 0x0080, 0x0100, 0x0000, 0x0030, 0x0080, 0x0080, 0x0000, 0x0200, 0x0400, 0x0080, 0x0080, 0x0080, 0x0080, 0x0040, 0x0080,
|
||||
0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x1000, 0x1800, 0x0080, 0x0080, 0x0080, 0x0000,
|
||||
0x0080, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
|
||||
0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000, 0x0000, 0x0000, 0x0030,
|
||||
0x0000, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
|
||||
0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x1000, 0x0080, 0x1000, 0x0000, 0x0000,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Interprets a source string to an object of type <typeparamref name="T" />.
|
||||
/// </summary>
|
||||
/// <param name="src">The source string.</param>
|
||||
/// <returns>The interpreted object.</returns>
|
||||
public static T Interpret(string src) {
|
||||
return Interpret(src, BinderAttribute.CreateBinderOfType(typeof(T)));
|
||||
}
|
||||
/// <summary>
|
||||
/// Interprets a source string to an object of type <typeparamref name="T" /> with a binder.
|
||||
/// </summary>
|
||||
/// <param name="src">The source string.</param>
|
||||
/// <param name="binder">The binder.</param>
|
||||
/// <returns>The interpreted object.</returns>
|
||||
public static T Interpret(string src, Binder binder) {
|
||||
return new PdtInterpreter<T>(src, binder).Interpret();
|
||||
}
|
||||
|
||||
readonly string _src;
|
||||
readonly Binder _binder;
|
||||
protected int Position { get; private set; }
|
||||
#pragma warning disable IDE1006
|
||||
protected char cc { get { return _src[Position]; } }
|
||||
protected int ct { get { return cm[cc]; } }
|
||||
protected string tokenb(int flag) { // Token Whitelist
|
||||
int sp = Position;
|
||||
while ((ct & flag) == 0) Position++;
|
||||
return _src.Substring(sp, Position - sp);
|
||||
}
|
||||
protected string tokenw(int flag) { // Token Whitelist
|
||||
int sp = Position;
|
||||
while ((ct & flag) != 0) Position++;
|
||||
return _src.Substring(sp, Position - sp);
|
||||
}
|
||||
protected void ws() {
|
||||
while ((ct & 0x0001) != 0) Position++;
|
||||
}
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
protected char GetChar() {
|
||||
char r = cc;
|
||||
Position++;
|
||||
return r;
|
||||
}
|
||||
protected string GetIdentifier() {
|
||||
if ((ct & 0x0020) == 0) return "";
|
||||
return tokenw(0x0010);
|
||||
}
|
||||
protected string GetNumber() {
|
||||
return tokenw(0x0040);
|
||||
}
|
||||
protected string GetString() {
|
||||
int sp = Position;
|
||||
do {
|
||||
if (cc == '\\') Position++;
|
||||
Position++;
|
||||
} while (ct != 0x0100);
|
||||
Position++;
|
||||
return Regex.Replace(_src.Substring(sp + 1, Position - sp - 2), @"\\(.)", "$1");
|
||||
}
|
||||
protected PdtExpression GetExp() {
|
||||
var ins = new LinkedList<PdtInstruction>();
|
||||
int _;
|
||||
InterpretExp(ins, -2, out _);
|
||||
return new PdtExpression(ins);
|
||||
}
|
||||
|
||||
readonly Dictionary<string, PdtExpression> defs = new Dictionary<string, PdtExpression>();
|
||||
/// <summary>
|
||||
/// Creates an instance of the <see cref="PdtInterpreter{T}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="src">The source string.</param>
|
||||
/// <param name="binder">The binder. May be <c>null</c>.</param>
|
||||
public PdtInterpreter(string src, Binder binder) {
|
||||
_src = src;
|
||||
_binder = binder;
|
||||
if (_binder == null)
|
||||
_binder = BinderAttribute.CreateBinderOfType(typeof(T));
|
||||
}
|
||||
/// <summary>
|
||||
/// Interprets the source to an object of type <typeparamref name="T" />.
|
||||
/// </summary>
|
||||
/// <returns>The interpreted object.</returns>
|
||||
public T Interpret() {
|
||||
InterpretDirectives();
|
||||
return (T)InterpretObject(typeof(T));
|
||||
}
|
||||
void InterpretDirectives() {
|
||||
bool flag = false;
|
||||
ws();
|
||||
while (cc == '#') {
|
||||
Position++;
|
||||
switch (GetIdentifier()) {
|
||||
case "ver":
|
||||
ws();
|
||||
Logger.Log("main", 3, "PDT", "Legacy PDT directive #ver={0} found. Ignoring.", GetNumber());
|
||||
break;
|
||||
case "format":
|
||||
ws();
|
||||
if (GetNumber() != "1")
|
||||
throw new NotSupportedException("Format not supported");
|
||||
flag = true;
|
||||
break;
|
||||
case "define":
|
||||
if (!flag) throw new FormatException("Format directive not found");
|
||||
ws();
|
||||
string dname = GetIdentifier();
|
||||
ws();
|
||||
PdtExpression dexp = GetExp();
|
||||
defs.Add(dname, dexp);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported directive found");
|
||||
}
|
||||
ws();
|
||||
}
|
||||
if (!flag) throw new FormatException("Format directive not found");
|
||||
}
|
||||
object InterpretObject(Type type) {
|
||||
var result = ReflectionHelper.InvokeEmptyConstructor(type);
|
||||
bool dictflag = ReflectionHelper.IsGenericDictionary(type);
|
||||
while (true) {
|
||||
try { ws(); }
|
||||
catch (IndexOutOfRangeException) { return result; }
|
||||
object pkey = InterpretKey(type);
|
||||
char c = GetChar();
|
||||
switch (c) {
|
||||
case '{':
|
||||
if (dictflag) {
|
||||
var ktype = type.GetGenericArguments()[0];
|
||||
var ptype = type.GetGenericArguments()[1];
|
||||
object key = _binder.ChangeType(pkey, ktype, null);
|
||||
object value = InterpretObject(ptype);
|
||||
((IDictionary)result).Add(key, value);
|
||||
}
|
||||
else {
|
||||
MemberInfo prop;
|
||||
bool flag = ReflectionHelper.TryFindMemberWithAttribute<ElementListAttribute>(type, out prop);
|
||||
if (!flag && pkey is string) prop = ReflectionHelper.GetMember(type, (string)pkey);
|
||||
Type ptype = ReflectionHelper.GetMemberType(prop);
|
||||
if (ReflectionHelper.IsGenericDictionary(ptype)) {
|
||||
var ktype = ptype.GetGenericArguments()[0];
|
||||
var vtype = ptype.GetGenericArguments()[1];
|
||||
if (flag) {
|
||||
object key = _binder.ChangeType(pkey, ktype, null);
|
||||
object value = InterpretObject(vtype);
|
||||
((IDictionary)ReflectionHelper.GetValue(prop, result)).Add(key, value);
|
||||
}
|
||||
else ReflectionHelper.SetValue(prop, result, InterpretObject(ptype));
|
||||
}
|
||||
else ReflectionHelper.SetValue(prop, result, InterpretObject(ptype));
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
case ';':
|
||||
var exp = c == ';' ? _emptyexp : GetExp();
|
||||
if (dictflag) {
|
||||
var ktype = type.GetGenericArguments()[0];
|
||||
var vtype = type.GetGenericArguments()[1];
|
||||
object key = _binder.ChangeType(pkey, ktype, null);
|
||||
object value = _binder.ChangeType(exp, vtype, null);
|
||||
((IDictionary)result).Add(key, value);
|
||||
}
|
||||
else {
|
||||
MemberInfo prop;
|
||||
bool flag = ReflectionHelper.TryFindMemberWithAttribute<PropertyListAttribute>(type, out prop);
|
||||
if (!flag && pkey is string) prop = ReflectionHelper.GetMember(type, (string)pkey);
|
||||
var ptype = ReflectionHelper.GetMemberType(prop);
|
||||
if (!typeof(IDictionary).IsAssignableFrom(ptype)) {
|
||||
object value = _binder.ChangeType(exp, ptype, null);
|
||||
ReflectionHelper.SetValue(prop, result, value, _binder);
|
||||
}
|
||||
else {
|
||||
var ktype = ptype.GetGenericArguments()[0];
|
||||
var vtype = ptype.GetGenericArguments()[1];
|
||||
object key = _binder.ChangeType(pkey, ktype, null);
|
||||
object value = _binder.ChangeType(exp, vtype, null);
|
||||
((IDictionary)ReflectionHelper.GetValue(prop, result)).Add(key, value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '}':
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected virtual object InterpretKey(Type type) {
|
||||
return tokenb(0x1000).Trim();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user