/// <summary> /// Logs the content of each file in the given compilation as Information using the given logger. /// If the id of a file is consistent with the one assigned to a code snippet, /// strips the lines of code that correspond to the wrapping defined by WrapSnippet. /// Throws an ArgumentNullException if the given compilation is null. /// </summary> private static void PrintFileContentInMemory(Compilation compilation, ILogger logger) { if (compilation == null) { throw new ArgumentNullException(nameof(compilation)); } foreach (var file in compilation.SourceFiles) { IEnumerable <string> inMemory = compilation.FileContent[file]; var stripWrapping = Options.IsCodeSnippet(file); QsCompilerError.Verify(!stripWrapping || inMemory.Count() >= 4, "expecting at least four lines of code for the compilation of a code snippet"); if (stripWrapping) { inMemory = inMemory.Skip(2).Take(inMemory.Count() - 4); } logger.Log( InformationCode.FileContentInMemory, Enumerable.Empty <string>(), stripWrapping ? null : file.Value, messageParam: $"{Environment.NewLine}{String.Concat(inMemory)}" ); } }
/// <summary> /// Logs the part of the given evaluated syntax tree that corresponds to each file /// in the given compilation as Information using the given logger. /// If the given evaluated tree is null, queries the tree contained in the given compilation instead. /// If the id of a file is consistent with the one assigned to a code snippet, /// strips the lines of code that correspond to the wrapping defined by WrapSnippet. /// Throws an ArgumentException if this is not possible because the given syntax tree is inconsistent with that wrapping. /// Throws an ArgumentNullException if the given compilation is null. /// </summary> private static void PrintSyntaxTree(IEnumerable <QsNamespace> evaluatedTree, Compilation compilation, ILogger logger) { if (compilation == null) { throw new ArgumentNullException(nameof(compilation)); } evaluatedTree = evaluatedTree ?? compilation.SyntaxTree.Values; foreach (var file in compilation.SourceFiles) { var stripWrapping = Options.IsCodeSnippet(file); var subtree = evaluatedTree.Select(ns => FilterBySourceFile.Apply(ns, file)).Where(ns => ns.Elements.Any()); void PrintTree(string serialization) => logger.Log( InformationCode.BuiltSyntaxTree, Enumerable.Empty <string>(), stripWrapping ? null : file.Value, messageParam: new string[] { "", serialization }); if (!stripWrapping) { PrintTree(JsonConvert.SerializeObject(subtree, Newtonsoft.Json.Formatting.Indented)); } else { PrintTree(JsonConvert.SerializeObject(StripSnippetWrapping(subtree), Newtonsoft.Json.Formatting.Indented)); } } }
/// <summary> /// Logs the tokenization of each file in the given compilation as Information using the given logger. /// If the id of a file is consistent with the one assigned to a code snippet, /// strips the tokens that correspond to the wrapping defined by WrapSnippet. /// Throws an ArgumentNullException if the given compilation is null. /// </summary> private static void PrintContentTokenization(Compilation compilation, ILogger logger) { if (compilation == null) { throw new ArgumentNullException(nameof(compilation)); } foreach (var file in compilation.SourceFiles) { var tokenization = compilation.Tokenization[file].Select(tokens => tokens.Select(token => token.Kind)); var stripWrapping = Options.IsCodeSnippet(file); QsCompilerError.Verify(!stripWrapping || tokenization.Count() >= 4, "expecting at least four lines of code for the compilation of a code snippet"); if (stripWrapping) { tokenization = tokenization.Skip(2).Take(tokenization.Count() - 4).ToImmutableArray(); } var serialization = tokenization .Select(line => line.Select(item => JsonConvert.SerializeObject(item, Newtonsoft.Json.Formatting.Indented))) .Zip(Enumerable.Range(1, tokenization.Count()), (ts, i) => ts.Any() ? $"\n[ln {i}]: \n{String.Join("\n", ts)} \n" : ""); serialization = new string[] { "" }.Concat(serialization); logger.Log( InformationCode.BuiltTokenization, Enumerable.Empty <string>(), stripWrapping ? null : file.Value, messageParam: serialization.ToArray() ); } }
/// <summary> /// Generates formatted Q# code for the file with the given uri based on the syntax tree in the given compilation. /// If the id of the file is consistent with the one assigned to a code snippet, /// logs the generated code using the given logger. /// Creates a file containing the generated code in the given output folder otherwise. /// Returns true if the generation succeeded, and false if an exception was thrown. /// Throws an ArgumentNullException if the given compilation, the file uri or its absolute path are null. /// </summary> private static bool GenerateFormattedQsFile(Compilation compilation, NonNullable <string> fileName, string outputFolder, ILogger logger) { if (compilation == null) { throw new ArgumentNullException(nameof(compilation)); } var code = Enumerable.Empty <string>(); try { code = code.Concat(GenerateQsCode(compilation, fileName, logger).Where(c => !String.IsNullOrWhiteSpace(c))); } catch (Exception ex) { logger?.Log(ErrorCode.QsGenerationFailed, Enumerable.Empty <string>(), fileName.Value); logger?.Log(ex); return(false); } if (Options.IsCodeSnippet(fileName)) { code = new string[] { "" }.Concat(Formatting.Indent(code.ToArray())); logger?.Log(InformationCode.GeneratedQsCode, Enumerable.Empty <string>(), messageParam: code.ToArray()); } else { var content = String.Join(Environment.NewLine, code.Select(block => $"{block}{Environment.NewLine}{Environment.NewLine}")); CompilationLoader.GeneratedFile(fileName, outputFolder ?? "FormattedFiles", ".qs", content); } return(true); }
/// <summary> /// Generates formatted Q# code based on the part of the syntax tree that corresponds to each file in the given compilation. /// If the id of a file is consistent with the one assigned to a code snippet, /// strips the lines of code that correspond to the wrapping defined by WrapSnippet. /// Throws an ArgumentException if this is not possible because the given syntax tree is inconsistent with that wrapping. /// Throws an ArgumentNullException if the given compilation is null. /// </summary> private static IEnumerable <string> GenerateQsCode(Compilation compilation, NonNullable <string> file, ILogger logger) { if (compilation == null) { throw new ArgumentNullException(nameof(compilation)); } if (Options.IsCodeSnippet(file)) { var subtree = compilation.SyntaxTree.Values.Select(ns => FilterBySourceFile.Apply(ns, file)).Where(ns => ns.Elements.Any()); return(DiagnoseCompilation.StripSnippetWrapping(subtree).Select(SyntaxTreeToQsharp.Default.ToCode)); } else { var imports = compilation.SyntaxTree.Values .ToImmutableDictionary(ns => ns.Name, ns => compilation.OpenDirectives(file, ns.Name).ToImmutableArray()); var success = SyntaxTreeToQsharp.Apply(out List <ImmutableDictionary <NonNullable <string>, string> > generated, compilation.SyntaxTree.Values, (file, imports)); if (!success) { logger?.Log(WarningCode.UnresolvedItemsInGeneratedQs, Enumerable.Empty <string>(), file.Value); } return(generated.Single().Select(entry => { var nsComments = compilation.NamespaceComments(file, entry.Key); string FormatComments(IEnumerable <string> comments) => string.Join( Environment.NewLine, comments.Select(line => line.Trim()).Select(line => string.IsNullOrWhiteSpace(line) ? "" : $"// {line}")) .Trim(); var leadingComments = entry.Value.StartsWith("///") ? $"{FormatComments(nsComments.OpeningComments)}{Environment.NewLine}" : FormatComments(nsComments.OpeningComments); var trailingComments = FormatComments(nsComments.ClosingComments); var code = new string[] { leadingComments, entry.Value, trailingComments }.Where(s => !string.IsNullOrWhiteSpace(s)); return string.Join(Environment.NewLine, code); })); } }
/// <summary> /// Logs the generated Q# code for the part of the given evaluated syntax tree that corresponds to each file /// in the given compilation as Information using the given logger. /// If the given evaluated tree is null, queries the tree contained in the given compilation instead. /// If the id of a file is consistent with the one assigned to a code snippet, /// strips the lines of code that correspond to the wrapping defined by WrapSnippet. /// Throws an ArgumentException if this is not possible because the given syntax tree is inconsistent with that wrapping. /// Throws an ArgumentNullException if the given compilation is null. /// </summary> private static void PrintGeneratedQs(IEnumerable <QsNamespace> evaluatedTree, Compilation compilation, ILogger logger) { if (compilation == null) { throw new ArgumentNullException(nameof(compilation)); } evaluatedTree = evaluatedTree ?? compilation.SyntaxTree.Values; foreach (var file in compilation.SourceFiles) { if (Options.IsCodeSnippet(file)) { var subtree = evaluatedTree.Select(ns => FilterBySourceFile.Apply(ns, file)).Where(ns => ns.Elements.Any()); var code = new string[] { "" }.Concat(StripSnippetWrapping(subtree).Select(FormatCompilation.FormatStatement)); logger.Log(InformationCode.FormattedQsCode, Enumerable.Empty <string>(), messageParam: code.ToArray()); } else { var imports = evaluatedTree.ToImmutableDictionary(ns => ns.Name, ns => compilation.OpenDirectives(file, ns.Name).ToImmutableArray()); SyntaxTreeToQs.Apply(out List <ImmutableDictionary <NonNullable <string>, string> > generated, evaluatedTree, (file, imports)); var code = new string[] { "" }.Concat(generated.Single().Values.Select(nsCode => $"{nsCode}{Environment.NewLine}")); logger.Log(InformationCode.FormattedQsCode, Enumerable.Empty <string>(), file.Value, messageParam: code.ToArray()); }; } }