Skip to content

Commit c8dd0e9

Browse files
committed
Fixes #49; advances #63
1 parent 1845e49 commit c8dd0e9

File tree

9 files changed

+115
-58
lines changed

9 files changed

+115
-58
lines changed

ExpressionToString.sln

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 15
4-
VisualStudioVersion = 15.0.28307.705
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.29009.5
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExpressionToString", "ExpressionToString\ExpressionToString.csproj", "{909307C8-6E39-43B8-A0C9-6AB9FC42D1B1}"
77
EndProject

Shared/CSharpCodeWriter.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ public class CSharpCodeWriter : WriterBase {
1919
public CSharpCodeWriter(object o) : base(o, FormatterNames.CSharp) { }
2020
public CSharpCodeWriter(object o, out Dictionary<string, (int start, int length)> pathSpans) : base(o, FormatterNames.CSharp, out pathSpans) { }
2121

22-
// TODO handle order of operations -- https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/
23-
2422
private static readonly Dictionary<ExpressionType, string> simpleBinaryOperators = new Dictionary<ExpressionType, string>() {
2523
[Add] = "+",
2624
[AddChecked] = "+",

Shared/Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<Compile Include="$(MSBuildThisFileDirectory)FactoryMethodsFormatter.cs" />
1515
<Compile Include="$(MSBuildThisFileDirectory)Util\Extensions\BlockExpression.cs" />
1616
<Compile Include="$(MSBuildThisFileDirectory)Util\Extensions\IListT.cs" />
17+
<Compile Include="$(MSBuildThisFileDirectory)Util\Extensions\Match.cs" />
1718
<Compile Include="$(MSBuildThisFileDirectory)Util\Extensions\MethodInfo.cs" />
1819
<Compile Include="$(MSBuildThisFileDirectory)Util\Extensions\ParameterInfo.cs" />
1920
<Compile Include="$(MSBuildThisFileDirectory)VBExpressionMetadata.cs" />

Shared/Util/Extensions/Match.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Text.RegularExpressions;
6+
using System.Threading.Tasks;
7+
8+
namespace ExpressionToString.Util {
9+
public static class MatchExtensions {
10+
public static void Deconstruct(this Match match, out string item1, out string item2) {
11+
item1 = match.Groups[1].Value;
12+
item2 = match.Groups[2].Value;
13+
}
14+
}
15+
}

Shared/Util/Functions.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using System.Linq.Expressions;
55
using System.Reflection;
6+
using System.Text.RegularExpressions;
67
using static ExpressionToString.FormatterNames;
78

89
namespace ExpressionToString.Util {
@@ -88,7 +89,6 @@ public static (bool isLiteral, string repr) TryRenderLiteral(object o, string la
8889
ret = $"{{ {values} }}";
8990
}
9091
} else if (type.IsTupleType()) {
91-
// TODO render System.Tuple using Tuple.Create("abcd",5) ? #Tuple?
9292
ret = "(" + TupleValues(o).Select(x => RenderLiteral(x, language)).Joined(", ") + ")";
9393
} else if (type.IsNumeric()) {
9494
ret = o.ToString();
@@ -281,5 +281,16 @@ public static string ResolveLanguage(string language) {
281281
return CSharp;
282282
}
283283
}
284+
285+
static Regex re = new Regex(@"(?:^|\.)(\w+)(?:\[(\d+)\])?");
286+
public static object ResolvePath(object o, string path) {
287+
foreach (var (propertyName, index) in re.Matches(path).Cast<Match>()) {
288+
o = o.GetType().GetProperty(propertyName).GetValue(o);
289+
if (!index.IsNullOrWhitespace()) {
290+
o = o.GetType().GetIndexers(true).Single(x => x.GetIndexParameters().Single().ParameterType == typeof(int)).GetValue(o, new object[] { int.Parse(index) });
291+
}
292+
}
293+
return o;
294+
}
284295
}
285296
}

Visualizer.Shared/VisualizerData.cs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,18 @@ public string Language {
3333
set => this.NotifyChanged(ref _language, value, args => PropertyChanged?.Invoke(this, args));
3434
}
3535

36+
public string Path { get; set; }
37+
3638
[field: NonSerialized]
3739
public event PropertyChangedEventHandler PropertyChanged;
40+
41+
public VisualizerDataOptions(VisualizerDataOptions options = null) {
42+
if (options != null) {
43+
_formatter = options.Formatter;
44+
_language = options.Language;
45+
Path = options.Path;
46+
}
47+
}
3848
}
3949

4050
[Serializable]
@@ -70,6 +80,9 @@ public VisualizerData() { }
7080

