diff --git a/ChaosUtil.Debug.csproj b/ChaosUtil.Debug.csproj
index 37ec86c..a9117ea 100644
--- a/ChaosUtil.Debug.csproj
+++ b/ChaosUtil.Debug.csproj
@@ -23,6 +23,7 @@
+
diff --git a/source/AdvancedDebuggerDisplay/EvaluatedToken.cs b/source/AdvancedDebuggerDisplay/EvaluatedToken.cs
new file mode 100644
index 0000000..c7c0dac
--- /dev/null
+++ b/source/AdvancedDebuggerDisplay/EvaluatedToken.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Linq;
+
+namespace ChaosUtil.Debug.AdvancedDebuggerDisplay
+{
+ class EvaluatedToken : Token
+ {
+ static string Trim(string str) => str.Trim();
+
+ readonly Type targetType;
+ readonly object obj;
+
+ public EvaluatedToken(object obj, string token)
+ : base(token)
+ {
+ Type targetType = obj.GetType();
+ foreach (string member in token.Split('.').Select(Trim))
+ {
+ // TODO: support indexers
+
+ if (Members.Field.GetValue(ref targetType, ref obj, member))
+ continue;
+
+ if (Members.Property.GetValue(ref targetType, ref obj, member))
+ continue;
+
+ if (Members.Method.GetValue(ref targetType, ref obj, member))
+ continue;
+
+ throw new Exception($"Could not evaluate \"{member}\".");
+ }
+
+ this.targetType = targetType;
+ this.obj = obj;
+ }
+
+ public override string Value()
+ => AdvancedDebuggerDisplayExtensions.DebugDisplayInternal(targetType, obj);
+ }
+}
diff --git a/source/AdvancedDebuggerDisplay/Members/Field.cs b/source/AdvancedDebuggerDisplay/Members/Field.cs
new file mode 100644
index 0000000..50af031
--- /dev/null
+++ b/source/AdvancedDebuggerDisplay/Members/Field.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Reflection;
+using FieldMap = System.Collections.Generic.Dictionary<
+ System.Type,
+ System.Collections.Generic.Dictionary<
+ string,
+ System.Reflection.FieldInfo
+ >
+ >;
+
+namespace ChaosUtil.Debug.AdvancedDebuggerDisplay.Members
+{
+ static class Field
+ {
+ static readonly FieldMap fieldMap = new FieldMap();
+
+ public static bool GetValue(ref Type targetType, ref object obj, string fieldName)
+ {
+ Type t = targetType;
+ FieldInfo field = MemberMap.GetMember(fieldMap, targetType, fieldName, flags => t.GetField(fieldName, flags));
+ if (field != null)
+ {
+ obj = field.GetValue(obj);
+ targetType = field.FieldType;
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/source/AdvancedDebuggerDisplay/Members/MemberMap.cs b/source/AdvancedDebuggerDisplay/Members/MemberMap.cs
new file mode 100644
index 0000000..bcf7be5
--- /dev/null
+++ b/source/AdvancedDebuggerDisplay/Members/MemberMap.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Reflection;
+using SysCol = System.Collections.Generic;
+
+namespace ChaosUtil.Debug.AdvancedDebuggerDisplay.Members
+{
+ static class MemberMap
+ {
+ public static Member GetMember(
+ SysCol.Dictionary> memberMap,
+ Type type,
+ string memberName,
+ Func getMember
+ )
+ where Member : class
+ {
+ SysCol.Dictionary typeMemberMap;
+ if (!memberMap.TryGetValue(type, out typeMemberMap))
+ memberMap[type] = typeMemberMap = new SysCol.Dictionary();
+
+ Member member;
+ if (!typeMemberMap.TryGetValue(memberName, out member))
+ typeMemberMap[memberName] = member = getMember(BindingFlags.Public | BindingFlags.Instance) ?? getMember(BindingFlags.NonPublic | BindingFlags.Instance);
+
+ return member;
+ }
+ }
+}
diff --git a/source/AdvancedDebuggerDisplay/Members/Method.cs b/source/AdvancedDebuggerDisplay/Members/Method.cs
new file mode 100644
index 0000000..4f9a95a
--- /dev/null
+++ b/source/AdvancedDebuggerDisplay/Members/Method.cs
@@ -0,0 +1,98 @@
+using ChaosUtil.Primitives;
+using System;
+using System.Reflection;
+using MethodMap = System.Collections.Generic.Dictionary<
+ System.Type,
+ System.Collections.Generic.Dictionary<
+ string,
+ System.Reflection.MethodInfo
+ >
+ >;
+
+namespace ChaosUtil.Debug.AdvancedDebuggerDisplay.Members
+{
+ static class Method
+ {
+ static readonly MethodMap methodMap = new MethodMap();
+
+ static readonly MethodInfo debugDisplayMethod = typeof(AdvancedDebuggerDisplayExtensions).GetMethod(
+ nameof(AdvancedDebuggerDisplayExtensions.DebugDisplay),
+ BindingFlags.Public | BindingFlags.Static
+ );
+
+ public static bool GetValue(ref Type targetType, ref object obj, string member)
+ => GetMethodObject(ref targetType, ref obj, member)
+ || GetMethodInvocation(ref targetType, ref obj, member);
+
+ static MethodInfo FindMethod(Type targetType, string methodName, BindingFlags flags)
+ {
+ MethodInfo meth = targetType.GetMethod(methodName, flags, null, Array.empty, Array.empty);
+ if (meth != null)
+ return meth;
+
+ // TODO: properly support extension methods
+ if (methodName == nameof(AdvancedDebuggerDisplayExtensions.DebugDisplay))
+ return debugDisplayMethod.MakeGenericMethod(targetType);
+
+ return null;
+ }
+
+ public static bool GetMethodObject(ref Type targetType, ref object obj, string methodName)
+ {
+ Type t = targetType;
+ MethodInfo meth = MemberMap.GetMember(
+ methodMap,
+ targetType,
+ methodName,
+ flags => FindMethod(t, methodName, flags)
+ );
+ if (meth != null)
+ {
+ targetType = typeof(MethodInfo);
+ obj = meth;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ public static bool GetMethodInvocation(ref Type targetType, ref object obj, string invocation)
+ {
+ if (invocation.Length < 3)
+ return false; // at least one letter for method name plus one pair of parantheses
+
+ int paraOpen = invocation.IndexOf('(');
+ if (paraOpen < 0)
+ return false;
+
+ int paraClose = invocation.LastIndexOf(')');
+ if (paraClose != invocation.Length - 1)
+ return false;
+
+ int argsLength = paraClose - paraOpen - 1;
+ if (argsLength != 0)
+ // TODO: support method arguments
+ throw new NotImplementedException("Currently only parameterless method calls are supported.");
+
+ string methodName = invocation.Substring(0, paraOpen);
+ Type t = targetType;
+ MethodInfo meth = MemberMap.GetMember(
+ methodMap,
+ targetType,
+ methodName,
+ flags => FindMethod(t, methodName, flags)
+ );
+ if (meth != null)
+ {
+ if (meth.IsStatic)
+ obj = meth.Invoke(null, new[] { obj });
+ else
+ obj = meth.Invoke(obj, Array