diff --git a/Shared/CSharpBlockMetadata.cs b/Shared/CSharpBlockMetadata.cs new file mode 100644 index 0000000..9b32582 --- /dev/null +++ b/Shared/CSharpBlockMetadata.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static ExpressionToString.CSharpMultilineBlockTypes; + +namespace ExpressionToString { + //internal class CSharpBlockMetadata { + // internal CSharpMultilineBlockTypes BlockType { get; private set; } = Inline; + // internal string Delimiter { get; private set; } = ","; + // internal static CSharpBlockMetadata CreateMetadata(CSharpMultilineBlockTypes blockType = Inline, string delimiter = ",") => new CSharpBlockMetadata { + // BlockType = blockType, + // Delimiter = delimiter + // }; + // internal void Deconstruct(out CSharpMultilineBlockTypes blockType, out string delimiter) { + // blockType = BlockType; + // delimiter = Delimiter; + // } + //} +} diff --git a/Shared/CSharpCodeWriter.cs b/Shared/CSharpCodeWriter.cs index afb1d3e..4950d41 100644 --- a/Shared/CSharpCodeWriter.cs +++ b/Shared/CSharpCodeWriter.cs @@ -11,6 +11,7 @@ using static System.Linq.Enumerable; using static System.Linq.Expressions.ExpressionType; using static System.Linq.Expressions.GotoExpressionKind; +using static ExpressionToString.CSharpMultilineBlockTypes; namespace ExpressionToString { public class CSharpCodeWriter : WriterBase { @@ -202,7 +203,20 @@ protected override void WriteLambda(LambdaExpression expr) { Write("("); WriteNodes("Parameters", expr.Parameters, false, ", ", true); Write(") => "); - WriteNode("Body", expr.Body); + + if (CanInline(expr.Body)) { + WriteNode("Body", expr.Body); + return; + } + + Write("{"); + Indent(); + WriteEOL(); + if (expr.Body.Type != typeof(void)) { Write("return "); } + WriteNode("Body", expr.Body, CSharpMultilineBlockTypes.Block); + WriteStatementEnd(expr.Body); + WriteEOL(true); + Write("}"); } protected override void WriteParameterDeclarationImpl(ParameterExpression prm) { @@ -437,24 +451,55 @@ protected override void WriteNewArray(NewArrayExpression expr) { } } + private bool CanInline(Expression expr) { + switch (expr) { + case ConditionalExpression cexpr when cexpr.Type == typeof(void): + case BlockExpression bexpr when + bexpr.Expressions.Count > 1 || + bexpr.Variables.Any() || + (bexpr.Expressions.Count == 1 && CanInline(bexpr.Expressions.First())): + case SwitchExpression _: + case LambdaExpression _: + case TryExpression _: + return false; + case RuntimeVariablesExpression _: + throw new NotImplementedException(); + } + return true; + } + protected override void WriteConditional(ConditionalExpression expr) { - if (expr.Type == typeof(void)) { // if block, or if..else block - Write("if ("); - WriteNode("Test", expr.Test, false, true); - Write(") "); - WriteNode("IfTrue", expr.IfTrue, false, true); - WriteSemicolon(expr.IfTrue); - if (!expr.IfFalse.IsEmpty()) { - Write(" else "); - WriteNode("IfFalse", expr.IfFalse, false, true); - WriteSemicolon(expr.IfFalse); - } - } else { - WriteNode("Test", expr.Test, false, true); + if (expr.Type != typeof(void)) { + WriteNode("Test", expr.Test); Write(" ? "); WriteNode("IfTrue", expr.IfTrue); Write(" : "); WriteNode("IfFalse", expr.IfFalse); + return; + } + + Write("if ("); + WriteNode("Test", expr.Test, Test); + Write(") {"); + Indent(); + WriteEOL(); + WriteNode("IfTrue", expr.IfTrue, CSharpMultilineBlockTypes.Block); + WriteStatementEnd(expr.IfTrue); + WriteEOL(true); + Write("}"); + if (!expr.IfFalse.IsEmpty()) { + Write(" else "); + if (!(expr.IfFalse is ConditionalExpression)) { + Write("{"); + Indent(); + WriteEOL(); + } + WriteNode("IfFalse", expr.IfFalse, false, true); + WriteStatementEnd(expr.IfFalse); + if (!(expr.IfFalse is ConditionalExpression)) { + WriteEOL(true); + Write("}"); + } } } @@ -486,31 +531,46 @@ protected override void WriteInvocation(InvocationExpression expr) { protected override void WriteIndex(IndexExpression expr) => WriteIndexerAccess("Object", expr.Object, "Arguments", expr.Arguments); - protected override void WriteBlock(BlockExpression expr, bool? explicitBlock) { - var useExplicitBlock = explicitBlock ?? expr.Variables.Count > 0; - if (useExplicitBlock) { - Write("{"); - Indent(); - WriteEOL(); - expr.Variables.ForEach((v, index) => { - if (index > 0) { WriteEOL(); } - WriteNode($"Variables[{index}]", v, true); + protected override void WriteBlock(BlockExpression expr, object metadata) { + var blockType = (CSharpMultilineBlockTypes)(metadata ?? Inline); + if (blockType == CSharpMultilineBlockTypes.Block) { + expr.Variables.ForEach((subexpr, index) => { + WriteNode($"Variable[{index}]", subexpr, true); Write(";"); + WriteEOL(); + }); + expr.Expressions.ForEach((subexpr, index) => { + if (index > 0) { WriteEOL(); } + if (subexpr is LabelExpression) { TrimEnd(); } + WriteNode($"Expressions[{index}]", subexpr, CSharpMultilineBlockTypes.Block); + WriteStatementEnd(subexpr); }); + return; } + + if (expr.HasMultipleLines()) { + if (blockType == Inline) { Write("("); } + Indent(); + WriteEOL(); + } + WriteNodes("Variables", expr.Variables, true, ",", true); expr.Expressions.ForEach((subexpr, index) => { - if (index > 0 || expr.Variables.Count > 0) { WriteEOL(); } + if (index > 0 || expr.Variables.Count > 0) { + var previousExpr = index > 0 ? expr.Expressions[index - 1] : null; + if (previousExpr is null || !(previousExpr is LabelExpression || subexpr is RuntimeVariablesExpression)) { Write(","); } + WriteEOL(); + } if (subexpr is LabelExpression) { TrimEnd(); } WriteNode($"Expressions[{index}]", subexpr); - WriteSemicolon(subexpr); }); - if (useExplicitBlock) { + if (expr.HasMultipleLines()) { WriteEOL(true); - Write("}"); + if (blockType == Inline) { Write(")"); } } + return; } - private void WriteSemicolon(Expression expr) { + private void WriteStatementEnd(Expression expr) { switch (expr) { case ConditionalExpression cexpr when cexpr.Type == typeof(void): case BlockExpression _: @@ -533,15 +593,15 @@ protected override void WriteSwitchCase(SwitchCase switchCase) { }); Indent(); WriteEOL(); - WriteNode("Body", switchCase.Body, false, false); - WriteSemicolon(switchCase.Body); + WriteNode("Body", switchCase.Body, CSharpMultilineBlockTypes.Block); + WriteStatementEnd(switchCase.Body); WriteEOL(); Write("break;"); } protected override void WriteSwitch(SwitchExpression expr) { Write("switch ("); - WriteNode("SwitchValue", expr.SwitchValue, false, true); + WriteNode("SwitchValue", expr.SwitchValue, Test); Write(") {"); Indent(); WriteEOL(); @@ -555,8 +615,8 @@ protected override void WriteSwitch(SwitchExpression expr) { Write("default:"); Indent(); WriteEOL(); - WriteNode("DefaultBody", expr.DefaultBody); - WriteSemicolon(expr.DefaultBody); + WriteNode("DefaultBody", expr.DefaultBody, CSharpMultilineBlockTypes.Block); + WriteStatementEnd(expr.DefaultBody); Dedent(); } WriteEOL(true); @@ -575,15 +635,15 @@ protected override void WriteCatchBlock(CatchBlock catchBlock) { Write(") "); if (catchBlock.Filter != null) { Write("when ("); - WriteNode("Filter", catchBlock.Filter, false, true); + WriteNode("Filter", catchBlock.Filter, false, Test); Write(") "); } } Write("{"); Indent(); WriteEOL(); - WriteNode("Body", catchBlock.Body); - WriteSemicolon(catchBlock.Body); + WriteNode("Body", catchBlock.Body, CSharpMultilineBlockTypes.Block); + WriteStatementEnd(catchBlock.Body); WriteEOL(true); Write("}"); } @@ -593,7 +653,7 @@ protected override void WriteTry(TryExpression expr) { Indent(); WriteEOL(); WriteNode("Body", expr.Body); - WriteSemicolon(expr.Body); + WriteStatementEnd(expr.Body); WriteEOL(true); Write("}"); expr.Handlers.ForEach((catchBlock, index) => { @@ -605,7 +665,7 @@ protected override void WriteTry(TryExpression expr) { Indent(); WriteEOL(); WriteNode("Fault", expr.Fault); - WriteSemicolon(expr.Fault); + WriteStatementEnd(expr.Fault); WriteEOL(true); Write("}"); } @@ -614,7 +674,7 @@ protected override void WriteTry(TryExpression expr) { Indent(); WriteEOL(); WriteNode("Finally", expr.Finally); - WriteSemicolon(expr.Finally); + WriteStatementEnd(expr.Finally); WriteEOL(true); Write("}"); } @@ -660,8 +720,8 @@ protected override void WriteLoop(LoopExpression expr) { Write("while (true) {"); Indent(); WriteEOL(); - WriteNode("Body", expr.Body); - WriteSemicolon(expr.Body); + WriteNode("Body", expr.Body,CSharpMultilineBlockTypes.Block); + WriteStatementEnd(expr.Body); WriteEOL(true); Write("}"); } diff --git a/Shared/CSharpMultilineBlockTypes.cs b/Shared/CSharpMultilineBlockTypes.cs new file mode 100644 index 0000000..bf98a55 --- /dev/null +++ b/Shared/CSharpMultilineBlockTypes.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExpressionToString { + public enum CSharpMultilineBlockTypes { + Inline, + Test, + Block + } +} diff --git a/Shared/FactoryMethodsFormatter.cs b/Shared/FactoryMethodsFormatter.cs index f90b6ab..28c0525 100644 --- a/Shared/FactoryMethodsFormatter.cs +++ b/Shared/FactoryMethodsFormatter.cs @@ -369,7 +369,7 @@ protected override void WriteIndex(IndexExpression expr) { WriteMethodCall(() => ArrayAccess(expr.Object, expr.Arguments.ToArray())); } - protected override void WriteBlock(BlockExpression expr, bool? explicitBlock = null) { + protected override void WriteBlock(BlockExpression expr, object metadata) { if (expr.Type != expr.Expressions.Last().Type) { if (expr.Variables.Any()) { WriteMethodCall(() => Block(expr.Type, expr.Variables, expr.Expressions.ToArray())); diff --git a/Shared/Shared.projitems b/Shared/Shared.projitems index 127a230..a755642 100644 --- a/Shared/Shared.projitems +++ b/Shared/Shared.projitems @@ -9,10 +9,14 @@ ExpressionToString + + + + diff --git a/Shared/Util/Extensions/BlockExpression.cs b/Shared/Util/Extensions/BlockExpression.cs new file mode 100644 index 0000000..a3f7041 --- /dev/null +++ b/Shared/Util/Extensions/BlockExpression.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace ExpressionToString.Util { + public static class BlockExpressionExtensions { + public static bool HasMultipleLines(this BlockExpression expr) => expr.Variables.Any() || expr.Expressions.Count > 1; + } +} diff --git a/Shared/VBBlockMetadata.cs b/Shared/VBBlockMetadata.cs new file mode 100644 index 0000000..9e80c20 --- /dev/null +++ b/Shared/VBBlockMetadata.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExpressionToString { + internal class VBBlockMetadata { + internal bool IsInMutiline { get; private set; } + internal bool ParentIsBlock { get; set; } + internal static VBBlockMetadata CreateMetadata(bool isInMultiline, bool parentIsBlock) => new VBBlockMetadata { + IsInMutiline = isInMultiline, + ParentIsBlock = parentIsBlock + }; + } +} diff --git a/Shared/VBCodeWriter.cs b/Shared/VBCodeWriter.cs index bf922c8..3cd082e 100644 --- a/Shared/VBCodeWriter.cs +++ b/Shared/VBCodeWriter.cs @@ -12,6 +12,7 @@ using static System.Linq.Expressions.ExpressionType; using static System.Linq.Expressions.GotoExpressionKind; using static ExpressionToString.Util.Methods; +using static ExpressionToString.VBBlockMetadata; namespace ExpressionToString { public class VBCodeWriter : WriterBase { @@ -253,18 +254,26 @@ protected override void WriteUnary(UnaryExpression expr) => WriteUnary(expr.NodeType, "Operand", expr.Operand, expr.Type, expr.GetType().Name); protected override void WriteLambda(LambdaExpression expr) { - if (expr.ReturnType == typeof(void)) { - Write("Sub"); - } else { - Write("Function"); - } - Write("("); + var lambdaKeyword = expr.ReturnType == typeof(void) ? "Sub" : "Function"; + Write($"{lambdaKeyword}("); expr.Parameters.ForEach((prm, index) => { if (index > 0) { Write(", "); } WriteNode($"Parameters[{index}]", prm, true); }); - Write(") "); - WriteNode("Body", expr.Body); + Write(")"); + + if (CanInline(expr.Body)) { + Write(" "); + WriteNode("Body", expr.Body); + return; + } + + Indent(); + WriteEOL(); + if (expr.Body.Type != typeof(void)) { Write("Return "); } + WriteNode("Body", expr.Body, CreateMetadata(true,false)); + WriteEOL(true); + Write($"End {lambdaKeyword}"); } protected override void WriteParameterDeclarationImpl(ParameterExpression prm) { @@ -541,49 +550,56 @@ protected override void WriteNewArray(NewArrayExpression expr) { } } - private bool IndentIfBlockSyntax(string path, Expression expr, (bool leading, bool trailing) nonblockSpaces, bool? explicitBlock = false) { - if (IsBlockSyntax(expr)) { - if (explicitBlock ?? false) { - WriteNode(path, expr, false, true); - } else { - Indent(); - WriteEOL(); - WriteNode(path, expr, false, explicitBlock); - WriteEOL(true); - } - return true; - } else { - if (nonblockSpaces.leading) { Write(" "); } - WriteNode(path, expr); - if (nonblockSpaces.trailing) { Write(" "); } - return false; - } - } - protected override void WriteConditional(ConditionalExpression expr) { - if (expr.Type == typeof(void)) { - var lastClauseIsBlock = false; - Write("If"); - IndentIfBlockSyntax("Test", expr.Test, (true, true)); - Write("Then"); - lastClauseIsBlock = IsBlockSyntax(expr.IfTrue); - IndentIfBlockSyntax("IfTrue", expr.IfTrue, (true, !expr.IfFalse.IsEmpty())); - if (!expr.IfFalse.IsEmpty()) { - Write("Else"); - lastClauseIsBlock = IndentIfBlockSyntax("IfFalse", expr.IfFalse, (true, false)); - } - if (lastClauseIsBlock) { - Write("End If"); - } - } else { + if (expr.Type != typeof(void)) { Write("If("); - IndentIfBlockSyntax("Test", expr.Test, (false, false), true); + WriteNode("Test", expr.Test); Write(", "); WriteNode("IfTrue", expr.IfTrue); Write(", "); WriteNode("IfFalse", expr.IfFalse); Write(")"); + return; } + + var metadata = CreateMetadata(true, false); + + if (CanInline(expr.Test)) { + Write("If "); + WriteNode("Test", expr.Test); + Write(" Then"); + } else { + Write("If"); + Indent(); + WriteEOL(); + WriteNode("Test", expr.Test, metadata); + WriteEOL(true); + Write("Then"); + } + + var canInline = new[] { expr.IfTrue, expr.IfFalse }.All(x => CanInline(x)); + if (canInline) { + Write(" "); + WriteNode("IfTrue", expr.IfTrue); + if (!expr.IfFalse.IsEmpty()) { + Write(" Else "); + WriteNode("IfFalse", expr.IfFalse); + } + return; + } + + Indent(); + WriteEOL(); + WriteNode("IfTrue", expr.IfTrue, metadata); + WriteEOL(true); + if (!expr.IfFalse.IsEmpty()) { + Write("Else"); + Indent(); + WriteEOL(); + WriteNode("IfFalse", expr.IfFalse, metadata); + WriteEOL(true); + } + Write("End If"); } protected override void WriteDefault(DefaultExpression expr) => @@ -615,39 +631,45 @@ protected override void WriteInvocation(InvocationExpression expr) { protected override void WriteIndex(IndexExpression expr) => WriteIndexerAccess("Object", expr.Object, "Arguments", expr.Arguments); - protected override void WriteBlock(BlockExpression expr, bool? explicitBlock = null) { - var useExplicitBlock = explicitBlock ?? expr.Variables.Count > 0; - if (useExplicitBlock) { + protected override void WriteBlock(BlockExpression expr, object metadata) { + var blockMetedata = metadata as VBBlockMetadata ?? CreateMetadata(false, false); + var useBlockConstruct = !blockMetedata.IsInMutiline || (expr.Variables.Any() && blockMetedata.ParentIsBlock); + if (useBlockConstruct) { Write("Block"); Indent(); WriteEOL(); - expr.Variables.ForEach((v, index) => { - if (index > 0) { WriteEOL(); } - Write("Dim "); - WriteNode($"Variables[{index}]", v, true); - }); } + expr.Variables.ForEach((v, index) => { + if (index > 0) { WriteEOL(); } + Write("Dim "); + WriteNode($"Variables[{index}]", v, true); + }); expr.Expressions.ForEach((subexpr, index) => { if (index > 0 || expr.Variables.Count > 0) { WriteEOL(); } if (subexpr is LabelExpression) { TrimEnd(); } - WriteNode($"Expressions[{index}]", subexpr); + WriteNode($"Expressions[{index}]", subexpr, CreateMetadata(true, true)); }); - if (useExplicitBlock) { + if (useBlockConstruct) { WriteEOL(true); Write("End Block"); } } - private bool IsBlockSyntax(Expression expr) { + private bool CanInline(Expression expr) { switch (expr) { case ConditionalExpression cexpr when cexpr.Type == typeof(void): - case BlockExpression _: + case BlockExpression bexpr when + bexpr.Expressions.Count > 1 || + bexpr.Variables.Any() || + (bexpr.Expressions.Count == 1 && CanInline(bexpr.Expressions.First())): case SwitchExpression _: + case LambdaExpression _: case TryExpression _: + return false; case RuntimeVariablesExpression _: - return true; + throw new NotImplementedException(); } - return false; + return true; } protected override void WriteSwitchCase(SwitchCase switchCase) { @@ -655,25 +677,22 @@ protected override void WriteSwitchCase(SwitchCase switchCase) { WriteNodes("TestValues", switchCase.TestValues); Indent(); WriteEOL(); - WriteNode("Body", switchCase.Body); + WriteNode("Body", switchCase.Body, CreateMetadata(true, false)); + Dedent(); } protected override void WriteSwitch(SwitchExpression expr) { Write("Select Case "); Indent(); - WriteNode("SwitchValue", expr.SwitchValue, false, true); + WriteNode("SwitchValue", expr.SwitchValue); WriteEOL(); - expr.Cases.ForEach((switchCase, index) => { - if (index > 0) { WriteEOL(); } - WriteNode($"Cases[{index}]", switchCase); - Dedent(); - }); + WriteNodes("Cases", expr.Cases, true, ""); if (expr.DefaultBody != null) { if (expr.Cases.Count > 0) { WriteEOL(); } Write("Case Else"); Indent(); WriteEOL(); - WriteNode("DefaultBody", expr.DefaultBody); + WriteNode("DefaultBody", expr.DefaultBody, CreateMetadata(true, false)); Dedent(); } WriteEOL(true); @@ -690,18 +709,18 @@ protected override void WriteCatchBlock(CatchBlock catchBlock) { } if (catchBlock.Filter != null) { Write(" When "); - WriteNode("Filter", catchBlock.Filter, false, true); + WriteNode("Filter", catchBlock.Filter); } Indent(); WriteEOL(); - WriteNode("Body", catchBlock.Body); + WriteNode("Body", catchBlock.Body, CreateMetadata(true, false)); } protected override void WriteTry(TryExpression expr) { Write("Try"); Indent(); WriteEOL(); - WriteNode("Body", expr.Body, false, false); + WriteNode("Body", expr.Body, CreateMetadata(true, false)); WriteEOL(true); expr.Handlers.ForEach((catchBlock, index) => { WriteNode($"Handlers[{index}]", catchBlock); @@ -711,14 +730,14 @@ protected override void WriteTry(TryExpression expr) { Write("Fault"); Indent(); WriteEOL(); - WriteNode("Fault", expr.Fault, false, false); + WriteNode("Fault", expr.Fault, CreateMetadata(true, false)); WriteEOL(true); } if (expr.Finally != null) { Write("Finally"); Indent(); WriteEOL(); - WriteNode("Finally", expr.Finally, false, false); + WriteNode("Finally", expr.Finally, CreateMetadata(true, false)); WriteEOL(true); } Write("End Try"); @@ -764,7 +783,7 @@ protected override void WriteLoop(LoopExpression expr) { Write("Do"); Indent(); WriteEOL(); - WriteNode("Body", expr.Body); + WriteNode("Body", expr.Body, CreateMetadata(true, false)); WriteEOL(true); Write("Loop"); } @@ -833,7 +852,7 @@ protected override void WriteInvokeMemberBinder(InvokeMemberBinder binder, IList VerifyCount(args, 1, null); WriteNode("Arguments[0]", args[0]); Write($".{binder.Name}"); - if (args.Count>1) { + if (args.Count > 1) { Write("("); WriteNodes(args.Skip(1).Select((arg, index) => ($"Arguments[{index + 1}]", arg))); Write(")"); diff --git a/Shared/WriterBase.cs b/Shared/WriterBase.cs index 06235c4..2d6a437 100644 --- a/Shared/WriterBase.cs +++ b/Shared/WriterBase.cs @@ -63,8 +63,9 @@ protected virtual void PreWrite() { } /// Write a string-rendering of an expression or other type used in expression trees /// Object to be rendered /// For ParameterExpression, this is a parameter declaration - /// For BlockExpression, controls explicit block rendering: true forces the rendering; false prevents the rendering; and null determines automatically - protected void WriteNode(string pathSegment, object o, bool parameterDeclaration = false, bool? explicitBlock = null) { + /// For BlockExpression, sets the preferred block type + /// + protected void WriteNode(string pathSegment, object o, bool parameterDeclaration = false, object blockMetadata = null) { if (!pathSegment.IsNullOrWhitespace()) { pathSegments.Add(pathSegment); } var start = sb.Length; try { @@ -72,8 +73,8 @@ protected void WriteNode(string pathSegment, object o, bool parameterDeclaration case ParameterExpression pexpr when parameterDeclaration: WriteParameterDeclarationImpl(pexpr); break; - case BlockExpression bexpr when explicitBlock != null: - WriteBlock(bexpr, explicitBlock); + case BlockExpression bexpr when blockMetadata != null: + WriteBlock(bexpr, blockMetadata); break; case Expression expr: WriteExpression(expr); @@ -111,6 +112,7 @@ protected void WriteNode(string pathSegment, object o, bool parameterDeclaration } } protected void WriteNode((string pathSegment, object o) x) => WriteNode(x.pathSegment, x.o); + protected void WriteNode(string pathSegment, object o, object blockMetadata) => WriteNode(pathSegment, o, false, blockMetadata); private readonly HashSet binaryExpressionTypes = new[] { Add, AddChecked, Divide, Modulo, Multiply, MultiplyChecked, Power, Subtract, SubtractChecked, // mathematical operators @@ -204,7 +206,7 @@ private void WriteExpression(Expression expr) { break; case Block: - WriteBlock(expr as BlockExpression); + WriteBlock(expr as BlockExpression, null); break; case Switch: @@ -304,7 +306,8 @@ protected void WriteNodes(IEnumerable<(string pathSegment, T o)> pathsItems, protected void WriteNodes(string pathSegment, IEnumerable items, bool writeEOL, string delimiter = ", ", bool parameterDeclaration = false) => WriteNodes(items.Select((arg, index) => ($"{pathSegment}[{index}]", arg)), writeEOL, delimiter, parameterDeclaration); - protected void WriteNodes(string pathSegment, IEnumerable items, string delimiter = ", ") => WriteNodes(pathSegment, items, false, delimiter); + protected void WriteNodes(string pathSegment, IEnumerable items, string delimiter = ", ", bool parameterDeclaration=false) => + WriteNodes(pathSegment, items, false, delimiter, parameterDeclaration); protected void TrimEnd(bool trimEOL = false) => sb.TrimEnd(trimEOL); @@ -329,7 +332,7 @@ protected void WriteNodes(string pathSegment, IEnumerable items, bool writ protected abstract void WriteIndex(IndexExpression expr); // .NET 4 expression types - protected abstract void WriteBlock(BlockExpression expr, bool? explicitBlock = null); + protected abstract void WriteBlock(BlockExpression expr, object metadata); protected abstract void WriteSwitch(SwitchExpression expr); protected abstract void WriteTry(TryExpression expr); protected abstract void WriteLabel(LabelExpression expr); diff --git a/Tests.Common/Constructed/MakeBlock.cs b/Tests.Common/Constructed/MakeBlock.cs index 96a2c3c..500c503 100644 --- a/Tests.Common/Constructed/MakeBlock.cs +++ b/Tests.Common/Constructed/MakeBlock.cs @@ -13,10 +13,14 @@ public void BlockNoVariables() => RunTest( Constant(true), Constant(true) ), - @"true; -true;", - @"True -True", + @"( + true, + true +)", + @"Block + True + True +End Block", @"Block( Constant(true), Constant(true) @@ -31,11 +35,11 @@ public void BlockSingleVariable() => RunTest( Constant(true), Constant(true) ), - @"{ - int i; - true; - true; -}", + @"( + int i, + true, + true +)", @"Block Dim i As Integer True @@ -55,12 +59,12 @@ public void BlockMultipleVariable() => RunTest( Constant(true), Constant(true) ), - @"{ - int i; - string s1; - true; - true; -}", + @"( + int i, + string s1, + true, + true +)", @"Block Dim i As Integer Dim s1 As String diff --git a/Tests.Common/Constructed/MakeConditional.cs b/Tests.Common/Constructed/MakeConditional.cs index 5661121..aa32b29 100644 --- a/Tests.Common/Constructed/MakeConditional.cs +++ b/Tests.Common/Constructed/MakeConditional.cs @@ -18,7 +18,11 @@ public void VoidConditionalWithElse() => RunTest( writeLineTrue, writeLineFalse ), - "if (true) Console.WriteLine(true); else Console.WriteLine(false);", + @"if (true) { + Console.WriteLine(true); +} else { + Console.WriteLine(false); +}", "If True Then Console.WriteLine(True) Else Console.WriteLine(False)", @"IfThenElse( Constant(true), @@ -41,7 +45,11 @@ public void VoidConditional1WithElse() => RunTest( writeLineTrue, writeLineFalse ), - @"if (true) Console.WriteLine(true); else Console.WriteLine(false);", + @"if (true) { + Console.WriteLine(true); +} else { + Console.WriteLine(false); +}", @"If True Then Console.WriteLine(True) Else Console.WriteLine(False)", @"IfThenElse( Constant(true), @@ -64,7 +72,9 @@ public void VoidConditionalWithoutElse() => RunTest( writeLineTrue, Empty() ), - "if (true) Console.WriteLine(true);", + @"if (true) { + Console.WriteLine(true); +}", "If True Then Console.WriteLine(True)", @"IfThen( Constant(true), @@ -82,7 +92,9 @@ public void VoidConditional1WithoutElse() => RunTest( Constant(true), writeLineTrue ), - "if (true) Console.WriteLine(true);", + @"if (true) { + Console.WriteLine(true); +}", "If True Then Console.WriteLine(True)", @"IfThen( Constant(true), @@ -146,10 +158,10 @@ public void MultilineTestPart() => RunTest( trueLength, falseLength ), - @"{ - true; - true; -} ? ""true"".Length : ""false"".Length", + @"( + true, + true +) ? ""true"".Length : ""false"".Length", @"If(Block True True @@ -177,10 +189,12 @@ public void MultilineTestPart1() => RunTest( Block(Constant(true), Constant(true)), writeLineTrue ), - @"if ({ - true; - true; -}) Console.WriteLine(true);", + @"if ( + true, + true +) { + Console.WriteLine(true); +}", @"If True True @@ -237,7 +251,11 @@ public void NestedIfThen() => RunTest( writeLineTrue ) ), - @"if (true) if (true) Console.WriteLine(true);", + @"if (true) { + if (true) { + Console.WriteLine(true); + } +}", @"If True Then If True Then Console.WriteLine(True) End If", @@ -253,6 +271,7 @@ If True Then Console.WriteLine(True) )" ); + // TODO nested ifs look strange in VB.NET [Fact] [Trait("Category", Conditionals)] public void NestedElse() => RunTest( @@ -264,8 +283,14 @@ public void NestedElse() => RunTest( writeLineTrue ) ), - @"if (true) Console.WriteLine(true); else if (true) Console.WriteLine(true);", - @"If True Then Console.WriteLine(True) Else + @"if (true) { + Console.WriteLine(true); +} else if (true) { + Console.WriteLine(true); +}", + @"If True Then + Console.WriteLine(True) +Else If True Then Console.WriteLine(True) End If", @"IfThenElse( diff --git a/Tests.Common/Constructed/MakeLabel.cs b/Tests.Common/Constructed/MakeLabel.cs index 10bd296..d53b441 100644 --- a/Tests.Common/Constructed/MakeLabel.cs +++ b/Tests.Common/Constructed/MakeLabel.cs @@ -19,15 +19,15 @@ public void ConstructLabel() => RunTest( Constant(true) ) ), - @"{ - int i; - { - int j; - true; + @"( + int i, + ( + int j, + true, target: - true; - } -}", + true + ) +)", @"Block Dim i As Integer Block @@ -60,14 +60,14 @@ public void ConstructLabel1() => RunTest( Constant(true) ) ), - @"{ - int i; - { - int j; + @"( + int i, + ( + int j, target: - true; - } -}", + true + ) +)", @"Block Dim i As Integer Block diff --git a/Tests.Common/Constructed/MakeLambda.cs b/Tests.Common/Constructed/MakeLambda.cs index 3dbefe5..cee1bfe 100644 --- a/Tests.Common/Constructed/MakeLambda.cs +++ b/Tests.Common/Constructed/MakeLambda.cs @@ -8,9 +8,9 @@ public partial class ConstructedBase { [Fact] [Trait("Category", Lambdas)] public void NoParametersVoidReturn() => RunTest( - Lambda(Call(writeline0)), - "() => Console.WriteLine()", - "Sub() Console.WriteLine", + Lambda(Call(writeline0)), + "() => Console.WriteLine()", + "Sub() Console.WriteLine", @"Lambda( Call( typeof(Console).GetMethod(""WriteLine"") @@ -21,9 +21,9 @@ public void NoParametersVoidReturn() => RunTest( [Fact] [Trait("Category", Lambdas)] public void OneParameterVoidReturn() => RunTest( - Lambda(Call(writeline1, s), s), - "(string s) => Console.WriteLine(s)", - "Sub(s As String) Console.WriteLine(s)", + Lambda(Call(writeline1, s), s), + "(string s) => Console.WriteLine(s)", + "Sub(s As String) Console.WriteLine(s)", @"Lambda( Call( typeof(Console).GetMethod(""WriteLine""), @@ -41,7 +41,7 @@ public void OneParameterVoidReturn() => RunTest( public void TwoParametersVoidReturn() => RunTest( Lambda(Call(writeline1, Add(s1, s2, concat)), s1, s2), "(string s1, string s2) => Console.WriteLine(s1 + s2)", - "Sub(s1 As String, s2 As String) Console.WriteLine(s1 + s2)", + "Sub(s1 As String, s2 As String) Console.WriteLine(s1 + s2)", @"Lambda( Call( typeof(Console).GetMethod(""WriteLine""), @@ -63,7 +63,7 @@ public void TwoParametersVoidReturn() => RunTest( public void NoParametersNonVoidReturn() => RunTest( Lambda(Constant("abcd")), "() => \"abcd\"", - "Function() \"abcd\"", + "Function() \"abcd\"", @"Lambda( Constant(""abcd"") )" @@ -74,7 +74,7 @@ public void NoParametersNonVoidReturn() => RunTest( public void OneParameterNonVoidReturn() => RunTest( Lambda(s, s), "(string s) => s", - "Function(s As String) s", + "Function(s As String) s", @"Lambda(s, var s = Parameter( typeof(string), @@ -105,7 +105,11 @@ public void TwoParametersNonVoidReturn() => RunTest( [Fact] [Trait("Category", Lambdas)] public void NamedLambda() => RunTest( - Lambda(Add(s1, s2, concat), "name", new [] { s1, s2 }), + Lambda( + Add(s1, s2, concat), + "name", + new[] { s1, s2 } + ), "(string s1, string s2) => s1 + s2", "Function(s1 As String, s2 As String) s1 + s2", @"Lambda( @@ -120,6 +124,58 @@ public void NamedLambda() => RunTest( ""s2"" ) } +)" + ); + + [Fact] + [Trait("Category", Lambdas)] + public void MultilineLambda() => RunTest( + Lambda( + IfThen(Constant(true), writeLineTrue) + ), + @"() => { + if (true) { + Console.WriteLine(true); + } +}", + @"Sub() + If True Then Console.WriteLine(True) +End Sub", + @"Lambda( + IfThen( + Constant(true), + Call( + typeof(Console).GetMethod(""WriteLine""), + Constant(true) + ) + ) +)" + ); + + [Fact] + [Trait("Category", Lambdas)] + public void NestedLambda() => RunTest( + Lambda( + Lambda(Add(s1, s2, concat), s1, s2) + ), + @"() => { + return (string s1, string s2) => s1 + s2; +}", + @"Function() + Return Function(s1 As String, s2 As String) s1 + s2 +End Function", + @"Lambda( + Lambda( + Add(s1, s2), + var s1 = Parameter( + typeof(string), + ""s1"" + ), + var s2 = Parameter( + typeof(string), + ""s2"" + ) + ) )" ); } diff --git a/Tests.Common/Constructed/MakeRuntimeVariables.cs b/Tests.Common/Constructed/MakeRuntimeVariables.cs index 8f87d80..82a4070 100644 --- a/Tests.Common/Constructed/MakeRuntimeVariables.cs +++ b/Tests.Common/Constructed/MakeRuntimeVariables.cs @@ -22,11 +22,11 @@ public void RuntimeVariablesWithinBlock() => RunTest( Constant(true), RuntimeVariables(x, s1) ), - @"{ - string s2; - true; + @"( + string s2, + true // variables -- double x, string s1 -}", +)", @"Block Dim s2 As String True diff --git a/Tests.Common/Constructed/MakeSwitch.cs b/Tests.Common/Constructed/MakeSwitch.cs index 81c9b6c..fde485a 100644 --- a/Tests.Common/Constructed/MakeSwitch.cs +++ b/Tests.Common/Constructed/MakeSwitch.cs @@ -229,10 +229,10 @@ public void SwitchOnMultipleStatementsWithDefault() => RunTest( Constant(5) ) ), - @"switch ({ - i; - j; -}) { + @"switch ( + i, + j +) { case 4: Console.WriteLine(true); break; @@ -331,10 +331,10 @@ public void SwitchOnMultipleStatementsWithoutDefault() => RunTest( Constant(5) ) ), - @"switch ({ - i; - j; -}) { + @"switch ( + i, + j +) { case 4: Console.WriteLine(true); break; diff --git a/Tests.Common/Constructed/MakeTry.cs b/Tests.Common/Constructed/MakeTry.cs index ad45de9..73806e3 100644 --- a/Tests.Common/Constructed/MakeTry.cs +++ b/Tests.Common/Constructed/MakeTry.cs @@ -162,10 +162,10 @@ public void ConstructCatchMultiStatementWithFilter() => RunTest( [Trait("Category", Try)] public void ConstructCatchWithMultiStatementFilter() => RunTest( Catch(ex, writeLineTrue, Block(Constant(true), Constant(true))), - @"catch (Exception ex) when ({ - true; - true; -}) { + @"catch (Exception ex) when ( + true, + true +) { Console.WriteLine(true); }", @"Catch ex As Exception When Block diff --git a/Tests.Common/Constructed/Misc.cs b/Tests.Common/Constructed/Misc.cs index d46dec0..c662f0e 100644 --- a/Tests.Common/Constructed/Misc.cs +++ b/Tests.Common/Constructed/Misc.cs @@ -99,12 +99,12 @@ public void MakeQuoted() => RunTest( Lambda(writeLineTrue) ) ), - @"{ - double x; + @"( + double x, // --- Quoted - begin () => Console.WriteLine(true) // --- Quoted - end -}", +)", @"Block Dim x As Double ' --- Quoted - begin