Skip to content

Commit

Permalink
Fixes #49; advances #63
Browse files Browse the repository at this point in the history
  • Loading branch information
zspitz committed Jun 25, 2019
1 parent 1845e49 commit c8dd0e9
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 58 deletions.
4 changes: 2 additions & 2 deletions ExpressionToString.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.705
# Visual Studio Version 16
VisualStudioVersion = 16.0.29009.5
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExpressionToString", "ExpressionToString\ExpressionToString.csproj", "{909307C8-6E39-43B8-A0C9-6AB9FC42D1B1}"
EndProject
Expand Down
2 changes: 0 additions & 2 deletions Shared/CSharpCodeWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ public class CSharpCodeWriter : WriterBase {
public CSharpCodeWriter(object o) : base(o, FormatterNames.CSharp) { }
public CSharpCodeWriter(object o, out Dictionary<string, (int start, int length)> pathSpans) : base(o, FormatterNames.CSharp, out pathSpans) { }

// TODO handle order of operations -- https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/

private static readonly Dictionary<ExpressionType, string> simpleBinaryOperators = new Dictionary<ExpressionType, string>() {
[Add] = "+",
[AddChecked] = "+",
Expand Down
1 change: 1 addition & 0 deletions Shared/Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<Compile Include="$(MSBuildThisFileDirectory)FactoryMethodsFormatter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Util\Extensions\BlockExpression.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Util\Extensions\IListT.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Util\Extensions\Match.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Util\Extensions\MethodInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Util\Extensions\ParameterInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)VBExpressionMetadata.cs" />
Expand Down
15 changes: 15 additions & 0 deletions Shared/Util/Extensions/Match.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace ExpressionToString.Util {
public static class MatchExtensions {
public static void Deconstruct(this Match match, out string item1, out string item2) {
item1 = match.Groups[1].Value;
item2 = match.Groups[2].Value;
}
}
}
13 changes: 12 additions & 1 deletion Shared/Util/Functions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.RegularExpressions;
using static ExpressionToString.FormatterNames;

namespace ExpressionToString.Util {
Expand Down Expand Up @@ -88,7 +89,6 @@ public static (bool isLiteral, string repr) TryRenderLiteral(object o, string la
ret = $"{{ {values} }}";
}
} else if (type.IsTupleType()) {
// TODO render System.Tuple using Tuple.Create("abcd",5) ? #Tuple?
ret = "(" + TupleValues(o).Select(x => RenderLiteral(x, language)).Joined(", ") + ")";
} else if (type.IsNumeric()) {
ret = o.ToString();
Expand Down Expand Up @@ -281,5 +281,16 @@ public static string ResolveLanguage(string language) {
return CSharp;
}
}

static Regex re = new Regex(@"(?:^|\.)(\w+)(?:\[(\d+)\])?");
public static object ResolvePath(object o, string path) {
foreach (var (propertyName, index) in re.Matches(path).Cast<Match>()) {
o = o.GetType().GetProperty(propertyName).GetValue(o);
if (!index.IsNullOrWhitespace()) {
o = o.GetType().GetIndexers(true).Single(x => x.GetIndexParameters().Single().ParameterType == typeof(int)).GetValue(o, new object[] { int.Parse(index) });
}
}
return o;
}
}
}
34 changes: 27 additions & 7 deletions Visualizer.Shared/VisualizerData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,18 @@ public string Language {
set => this.NotifyChanged(ref _language, value, args => PropertyChanged?.Invoke(this, args));
}

public string Path { get; set; }

[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;

public VisualizerDataOptions(VisualizerDataOptions options = null) {
if (options != null) {
_formatter = options.Formatter;
_language = options.Language;
Path = options.Path;
}
}
}

[Serializable]
Expand Down Expand Up @@ -70,6 +80,9 @@ public VisualizerData() { }

