diff --git a/Generator.Tests/PietMethodGeneratorTests.cs b/Generator.Tests/PietMethodGeneratorTests.cs
index 68e8f6e..b079fb0 100644
--- a/Generator.Tests/PietMethodGeneratorTests.cs
+++ b/Generator.Tests/PietMethodGeneratorTests.cs
@@ -1617,7 +1617,12 @@ public partial class Sample
.FirstOrDefault(static t => t.Contains("partial class Sample")) ?? string.Empty;
Assert.Contains("public static partial", generatedMethod, "Expected static modifier was not found.");
- Assert.DoesNotContain("namespace ", generatedMethod, "Global namespace method should not emit a namespace declaration.");
+ // The runtime is appended after the method code in the combined file; extract only the method section.
+ const string runtimeSeparator = "\nnamespace Esolang.Piet.__Generated";
+ var methodSection = generatedMethod.Contains(runtimeSeparator)
+ ? generatedMethod[..generatedMethod.IndexOf(runtimeSeparator, StringComparison.Ordinal)]
+ : generatedMethod;
+ Assert.DoesNotContain("namespace ", methodSection, "Global namespace method should not emit a namespace declaration.");
}
catch (Exception e) when (e is AssertFailedException or TargetInvocationException)
{
diff --git a/Generator/MethodGenerator.Runtime.cs b/Generator/MethodGenerator.Runtime.cs
index 0ecfabd..ac6eb6a 100644
--- a/Generator/MethodGenerator.Runtime.cs
+++ b/Generator/MethodGenerator.Runtime.cs
@@ -1,3 +1,4 @@
+using Microsoft.CodeAnalysis.CSharp;
using System.Text;
namespace Esolang.Piet.Generator;
@@ -7,7 +8,7 @@ partial class MethodGenerator
///
/// The generated file name for the shared Piet interpreter runtime.
///
- public const string GeneratePietRuntimeFileName = "PietRuntime.g.cs";
+ public const string GeneratePietRuntimeFileName = GeneratedMethodsFileName;
///
/// The features that may be needed by the generated methods.
@@ -40,12 +41,10 @@ enum GeneratorFeatures
///
/// Tries to generate the source code for the shared Piet interpreter runtime if needed.
///
- static bool TryMakePietRuntimeSource(GeneratorFeatures features, out string filename, out string source)
+ static void AppendPietRuntimeSource(GeneratorFeatures features, LanguageVersion languageVersion, StringBuilder builder)
{
- filename = default!;
- source = default!;
if (features == GeneratorFeatures.None)
- return false;
+ return;
var enableLogging = (features & GeneratorFeatures.UseLogging) != 0;
// `logger? = null,` or string.Emtpy
var withLoggingParameter = enableLogging ? "global::Microsoft.Extensions.Logging.ILogger? logger = null," : string.Empty;
@@ -60,20 +59,13 @@ Func callLogExecuting
"""
: static (_, _, _) => string.Empty;
- var builder = new StringBuilder();
- builder.AppendLine("""
- //
- #nullable enable
- #pragma warning disable CS1591
- using System;
- using System.ComponentModel;
- using System.Collections.Generic;
- using System.Threading;
+ var fileOrInternal = IsLanguageVersionAtLeastCSharp11(languageVersion) ? "file" : "internal";
+ builder.AppendLine($$"""
namespace Esolang.Piet.__Generated
{
[EditorBrowsable(EditorBrowsableState.Never)]
- internal static class PietRuntime
+ {{fileOrInternal}} static class PietRuntime
{
private static readonly int[] s_hue = new int[]
{
@@ -912,10 +904,10 @@ internal static async IAsyncEnumerable ExecuteAsyncEnumerable(
""");
if ((features & GeneratorFeatures.UseLogging) != 0)
- builder.AppendLine("""
+ builder.AppendLine($$"""
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
- internal static class LoggerUtilities
+ {{fileOrInternal}} static class LoggerUtilities
{
private static readonly global::System.Action ExecutingCommand =
global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Trace, 0, "Executing Piet command: op={Op}, value={Value}, index={Index}");
@@ -934,46 +926,35 @@ public static void LogExecuting(global::Microsoft.Extensions.Logging.ILogger? lo
}
""");
- filename = GeneratePietRuntimeFileName;
- source = builder.ToString();
- return true;
}
///
/// The generated file name for the shared Piet++ interpreter runtime.
///
- public const string GeneratePietPlusPlusRuntimeFileName = "PietPlusPlusRuntime.g.cs";
+ public const string GeneratePietPlusPlusRuntimeFileName = GeneratedMethodsFileName;
///
/// Tries to generate the source code for the shared Piet++ interpreter runtime if needed.
///
- static bool TryMakePietPlusPlusRuntimeSource(GeneratorFeatures features, out string filename, out string source)
+ static void AppendPietPlusPlusRuntimeSource(GeneratorFeatures features, LanguageVersion languageVersion, StringBuilder builder)
{
- filename = default!;
- source = default!;
var ppFeatures = features & (GeneratorFeatures.SyncPlusPlus | GeneratorFeatures.AsyncPlusPlus
| GeneratorFeatures.EnumerablePlusPlus | GeneratorFeatures.AsyncEnumerablePlusPlus);
if (ppFeatures == GeneratorFeatures.None)
- return false;
+ return;
var needsSync = (ppFeatures & (GeneratorFeatures.SyncPlusPlus | GeneratorFeatures.EnumerablePlusPlus)) != 0;
var needsAsync = (ppFeatures & (GeneratorFeatures.AsyncPlusPlus | GeneratorFeatures.AsyncEnumerablePlusPlus)) != 0;
var needsEnumerable = (ppFeatures & GeneratorFeatures.EnumerablePlusPlus) != 0;
var needsAsyncEnumerable = (ppFeatures & GeneratorFeatures.AsyncEnumerablePlusPlus) != 0;
- var builder = new StringBuilder();
- builder.AppendLine("""
- //
- #nullable enable
- #pragma warning disable CS1591
- using System;
- using System.Collections.Generic;
- using System.Threading;
+ var fileOrInternal = IsLanguageVersionAtLeastCSharp11(languageVersion) ? "file" : "internal";
+ builder.AppendLine($$"""
namespace Esolang.Piet.__Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
- internal readonly struct PietPlusPlusValue
+ {{fileOrInternal}} readonly struct PietPlusPlusValue
{
readonly int _intVal;
readonly List? _stack;
@@ -987,7 +968,7 @@ internal readonly struct PietPlusPlusValue
}
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
- internal static class PietPlusPlusRuntime
+ {{fileOrInternal}} static class PietPlusPlusRuntime
{
private const byte Black = 0;
private const byte White = 63;
@@ -1593,8 +1574,5 @@ public static async IAsyncEnumerable ExecuteAsyncEnumerable(
}
""");
- filename = GeneratePietPlusPlusRuntimeFileName;
- source = builder.ToString();
- return true;
}
}
diff --git a/Generator/MethodGenerator.cs b/Generator/MethodGenerator.cs
index ac3e4f2..c091f0c 100644
--- a/Generator/MethodGenerator.cs
+++ b/Generator/MethodGenerator.cs
@@ -44,7 +44,12 @@ public partial class MethodGenerator : IIncrementalGenerator
{{CommentAutoGenerated}}
#nullable enable
#pragma warning disable CS0219
+ #pragma warning disable CS1591
#pragma warning disable CS1998
+ using System;
+ using System.ComponentModel;
+ using System.Collections.Generic;
+ using System.Threading;
""";
@@ -195,12 +200,10 @@ parseOptions is CSharpParseOptions csharpParseOptions
if (emittedCount > 0)
{
+ AppendPietRuntimeSource(features, currentLanguageVersion, builder);
+ AppendPietPlusPlusRuntimeSource(features, currentLanguageVersion, builder);
context.AddSource(GeneratedMethodsFileName, builder.ToString());
}
- if (TryMakePietRuntimeSource(features, out var runtimeFileName, out var runtimeSource))
- context.AddSource(runtimeFileName, runtimeSource);
- if (TryMakePietPlusPlusRuntimeSource(features, out var ppRuntimeFileName, out var ppRuntimeSource))
- context.AddSource(ppRuntimeFileName, ppRuntimeSource);
});
}
@@ -1194,6 +1197,16 @@ static bool IsLanguageVersionAtLeastCSharp8(LanguageVersion languageVersion)
_ => languageVersion >= LanguageVersion.CSharp8,
};
+ static bool IsLanguageVersionAtLeastCSharp11(LanguageVersion languageVersion)
+ => languageVersion switch
+ {
+ LanguageVersion.Default => true,
+ LanguageVersion.Latest => true,
+ LanguageVersion.Preview => true,
+ LanguageVersion.LatestMajor => true,
+ _ => languageVersion >= LanguageVersion.CSharp11,
+ };
+
static AdditionalImageFile? TryResolveImagePath(string imagePath, ImmutableArray additionalImageFiles)
{
const string dataUrlStarts = "data:";