diff --git a/src/MLIR.Generators/DialectSourceEmitter.cs b/src/MLIR.Generators/DialectSourceEmitter.cs index 3f33b88..faee84e 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 " writer.WriteDelimitedList(" + fieldName + ", \" \");\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 " 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 799c3b6..1364785 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); - } + writer.WriteDelimitedList(Arguments, string.Empty); writer.WriteToken(ColonToken, string.Empty); } diff --git a/src/MLIR/Syntax/DelimitedSyntaxList.cs b/src/MLIR/Syntax/DelimitedSyntaxList.cs index 2a030e9..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. /// @@ -51,6 +56,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 (!IsPresent) + { + 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..a2e82f7 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); - } + writer.WriteDelimitedList(OperandList, string.Empty); - writer.WriteToken(SuccessorList[i], i > 0 ? " " : string.Empty); - } - - writer.WriteToken(SuccessorList.CloseToken!.Value, string.Empty); - } + writer.WriteDelimitedList(SuccessorList, " "); 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); - } + 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 129cef0..0b3d254 100644 --- a/tests/MLIR.Tests/ConstructionTests.cs +++ b/tests/MLIR.Tests/ConstructionTests.cs @@ -196,4 +196,39 @@ [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(); + writer.WriteDelimitedList(list, string.Empty); + + Assert.Equal("(%arg0: i32, %arg1: i64)", writer.ToString()); + } + + [Fact] + public void DelimitedSyntaxListWriteToDoesNothingWhenNotPresent() + { + var list = new DelimitedSyntaxList( + null, + [], + [], + null); + + Assert.False(list.IsPresent); + + var writer = new SyntaxWriter(); + writer.WriteDelimitedList(list, string.Empty); + + Assert.Equal(string.Empty, writer.ToString()); + } }