public VisualizerData(object o, VisualizerDataOptions options = null) {
Options = options ?? new VisualizerDataOptions();
if (!options.Path.IsNullOrWhitespace()) {
o = (ResolvePath(o, options.Path) as Expression).ExtractValue();
}
Source = WriterBase.Create(o, Options.Formatter, Options.Language, out var pathSpans).ToString();
PathSpans = pathSpans;
CollectedEndNodes = new List<ExpressionNodeData>();
Expand Down Expand Up @@ -130,6 +143,7 @@ public class ExpressionNodeData : INotifyPropertyChanged {
private List<(string @namespace, string typename)> _baseTypes;
public List<(string @namespace, string typename)> BaseTypes => _baseTypes;
public string WatchExpressionFormatString { get; set; }
public bool EnableValueInNewWindow { get; set; }

public EndNodeData EndNodeData => new EndNodeData {
Closure = Closure,
Expand Down Expand Up @@ -180,17 +194,19 @@ internal ExpressionNodeData(object o, (string aggregatePath, string pathFromPare
break;
}

object value = null;

// fill StringValue and EndNodeType properties, for expressions
switch (expr) {
case ConstantExpression cexpr when !cexpr.Type.IsClosureClass():
StringValue = StringValue(cexpr.Value, language);
value = cexpr.Value;
EndNodeType = Constant;
break;
case ParameterExpression pexpr1:
EndNodeType = Parameter;
break;
case Expression e1 when expr.IsClosedVariable():
StringValue = StringValue(expr.ExtractValue(), language);
value = expr.ExtractValue();
EndNodeType = ClosedVar;
break;
case DefaultExpression defexpr:
Expand All @@ -199,6 +215,11 @@ internal ExpressionNodeData(object o, (string aggregatePath, string pathFromPare
}
if (EndNodeType != null) { visualizerData.CollectedEndNodes.Add(this); }

if (value != null) {
StringValue = StringValue(value, language);
EnableValueInNewWindow = value.GetType().InheritsFromOrImplementsAny(NodeTypes);
}

break;
case MemberBinding mbind:
NodeType = mbind.BindingType.ToString();
Expand All @@ -222,7 +243,7 @@ internal ExpressionNodeData(object o, (string aggregatePath, string pathFromPare
WatchExpressionFormatString = "{0}";
} else if (pi != null) {
var watchPathFromParent = PathFromParent;
if (visualizerData.Options.Language==CSharp) {
if (visualizerData.Options.Language == CSharp) {
WatchExpressionFormatString = $"(({pi.DeclaringType.FullName}){parentWatchExpression}).{watchPathFromParent}";
} else { //VisualBasic
watchPathFromParent = watchPathFromParent.Replace("[", "(").Replace("]", ")");
Expand Down Expand Up @@ -257,12 +278,14 @@ internal ExpressionNodeData(object o, (string aggregatePath, string pathFromPare
// populate URLs
if (pi != null) {
ParentProperty = (pi.DeclaringType.Namespace, pi.DeclaringType.Name, pi.Name);
}
}

if (!baseTypes.TryGetValue(o.GetType(), out _baseTypes)) {
_baseTypes = o.GetType().BaseTypes(true, true).Where(x => x != typeof(object) && x.IsPublic).Select(x => (x.Namespace, x.Name)).Distinct().ToList();
baseTypes[o.GetType()] = _baseTypes;
}


}

private static List<(Type, string[])> preferredPropertyOrders = new List<(Type, string[])> {
Expand Down Expand Up @@ -313,6 +336,3 @@ public enum EndNodeTypes {
Default
}
}


// TODO write method to load span into this ExpressionNodeData
2 changes: 2 additions & 0 deletions Visualizer.Shared/VisualizerDataControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<my:RootConverter x:Key="RootConverter" />
<my:ConditionalFormatConverter x:Key="ConditionalFormatConverter" />
<my:AnyVisibilityConverter x:Key="AnyVisibilityConverter" />
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<SolidColorBrush x:Key="TypeColor" Color="#066555" />
<HierarchicalDataTemplate x:Key="ExpressionNodeTemplate" ItemsSource="{Binding Children}">
<TextBlock>
Expand All @@ -23,6 +24,7 @@
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy watch expression" Click="CopyWatchExpression_Click" />
<MenuItem Header="Open value in new window" Click="OpenNewWindow_Click" Visibility="{Binding EnableValueInNewWindow, Converter={StaticResource BooleanToVisibilityConverter}}" />
<MenuItem Header="Help" Loaded="HelpContextMenu_Loaded" />
</ContextMenu>
</TextBlock.ContextMenu>
Expand Down
10 changes: 10 additions & 0 deletions Visualizer.Shared/VisualizerDataControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,5 +194,15 @@ private void CopyWatchExpression_Click(object sender, RoutedEventArgs e) {
var node = (ExpressionNodeData)((MenuItem)sender).DataContext;
Clipboard.SetText(string.Format(node.WatchExpressionFormatString, txbRootExpression.Text));
}

private void OpenNewWindow_Click(object sender, RoutedEventArgs e) {
var options = new VisualizerDataOptions(_options);
options.Path = ((ExpressionNodeData)((MenuItem)sender).DataContext).FullPath;
var window = new VisualizerWindow();
var control = window.Content as VisualizerDataControl;
control.ObjectProvider = ObjectProvider;
control.Options = options;
window.ShowDialog();
}
}
}
92 changes: 46 additions & 46 deletions _visualizerTests/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ static void Main(string[] args) {
//var expr = foo.GetExpression();

//var i = 5;
//Expression<Func<Expression<Func<string>>>> expr = () => expr1;
Expression<Func<int>> expr1 = () => 5;
Expression<Func<Expression<Func<int>>>> expr = () => expr1;

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

var hour = Variable(typeof(int), "hour");
var msg = Variable(typeof(string), "msg");
var block = Block(
// specify the variables available within the block
new[] { hour, msg },
// hour =
Assign(hour,
// DateTime.Now.Hour
MakeMemberAccess(
MakeMemberAccess(
null,
typeof(DateTime).GetMember("Now").Single()
),
typeof(DateTime).GetMember("Hour").Single()
)
),
// if ( ... ) { ... } else { ... }
IfThenElse(
// ... && ...
AndAlso(
// hour >= 6
GreaterThanOrEqual(
hour,
Constant(6)
),
// hour <= 18
LessThanOrEqual(
hour,
Constant(18)
)
),
// msg = "Good day"
Assign(msg, Constant("Good day")),
// msg = Good night"
Assign(msg, Constant("Good night"))
),
// Console.WriteLine(msg);
Call(
typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }),
msg
),
hour
);
Expression<Action> expr = Lambda<Action>(block);
//var hour = Variable(typeof(int), "hour");
//var msg = Variable(typeof(string), "msg");
//var block = Block(
// // specify the variables available within the block
// new[] { hour, msg },
// // hour =
// Assign(hour,
// // DateTime.Now.Hour
// MakeMemberAccess(
// MakeMemberAccess(
// null,
// typeof(DateTime).GetMember("Now").Single()
// ),
// typeof(DateTime).GetMember("Hour").Single()
// )
// ),
// // if ( ... ) { ... } else { ... }
// IfThenElse(
// // ... && ...
// AndAlso(
// // hour >= 6
// GreaterThanOrEqual(
// hour,
// Constant(6)
// ),
// // hour <= 18
// LessThanOrEqual(
// hour,
// Constant(18)
// )
// ),
// // msg = "Good day"
// Assign(msg, Constant("Good day")),
// // msg = Good night"
// Assign(msg, Constant("Good night"))
// ),
// // Console.WriteLine(msg);
// Call(
// typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }),
// msg
// ),
// hour
//);
//Expression<Action> expr = Lambda<Action>(block);

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


static Expression<Func<int, int>> expr1 = ((Func<Expression<Func<int, int>>>)(() => {
var value = Parameter(typeof(int), "value");
var result = Parameter(typeof(int), "result");
Expand Down

0 comments on commit c8dd0e9

Please sign in to comment.