11namespace MLIR . Generators ;
22
3+ using System . Collections . Generic ;
34using System . Globalization ;
45using System . Text ;
56using MLIR . ODS . Model ;
7+ using MLIR . ODS . Model . AssemblyFormat ;
8+ using MLIR . Text ;
69
710internal static class DialectSourceEmitter
811{
@@ -29,6 +32,12 @@ public static string GenerateDialectSource(DialectModel dialect)
2932 AppendOperationClass ( builder , operation ) ;
3033 builder . AppendLine ( ) ;
3134
35+ if ( operation . AssemblyFormat != null )
36+ {
37+ AppendOperationBodySyntaxClass ( builder , operation ) ;
38+ builder . AppendLine ( ) ;
39+ }
40+
3241 if ( operation . HasCustomAssemblyFormat )
3342 {
3443 AppendAssemblyFormatClass ( builder , operation ) ;
@@ -169,6 +178,308 @@ private static void AppendOperationClass(StringBuilder builder, OperationModel o
169178 builder . AppendLine ( "}" ) ;
170179 }
171180
181+ private static void AppendOperationBodySyntaxClass ( StringBuilder builder , OperationModel operation )
182+ {
183+ var className = DialectGeneratorNaming . GetOperationClassName ( operation ) ;
184+ var assemblyFormat = operation . AssemblyFormat ! ;
185+ var fields = ComputeBodySyntaxFields ( operation , assemblyFormat ) ;
186+
187+ builder . AppendLine ( "public sealed class " + className + "BodySyntax : OperationBodySyntax" ) ;
188+ builder . AppendLine ( "{" ) ;
189+
190+ // Constructor
191+ builder . Append ( " public " + className + "BodySyntax(" ) ;
192+ for ( var i = 0 ; i < fields . Count ; i ++ )
193+ {
194+ if ( i > 0 )
195+ {
196+ builder . Append ( ", " ) ;
197+ }
198+
199+ builder . Append ( fields [ i ] . CsType + " " + LowerFirst ( fields [ i ] . Name ) ) ;
200+ }
201+
202+ builder . AppendLine ( ")" ) ;
203+ builder . AppendLine ( " {" ) ;
204+ foreach ( var field in fields )
205+ {
206+ builder . AppendLine ( " " + field . Name + " = " + LowerFirst ( field . Name ) + ";" ) ;
207+ }
208+
209+ builder . AppendLine ( " }" ) ;
210+
211+ if ( fields . Count > 0 )
212+ {
213+ builder . AppendLine ( ) ;
214+ foreach ( var field in fields )
215+ {
216+ builder . AppendLine ( " public " + field . CsType + " " + field . Name + " { get; }" ) ;
217+ }
218+ }
219+
220+ builder . AppendLine ( ) ;
221+ builder . AppendLine ( " public override bool TryGetGenericBody(out GenericOperationBodySyntax? genericBody)" ) ;
222+ builder . AppendLine ( " {" ) ;
223+ builder . AppendLine ( " genericBody = null;" ) ;
224+ builder . AppendLine ( " return false;" ) ;
225+ builder . AppendLine ( " }" ) ;
226+ builder . AppendLine ( ) ;
227+ builder . AppendLine ( " public override void WriteTo(Text.SyntaxWriter writer, int indentLevel, System.Action<Text.SyntaxWriter, RegionSyntax, int> writeRegion)" ) ;
228+ builder . AppendLine ( " {" ) ;
229+ foreach ( var field in fields )
230+ {
231+ builder . Append ( field . WriteToCode ) ;
232+ }
233+
234+ builder . AppendLine ( " }" ) ;
235+ builder . AppendLine ( "}" ) ;
236+ }
237+
238+ private static IReadOnlyList < BodySyntaxField > ComputeBodySyntaxFields ( OperationModel operation , AssemblyFormatModel assemblyFormat )
239+ {
240+ var fields = new List < BodySyntaxField > ( ) ;
241+ var usedNames = new HashSet < string > ( StringComparer . Ordinal ) ;
242+
243+ foreach ( var element in assemblyFormat . Elements )
244+ {
245+ AppendBodySyntaxFields ( fields , usedNames , element , operation ) ;
246+ }
247+
248+ return fields ;
249+ }
250+
251+ private static void AppendBodySyntaxFields ( List < BodySyntaxField > fields , HashSet < string > usedNames , Element element , OperationModel operation )
252+ {
253+ switch ( element )
254+ {
255+ case LiteralChunk literal :
256+ foreach ( var lit in literal . Value )
257+ {
258+ switch ( lit )
259+ {
260+ case PunctuationLiteral punc :
261+ {
262+ var name = MakeUnique ( GetPunctuationFieldName ( punc . TokenKind ) , usedNames ) ;
263+ fields . Add ( new BodySyntaxField ( name , "SyntaxToken" ,
264+ " writer.WriteToken(" + name + ", string.Empty);\n " ) ) ;
265+ break ;
266+ }
267+
268+ case KeywordLiteral kw :
269+ {
270+ var name = MakeUnique ( DialectGeneratorNaming . ToPascalCase ( kw . Spelling ) + "Keyword" , usedNames ) ;
271+ fields . Add ( new BodySyntaxField ( name , "SyntaxToken" ,
272+ " writer.WriteToken(" + name + ", \" \" );\n " ) ) ;
273+ break ;
274+ }
275+
276+ // WhitespaceLiteral, NewlineLiteral, EmptyLiteral → no field; spacing is in stored trivia
277+ }
278+ }
279+
280+ break ;
281+
282+ case VariableChunk variable :
283+ {
284+ var pascalName = DialectGeneratorNaming . ToPascalCase ( variable . Name ) ;
285+ if ( ContainsName ( operation . Attributes , variable . Name ) )
286+ {
287+ var name = MakeUnique ( pascalName , usedNames ) ;
288+ fields . Add ( new BodySyntaxField ( name , "AttributeValueSyntax" ,
289+ " " + name + ".WriteTo(writer, \" \" );\n " ) ) ;
290+ }
291+ else
292+ {
293+ // Operand, result variable, or unknown → SyntaxToken
294+ var name = MakeUnique ( pascalName , usedNames ) ;
295+ fields . Add ( new BodySyntaxField ( name , "SyntaxToken" ,
296+ " writer.WriteToken(" + name + ", \" \" );\n " ) ) ;
297+ }
298+
299+ break ;
300+ }
301+
302+ case AttrDictDirectiveChunk _:
303+ case AttrDictWithKeywordDirectiveChunk _:
304+ {
305+ var name = MakeUnique ( "AttrDict" , usedNames ) ;
306+ fields . Add ( new BodySyntaxField ( name , "DelimitedSyntaxList<NamedAttributeSyntax>" ,
307+ GenerateDelimitedNamedAttributeWriteTo ( name ) ) ) ;
308+ break ;
309+ }
310+
311+ case PropDictDirectiveChunk _:
312+ {
313+ var name = MakeUnique ( "PropDict" , usedNames ) ;
314+ fields . Add ( new BodySyntaxField ( name , "DelimitedSyntaxList<NamedAttributeSyntax>" ,
315+ GenerateDelimitedNamedAttributeWriteTo ( name ) ) ) ;
316+ break ;
317+ }
318+
319+ case RegionsDirectiveChunk _:
320+ {
321+ var name = MakeUnique ( "Regions" , usedNames ) ;
322+ fields . Add ( new BodySyntaxField ( name , "IReadOnlyList<RegionSyntax>" ,
323+ " foreach (var region in " + name + ")\n " +
324+ " {\n " +
325+ " writeRegion(writer, region, indentLevel);\n " +
326+ " }\n " ) ) ;
327+ break ;
328+ }
329+
330+ case TypeDirectiveChunk typeDir :
331+ {
332+ var baseName = typeDir . Operand is VariableOperand varOp
333+ ? DialectGeneratorNaming . ToPascalCase ( varOp . Name ) + "Type"
334+ : "Type" ;
335+ var name = MakeUnique ( baseName , usedNames ) ;
336+ fields . Add ( new BodySyntaxField ( name , "TypeSyntax" ,
337+ " " + name + ".WriteTo(writer, \" \" );\n " ) ) ;
338+ break ;
339+ }
340+
341+ case SuccessorsDirectiveChunk _:
342+ {
343+ var name = MakeUnique ( "Successors" , usedNames ) ;
344+ fields . Add ( new BodySyntaxField ( name , "DelimitedSyntaxList<SyntaxToken>" ,
345+ GenerateDelimitedTokenWriteTo ( name ) ) ) ;
346+ break ;
347+ }
348+
349+ case OperandsDirectiveChunk _:
350+ {
351+ var name = MakeUnique ( "Operands" , usedNames ) ;
352+ fields . Add ( new BodySyntaxField ( name , "DelimitedSyntaxList<SyntaxToken>" ,
353+ GenerateDelimitedTokenWriteTo ( name ) ) ) ;
354+ break ;
355+ }
356+
357+ // OptionalGroup, OilistDirectiveChunk, CustomDirectiveChunk, FunctionalTypeDirectiveChunk,
358+ // QualifiedDirectiveChunk, RefDirectiveChunk, ResultsDirectiveChunk → not stored in this CST class
359+ }
360+ }
361+
362+ private static string GenerateDelimitedNamedAttributeWriteTo ( string fieldName )
363+ {
364+ return
365+ " if (" + fieldName + ".OpenToken != null)\n " +
366+ " {\n " +
367+ " writer.WriteToken(" + fieldName + ".OpenToken.Value, \" \" );\n " +
368+ " for (var i = 0; i < " + fieldName + ".Count; i++)\n " +
369+ " {\n " +
370+ " if (i > 0)\n " +
371+ " {\n " +
372+ " writer.WriteToken(" + fieldName + ".SeparatorTokens[i - 1], string.Empty);\n " +
373+ " }\n " +
374+ " " + fieldName + "[i].WriteTo(writer, i > 0 ? \" \" : string.Empty);\n " +
375+ " }\n " +
376+ " writer.WriteToken(" + fieldName + ".CloseToken!.Value, string.Empty);\n " +
377+ " }\n " ;
378+ }
379+
380+ private static string GenerateDelimitedTokenWriteTo ( string fieldName )
381+ {
382+ return
383+ " if (" + fieldName + ".OpenToken != null)\n " +
384+ " {\n " +
385+ " writer.WriteToken(" + fieldName + ".OpenToken.Value, \" \" );\n " +
386+ " for (var i = 0; i < " + fieldName + ".Count; i++)\n " +
387+ " {\n " +
388+ " if (i > 0)\n " +
389+ " {\n " +
390+ " writer.WriteToken(" + fieldName + ".SeparatorTokens[i - 1], string.Empty);\n " +
391+ " }\n " +
392+ " writer.WriteToken(" + fieldName + "[i], i > 0 ? \" \" : string.Empty);\n " +
393+ " }\n " +
394+ " writer.WriteToken(" + fieldName + ".CloseToken!.Value, string.Empty);\n " +
395+ " }\n " ;
396+ }
397+
398+ private static string GetPunctuationFieldName ( TokenKind tokenKind )
399+ {
400+ return tokenKind switch
401+ {
402+ TokenKind . Comma => "CommaToken" ,
403+ TokenKind . LParen => "LParenToken" ,
404+ TokenKind . RParen => "RParenToken" ,
405+ TokenKind . LBracket => "LBracketToken" ,
406+ TokenKind . RBracket => "RBracketToken" ,
407+ TokenKind . LBrace => "LBraceToken" ,
408+ TokenKind . RBrace => "RBraceToken" ,
409+ TokenKind . Arrow => "ArrowToken" ,
410+ TokenKind . Colon => "ColonToken" ,
411+ TokenKind . Equal => "EqualToken" ,
412+ TokenKind . LessThan => "LessThanToken" ,
413+ TokenKind . GreaterThan => "GreaterThanToken" ,
414+ TokenKind . Question => "QuestionToken" ,
415+ TokenKind . Star => "StarToken" ,
416+ TokenKind . Plus => "PlusToken" ,
417+ TokenKind . Minus => "MinusToken" ,
418+ TokenKind . Dot => "DotToken" ,
419+ TokenKind . At => "AtToken" ,
420+ TokenKind . Hash => "HashToken" ,
421+ _ => "Token" ,
422+ } ;
423+ }
424+
425+ private static string MakeUnique ( string baseName , HashSet < string > used )
426+ {
427+ if ( used . Add ( baseName ) )
428+ {
429+ return baseName ;
430+ }
431+
432+ for ( var i = 2 ; ; i ++ )
433+ {
434+ var candidate = baseName + i . ToString ( CultureInfo . InvariantCulture ) ;
435+ if ( used . Add ( candidate ) )
436+ {
437+ return candidate ;
438+ }
439+ }
440+ }
441+
442+ private static string LowerFirst ( string name )
443+ {
444+ if ( name . Length == 0 )
445+ {
446+ return name ;
447+ }
448+
449+ return char . ToLowerInvariant ( name [ 0 ] ) + name . Substring ( 1 ) ;
450+ }
451+
452+ private static bool ContainsName ( IReadOnlyList < string > names , string name )
453+ {
454+ foreach ( var n in names )
455+ {
456+ if ( string . Equals ( n , name , StringComparison . Ordinal ) )
457+ {
458+ return true ;
459+ }
460+ }
461+
462+ return false ;
463+ }
464+
465+ private sealed class BodySyntaxField
466+ {
467+ public BodySyntaxField ( string name , string csType , string writeToCode )
468+ {
469+ Name = name ;
470+ CsType = csType ;
471+ WriteToCode = writeToCode ;
472+ }
473+
474+ public string Name { get ; }
475+ public string CsType { get ; }
476+
477+ /// <summary>
478+ /// C# code (indented for the WriteTo body, ending with a newline) that writes this field.
479+ /// </summary>
480+ public string WriteToCode { get ; }
481+ }
482+
172483 private static void AppendAssemblyFormatClass ( StringBuilder builder , OperationModel operation )
173484 {
174485 var className = DialectGeneratorNaming . GetOperationClassName ( operation ) ;
0 commit comments