From b1cbb7c665e432d79e05328aea86dff348bbb3d9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:32:53 +0000 Subject: [PATCH 1/4] Initial plan From c8fb60f45c55067f18fdc1e7e09e70e98edfdc2b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:42:21 +0000 Subject: [PATCH 2/4] Add WriteTo method to DelimitedSyntaxList to eliminate manual token iteration patterns Agent-Logs-Url: https://github.com/jonathanvdc/MLIR.NET/sessions/6bc7d9d3-59a3-4d9e-a98f-2ed26061d21b Co-authored-by: jonathanvdc <9839946+jonathanvdc@users.noreply.github.com> --- src/MLIR.Generators/DialectSourceEmitter.cs | 30 +------------ src/MLIR/Syntax/BlockSyntax.cs | 16 +------ src/MLIR/Syntax/DelimitedSyntaxList.cs | 33 ++++++++++++++ src/MLIR/Syntax/GenericOperationBodySyntax.cs | 45 ++----------------- tests/MLIR.Tests/ConstructionTests.cs | 33 ++++++++++++++ 5 files changed, 72 insertions(+), 85 deletions(-) diff --git a/src/MLIR.Generators/DialectSourceEmitter.cs b/src/MLIR.Generators/DialectSourceEmitter.cs index 3f33b88..d973b3a 100644 --- a/src/MLIR.Generators/DialectSourceEmitter.cs +++ b/src/MLIR.Generators/DialectSourceEmitter.cs @@ -361,38 +361,12 @@ private static void AppendBodySyntaxFields(List fields, HashSet private static string GenerateDelimitedNamedAttributeWriteTo(string fieldName) { - return - " if (" + fieldName + ".OpenToken != null)\n" + - " {\n" + - " writer.WriteToken(" + fieldName + ".OpenToken.Value, \" \");\n" + - " for (var i = 0; i < " + fieldName + ".Count; i++)\n" + - " {\n" + - " if (i > 0)\n" + - " {\n" + - " writer.WriteToken(" + fieldName + ".SeparatorTokens[i - 1], string.Empty);\n" + - " }\n" + - " " + fieldName + "[i].WriteTo(writer, i > 0 ? \" \" : string.Empty);\n" + - " }\n" + - " writer.WriteToken(" + fieldName + ".CloseToken!.Value, string.Empty);\n" + - " }\n"; + return " " + fieldName + ".WriteTo(writer, \" \", static (item, w, trivia) => item.WriteTo(w, trivia));\n"; } private static string GenerateDelimitedTokenWriteTo(string fieldName) { - return - " if (" + fieldName + ".OpenToken != null)\n" + - " {\n" + - " writer.WriteToken(" + fieldName + ".OpenToken.Value, \" \");\n" + - " for (var i = 0; i < " + fieldName + ".Count; i++)\n" + - " {\n" + - " if (i > 0)\n" + - " {\n" + - " writer.WriteToken(" + fieldName + ".SeparatorTokens[i - 1], string.Empty);\n" + - " }\n" + - " writer.WriteToken(" + fieldName + "[i], i > 0 ? \" \" : string.Empty);\n" + - " }\n" + - " writer.WriteToken(" + fieldName + ".CloseToken!.Value, string.Empty);\n" + - " }\n"; + return " " + fieldName + ".WriteTo(writer, \" \", static (item, w, trivia) => w.WriteToken(item, trivia));\n"; } private static string GetPunctuationFieldName(TokenKind tokenKind) diff --git a/src/MLIR/Syntax/BlockSyntax.cs b/src/MLIR/Syntax/BlockSyntax.cs index 799c3b6..bd57253 100644 --- a/src/MLIR/Syntax/BlockSyntax.cs +++ b/src/MLIR/Syntax/BlockSyntax.cs @@ -89,21 +89,7 @@ public void WriteTo( { writer.WriteToken(LabelToken, "\n", blockIndentLevel); - if (Arguments.OpenToken != null) - { - writer.WriteToken(Arguments.OpenToken.Value, string.Empty); - for (var i = 0; i < Arguments.Count; i++) - { - if (i > 0) - { - writer.WriteToken(Arguments.SeparatorTokens[i - 1], string.Empty); - } - - Arguments[i].WriteTo(writer, i > 0 ? " " : string.Empty); - } - - writer.WriteToken(Arguments.CloseToken!.Value, string.Empty); - } + Arguments.WriteTo(writer, string.Empty, static (arg, w, trivia) => arg.WriteTo(w, trivia)); writer.WriteToken(ColonToken, string.Empty); } diff --git a/src/MLIR/Syntax/DelimitedSyntaxList.cs b/src/MLIR/Syntax/DelimitedSyntaxList.cs index 2a030e9..02a31c6 100644 --- a/src/MLIR/Syntax/DelimitedSyntaxList.cs +++ b/src/MLIR/Syntax/DelimitedSyntaxList.cs @@ -51,6 +51,39 @@ public sealed class DelimitedSyntaxList( /// The item index. public T this[int index] => Items[index]; + /// + /// Writes this list to the supplied syntax writer if an opening delimiter token is present. + /// Writes the opening delimiter, then each element (interleaved with separator tokens), then + /// the closing delimiter. When is this method + /// does nothing. + /// + /// The syntax writer to write to. + /// The fallback leading trivia to use for the opening delimiter token. + /// A delegate that writes a single element to the writer. + public void WriteTo( + Text.SyntaxWriter writer, + string openLeadingTrivia, + System.Action writeElement) + { + if (OpenToken == null) + { + return; + } + + writer.WriteToken(OpenToken.Value, openLeadingTrivia); + for (var i = 0; i < Count; i++) + { + if (i > 0) + { + writer.WriteToken(SeparatorTokens[i - 1], string.Empty); + } + + writeElement(Items[i], writer, i > 0 ? " " : string.Empty); + } + + writer.WriteToken(CloseToken!.Value, string.Empty); + } + /// /// Returns an enumerator over the list items. /// diff --git a/src/MLIR/Syntax/GenericOperationBodySyntax.cs b/src/MLIR/Syntax/GenericOperationBodySyntax.cs index 12df9a9..283e93d 100644 --- a/src/MLIR/Syntax/GenericOperationBodySyntax.cs +++ b/src/MLIR/Syntax/GenericOperationBodySyntax.cs @@ -79,55 +79,16 @@ public override bool TryGetGenericBody(out GenericOperationBodySyntax? genericBo /// public override void WriteTo(Text.SyntaxWriter writer, int indentLevel) { - writer.WriteToken(OperandList.OpenToken!.Value, string.Empty); - for (var i = 0; i < OperandList.Count; i++) - { - if (i > 0) - { - writer.WriteToken(OperandList.SeparatorTokens[i - 1], string.Empty); - } - - writer.WriteToken(OperandList[i], i > 0 ? " " : string.Empty); - } - - writer.WriteToken(OperandList.CloseToken!.Value, string.Empty); - - if (SuccessorList.OpenToken != null) - { - writer.WriteToken(SuccessorList.OpenToken.Value, " "); - for (var i = 0; i < SuccessorList.Count; i++) - { - if (i > 0) - { - writer.WriteToken(SuccessorList.SeparatorTokens[i - 1], string.Empty); - } + OperandList.WriteTo(writer, string.Empty, static (token, w, trivia) => w.WriteToken(token, trivia)); - writer.WriteToken(SuccessorList[i], i > 0 ? " " : string.Empty); - } - - writer.WriteToken(SuccessorList.CloseToken!.Value, string.Empty); - } + SuccessorList.WriteTo(writer, " ", static (token, w, trivia) => w.WriteToken(token, trivia)); foreach (var region in Regions) { writer.WriteRegion(region, indentLevel); } - if (Attributes.OpenToken != null) - { - writer.WriteToken(Attributes.OpenToken.Value, " "); - for (var i = 0; i < Attributes.Count; i++) - { - if (i > 0) - { - writer.WriteToken(Attributes.SeparatorTokens[i - 1], string.Empty); - } - - Attributes[i].WriteTo(writer, i > 0 ? " " : string.Empty); - } - - writer.WriteToken(Attributes.CloseToken!.Value, string.Empty); - } + Attributes.WriteTo(writer, " ", static (attr, w, trivia) => attr.WriteTo(w, trivia)); if (TypeSignatureColonToken != null && TypeSignatureSyntax != null) { diff --git a/tests/MLIR.Tests/ConstructionTests.cs b/tests/MLIR.Tests/ConstructionTests.cs index 129cef0..80e7645 100644 --- a/tests/MLIR.Tests/ConstructionTests.cs +++ b/tests/MLIR.Tests/ConstructionTests.cs @@ -196,4 +196,37 @@ [new SyntaxToken("%0")], Assert.True(module.Operations[0].HasCustomAssemblyBody); Assert.Equal("0", module.Operations[0].Attributes[0].RawValue.Text); } + + [Fact] + public void DelimitedSyntaxListWriteToWritesAllTokensAndElements() + { + var list = new DelimitedSyntaxList( + new SyntaxToken("("), + [ + new BlockArgumentSyntax(new SyntaxToken("%arg0"), new SyntaxToken(":"), new RawTypeSyntax(new RawSyntaxText("i32"))), + new BlockArgumentSyntax(new SyntaxToken("%arg1"), new SyntaxToken(":"), new RawTypeSyntax(new RawSyntaxText("i64"))), + ], + [new SyntaxToken(",")], + new SyntaxToken(")")); + + var writer = new SyntaxWriter(); + list.WriteTo(writer, string.Empty, static (arg, w, trivia) => arg.WriteTo(w, trivia)); + + Assert.Equal("(%arg0: i32, %arg1: i64)", writer.ToString()); + } + + [Fact] + public void DelimitedSyntaxListWriteToDoesNothingWhenOpenTokenIsNull() + { + var list = new DelimitedSyntaxList( + null, + [], + [], + null); + + var writer = new SyntaxWriter(); + list.WriteTo(writer, string.Empty, static (arg, w, trivia) => arg.WriteTo(w, trivia)); + + Assert.Equal(string.Empty, writer.ToString()); + } } From 03703ffc0da17aae1fa6be1d2bf2aca7bb65ffa3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:58:19 +0000 Subject: [PATCH 3/4] Add WriteDelimitedList overloads to SyntaxWriter; update callers to use them Agent-Logs-Url: https://github.com/jonathanvdc/MLIR.NET/sessions/0f179478-69f1-4b5b-b698-33c13ee1012d Co-authored-by: jonathanvdc <9839946+jonathanvdc@users.noreply.github.com> --- src/MLIR.Generators/DialectSourceEmitter.cs | 4 +-- src/MLIR/Syntax/BlockSyntax.cs | 2 +- src/MLIR/Syntax/GenericOperationBodySyntax.cs | 6 ++-- src/MLIR/Text/SyntaxWriter.cs | 33 +++++++++++++++++++ tests/MLIR.Tests/ConstructionTests.cs | 4 +-- 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/MLIR.Generators/DialectSourceEmitter.cs b/src/MLIR.Generators/DialectSourceEmitter.cs index d973b3a..faee84e 100644 --- a/src/MLIR.Generators/DialectSourceEmitter.cs +++ b/src/MLIR.Generators/DialectSourceEmitter.cs @@ -361,12 +361,12 @@ private static void AppendBodySyntaxFields(List fields, HashSet private static string GenerateDelimitedNamedAttributeWriteTo(string fieldName) { - return " " + fieldName + ".WriteTo(writer, \" \", static (item, w, trivia) => item.WriteTo(w, trivia));\n"; + return " writer.WriteDelimitedList(" + fieldName + ", \" \");\n"; } private static string GenerateDelimitedTokenWriteTo(string fieldName) { - return " " + fieldName + ".WriteTo(writer, \" \", static (item, w, trivia) => w.WriteToken(item, trivia));\n"; + return " writer.WriteDelimitedList(" + fieldName + ", \" \");\n"; } private static string GetPunctuationFieldName(TokenKind tokenKind) diff --git a/src/MLIR/Syntax/BlockSyntax.cs b/src/MLIR/Syntax/BlockSyntax.cs index bd57253..1364785 100644 --- a/src/MLIR/Syntax/BlockSyntax.cs +++ b/src/MLIR/Syntax/BlockSyntax.cs @@ -89,7 +89,7 @@ public void WriteTo( { writer.WriteToken(LabelToken, "\n", blockIndentLevel); - Arguments.WriteTo(writer, string.Empty, static (arg, w, trivia) => arg.WriteTo(w, trivia)); + writer.WriteDelimitedList(Arguments, string.Empty); writer.WriteToken(ColonToken, string.Empty); } diff --git a/src/MLIR/Syntax/GenericOperationBodySyntax.cs b/src/MLIR/Syntax/GenericOperationBodySyntax.cs index 283e93d..a2e82f7 100644 --- a/src/MLIR/Syntax/GenericOperationBodySyntax.cs +++ b/src/MLIR/Syntax/GenericOperationBodySyntax.cs @@ -79,16 +79,16 @@ public override bool TryGetGenericBody(out GenericOperationBodySyntax? genericBo /// public override void WriteTo(Text.SyntaxWriter writer, int indentLevel) { - OperandList.WriteTo(writer, string.Empty, static (token, w, trivia) => w.WriteToken(token, trivia)); + writer.WriteDelimitedList(OperandList, string.Empty); - SuccessorList.WriteTo(writer, " ", static (token, w, trivia) => w.WriteToken(token, trivia)); + writer.WriteDelimitedList(SuccessorList, " "); foreach (var region in Regions) { writer.WriteRegion(region, indentLevel); } - Attributes.WriteTo(writer, " ", static (attr, w, trivia) => attr.WriteTo(w, trivia)); + writer.WriteDelimitedList(Attributes, " "); if (TypeSignatureColonToken != null && TypeSignatureSyntax != null) { diff --git a/src/MLIR/Text/SyntaxWriter.cs b/src/MLIR/Text/SyntaxWriter.cs index a2e349f..df030b5 100644 --- a/src/MLIR/Text/SyntaxWriter.cs +++ b/src/MLIR/Text/SyntaxWriter.cs @@ -82,6 +82,39 @@ public void WriteBlock(BlockSyntax block, int regionIndentLevel) block.WriteTo(this, regionIndentLevel); } + /// + /// Writes a delimited list of syntax tokens. + /// Does nothing when has no opening delimiter token. + /// + /// The delimited token list to write. + /// The fallback leading trivia for the opening delimiter token. + public void WriteDelimitedList(DelimitedSyntaxList list, string openLeadingTrivia) + { + list.WriteTo(this, openLeadingTrivia, static (token, writer, trivia) => writer.WriteToken(token, trivia)); + } + + /// + /// Writes a delimited list of named attribute syntax nodes. + /// Does nothing when has no opening delimiter token. + /// + /// The delimited attribute list to write. + /// The fallback leading trivia for the opening delimiter token. + public void WriteDelimitedList(DelimitedSyntaxList list, string openLeadingTrivia) + { + list.WriteTo(this, openLeadingTrivia, static (attr, writer, trivia) => attr.WriteTo(writer, trivia)); + } + + /// + /// Writes a delimited list of block argument syntax nodes. + /// Does nothing when has no opening delimiter token. + /// + /// The delimited block argument list to write. + /// The fallback leading trivia for the opening delimiter token. + public void WriteDelimitedList(DelimitedSyntaxList list, string openLeadingTrivia) + { + list.WriteTo(this, openLeadingTrivia, static (arg, writer, trivia) => arg.WriteTo(writer, trivia)); + } + /// /// Writes a token using its preserved trivia when present, or synthesized trivia otherwise. /// diff --git a/tests/MLIR.Tests/ConstructionTests.cs b/tests/MLIR.Tests/ConstructionTests.cs index 80e7645..33f5268 100644 --- a/tests/MLIR.Tests/ConstructionTests.cs +++ b/tests/MLIR.Tests/ConstructionTests.cs @@ -210,7 +210,7 @@ [new SyntaxToken(",")], new SyntaxToken(")")); var writer = new SyntaxWriter(); - list.WriteTo(writer, string.Empty, static (arg, w, trivia) => arg.WriteTo(w, trivia)); + writer.WriteDelimitedList(list, string.Empty); Assert.Equal("(%arg0: i32, %arg1: i64)", writer.ToString()); } @@ -225,7 +225,7 @@ public void DelimitedSyntaxListWriteToDoesNothingWhenOpenTokenIsNull() null); var writer = new SyntaxWriter(); - list.WriteTo(writer, string.Empty, static (arg, w, trivia) => arg.WriteTo(w, trivia)); + writer.WriteDelimitedList(list, string.Empty); Assert.Equal(string.Empty, writer.ToString()); } From 298bdb893e44c7cab5c72989272256a43b27aadb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 15:20:51 +0000 Subject: [PATCH 4/4] Add IsPresent property to DelimitedSyntaxList; replace OpenToken null checks with it Agent-Logs-Url: https://github.com/jonathanvdc/MLIR.NET/sessions/fed83386-d728-477b-ab0a-091f423d1e9d Co-authored-by: jonathanvdc <9839946+jonathanvdc@users.noreply.github.com> --- src/MLIR/Syntax/DelimitedSyntaxList.cs | 9 +++++++-- tests/MLIR.Tests/ConstructionTests.cs | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/MLIR/Syntax/DelimitedSyntaxList.cs b/src/MLIR/Syntax/DelimitedSyntaxList.cs index 02a31c6..5858b78 100644 --- a/src/MLIR/Syntax/DelimitedSyntaxList.cs +++ b/src/MLIR/Syntax/DelimitedSyntaxList.cs @@ -40,6 +40,11 @@ public sealed class DelimitedSyntaxList( /// public SyntaxToken? CloseToken { get; } = closeToken; + /// + /// Gets a value indicating whether this list is present in the source, i.e., has an opening delimiter token. + /// + public bool IsPresent => OpenToken.HasValue; + /// /// Gets the number of items in the list. /// @@ -54,7 +59,7 @@ public sealed class DelimitedSyntaxList( /// /// Writes this list to the supplied syntax writer if an opening delimiter token is present. /// Writes the opening delimiter, then each element (interleaved with separator tokens), then - /// the closing delimiter. When is this method + /// the closing delimiter. When is this method /// does nothing. /// /// The syntax writer to write to. @@ -65,7 +70,7 @@ public void WriteTo( string openLeadingTrivia, System.Action writeElement) { - if (OpenToken == null) + if (!IsPresent) { return; } diff --git a/tests/MLIR.Tests/ConstructionTests.cs b/tests/MLIR.Tests/ConstructionTests.cs index 33f5268..0b3d254 100644 --- a/tests/MLIR.Tests/ConstructionTests.cs +++ b/tests/MLIR.Tests/ConstructionTests.cs @@ -216,7 +216,7 @@ [new SyntaxToken(",")], } [Fact] - public void DelimitedSyntaxListWriteToDoesNothingWhenOpenTokenIsNull() + public void DelimitedSyntaxListWriteToDoesNothingWhenNotPresent() { var list = new DelimitedSyntaxList( null, @@ -224,6 +224,8 @@ public void DelimitedSyntaxListWriteToDoesNothingWhenOpenTokenIsNull() [], null); + Assert.False(list.IsPresent); + var writer = new SyntaxWriter(); writer.WriteDelimitedList(list, string.Empty);