private static int WriteCSharpSourceFiles( string inputFile, bool writeSource, bool writeTests, bool writeSignatures, string outputFile ) { var tree = ReadTree(inputFile); // The syntax.xml doc contains some nodes that are useful for other tools, but which are // not needed by this syntax generator. Specifically, we have `<Choice>` and // `<Sequence>` nodes in the xml file to help others tools understand the relationship // between some fields (i.e. 'only one of these children can be non-null'). To make our // life easier, we just flatten all those nodes, grabbing all the nested `<Field>` nodes // and placing into a single linear list that we can then process. TreeFlattening.FlattenChildren(tree); if (writeSignatures) { SignatureWriter.Write(Console.Out, tree); } else { if (writeSource) { var outputPath = outputFile.Trim('"'); var prefix = Path.GetFileName(inputFile); var outputMainFile = Path.Combine(outputPath, $"{prefix}.Main.Generated.cs"); var outputInternalFile = Path.Combine( outputPath, $"{prefix}.Internal.Generated.cs" ); var outputSyntaxFile = Path.Combine( outputPath, $"{prefix}.Syntax.Generated.cs" ); WriteToFile(writer => SourceWriter.WriteMain(writer, tree), outputMainFile); WriteToFile( writer => SourceWriter.WriteInternal(writer, tree), outputInternalFile ); WriteToFile(writer => SourceWriter.WriteSyntax(writer, tree), outputSyntaxFile); } if (writeTests) { WriteToFile(writer => TestWriter.Write(writer, tree), outputFile); } } return(0); }
public void Execute(GeneratorExecutionContext context) { var syntaxXml = context.AdditionalFiles.SingleOrDefault(a => Path.GetFileName(a.Path) == "Syntax.xml"); if (syntaxXml == null) { context.ReportDiagnostic(Diagnostic.Create(s_MissingSyntaxXml, location: null)); return; } var syntaxXmlText = syntaxXml.GetText(); if (syntaxXmlText == null) { context.ReportDiagnostic(Diagnostic.Create(s_UnableToReadSyntaxXml, location: null)); return; } Tree tree; var reader = XmlReader.Create(new SourceTextReader(syntaxXmlText), new XmlReaderSettings { DtdProcessing = DtdProcessing.Prohibit }); try { var serializer = new XmlSerializer(typeof(Tree)); tree = (Tree)serializer.Deserialize(reader); } catch (InvalidOperationException ex) when(ex.InnerException is XmlException) { var xmlException = (XmlException)ex.InnerException; var line = syntaxXmlText.Lines[xmlException.LineNumber - 1]; // LineNumber is one-based. int offset = xmlException.LinePosition - 1; // LinePosition is one-based var position = line.Start + offset; var span = new TextSpan(position, 0); var lineSpan = syntaxXmlText.Lines.GetLinePositionSpan(span); context.ReportDiagnostic( Diagnostic.Create( s_SyntaxXmlError, location: Location.Create(syntaxXml.Path, span, lineSpan), xmlException.Message)); return; } TreeFlattening.FlattenChildren(tree); AddResult(writer => SourceWriter.WriteMain(writer, tree, context.CancellationToken), "Syntax.xml.Main.Generated.cs"); AddResult(writer => SourceWriter.WriteInternal(writer, tree, context.CancellationToken), "Syntax.xml.Internal.Generated.cs"); AddResult(writer => SourceWriter.WriteSyntax(writer, tree, context.CancellationToken), "Syntax.xml.Syntax.Generated.cs"); void AddResult(Action <TextWriter> writeFunction, string hintName) { // Write out the contents to a StringBuilder to avoid creating a single large string // in memory var stringBuilder = new StringBuilder(); using (var textWriter = new StringWriter(stringBuilder)) { writeFunction(textWriter); } // And create a SourceText from the StringBuilder, once again avoiding allocating a single massive string context.AddSource(hintName, SourceText.From(new StringBuilderReader(stringBuilder), stringBuilder.Length, encoding: Encoding.UTF8)); } }