7181
public VisualizerData(object o, VisualizerDataOptions options = null) {
7282
Options = options ?? new VisualizerDataOptions();
83+
if (!options.Path.IsNullOrWhitespace()) {
84+
o = (ResolvePath(o, options.Path) as Expression).ExtractValue();
85+
}
7386
Source = WriterBase.Create(o, Options.Formatter, Options.Language, out var pathSpans).ToString();
7487
PathSpans = pathSpans;
7588
CollectedEndNodes = new List<ExpressionNodeData>();
@@ -130,6 +143,7 @@ public class ExpressionNodeData : INotifyPropertyChanged {
130143
private List<(string @namespace, string typename)> _baseTypes;
131144
public List<(string @namespace, string typename)> BaseTypes => _baseTypes;
132145
public string WatchExpressionFormatString { get; set; }
146+
public bool EnableValueInNewWindow { get; set; }
133147

134148
public EndNodeData EndNodeData => new EndNodeData {
135149
Closure = Closure,
@@ -180,17 +194,19 @@ internal ExpressionNodeData(object o, (string aggregatePath, string pathFromPare
180194
break;
181195
}
182196

197+
object value = null;
198+
183199
// fill StringValue and EndNodeType properties, for expressions
184200
switch (expr) {
185201
case ConstantExpression cexpr when !cexpr.Type.IsClosureClass():
186-
StringValue = StringValue(cexpr.Value, language);
202+
value = cexpr.Value;
187203
EndNodeType = Constant;
188204
break;
189205
case ParameterExpression pexpr1:
190206
EndNodeType = Parameter;
191207
break;
192208
case Expression e1 when expr.IsClosedVariable():
193-
StringValue = StringValue(expr.ExtractValue(), language);
209+
value = expr.ExtractValue();
194210
EndNodeType = ClosedVar;
195211
break;
196212
case DefaultExpression defexpr:
@@ -199,6 +215,11 @@ internal ExpressionNodeData(object o, (string aggregatePath, string pathFromPare
199215
}
200216
if (EndNodeType != null) { visualizerData.CollectedEndNodes.Add(this); }
201217

218+
if (value != null) {
219+
StringValue = StringValue(value, language);
220+
EnableValueInNewWindow = value.GetType().InheritsFromOrImplementsAny(NodeTypes);
221+
}
222+
202223
break;
203224
case MemberBinding mbind:
204225
NodeType = mbind.BindingType.ToString();
@@ -222,7 +243,7 @@ internal ExpressionNodeData(object o, (string aggregatePath, string pathFromPare
222243
WatchExpressionFormatString = "{0}";
223244
} else if (pi != null) {
224245
var watchPathFromParent = PathFromParent;
225-
if (visualizerData.Options.Language==CSharp) {
246+
if (visualizerData.Options.Language == CSharp) {
226247
WatchExpressionFormatString = $"(({pi.DeclaringType.FullName}){parentWatchExpression}).{watchPathFromParent}";
227248
} else { //VisualBasic
228249
watchPathFromParent = watchPathFromParent.Replace("[", "(").Replace("]", ")");
@@ -257,12 +278,14 @@ internal ExpressionNodeData(object o, (string aggregatePath, string pathFromPare
257278
// populate URLs
258279
if (pi != null) {
259280
ParentProperty = (pi.DeclaringType.Namespace, pi.DeclaringType.Name, pi.Name);
260-
}
281+
}
261282

262283
if (!baseTypes.TryGetValue(o.GetType(), out _baseTypes)) {
263284
_baseTypes = o.GetType().BaseTypes(true, true).Where(x => x != typeof(object) && x.IsPublic).Select(x => (x.Namespace, x.Name)).Distinct().ToList();
264285
baseTypes[o.GetType()] = _baseTypes;
265286
}
287+
288+
266289
}
267290

268291
private static List<(Type, string[])> preferredPropertyOrders = new List<(Type, string[])> {
@@ -313,6 +336,3 @@ public enum EndNodeTypes {
313336
Default
314337
}
315338
}
316-
317-
318-
// TODO write method to load span into this ExpressionNodeData

Visualizer.Shared/VisualizerDataControl.xaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<my:RootConverter x:Key="RootConverter" />
44
<my:ConditionalFormatConverter x:Key="ConditionalFormatConverter" />
55
<my:AnyVisibilityConverter x:Key="AnyVisibilityConverter" />
6+
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
67
<SolidColorBrush x:Key="TypeColor" Color="#066555" />
78
<HierarchicalDataTemplate x:Key="ExpressionNodeTemplate" ItemsSource="{Binding Children}">
89
<TextBlock>
@@ -23,6 +24,7 @@
2324
<TextBlock.ContextMenu>
2425
<ContextMenu>
2526
<MenuItem Header="Copy watch expression" Click="CopyWatchExpression_Click" />
27+
<MenuItem Header="Open value in new window" Click="OpenNewWindow_Click" Visibility="{Binding EnableValueInNewWindow, Converter={StaticResource BooleanToVisibilityConverter}}" />
2628
<MenuItem Header="Help" Loaded="HelpContextMenu_Loaded" />
2729
</ContextMenu>
2830
</TextBlock.ContextMenu>

Visualizer.Shared/VisualizerDataControl.xaml.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,5 +194,15 @@ private void CopyWatchExpression_Click(object sender, RoutedEventArgs e) {
194194
var node = (ExpressionNodeData)((MenuItem)sender).DataContext;
195195
Clipboard.SetText(string.Format(node.WatchExpressionFormatString, txbRootExpression.Text));
196196
}
197+
198+
private void OpenNewWindow_Click(object sender, RoutedEventArgs e) {
199+
var options = new VisualizerDataOptions(_options);
200+
options.Path = ((ExpressionNodeData)((MenuItem)sender).DataContext).FullPath;
201+
var window = new VisualizerWindow();
202+
var control = window.Content as VisualizerDataControl;
203+
control.ObjectProvider = ObjectProvider;
204+
control.Options = options;
205+
window.ShowDialog();
206+
}
197207
}
198208
}

_visualizerTests/Program.cs

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ static void Main(string[] args) {
5656
//var expr = foo.GetExpression();
5757

5858
//var i = 5;
59-
//Expression<Func<Expression<Func<string>>>> expr = () => expr1;
59+
Expression<Func<int>> expr1 = () => 5;
60+
Expression<Func<Expression<Func<int>>>> expr = () => expr1;
6061

6162
//Expression<Func<string>> expr = Lambda<Func<string>>(
6263
// MakeMemberAccess(
@@ -85,50 +86,50 @@ static void Main(string[] args) {
8586
//IQueryable<Person> personSource = null;
8687
//Expression<Func<Person, bool>> expr = person => person.LastName.StartsWith("A");
8788

88-
var hour = Variable(typeof(int), "hour");
89-
var msg = Variable(typeof(string), "msg");
90-
var block = Block(
91-
// specify the variables available within the block
92-
new[] { hour, msg },
93-
// hour =
94-
Assign(hour,
95-
// DateTime.Now.Hour
96-
MakeMemberAccess(
97-
MakeMemberAccess(
98-
null,
99-
typeof(DateTime).GetMember("Now").Single()
100-
),
101-
typeof(DateTime).GetMember("Hour").Single()
102-
)
103-
),
104-
// if ( ... ) { ... } else { ... }
105-
IfThenElse(
106-
// ... && ...
107-
AndAlso(
108-
// hour >= 6
109-
GreaterThanOrEqual(
110-
hour,
111-
Constant(6)
112-
),
113-
// hour <= 18
114-
LessThanOrEqual(
115-
hour,
116-
Constant(18)
117-
)
118-
),
119-
// msg = "Good day"
120-
Assign(msg, Constant("Good day")),
121-
// msg = Good night"
122-
Assign(msg, Constant("Good night"))
123-
),
124-
// Console.WriteLine(msg);
125-
Call(
126-
typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }),
127-
msg
128-
),
129-
hour
130-
);
131-
Expression<Action> expr = Lambda<Action>(block);
89+
//var hour = Variable(typeof(int), "hour");
90+
//var msg = Variable(typeof(string), "msg");
91+
//var block = Block(
92+
// // specify the variables available within the block
93+
// new[] { hour, msg },
94+
// // hour =
95+
// Assign(hour,
96+
// // DateTime.Now.Hour
97+
// MakeMemberAccess(
98+
// MakeMemberAccess(
99+
// null,
100+
// typeof(DateTime).GetMember("Now").Single()
101+
// ),
102+
// typeof(DateTime).GetMember("Hour").Single()
103+
// )
104+
// ),
105+
// // if ( ... ) { ... } else { ... }
106+
// IfThenElse(
107+
// // ... && ...
108+
// AndAlso(
109+
// // hour >= 6
110+
// GreaterThanOrEqual(
111+
// hour,
112+
// Constant(6)
113+
// ),
114+
// // hour <= 18
115+
// LessThanOrEqual(
116+
// hour,
117+
// Constant(18)
118+
// )
119+
// ),
120+
// // msg = "Good day"
121+
// Assign(msg, Constant("Good day")),
122+
// // msg = Good night"
123+
// Assign(msg, Constant("Good night"))
124+
// ),
125+
// // Console.WriteLine(msg);
126+
// Call(
127+
// typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }),
128+
// msg
129+
// ),
130+
// hour
131+
//);
132+
//Expression<Action> expr = Lambda<Action>(block);
132133

133134
//var constant = Constant(new List<int>());
134135
//Expression expr = Or(
@@ -215,7 +216,6 @@ static void Main(string[] args) {
215216
//Console.ReadKey(true);
216217
}
217218

218-
219219
static Expression<Func<int, int>> expr1 = ((Func<Expression<Func<int, int>>>)(() => {
220220
var value = Parameter(typeof(int), "value");
221221
var result = Parameter(typeof(int), "result");

0 commit comments

Comments
 (0)