From c8dd0e9aa5502f1e9af7e8d37fa51e9bf98b8578 Mon Sep 17 00:00:00 2001 From: Zev Spitz Date: Tue, 25 Jun 2019 07:04:39 +0300 Subject: [PATCH] Fixes #49; advances #63 --- ExpressionToString.sln | 4 +- Shared/CSharpCodeWriter.cs | 2 - Shared/Shared.projitems | 1 + Shared/Util/Extensions/Match.cs | 15 +++ Shared/Util/Functions.cs | 13 ++- Visualizer.Shared/VisualizerData.cs | 34 +++++-- Visualizer.Shared/VisualizerDataControl.xaml | 2 + .../VisualizerDataControl.xaml.cs | 10 ++ _visualizerTests/Program.cs | 92 +++++++++---------- 9 files changed, 115 insertions(+), 58 deletions(-) create mode 100644 Shared/Util/Extensions/Match.cs diff --git a/ExpressionToString.sln b/ExpressionToString.sln index c214e11..dc2ca04 100644 --- a/ExpressionToString.sln +++ b/ExpressionToString.sln @@ -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 diff --git a/Shared/CSharpCodeWriter.cs b/Shared/CSharpCodeWriter.cs index a0a926e..b7c7141 100644 --- a/Shared/CSharpCodeWriter.cs +++ b/Shared/CSharpCodeWriter.cs @@ -19,8 +19,6 @@ public class CSharpCodeWriter : WriterBase { public CSharpCodeWriter(object o) : base(o, FormatterNames.CSharp) { } public CSharpCodeWriter(object o, out Dictionary 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 simpleBinaryOperators = new Dictionary() { [Add] = "+", [AddChecked] = "+", diff --git a/Shared/Shared.projitems b/Shared/Shared.projitems index 6b2fcec..e09eeed 100644 --- a/Shared/Shared.projitems +++ b/Shared/Shared.projitems @@ -14,6 +14,7 @@ + diff --git a/Shared/Util/Extensions/Match.cs b/Shared/Util/Extensions/Match.cs new file mode 100644 index 0000000..a7ef8b5 --- /dev/null +++ b/Shared/Util/Extensions/Match.cs @@ -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; + } + } +} diff --git a/Shared/Util/Functions.cs b/Shared/Util/Functions.cs index 550191b..eb2eb3e 100644 --- a/Shared/Util/Functions.cs +++ b/Shared/Util/Functions.cs @@ -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 { @@ -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(); @@ -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()) { + 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; + } } } diff --git a/Visualizer.Shared/VisualizerData.cs b/Visualizer.Shared/VisualizerData.cs index 76cb64c..ce64f4f 100644 --- a/Visualizer.Shared/VisualizerData.cs +++ b/Visualizer.Shared/VisualizerData.cs @@ -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] @@ -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(); @@ -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, @@ -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: @@ -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(); @@ -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("]", ")"); @@ -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[])> { @@ -313,6 +336,3 @@ public enum EndNodeTypes { Default } } - - -// TODO write method to load span into this ExpressionNodeData \ No newline at end of file diff --git a/Visualizer.Shared/VisualizerDataControl.xaml b/Visualizer.Shared/VisualizerDataControl.xaml index 50b2444..b00c97a 100644 --- a/Visualizer.Shared/VisualizerDataControl.xaml +++ b/Visualizer.Shared/VisualizerDataControl.xaml @@ -3,6 +3,7 @@ + @@ -23,6 +24,7 @@ + diff --git a/Visualizer.Shared/VisualizerDataControl.xaml.cs b/Visualizer.Shared/VisualizerDataControl.xaml.cs index 314ec45..1c7e4bb 100644 --- a/Visualizer.Shared/VisualizerDataControl.xaml.cs +++ b/Visualizer.Shared/VisualizerDataControl.xaml.cs @@ -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(); + } } } diff --git a/_visualizerTests/Program.cs b/_visualizerTests/Program.cs index 6dc815c..6dac9d0 100644 --- a/_visualizerTests/Program.cs +++ b/_visualizerTests/Program.cs @@ -56,7 +56,8 @@ static void Main(string[] args) { //var expr = foo.GetExpression(); //var i = 5; - //Expression>>> expr = () => expr1; + Expression> expr1 = () => 5; + Expression>>> expr = () => expr1; //Expression> expr = Lambda>( // MakeMemberAccess( @@ -85,50 +86,50 @@ static void Main(string[] args) { //IQueryable personSource = null; //Expression> 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 expr = Lambda(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 expr = Lambda(block); //var constant = Constant(new List()); //Expression expr = Or( @@ -215,7 +216,6 @@ static void Main(string[] args) { //Console.ReadKey(true); } - static Expression> expr1 = ((Func>>)(() => { var value = Parameter(typeof(int), "value"); var result = Parameter(typeof(int), "result");