diff --git a/samples/simple/ExamplePythonDependency/ExamplePythonDependency.csproj b/samples/simple/ExamplePythonDependency/ExamplePythonDependency.csproj
index 47854904..eaddc601 100644
--- a/samples/simple/ExamplePythonDependency/ExamplePythonDependency.csproj
+++ b/samples/simple/ExamplePythonDependency/ExamplePythonDependency.csproj
@@ -30,7 +30,7 @@
-
+
diff --git a/src/CSnakes/CSnakes.csproj b/src/CSnakes.SourceGeneration/CSnakes.SourceGeneration.csproj
similarity index 97%
rename from src/CSnakes/CSnakes.csproj
rename to src/CSnakes.SourceGeneration/CSnakes.SourceGeneration.csproj
index 2a250515..3a42def7 100644
--- a/src/CSnakes/CSnakes.csproj
+++ b/src/CSnakes.SourceGeneration/CSnakes.SourceGeneration.csproj
@@ -1,33 +1,33 @@
-
-
- netstandard2.0
- true
- $(NoWarn);RS1035
- true
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
- all
- runtime; build; native; contentfiles; analyzers
-
-
-
-
-
- $(GetTargetPathDependsOn);GetDependencyTargetPaths
-
-
-
-
-
-
-
-
-
-
+
+
+ netstandard2.0
+ true
+ $(NoWarn);RS1035
+ true
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
+
+
+ $(GetTargetPathDependsOn);GetDependencyTargetPaths
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CSnakes/CallerArgumentExpressionAttribute.cs b/src/CSnakes.SourceGeneration/CallerArgumentExpressionAttribute.cs
similarity index 97%
rename from src/CSnakes/CallerArgumentExpressionAttribute.cs
rename to src/CSnakes.SourceGeneration/CallerArgumentExpressionAttribute.cs
index 0df37a44..8e87a417 100644
--- a/src/CSnakes/CallerArgumentExpressionAttribute.cs
+++ b/src/CSnakes.SourceGeneration/CallerArgumentExpressionAttribute.cs
@@ -1,44 +1,44 @@
-// credit: https://github.com/SimonCropp/Polyfill/blob/main/src/Polyfill/CallerArgumentExpressionAttribute.cs
-// Bringing in the file rather than using the library to avoid problems of additional libraries
-// within a source generator.
-
-//
-#pragma warning disable
-
-#if NETFRAMEWORK || NETSTANDARD || NETCOREAPP2X
-
-namespace System.Runtime.CompilerServices;
-
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using Link = System.ComponentModel.DescriptionAttribute;
-
-///
-/// Indicates that a parameter captures the expression passed for another parameter as a string.
-///
-[ExcludeFromCodeCoverage]
-[DebuggerNonUserCode]
-[AttributeUsage(AttributeTargets.Parameter)]
-[Link("https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.callerargumentexpressionattribute")]
-#if PolyPublic
-public
-#endif
-sealed class CallerArgumentExpressionAttribute :
- Attribute
-{
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The name of the parameter whose expression should be captured as a string.
- ///
- public CallerArgumentExpressionAttribute(string parameterName) =>
- ParameterName = parameterName;
-
- ///
- /// Gets the name of the parameter whose expression should be captured as a string.
- ///
- public string ParameterName { get; }
-}
-
+// credit: https://github.com/SimonCropp/Polyfill/blob/main/src/Polyfill/CallerArgumentExpressionAttribute.cs
+// Bringing in the file rather than using the library to avoid problems of additional libraries
+// within a source generator.
+
+//
+#pragma warning disable
+
+#if NETFRAMEWORK || NETSTANDARD || NETCOREAPP2X
+
+namespace System.Runtime.CompilerServices;
+
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using Link = System.ComponentModel.DescriptionAttribute;
+
+///
+/// Indicates that a parameter captures the expression passed for another parameter as a string.
+///
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+[AttributeUsage(AttributeTargets.Parameter)]
+[Link("https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.callerargumentexpressionattribute")]
+#if PolyPublic
+public
+#endif
+sealed class CallerArgumentExpressionAttribute :
+ Attribute
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The name of the parameter whose expression should be captured as a string.
+ ///
+ public CallerArgumentExpressionAttribute(string parameterName) =>
+ ParameterName = parameterName;
+
+ ///
+ /// Gets the name of the parameter whose expression should be captured as a string.
+ ///
+ public string ParameterName { get; }
+}
+
#endif
\ No newline at end of file
diff --git a/src/CSnakes/CaseHelper.cs b/src/CSnakes.SourceGeneration/CaseHelper.cs
similarity index 97%
rename from src/CSnakes/CaseHelper.cs
rename to src/CSnakes.SourceGeneration/CaseHelper.cs
index ef40d602..dadda49e 100644
--- a/src/CSnakes/CaseHelper.cs
+++ b/src/CSnakes.SourceGeneration/CaseHelper.cs
@@ -1,16 +1,16 @@
-namespace CSnakes
-{
- public static class CaseHelper
- {
- public static string ToPascalCase(this string snakeCase)
- {
- return string.Join("", snakeCase.Split('_').Select(s => s.Length > 1 ? char.ToUpperInvariant(s[0]) + s.Substring(1) : "_"));
- }
-
- public static string ToLowerPascalCase(this string snakeCase)
- {
- // Make sure the first letter is lowercase
- return char.ToLowerInvariant(snakeCase[0]) + ToPascalCase(snakeCase).Substring(1);
- }
- }
-}
+namespace CSnakes
+{
+ public static class CaseHelper
+ {
+ public static string ToPascalCase(this string snakeCase)
+ {
+ return string.Join("", snakeCase.Split('_').Select(s => s.Length > 1 ? char.ToUpperInvariant(s[0]) + s.Substring(1) : "_"));
+ }
+
+ public static string ToLowerPascalCase(this string snakeCase)
+ {
+ // Make sure the first letter is lowercase
+ return char.ToLowerInvariant(snakeCase[0]) + ToPascalCase(snakeCase).Substring(1);
+ }
+ }
+}
diff --git a/src/CSnakes/GeneratorError.cs b/src/CSnakes.SourceGeneration/GeneratorError.cs
similarity index 95%
rename from src/CSnakes/GeneratorError.cs
rename to src/CSnakes.SourceGeneration/GeneratorError.cs
index 8f840118..c0488320 100644
--- a/src/CSnakes/GeneratorError.cs
+++ b/src/CSnakes.SourceGeneration/GeneratorError.cs
@@ -1,27 +1,27 @@
-namespace CSnakes;
-
-public class GeneratorError
-{
-
- public string Message { get; }
-
- public int StartLine { get; }
-
- public int StartColumn { get; }
-
- public int EndLine { get; }
-
- public int EndColumn { get; }
-
- public string Code { get; }
-
- public GeneratorError(int startLine, int endLine, int startColumn, int endColumn, string message)
- {
- Message = message;
- StartLine = startLine;
- StartColumn = startColumn;
- EndLine = endLine == -1 ? startLine : endLine;
- EndColumn = endColumn;
- Code = "hello";
- }
-}
+namespace CSnakes;
+
+public class GeneratorError
+{
+
+ public string Message { get; }
+
+ public int StartLine { get; }
+
+ public int StartColumn { get; }
+
+ public int EndLine { get; }
+
+ public int EndColumn { get; }
+
+ public string Code { get; }
+
+ public GeneratorError(int startLine, int endLine, int startColumn, int endColumn, string message)
+ {
+ Message = message;
+ StartLine = startLine;
+ StartColumn = startColumn;
+ EndLine = endLine == -1 ? startLine : endLine;
+ EndColumn = endColumn;
+ Code = "hello";
+ }
+}
diff --git a/src/CSnakes/Keywords.cs b/src/CSnakes.SourceGeneration/Keywords.cs
similarity index 94%
rename from src/CSnakes/Keywords.cs
rename to src/CSnakes.SourceGeneration/Keywords.cs
index bf7eb966..34da11e9 100644
--- a/src/CSnakes/Keywords.cs
+++ b/src/CSnakes.SourceGeneration/Keywords.cs
@@ -1,97 +1,97 @@
-namespace CSnakes;
-internal static class Keywords
-{
- static readonly string[] cSharpKeywords = [
- "abstract",
- "as",
- "base",
- "bool",
- "break",
- "byte",
- "case",
- "catch",
- "char",
- "checked",
- "class",
- "const",
- "continue",
- "decimal",
- "default",
- "delegate",
- "do",
- "double",
- "else",
- "enum",
- "event",
- "explicit",
- "extern",
- "false",
- "finally",
- "fixed",
- "float",
- "for",
- "foreach",
- "goto",
- "if",
- "implicit",
- "in",
- "int",
- "interface",
- "internal",
- "is",
- "lock",
- "long",
- "namespace",
- "new",
- "null",
- "object",
- "operator",
- "out",
- "override",
- "params",
- "private",
- "protected",
- "public",
- "readonly",
- "ref",
- "return",
- "sbyte",
- "sealed",
- "short",
- "sizeof",
- "stackalloc",
- "static",
- "string",
- "struct",
- "switch",
- "this",
- "throw",
- "true",
- "try",
- "typeof",
- "uint",
- "ulong",
- "unchecked",
- "unsafe",
- "ushort",
- "using",
- "virtual",
- "void",
- "volatile",
- "while"
- ];
-
- public static bool IsKeyword(string name)
- {
- return cSharpKeywords.Contains(name);
- }
-
- public static string ValidIdentifier(string identifier)
- {
- if (!IsKeyword(identifier))
- {
- return identifier;
- }
- return $"@{identifier}";
- }
-}
+namespace CSnakes;
+internal static class Keywords
+{
+ static readonly string[] cSharpKeywords = [
+ "abstract",
+ "as",
+ "base",
+ "bool",
+ "break",
+ "byte",
+ "case",
+ "catch",
+ "char",
+ "checked",
+ "class",
+ "const",
+ "continue",
+ "decimal",
+ "default",
+ "delegate",
+ "do",
+ "double",
+ "else",
+ "enum",
+ "event",
+ "explicit",
+ "extern",
+ "false",
+ "finally",
+ "fixed",
+ "float",
+ "for",
+ "foreach",
+ "goto",
+ "if",
+ "implicit",
+ "in",
+ "int",
+ "interface",
+ "internal",
+ "is",
+ "lock",
+ "long",
+ "namespace",
+ "new",
+ "null",
+ "object",
+ "operator",
+ "out",
+ "override",
+ "params",
+ "private",
+ "protected",
+ "public",
+ "readonly",
+ "ref",
+ "return",
+ "sbyte",
+ "sealed",
+ "short",
+ "sizeof",
+ "stackalloc",
+ "static",
+ "string",
+ "struct",
+ "switch",
+ "this",
+ "throw",
+ "true",
+ "try",
+ "typeof",
+ "uint",
+ "ulong",
+ "unchecked",
+ "unsafe",
+ "ushort",
+ "using",
+ "virtual",
+ "void",
+ "volatile",
+ "while"
+ ];
+
+ public static bool IsKeyword(string name)
+ {
+ return cSharpKeywords.Contains(name);
+ }
+
+ public static string ValidIdentifier(string identifier)
+ {
+ if (!IsKeyword(identifier))
+ {
+ return identifier;
+ }
+ return $"@{identifier}";
+ }
+}
diff --git a/src/CSnakes/NotNullAttributePolyfill.cs b/src/CSnakes.SourceGeneration/NotNullAttributePolyfill.cs
similarity index 96%
rename from src/CSnakes/NotNullAttributePolyfill.cs
rename to src/CSnakes.SourceGeneration/NotNullAttributePolyfill.cs
index f16162cf..18a93f74 100644
--- a/src/CSnakes/NotNullAttributePolyfill.cs
+++ b/src/CSnakes.SourceGeneration/NotNullAttributePolyfill.cs
@@ -1,30 +1,30 @@
-// credit: https://github.com/SimonCropp/Polyfill/blob/main/src/Polyfill/Nullable/NotNullAttribute.cs
-// Bringing in the file rather than using the library to avoid problems of additional libraries
-// within a source generator.
-
-//
-#pragma warning disable
-
-#if NETSTANDARD2_0 || NETFRAMEWORK || NETCOREAPP2X
-
-namespace System.Diagnostics.CodeAnalysis;
-
-using Targets = AttributeTargets;
-
-///
-/// Specifies that an output is not even if the
-/// corresponding type allows it.
-///
-[ExcludeFromCodeCoverage]
-[DebuggerNonUserCode]
-[AttributeUsage(
- validOn: Targets.Field |
- Targets.Parameter |
- Targets.Property |
- Targets.ReturnValue)]
-#if PolyPublic
-public
-#endif
-sealed class NotNullAttribute :
- Attribute;
+// credit: https://github.com/SimonCropp/Polyfill/blob/main/src/Polyfill/Nullable/NotNullAttribute.cs
+// Bringing in the file rather than using the library to avoid problems of additional libraries
+// within a source generator.
+
+//
+#pragma warning disable
+
+#if NETSTANDARD2_0 || NETFRAMEWORK || NETCOREAPP2X
+
+namespace System.Diagnostics.CodeAnalysis;
+
+using Targets = AttributeTargets;
+
+///
+/// Specifies that an output is not even if the
+/// corresponding type allows it.
+///
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+[AttributeUsage(
+ validOn: Targets.Field |
+ Targets.Parameter |
+ Targets.Property |
+ Targets.ReturnValue)]
+#if PolyPublic
+public
+#endif
+sealed class NotNullAttribute :
+ Attribute;
#endif
\ No newline at end of file
diff --git a/src/CSnakes/NullableExtensions.cs b/src/CSnakes.SourceGeneration/NullableExtensions.cs
similarity index 97%
rename from src/CSnakes/NullableExtensions.cs
rename to src/CSnakes.SourceGeneration/NullableExtensions.cs
index 8acf77bf..8f11c562 100644
--- a/src/CSnakes/NullableExtensions.cs
+++ b/src/CSnakes.SourceGeneration/NullableExtensions.cs
@@ -1,26 +1,26 @@
-// credit: https://github.com/dotnet/razor/blob/main/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/NullableExtensions.cs
-
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-using System.Runtime;
-
-namespace CSnakes;
-internal static class NullableExtensions
-{
- [DebuggerStepThrough]
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- public static T AssumeNotNull(
- [NotNull] this T? obj,
- [CallerArgumentExpression(nameof(obj))] string? objExpression = null)
- where T : class
- => obj ?? throw new InvalidOperationException($"Expected '{objExpression}' to be non-null.");
-
- [DebuggerStepThrough]
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- public static T AssumeNotNull(
- [NotNull] this T? obj,
- [CallerArgumentExpression(nameof(obj))] string? objExpression = null)
- where T : struct
- => obj ?? throw new InvalidOperationException($"Expected '{objExpression}' to be non-null.");
+// credit: https://github.com/dotnet/razor/blob/main/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/NullableExtensions.cs
+
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime;
+
+namespace CSnakes;
+internal static class NullableExtensions
+{
+ [DebuggerStepThrough]
+ [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
+ public static T AssumeNotNull(
+ [NotNull] this T? obj,
+ [CallerArgumentExpression(nameof(obj))] string? objExpression = null)
+ where T : class
+ => obj ?? throw new InvalidOperationException($"Expected '{objExpression}' to be non-null.");
+
+ [DebuggerStepThrough]
+ [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
+ public static T AssumeNotNull(
+ [NotNull] this T? obj,
+ [CallerArgumentExpression(nameof(obj))] string? objExpression = null)
+ where T : struct
+ => obj ?? throw new InvalidOperationException($"Expected '{objExpression}' to be non-null.");
}
\ No newline at end of file
diff --git a/src/CSnakes/Packaging.targets b/src/CSnakes.SourceGeneration/Packaging.targets
similarity index 90%
rename from src/CSnakes/Packaging.targets
rename to src/CSnakes.SourceGeneration/Packaging.targets
index ca5504af..37ce9ac9 100644
--- a/src/CSnakes/Packaging.targets
+++ b/src/CSnakes.SourceGeneration/Packaging.targets
@@ -1,7 +1,7 @@
- CSnakes
- CSnakes
+ CSnakes.SourceGeneration
+ CSnakes.SourceGeneration
$(NoWarn);NU5128
diff --git a/src/CSnakes/Parser/PythonParser.Args.cs b/src/CSnakes.SourceGeneration/Parser/PythonParser.Args.cs
similarity index 98%
rename from src/CSnakes/Parser/PythonParser.Args.cs
rename to src/CSnakes.SourceGeneration/Parser/PythonParser.Args.cs
index 83aa7deb..7bf5579a 100644
--- a/src/CSnakes/Parser/PythonParser.Args.cs
+++ b/src/CSnakes.SourceGeneration/Parser/PythonParser.Args.cs
@@ -1,30 +1,30 @@
-using CSnakes.Parser.Types;
-using Superpower;
-using Superpower.Parsers;
-
-namespace CSnakes.Parser;
-public static partial class PythonParser
-{
- public static TokenListParser PythonStarArgTokenizer { get; } =
- (from star in Token.EqualTo(PythonToken.Asterisk)
- from name in Token.EqualTo(PythonToken.Identifier).Optional()
- select new PythonParameterType(name.HasValue ? name.Value.ToStringValue() : "args", PythonFunctionParameterType.Star))
- .Named("Star Arg");
-
- public static TokenListParser PythonDoubleStarArgTokenizer { get; } =
- (from star in Token.EqualTo(PythonToken.DoubleAsterisk)
- from name in Token.EqualTo(PythonToken.Identifier)
- select new PythonParameterType(name.ToStringValue(), PythonFunctionParameterType.DoubleStar))
- .Named("Double Star Arg");
-
- public static TokenListParser PythonNormalArgTokenizer { get; } =
- (from name in Token.EqualTo(PythonToken.Identifier)
- select new PythonParameterType(name.ToStringValue(), PythonFunctionParameterType.Normal))
- .Named("Normal Arg");
-
- public static TokenListParser PythonArgTokenizer { get; } =
- PythonStarArgTokenizer.AsNullable()
- .Or(PythonDoubleStarArgTokenizer.AsNullable())
- .Or(PythonNormalArgTokenizer.AsNullable())
- .Named("Arg");
-}
+using CSnakes.Parser.Types;
+using Superpower;
+using Superpower.Parsers;
+
+namespace CSnakes.Parser;
+public static partial class PythonParser
+{
+ public static TokenListParser PythonStarArgTokenizer { get; } =
+ (from star in Token.EqualTo(PythonToken.Asterisk)
+ from name in Token.EqualTo(PythonToken.Identifier).Optional()
+ select new PythonParameterType(name.HasValue ? name.Value.ToStringValue() : "args", PythonFunctionParameterType.Star))
+ .Named("Star Arg");
+
+ public static TokenListParser PythonDoubleStarArgTokenizer { get; } =
+ (from star in Token.EqualTo(PythonToken.DoubleAsterisk)
+ from name in Token.EqualTo(PythonToken.Identifier)
+ select new PythonParameterType(name.ToStringValue(), PythonFunctionParameterType.DoubleStar))
+ .Named("Double Star Arg");
+
+ public static TokenListParser PythonNormalArgTokenizer { get; } =
+ (from name in Token.EqualTo(PythonToken.Identifier)
+ select new PythonParameterType(name.ToStringValue(), PythonFunctionParameterType.Normal))
+ .Named("Normal Arg");
+
+ public static TokenListParser PythonArgTokenizer { get; } =
+ PythonStarArgTokenizer.AsNullable()
+ .Or(PythonDoubleStarArgTokenizer.AsNullable())
+ .Or(PythonNormalArgTokenizer.AsNullable())
+ .Named("Arg");
+}
diff --git a/src/CSnakes/Parser/PythonParser.Constants.cs b/src/CSnakes.SourceGeneration/Parser/PythonParser.Constants.cs
similarity index 97%
rename from src/CSnakes/Parser/PythonParser.Constants.cs
rename to src/CSnakes.SourceGeneration/Parser/PythonParser.Constants.cs
index de005cf4..f5e3744b 100644
--- a/src/CSnakes/Parser/PythonParser.Constants.cs
+++ b/src/CSnakes.SourceGeneration/Parser/PythonParser.Constants.cs
@@ -1,128 +1,128 @@
-using CSnakes.Parser.Types;
-using Superpower;
-using Superpower.Model;
-using Superpower.Parsers;
-using System.Globalization;
-
-namespace CSnakes.Parser;
-public static partial class PythonParser
-{
- public static TextParser UnderScoreOrDigit { get; } =
- Character.Matching(char.IsDigit, "digit").Or(Character.EqualTo('_'));
-
- public static TextParser IntegerConstantToken { get; } =
- from sign in Character.EqualTo('-').OptionalOrDefault()
- from firstdigit in Character.Digit
- from digits in UnderScoreOrDigit.Many().OptionalOrDefault([])
- select Unit.Value;
-
- public static TextParser DecimalConstantToken { get; } =
- from sign in Character.EqualTo('-').OptionalOrDefault()
- from first in Character.Digit
- from rest in UnderScoreOrDigit.Or(Character.In('.', 'e', 'E', '+', '-')).IgnoreMany()
- select Unit.Value;
-
- public static TextParser HexidecimalConstantToken { get; } =
- from prefix in Span.EqualTo("0x")
- from digits in Character.EqualTo('_').Or(Character.HexDigit).AtLeastOnce()
- select Unit.Value;
-
- public static TextParser BinaryConstantToken { get; } =
- from prefix in Span.EqualTo("0b")
- from digits in Character.In('0', '1', '_').AtLeastOnce()
- select Unit.Value;
-
- public static TextParser DoubleQuotedStringConstantToken { get; } =
- from open in Character.EqualTo('"')
- from chars in Character.ExceptIn('"').Many()
- from close in Character.EqualTo('"')
- select Unit.Value;
-
- public static TextParser SingleQuotedStringConstantToken { get; } =
- from open in Character.EqualTo('\'')
- from chars in Character.ExceptIn('\'').Many()
- from close in Character.EqualTo('\'')
- select Unit.Value;
-
- public static TokenListParser DoubleQuotedStringConstantTokenizer { get; } =
- Token.EqualTo(PythonToken.DoubleQuotedString)
- .Apply(ConstantParsers.DoubleQuotedString)
- .Select(s => new PythonConstant(s))
- .Named("Double Quoted String Constant");
-
- public static TokenListParser SingleQuotedStringConstantTokenizer { get; } =
- Token.EqualTo(PythonToken.SingleQuotedString)
- .Apply(ConstantParsers.SingleQuotedString)
- .Select(s => new PythonConstant(s))
- .Named("Single Quoted String Constant");
-
- public static TokenListParser DecimalConstantTokenizer { get; } =
- Token.EqualTo(PythonToken.Decimal)
- .Select(token => new PythonConstant(double.Parse(token.ToStringValue().Replace("_", ""), NumberStyles.Float, CultureInfo.InvariantCulture)))
- .Named("Decimal Constant");
-
- public static TokenListParser IntegerConstantTokenizer { get; } =
- Token.EqualTo(PythonToken.Integer)
- .Select(d => new PythonConstant(long.Parse(d.ToStringValue().Replace("_", ""), NumberStyles.Integer)))
- .Named("Integer Constant");
-
- public static TokenListParser HexidecimalIntegerConstantTokenizer { get; } =
- Token.EqualTo(PythonToken.HexidecimalInteger)
- .Select(d => new PythonConstant { Type = PythonConstant.ConstantType.HexidecimalInteger, IntegerValue = long.Parse(d.ToStringValue().Substring(2).Replace("_", ""), NumberStyles.HexNumber) })
- .Named("Hexidecimal Integer Constant");
-
- public static TokenListParser BinaryIntegerConstantTokenizer { get; } =
- Token.EqualTo(PythonToken.BinaryInteger)
- // TODO: Consider Binary Format specifier introduced in .NET 8 https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings#binary-format-specifier-b
- .Select(d => new PythonConstant { Type = PythonConstant.ConstantType.BinaryInteger, IntegerValue = (long)Convert.ToUInt64(d.ToStringValue().Substring(2).Replace("_", ""), 2) })
- .Named("Binary Integer Constant");
-
- public static TokenListParser BoolConstantTokenizer { get; } =
- Token.EqualTo(PythonToken.True).Or(Token.EqualTo(PythonToken.False))
- .Select(d => new PythonConstant(d.Kind == PythonToken.True))
- .Named("Bool Constant");
-
- public static TokenListParser NoneConstantTokenizer { get; } =
- Token.EqualTo(PythonToken.None)
- .Select(d => PythonConstant.FromNone())
- .Named("None Constant");
-
- // Any constant value
- public static TokenListParser ConstantValueTokenizer { get; } =
- DecimalConstantTokenizer.AsNullable()
- .Or(IntegerConstantTokenizer.AsNullable())
- .Or(HexidecimalIntegerConstantTokenizer.AsNullable())
- .Or(BinaryIntegerConstantTokenizer.AsNullable())
- .Or(BoolConstantTokenizer.AsNullable())
- .Or(NoneConstantTokenizer.AsNullable())
- .Or(DoubleQuotedStringConstantTokenizer.AsNullable())
- .Or(SingleQuotedStringConstantTokenizer.AsNullable())
- .Named("Constant");
-
- static class ConstantParsers
- {
- public static TextParser DoubleQuotedString { get; } =
- from open in Character.EqualTo('"')
- from chars in Character.ExceptIn('"', '\\')
- .Or(Character.EqualTo('\\')
- .IgnoreThen(
- Character.EqualTo('\\')
- .Or(Character.EqualTo('"'))
- .Named("escape sequence")))
- .Many()
- from close in Character.EqualTo('"')
- select new string(chars);
-
- public static TextParser SingleQuotedString { get; } =
- from open in Character.EqualTo('\'')
- from chars in Character.ExceptIn('\'', '\\')
- .Or(Character.EqualTo('\\')
- .IgnoreThen(
- Character.EqualTo('\\')
- .Or(Character.EqualTo('\''))
- .Named("escape sequence")))
- .Many()
- from close in Character.EqualTo('\'')
- select new string(chars);
- }
-}
+using CSnakes.Parser.Types;
+using Superpower;
+using Superpower.Model;
+using Superpower.Parsers;
+using System.Globalization;
+
+namespace CSnakes.Parser;
+public static partial class PythonParser
+{
+ public static TextParser UnderScoreOrDigit { get; } =
+ Character.Matching(char.IsDigit, "digit").Or(Character.EqualTo('_'));
+
+ public static TextParser IntegerConstantToken { get; } =
+ from sign in Character.EqualTo('-').OptionalOrDefault()
+ from firstdigit in Character.Digit
+ from digits in UnderScoreOrDigit.Many().OptionalOrDefault([])
+ select Unit.Value;
+
+ public static TextParser DecimalConstantToken { get; } =
+ from sign in Character.EqualTo('-').OptionalOrDefault()
+ from first in Character.Digit
+ from rest in UnderScoreOrDigit.Or(Character.In('.', 'e', 'E', '+', '-')).IgnoreMany()
+ select Unit.Value;
+
+ public static TextParser HexidecimalConstantToken { get; } =
+ from prefix in Span.EqualTo("0x")
+ from digits in Character.EqualTo('_').Or(Character.HexDigit).AtLeastOnce()
+ select Unit.Value;
+
+ public static TextParser BinaryConstantToken { get; } =
+ from prefix in Span.EqualTo("0b")
+ from digits in Character.In('0', '1', '_').AtLeastOnce()
+ select Unit.Value;
+
+ public static TextParser DoubleQuotedStringConstantToken { get; } =
+ from open in Character.EqualTo('"')
+ from chars in Character.ExceptIn('"').Many()
+ from close in Character.EqualTo('"')
+ select Unit.Value;
+
+ public static TextParser SingleQuotedStringConstantToken { get; } =
+ from open in Character.EqualTo('\'')
+ from chars in Character.ExceptIn('\'').Many()
+ from close in Character.EqualTo('\'')
+ select Unit.Value;
+
+ public static TokenListParser DoubleQuotedStringConstantTokenizer { get; } =
+ Token.EqualTo(PythonToken.DoubleQuotedString)
+ .Apply(ConstantParsers.DoubleQuotedString)
+ .Select(s => new PythonConstant(s))
+ .Named("Double Quoted String Constant");
+
+ public static TokenListParser SingleQuotedStringConstantTokenizer { get; } =
+ Token.EqualTo(PythonToken.SingleQuotedString)
+ .Apply(ConstantParsers.SingleQuotedString)
+ .Select(s => new PythonConstant(s))
+ .Named("Single Quoted String Constant");
+
+ public static TokenListParser DecimalConstantTokenizer { get; } =
+ Token.EqualTo(PythonToken.Decimal)
+ .Select(token => new PythonConstant(double.Parse(token.ToStringValue().Replace("_", ""), NumberStyles.Float, CultureInfo.InvariantCulture)))
+ .Named("Decimal Constant");
+
+ public static TokenListParser IntegerConstantTokenizer { get; } =
+ Token.EqualTo(PythonToken.Integer)
+ .Select(d => new PythonConstant(long.Parse(d.ToStringValue().Replace("_", ""), NumberStyles.Integer)))
+ .Named("Integer Constant");
+
+ public static TokenListParser HexidecimalIntegerConstantTokenizer { get; } =
+ Token.EqualTo(PythonToken.HexidecimalInteger)
+ .Select(d => new PythonConstant { Type = PythonConstant.ConstantType.HexidecimalInteger, IntegerValue = long.Parse(d.ToStringValue().Substring(2).Replace("_", ""), NumberStyles.HexNumber) })
+ .Named("Hexidecimal Integer Constant");
+
+ public static TokenListParser BinaryIntegerConstantTokenizer { get; } =
+ Token.EqualTo(PythonToken.BinaryInteger)
+ // TODO: Consider Binary Format specifier introduced in .NET 8 https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings#binary-format-specifier-b
+ .Select(d => new PythonConstant { Type = PythonConstant.ConstantType.BinaryInteger, IntegerValue = (long)Convert.ToUInt64(d.ToStringValue().Substring(2).Replace("_", ""), 2) })
+ .Named("Binary Integer Constant");
+
+ public static TokenListParser BoolConstantTokenizer { get; } =
+ Token.EqualTo(PythonToken.True).Or(Token.EqualTo(PythonToken.False))
+ .Select(d => new PythonConstant(d.Kind == PythonToken.True))
+ .Named("Bool Constant");
+
+ public static TokenListParser NoneConstantTokenizer { get; } =
+ Token.EqualTo(PythonToken.None)
+ .Select(d => PythonConstant.FromNone())
+ .Named("None Constant");
+
+ // Any constant value
+ public static TokenListParser ConstantValueTokenizer { get; } =
+ DecimalConstantTokenizer.AsNullable()
+ .Or(IntegerConstantTokenizer.AsNullable())
+ .Or(HexidecimalIntegerConstantTokenizer.AsNullable())
+ .Or(BinaryIntegerConstantTokenizer.AsNullable())
+ .Or(BoolConstantTokenizer.AsNullable())
+ .Or(NoneConstantTokenizer.AsNullable())
+ .Or(DoubleQuotedStringConstantTokenizer.AsNullable())
+ .Or(SingleQuotedStringConstantTokenizer.AsNullable())
+ .Named("Constant");
+
+ static class ConstantParsers
+ {
+ public static TextParser DoubleQuotedString { get; } =
+ from open in Character.EqualTo('"')
+ from chars in Character.ExceptIn('"', '\\')
+ .Or(Character.EqualTo('\\')
+ .IgnoreThen(
+ Character.EqualTo('\\')
+ .Or(Character.EqualTo('"'))
+ .Named("escape sequence")))
+ .Many()
+ from close in Character.EqualTo('"')
+ select new string(chars);
+
+ public static TextParser SingleQuotedString { get; } =
+ from open in Character.EqualTo('\'')
+ from chars in Character.ExceptIn('\'', '\\')
+ .Or(Character.EqualTo('\\')
+ .IgnoreThen(
+ Character.EqualTo('\\')
+ .Or(Character.EqualTo('\''))
+ .Named("escape sequence")))
+ .Many()
+ from close in Character.EqualTo('\'')
+ select new string(chars);
+ }
+}
diff --git a/src/CSnakes/Parser/PythonParser.Function.cs b/src/CSnakes.SourceGeneration/Parser/PythonParser.Function.cs
similarity index 97%
rename from src/CSnakes/Parser/PythonParser.Function.cs
rename to src/CSnakes.SourceGeneration/Parser/PythonParser.Function.cs
index 8e674c3f..486ee04c 100644
--- a/src/CSnakes/Parser/PythonParser.Function.cs
+++ b/src/CSnakes.SourceGeneration/Parser/PythonParser.Function.cs
@@ -1,116 +1,116 @@
-using Microsoft.CodeAnalysis.Text;
-using CSnakes.Parser.Types;
-using Superpower;
-using Superpower.Model;
-using Superpower.Parsers;
-
-using ParsedTokens = Superpower.Model.TokenList;
-
-namespace CSnakes.Parser;
-public static partial class PythonParser
-{
- public static TokenListParser PythonFunctionDefinitionTokenizer { get; } =
- (from def in Token.EqualTo(PythonToken.Def)
- from name in Token.EqualTo(PythonToken.Identifier)
- from parameters in PythonParameterListTokenizer.AssumeNotNull()
- from arrow in Token.EqualTo(PythonToken.Arrow).Optional().Then(returnType => PythonTypeDefinitionTokenizer.AssumeNotNull().OptionalOrDefault())
- from colon in Token.EqualTo(PythonToken.Colon)
- select new PythonFunctionDefinition(name.ToStringValue(), arrow, parameters))
- .Named("Function Definition");
-
- ///
- /// Checks if the line starts with def.
- ///
- /// Line to check
- ///
- static bool IsFunctionSignature(string line) =>
- line.StartsWith("def ") || line.StartsWith("async def");
-
- public static bool TryParseFunctionDefinitions(SourceText source, out PythonFunctionDefinition[] pythonSignatures, out GeneratorError[] errors)
- {
- // Go line by line
- TextLineCollection lines = source.Lines;
- List currentErrors = [];
- List<(IEnumerable lines, ParsedTokens tokens)> functionLines = [];
- List<(TextLine line, ParsedTokens tokens)> currentBuffer = [];
- bool unfinishedFunctionSpec = false;
- foreach (TextLine line in lines)
- {
- string lineOfCode = line.ToString();
- if (!IsFunctionSignature(lineOfCode) && !unfinishedFunctionSpec)
- {
- continue;
- }
-
- // Parse the function signature
- Result result = PythonTokenizer.Instance.TryTokenize(lineOfCode);
- if (!result.HasValue)
- {
- currentErrors.Add(new(
- line.LineNumber,
- line.LineNumber,
- result.ErrorPosition.Column,
- result.ErrorPosition.Column + result.Location.Length,
- result.FormatErrorMessageFragment())
- );
-
- // Reset buffer
- currentBuffer = [];
- unfinishedFunctionSpec = false;
- continue;
- }
-
- ParsedTokens repositionedTokens = new(result.Value.Select(token =>
- {
- Superpower.Model.TextSpan span = new(token.Span.Source!, new(token.Span.Position.Absolute, line.LineNumber, token.Span.Position.Column), token.Span.Length);
- Token t = new(token.Kind, span);
- return t;
- }).ToArray());
- currentBuffer.Add((line, repositionedTokens));
-
- // If this is a function definition on one line..
- if (repositionedTokens.Last().Kind == PythonToken.Colon)
- {
- IEnumerable bufferLines = currentBuffer.Select(x => x.line);
- ParsedTokens combinedTokens = new(currentBuffer.SelectMany(x => x.tokens).ToArray());
-
- functionLines.Add((bufferLines, combinedTokens));
- currentBuffer = [];
- unfinishedFunctionSpec = false;
- continue;
- }
- else
- {
- unfinishedFunctionSpec = true;
- }
- }
-
- List functionDefinitions = [];
-
- foreach ((IEnumerable currentLines, ParsedTokens tokens) in functionLines)
- {
- TokenListParserResult functionDefinition =
- PythonFunctionDefinitionTokenizer.TryParse(tokens);
- if (functionDefinition.HasValue)
- {
- functionDefinitions.Add(functionDefinition.Value);
- }
- else
- {
- // Error parsing the function definition
- int lineNumber = currentLines.First().LineNumber;
- currentErrors.Add(new(
- lineNumber,
- lineNumber + functionDefinition.ErrorPosition.Line - 1,
- functionDefinition.ErrorPosition.Column,
- functionDefinition.ErrorPosition.Column + 1,
- functionDefinition.FormatErrorMessageFragment())
- );
- }
- }
-
- pythonSignatures = [.. functionDefinitions];
- errors = [.. currentErrors];
- return errors.Length == 0;
- }
-}
+using Microsoft.CodeAnalysis.Text;
+using CSnakes.Parser.Types;
+using Superpower;
+using Superpower.Model;
+using Superpower.Parsers;
+
+using ParsedTokens = Superpower.Model.TokenList;
+
+namespace CSnakes.Parser;
+public static partial class PythonParser
+{
+ public static TokenListParser PythonFunctionDefinitionTokenizer { get; } =
+ (from def in Token.EqualTo(PythonToken.Def)
+ from name in Token.EqualTo(PythonToken.Identifier)
+ from parameters in PythonParameterListTokenizer.AssumeNotNull()
+ from arrow in Token.EqualTo(PythonToken.Arrow).Optional().Then(returnType => PythonTypeDefinitionTokenizer.AssumeNotNull().OptionalOrDefault())
+ from colon in Token.EqualTo(PythonToken.Colon)
+ select new PythonFunctionDefinition(name.ToStringValue(), arrow, parameters))
+ .Named("Function Definition");
+
+ ///
+ /// Checks if the line starts with def.
+ ///
+ /// Line to check
+ ///
+ static bool IsFunctionSignature(string line) =>
+ line.StartsWith("def ") || line.StartsWith("async def");
+
+ public static bool TryParseFunctionDefinitions(SourceText source, out PythonFunctionDefinition[] pythonSignatures, out GeneratorError[] errors)
+ {
+ // Go line by line
+ TextLineCollection lines = source.Lines;
+ List currentErrors = [];
+ List<(IEnumerable lines, ParsedTokens tokens)> functionLines = [];
+ List<(TextLine line, ParsedTokens tokens)> currentBuffer = [];
+ bool unfinishedFunctionSpec = false;
+ foreach (TextLine line in lines)
+ {
+ string lineOfCode = line.ToString();
+ if (!IsFunctionSignature(lineOfCode) && !unfinishedFunctionSpec)
+ {
+ continue;
+ }
+
+ // Parse the function signature
+ Result result = PythonTokenizer.Instance.TryTokenize(lineOfCode);
+ if (!result.HasValue)
+ {
+ currentErrors.Add(new(
+ line.LineNumber,
+ line.LineNumber,
+ result.ErrorPosition.Column,
+ result.ErrorPosition.Column + result.Location.Length,
+ result.FormatErrorMessageFragment())
+ );
+
+ // Reset buffer
+ currentBuffer = [];
+ unfinishedFunctionSpec = false;
+ continue;
+ }
+
+ ParsedTokens repositionedTokens = new(result.Value.Select(token =>
+ {
+ Superpower.Model.TextSpan span = new(token.Span.Source!, new(token.Span.Position.Absolute, line.LineNumber, token.Span.Position.Column), token.Span.Length);
+ Token t = new(token.Kind, span);
+ return t;
+ }).ToArray());
+ currentBuffer.Add((line, repositionedTokens));
+
+ // If this is a function definition on one line..
+ if (repositionedTokens.Last().Kind == PythonToken.Colon)
+ {
+ IEnumerable bufferLines = currentBuffer.Select(x => x.line);
+ ParsedTokens combinedTokens = new(currentBuffer.SelectMany(x => x.tokens).ToArray());
+
+ functionLines.Add((bufferLines, combinedTokens));
+ currentBuffer = [];
+ unfinishedFunctionSpec = false;
+ continue;
+ }
+ else
+ {
+ unfinishedFunctionSpec = true;
+ }
+ }
+
+ List functionDefinitions = [];
+
+ foreach ((IEnumerable currentLines, ParsedTokens tokens) in functionLines)
+ {
+ TokenListParserResult functionDefinition =
+ PythonFunctionDefinitionTokenizer.TryParse(tokens);
+ if (functionDefinition.HasValue)
+ {
+ functionDefinitions.Add(functionDefinition.Value);
+ }
+ else
+ {
+ // Error parsing the function definition
+ int lineNumber = currentLines.First().LineNumber;
+ currentErrors.Add(new(
+ lineNumber,
+ lineNumber + functionDefinition.ErrorPosition.Line - 1,
+ functionDefinition.ErrorPosition.Column,
+ functionDefinition.ErrorPosition.Column + 1,
+ functionDefinition.FormatErrorMessageFragment())
+ );
+ }
+ }
+
+ pythonSignatures = [.. functionDefinitions];
+ errors = [.. currentErrors];
+ return errors.Length == 0;
+ }
+}
diff --git a/src/CSnakes/Parser/PythonParser.Parameters.cs b/src/CSnakes.SourceGeneration/Parser/PythonParser.Parameters.cs
similarity index 97%
rename from src/CSnakes/Parser/PythonParser.Parameters.cs
rename to src/CSnakes.SourceGeneration/Parser/PythonParser.Parameters.cs
index e71c4e5d..dce0cc28 100644
--- a/src/CSnakes/Parser/PythonParser.Parameters.cs
+++ b/src/CSnakes.SourceGeneration/Parser/PythonParser.Parameters.cs
@@ -1,35 +1,35 @@
-using CSnakes.Parser.Types;
-using Superpower;
-using Superpower.Parsers;
-
-namespace CSnakes.Parser;
-public static partial class PythonParser
-{
- public static TokenListParser PositionalOnlyParameterParser { get; } =
- Token.EqualTo(PythonToken.Slash)
- .Select(d => new PythonFunctionParameter("/", null, null, PythonFunctionParameterType.Slash))
- .Named("Positional Only Signal");
-
- public static TokenListParser PythonParameterTokenizer { get; } =
- (from arg in PythonArgTokenizer
- from type in Token.EqualTo(PythonToken.Colon).Optional().Then(
- _ => PythonTypeDefinitionTokenizer.AssumeNotNull().OptionalOrDefault()
- )
- from defaultValue in Token.EqualTo(PythonToken.Equal).Optional().Then(
- _ => ConstantValueTokenizer.AssumeNotNull().OptionalOrDefault()
- )
- select new PythonFunctionParameter(arg.Name, type, defaultValue, arg.ParameterType))
- .Named("Parameter");
-
- public static TokenListParser ParameterOrSlash { get; } =
- PositionalOnlyParameterParser.AsNullable()
- .Or(PythonParameterTokenizer.AsNullable())
- .Named("Parameter");
-
- public static TokenListParser PythonParameterListTokenizer { get; } =
- (from openParen in Token.EqualTo(PythonToken.OpenParenthesis)
- from parameters in ParameterOrSlash.ManyDelimitedBy(Token.EqualTo(PythonToken.Comma))
- from closeParen in Token.EqualTo(PythonToken.CloseParenthesis)
- select parameters)
- .Named("Parameter List");
-}
+using CSnakes.Parser.Types;
+using Superpower;
+using Superpower.Parsers;
+
+namespace CSnakes.Parser;
+public static partial class PythonParser
+{
+ public static TokenListParser PositionalOnlyParameterParser { get; } =
+ Token.EqualTo(PythonToken.Slash)
+ .Select(d => new PythonFunctionParameter("/", null, null, PythonFunctionParameterType.Slash))
+ .Named("Positional Only Signal");
+
+ public static TokenListParser PythonParameterTokenizer { get; } =
+ (from arg in PythonArgTokenizer
+ from type in Token.EqualTo(PythonToken.Colon).Optional().Then(
+ _ => PythonTypeDefinitionTokenizer.AssumeNotNull().OptionalOrDefault()
+ )
+ from defaultValue in Token.EqualTo(PythonToken.Equal).Optional().Then(
+ _ => ConstantValueTokenizer.AssumeNotNull().OptionalOrDefault()
+ )
+ select new PythonFunctionParameter(arg.Name, type, defaultValue, arg.ParameterType))
+ .Named("Parameter");
+
+ public static TokenListParser ParameterOrSlash { get; } =
+ PositionalOnlyParameterParser.AsNullable()
+ .Or(PythonParameterTokenizer.AsNullable())
+ .Named("Parameter");
+
+ public static TokenListParser PythonParameterListTokenizer { get; } =
+ (from openParen in Token.EqualTo(PythonToken.OpenParenthesis)
+ from parameters in ParameterOrSlash.ManyDelimitedBy(Token.EqualTo(PythonToken.Comma))
+ from closeParen in Token.EqualTo(PythonToken.CloseParenthesis)
+ select parameters)
+ .Named("Parameter List");
+}
diff --git a/src/CSnakes/Parser/PythonParser.TypeDef.cs b/src/CSnakes.SourceGeneration/Parser/PythonParser.TypeDef.cs
similarity index 97%
rename from src/CSnakes/Parser/PythonParser.TypeDef.cs
rename to src/CSnakes.SourceGeneration/Parser/PythonParser.TypeDef.cs
index 48301164..286d71e8 100644
--- a/src/CSnakes/Parser/PythonParser.TypeDef.cs
+++ b/src/CSnakes.SourceGeneration/Parser/PythonParser.TypeDef.cs
@@ -1,34 +1,34 @@
-using CSnakes.Parser.Types;
-using Superpower;
-using Superpower.Model;
-using Superpower.Parsers;
-
-namespace CSnakes.Parser;
-public static partial class PythonParser
-{
- public static TextParser QualifiedName { get; } =
- Span.MatchedBy(
- Character.Letter.Or(Character.EqualTo('_'))
- .IgnoreThen(Character.LetterOrDigit.Or(Character.EqualTo('_')).Many())
- .AtLeastOnceDelimitedBy(Character.EqualTo('.'))
- );
-
- public static TokenListParser PythonTypeDefinitionTokenizer { get; } =
- (from name in Token.EqualTo(PythonToken.Identifier).Or(Token.EqualTo(PythonToken.None)).OptionalOrDefault()
-#pragma warning disable CS8620
- from openBracket in Token.EqualTo(PythonToken.OpenBracket)
- .Then(_ =>
- PythonTypeDefinitionTokenizer
- .AssumeNotNull()
- .ManyDelimitedBy(
- Token.EqualTo(PythonToken.Comma),
- Token.EqualTo(PythonToken.CloseBracket)
- )
- )
-#pragma warning restore CS8620
- .OptionalOrDefault()
- select name.HasValue ? new PythonTypeSpec(name.ToStringValue(), openBracket)
- : openBracket is null ? null : new PythonTypeSpec("argumentCollection__", openBracket)
- )
- .Named("Type Definition");
-}
+using CSnakes.Parser.Types;
+using Superpower;
+using Superpower.Model;
+using Superpower.Parsers;
+
+namespace CSnakes.Parser;
+public static partial class PythonParser
+{
+ public static TextParser QualifiedName { get; } =
+ Span.MatchedBy(
+ Character.Letter.Or(Character.EqualTo('_'))
+ .IgnoreThen(Character.LetterOrDigit.Or(Character.EqualTo('_')).Many())
+ .AtLeastOnceDelimitedBy(Character.EqualTo('.'))
+ );
+
+ public static TokenListParser PythonTypeDefinitionTokenizer { get; } =
+ (from name in Token.EqualTo(PythonToken.Identifier).Or(Token.EqualTo(PythonToken.None)).OptionalOrDefault()
+#pragma warning disable CS8620
+ from openBracket in Token.EqualTo(PythonToken.OpenBracket)
+ .Then(_ =>
+ PythonTypeDefinitionTokenizer
+ .AssumeNotNull()
+ .ManyDelimitedBy(
+ Token.EqualTo(PythonToken.Comma),
+ Token.EqualTo(PythonToken.CloseBracket)
+ )
+ )
+#pragma warning restore CS8620
+ .OptionalOrDefault()
+ select name.HasValue ? new PythonTypeSpec(name.ToStringValue(), openBracket)
+ : openBracket is null ? null : new PythonTypeSpec("argumentCollection__", openBracket)
+ )
+ .Named("Type Definition");
+}
diff --git a/src/CSnakes/Parser/PythonTokenizer.cs b/src/CSnakes.SourceGeneration/Parser/PythonTokenizer.cs
similarity index 98%
rename from src/CSnakes/Parser/PythonTokenizer.cs
rename to src/CSnakes.SourceGeneration/Parser/PythonTokenizer.cs
index a4d580f2..24637488 100644
--- a/src/CSnakes/Parser/PythonTokenizer.cs
+++ b/src/CSnakes.SourceGeneration/Parser/PythonTokenizer.cs
@@ -1,37 +1,37 @@
-using Superpower;
-using Superpower.Parsers;
-using Superpower.Tokenizers;
-
-namespace CSnakes.Parser;
-public static class PythonTokenizer
-{
- public static Tokenizer Instance { get; } =
- new TokenizerBuilder()
- .Ignore(Span.WhiteSpace)
- .Ignore(Comment.ShellStyle)
- .Match(Character.EqualTo('('), PythonToken.OpenParenthesis)
- .Match(Character.EqualTo(')'), PythonToken.CloseParenthesis)
- .Match(Character.EqualTo('['), PythonToken.OpenBracket)
- .Match(Character.EqualTo(']'), PythonToken.CloseBracket)
- .Match(Character.EqualTo(':'), PythonToken.Colon)
- .Match(Character.EqualTo(','), PythonToken.Comma)
- .Match(Character.EqualTo('*').IgnoreThen(Character.EqualTo('*')), PythonToken.DoubleAsterisk)
- .Match(Character.EqualTo('*'), PythonToken.Asterisk)
- .Match(Character.EqualTo('-').IgnoreThen(Character.EqualTo('>')), PythonToken.Arrow)
- .Match(Character.EqualTo('/'), PythonToken.Slash)
- .Match(Character.EqualTo('='), PythonToken.Equal)
- .Match(Span.EqualTo("def"), PythonToken.Def, requireDelimiters: true)
- .Match(Span.EqualTo("async"), PythonToken.Async, requireDelimiters: true)
- .Match(Span.EqualTo("..."), PythonToken.Ellipsis)
- .Match(Span.EqualTo("None"), PythonToken.None, requireDelimiters: true)
- .Match(Span.EqualTo("True"), PythonToken.True, requireDelimiters: true)
- .Match(Span.EqualTo("False"), PythonToken.False, requireDelimiters: true)
- .Match(PythonParser.QualifiedName, PythonToken.Identifier, requireDelimiters: true)
- .Match(PythonParser.IntegerConstantToken, PythonToken.Integer, requireDelimiters: true)
- .Match(PythonParser.DecimalConstantToken, PythonToken.Decimal, requireDelimiters: true)
- .Match(PythonParser.HexidecimalConstantToken, PythonToken.HexidecimalInteger, requireDelimiters: true)
- .Match(PythonParser.BinaryConstantToken, PythonToken.BinaryInteger, requireDelimiters: true)
- .Match(PythonParser.DoubleQuotedStringConstantToken, PythonToken.DoubleQuotedString)
- .Match(PythonParser.SingleQuotedStringConstantToken, PythonToken.SingleQuotedString)
- .Build();
-}
+using Superpower;
+using Superpower.Parsers;
+using Superpower.Tokenizers;
+
+namespace CSnakes.Parser;
+public static class PythonTokenizer
+{
+ public static Tokenizer Instance { get; } =
+ new TokenizerBuilder()
+ .Ignore(Span.WhiteSpace)
+ .Ignore(Comment.ShellStyle)
+ .Match(Character.EqualTo('('), PythonToken.OpenParenthesis)
+ .Match(Character.EqualTo(')'), PythonToken.CloseParenthesis)
+ .Match(Character.EqualTo('['), PythonToken.OpenBracket)
+ .Match(Character.EqualTo(']'), PythonToken.CloseBracket)
+ .Match(Character.EqualTo(':'), PythonToken.Colon)
+ .Match(Character.EqualTo(','), PythonToken.Comma)
+ .Match(Character.EqualTo('*').IgnoreThen(Character.EqualTo('*')), PythonToken.DoubleAsterisk)
+ .Match(Character.EqualTo('*'), PythonToken.Asterisk)
+ .Match(Character.EqualTo('-').IgnoreThen(Character.EqualTo('>')), PythonToken.Arrow)
+ .Match(Character.EqualTo('/'), PythonToken.Slash)
+ .Match(Character.EqualTo('='), PythonToken.Equal)
+ .Match(Span.EqualTo("def"), PythonToken.Def, requireDelimiters: true)
+ .Match(Span.EqualTo("async"), PythonToken.Async, requireDelimiters: true)
+ .Match(Span.EqualTo("..."), PythonToken.Ellipsis)
+ .Match(Span.EqualTo("None"), PythonToken.None, requireDelimiters: true)
+ .Match(Span.EqualTo("True"), PythonToken.True, requireDelimiters: true)
+ .Match(Span.EqualTo("False"), PythonToken.False, requireDelimiters: true)
+ .Match(PythonParser.QualifiedName, PythonToken.Identifier, requireDelimiters: true)
+ .Match(PythonParser.IntegerConstantToken, PythonToken.Integer, requireDelimiters: true)
+ .Match(PythonParser.DecimalConstantToken, PythonToken.Decimal, requireDelimiters: true)
+ .Match(PythonParser.HexidecimalConstantToken, PythonToken.HexidecimalInteger, requireDelimiters: true)
+ .Match(PythonParser.BinaryConstantToken, PythonToken.BinaryInteger, requireDelimiters: true)
+ .Match(PythonParser.DoubleQuotedStringConstantToken, PythonToken.DoubleQuotedString)
+ .Match(PythonParser.SingleQuotedStringConstantToken, PythonToken.SingleQuotedString)
+ .Build();
+}
diff --git a/src/CSnakes/Parser/PythonTokens.cs b/src/CSnakes.SourceGeneration/Parser/PythonTokens.cs
similarity index 93%
rename from src/CSnakes/Parser/PythonTokens.cs
rename to src/CSnakes.SourceGeneration/Parser/PythonTokens.cs
index 7816c2bc..5a5d1ab0 100644
--- a/src/CSnakes/Parser/PythonTokens.cs
+++ b/src/CSnakes.SourceGeneration/Parser/PythonTokens.cs
@@ -1,61 +1,61 @@
-using Superpower.Display;
-
-namespace CSnakes.Parser;
-
-public enum PythonToken
-{
- [Token(Example = "(")]
- OpenParenthesis,
-
- [Token(Example = ")")]
- CloseParenthesis,
-
- [Token(Example = "[")]
- OpenBracket,
-
- [Token(Example = "]")]
- CloseBracket,
-
- [Token(Example = ":")]
- Colon,
-
- [Token(Example = ",")]
- Comma,
-
- [Token(Example = "*")]
- Asterisk,
-
- [Token(Example = "**")]
- DoubleAsterisk,
-
- Identifier,
- QualifiedIdentifier,
-
- [Token(Example = "->")]
- Arrow,
-
- [Token(Example = "/")]
- Slash,
-
- [Token(Example = "=")]
- Equal,
-
- [Token(Example = "def")]
- Def,
-
- [Token(Example = "async")]
- Async,
-
- Integer,
- Decimal,
- HexidecimalInteger,
- BinaryInteger,
- DoubleQuotedString,
- SingleQuotedString,
- True,
- False,
- None,
-
- [Token(Example = "...")]
- Ellipsis
+using Superpower.Display;
+
+namespace CSnakes.Parser;
+
+public enum PythonToken
+{
+ [Token(Example = "(")]
+ OpenParenthesis,
+
+ [Token(Example = ")")]
+ CloseParenthesis,
+
+ [Token(Example = "[")]
+ OpenBracket,
+
+ [Token(Example = "]")]
+ CloseBracket,
+
+ [Token(Example = ":")]
+ Colon,
+
+ [Token(Example = ",")]
+ Comma,
+
+ [Token(Example = "*")]
+ Asterisk,
+
+ [Token(Example = "**")]
+ DoubleAsterisk,
+
+ Identifier,
+ QualifiedIdentifier,
+
+ [Token(Example = "->")]
+ Arrow,
+
+ [Token(Example = "/")]
+ Slash,
+
+ [Token(Example = "=")]
+ Equal,
+
+ [Token(Example = "def")]
+ Def,
+
+ [Token(Example = "async")]
+ Async,
+
+ Integer,
+ Decimal,
+ HexidecimalInteger,
+ BinaryInteger,
+ DoubleQuotedString,
+ SingleQuotedString,
+ True,
+ False,
+ None,
+
+ [Token(Example = "...")]
+ Ellipsis
}
\ No newline at end of file
diff --git a/src/CSnakes/Parser/Types/PythonConstant.cs b/src/CSnakes.SourceGeneration/Parser/Types/PythonConstant.cs
similarity index 96%
rename from src/CSnakes/Parser/Types/PythonConstant.cs
rename to src/CSnakes.SourceGeneration/Parser/Types/PythonConstant.cs
index 4516e6e7..05fb9123 100644
--- a/src/CSnakes/Parser/Types/PythonConstant.cs
+++ b/src/CSnakes.SourceGeneration/Parser/Types/PythonConstant.cs
@@ -1,77 +1,77 @@
-namespace CSnakes.Parser.Types;
-
-public class PythonConstant
-{
- public enum ConstantType
- {
- Integer,
- HexidecimalInteger,
- BinaryInteger,
- Float,
- String,
- Bool,
- None,
- }
-
- public PythonConstant() { }
-
- public PythonConstant(long value)
- {
- Type = ConstantType.Integer;
- IntegerValue = value;
- }
-
- public PythonConstant(double value)
- {
- Type = ConstantType.Float;
- FloatValue = value;
- }
-
- public PythonConstant(string value)
- {
- Type = ConstantType.String;
- StringValue = value;
- }
-
- public PythonConstant(bool value)
- {
- Type = ConstantType.Bool;
- BoolValue = value;
- }
-
- public static PythonConstant FromNone() => new PythonConstant { Type = ConstantType.None };
- public ConstantType Type { get; set; }
-
- public long IntegerValue { get; set; }
-
- public string? StringValue { get; set; }
-
- public double FloatValue { get; set; }
-
- public bool BoolValue { get; set; }
-
- public bool IsInteger { get => Type == ConstantType.Integer || Type == ConstantType.BinaryInteger || Type == ConstantType.HexidecimalInteger; }
-
- public override string ToString()
- {
- switch (Type)
- {
- case ConstantType.Integer:
- return IntegerValue.ToString();
- case ConstantType.HexidecimalInteger:
- return $"0x{IntegerValue:X}";
- case ConstantType.BinaryInteger:
- return $"0b{IntegerValue:X}";
- case ConstantType.Float:
- return FloatValue.ToString();
- case ConstantType.Bool:
- return BoolValue.ToString();
- case ConstantType.String:
- return StringValue ?? throw new ArgumentNullException(nameof(StringValue));
- case ConstantType.None:
- return "None";
- default:
- return "unknown";
- }
- }
-}
+namespace CSnakes.Parser.Types;
+
+public class PythonConstant
+{
+ public enum ConstantType
+ {
+ Integer,
+ HexidecimalInteger,
+ BinaryInteger,
+ Float,
+ String,
+ Bool,
+ None,
+ }
+
+ public PythonConstant() { }
+
+ public PythonConstant(long value)
+ {
+ Type = ConstantType.Integer;
+ IntegerValue = value;
+ }
+
+ public PythonConstant(double value)
+ {
+ Type = ConstantType.Float;
+ FloatValue = value;
+ }
+
+ public PythonConstant(string value)
+ {
+ Type = ConstantType.String;
+ StringValue = value;
+ }
+
+ public PythonConstant(bool value)
+ {
+ Type = ConstantType.Bool;
+ BoolValue = value;
+ }
+
+ public static PythonConstant FromNone() => new PythonConstant { Type = ConstantType.None };
+ public ConstantType Type { get; set; }
+
+ public long IntegerValue { get; set; }
+
+ public string? StringValue { get; set; }
+
+ public double FloatValue { get; set; }
+
+ public bool BoolValue { get; set; }
+
+ public bool IsInteger { get => Type == ConstantType.Integer || Type == ConstantType.BinaryInteger || Type == ConstantType.HexidecimalInteger; }
+
+ public override string ToString()
+ {
+ switch (Type)
+ {
+ case ConstantType.Integer:
+ return IntegerValue.ToString();
+ case ConstantType.HexidecimalInteger:
+ return $"0x{IntegerValue:X}";
+ case ConstantType.BinaryInteger:
+ return $"0b{IntegerValue:X}";
+ case ConstantType.Float:
+ return FloatValue.ToString();
+ case ConstantType.Bool:
+ return BoolValue.ToString();
+ case ConstantType.String:
+ return StringValue ?? throw new ArgumentNullException(nameof(StringValue));
+ case ConstantType.None:
+ return "None";
+ default:
+ return "unknown";
+ }
+ }
+}
diff --git a/src/CSnakes/Parser/Types/PythonFunctionDefinition.cs b/src/CSnakes.SourceGeneration/Parser/Types/PythonFunctionDefinition.cs
similarity index 97%
rename from src/CSnakes/Parser/Types/PythonFunctionDefinition.cs
rename to src/CSnakes.SourceGeneration/Parser/Types/PythonFunctionDefinition.cs
index 7c4b0641..75923cf0 100644
--- a/src/CSnakes/Parser/Types/PythonFunctionDefinition.cs
+++ b/src/CSnakes.SourceGeneration/Parser/Types/PythonFunctionDefinition.cs
@@ -1,36 +1,36 @@
-namespace CSnakes.Parser.Types;
-public class PythonFunctionDefinition(string name, PythonTypeSpec? returnType, PythonFunctionParameter[] pythonFunctionParameter)
-{
- public string Name { get; private set; } = name;
-
- private readonly PythonFunctionParameter[] _parameters = FixupArguments(pythonFunctionParameter);
-
- private static PythonFunctionParameter[] FixupArguments(PythonFunctionParameter[]? parameters)
- {
- if (parameters is null || parameters.Length == 0)
- return [];
-
- // Go through all parameters and mark those after the *arg as keyword only
- bool keywordOnly = false;
- for (int i = 1; i < parameters.Length; i++)
- {
- if (parameters[i].ParameterType == PythonFunctionParameterType.Star)
- {
- keywordOnly = true;
- continue;
- }
-
- parameters[i].IsKeywordOnly = keywordOnly;
- }
-
- return parameters;
- }
-
- public PythonTypeSpec ReturnType => returnType ?? PythonTypeSpec.Any;
-
- public PythonFunctionParameter[] Parameters => _parameters;
-
- public bool HasReturnTypeAnnotation() => returnType is not null;
-
- public bool IsAsync { get; set; }
-}
+namespace CSnakes.Parser.Types;
+public class PythonFunctionDefinition(string name, PythonTypeSpec? returnType, PythonFunctionParameter[] pythonFunctionParameter)
+{
+ public string Name { get; private set; } = name;
+
+ private readonly PythonFunctionParameter[] _parameters = FixupArguments(pythonFunctionParameter);
+
+ private static PythonFunctionParameter[] FixupArguments(PythonFunctionParameter[]? parameters)
+ {
+ if (parameters is null || parameters.Length == 0)
+ return [];
+
+ // Go through all parameters and mark those after the *arg as keyword only
+ bool keywordOnly = false;
+ for (int i = 1; i < parameters.Length; i++)
+ {
+ if (parameters[i].ParameterType == PythonFunctionParameterType.Star)
+ {
+ keywordOnly = true;
+ continue;
+ }
+
+ parameters[i].IsKeywordOnly = keywordOnly;
+ }
+
+ return parameters;
+ }
+
+ public PythonTypeSpec ReturnType => returnType ?? PythonTypeSpec.Any;
+
+ public PythonFunctionParameter[] Parameters => _parameters;
+
+ public bool HasReturnTypeAnnotation() => returnType is not null;
+
+ public bool IsAsync { get; set; }
+}
diff --git a/src/CSnakes/Parser/Types/PythonFunctionParameter.cs b/src/CSnakes.SourceGeneration/Parser/Types/PythonFunctionParameter.cs
similarity index 97%
rename from src/CSnakes/Parser/Types/PythonFunctionParameter.cs
rename to src/CSnakes.SourceGeneration/Parser/Types/PythonFunctionParameter.cs
index 929d6c40..af9b570f 100644
--- a/src/CSnakes/Parser/Types/PythonFunctionParameter.cs
+++ b/src/CSnakes.SourceGeneration/Parser/Types/PythonFunctionParameter.cs
@@ -1,17 +1,17 @@
-namespace CSnakes.Parser.Types;
-public class PythonFunctionParameter(string name, PythonTypeSpec? type, PythonConstant? defaultValue, PythonFunctionParameterType parameterType)
-{
- public string Name { get; } = name;
-
- public PythonTypeSpec Type { get; } = type ?? PythonTypeSpec.Any;
-
- public bool IsPositionalOnly { get; set; }
-
- public bool IsKeywordOnly { get; set; }
-
- public PythonConstant? DefaultValue { get; set; } = defaultValue;
-
- public PythonFunctionParameterType ParameterType { get; } = parameterType;
-
- public bool HasTypeAnnotation() => type is not null;
-}
+namespace CSnakes.Parser.Types;
+public class PythonFunctionParameter(string name, PythonTypeSpec? type, PythonConstant? defaultValue, PythonFunctionParameterType parameterType)
+{
+ public string Name { get; } = name;
+
+ public PythonTypeSpec Type { get; } = type ?? PythonTypeSpec.Any;
+
+ public bool IsPositionalOnly { get; set; }
+
+ public bool IsKeywordOnly { get; set; }
+
+ public PythonConstant? DefaultValue { get; set; } = defaultValue;
+
+ public PythonFunctionParameterType ParameterType { get; } = parameterType;
+
+ public bool HasTypeAnnotation() => type is not null;
+}
diff --git a/src/CSnakes/Parser/Types/PythonFunctionParameterType.cs b/src/CSnakes.SourceGeneration/Parser/Types/PythonFunctionParameterType.cs
similarity index 93%
rename from src/CSnakes/Parser/Types/PythonFunctionParameterType.cs
rename to src/CSnakes.SourceGeneration/Parser/Types/PythonFunctionParameterType.cs
index c905f5b6..029ec950 100644
--- a/src/CSnakes/Parser/Types/PythonFunctionParameterType.cs
+++ b/src/CSnakes.SourceGeneration/Parser/Types/PythonFunctionParameterType.cs
@@ -1,9 +1,9 @@
-namespace CSnakes.Parser.Types;
-
-public enum PythonFunctionParameterType
-{
- Star,
- DoubleStar,
- Slash,
- Normal
-}
+namespace CSnakes.Parser.Types;
+
+public enum PythonFunctionParameterType
+{
+ Star,
+ DoubleStar,
+ Slash,
+ Normal
+}
diff --git a/src/CSnakes/Parser/Types/PythonParameterType.cs b/src/CSnakes.SourceGeneration/Parser/Types/PythonParameterType.cs
similarity index 97%
rename from src/CSnakes/Parser/Types/PythonParameterType.cs
rename to src/CSnakes.SourceGeneration/Parser/Types/PythonParameterType.cs
index aac4169b..36cb4423 100644
--- a/src/CSnakes/Parser/Types/PythonParameterType.cs
+++ b/src/CSnakes.SourceGeneration/Parser/Types/PythonParameterType.cs
@@ -1,7 +1,7 @@
-namespace CSnakes.Parser.Types;
-
-public class PythonParameterType(string name, PythonFunctionParameterType parameterType)
-{
- public string Name { get; } = name;
- public PythonFunctionParameterType ParameterType { get; set; } = parameterType;
-}
+namespace CSnakes.Parser.Types;
+
+public class PythonParameterType(string name, PythonFunctionParameterType parameterType)
+{
+ public string Name { get; } = name;
+ public PythonFunctionParameterType ParameterType { get; set; } = parameterType;
+}
diff --git a/src/CSnakes/Parser/Types/PythonTypeSpec.cs b/src/CSnakes.SourceGeneration/Parser/Types/PythonTypeSpec.cs
similarity index 96%
rename from src/CSnakes/Parser/Types/PythonTypeSpec.cs
rename to src/CSnakes.SourceGeneration/Parser/Types/PythonTypeSpec.cs
index c7151d67..ffeb3267 100644
--- a/src/CSnakes/Parser/Types/PythonTypeSpec.cs
+++ b/src/CSnakes.SourceGeneration/Parser/Types/PythonTypeSpec.cs
@@ -1,16 +1,16 @@
-namespace CSnakes.Parser.Types;
-public class PythonTypeSpec(string name, PythonTypeSpec[] arguments)
-{
- public string Name { get; } = name;
-
- public PythonTypeSpec[] Arguments { get; } = arguments;
-
- public override string ToString() =>
- HasArguments() ?
- $"{Name}[{string.Join(", ", Arguments.Select(a => a.ToString()))}]" :
- Name;
-
- public bool HasArguments() => Arguments is not null && Arguments.Length > 0;
-
- public static PythonTypeSpec Any => new("Any", []);
-}
+namespace CSnakes.Parser.Types;
+public class PythonTypeSpec(string name, PythonTypeSpec[] arguments)
+{
+ public string Name { get; } = name;
+
+ public PythonTypeSpec[] Arguments { get; } = arguments;
+
+ public override string ToString() =>
+ HasArguments() ?
+ $"{Name}[{string.Join(", ", Arguments.Select(a => a.ToString()))}]" :
+ Name;
+
+ public bool HasArguments() => Arguments is not null && Arguments.Length > 0;
+
+ public static PythonTypeSpec Any => new("Any", []);
+}
diff --git a/src/CSnakes/PythonStaticGenerator.cs b/src/CSnakes.SourceGeneration/PythonStaticGenerator.cs
similarity index 97%
rename from src/CSnakes/PythonStaticGenerator.cs
rename to src/CSnakes.SourceGeneration/PythonStaticGenerator.cs
index 3a8b5d52..61731ee8 100644
--- a/src/CSnakes/PythonStaticGenerator.cs
+++ b/src/CSnakes.SourceGeneration/PythonStaticGenerator.cs
@@ -1,127 +1,127 @@
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Text;
-using CSnakes.Parser;
-using CSnakes.Parser.Types;
-using CSnakes.Reflection;
-
-namespace CSnakes;
-
-[Generator(LanguageNames.CSharp)]
-public class PythonStaticGenerator : IIncrementalGenerator
-{
- public void Initialize(IncrementalGeneratorInitializationContext context)
- {
- // System.Diagnostics.Debugger.Launch();
- var pythonFilesPipeline = context.AdditionalTextsProvider
- .Where(static text => Path.GetExtension(text.Path) == ".py")
- .Collect();
-
- context.RegisterSourceOutput(pythonFilesPipeline, static (sourceContext, inputFiles) =>
- {
- foreach (var file in inputFiles)
- {
- // Add environment path
- var @namespace = "CSnakes.Runtime";
-
- var fileName = Path.GetFileNameWithoutExtension(file.Path);
-
- // Convert snakecase to pascal case
- var pascalFileName = string.Join("", fileName.Split('_').Select(s => char.ToUpperInvariant(s[0]) + s.Substring(1)));
- // Read the file
- var code = file.GetText(sourceContext.CancellationToken);
-
- if (code is null) continue;
-
- // Parse the Python file
- var result = PythonParser.TryParseFunctionDefinitions(code, out PythonFunctionDefinition[] functions, out GeneratorError[]? errors);
-
- foreach (var error in errors)
- {
- // Update text span
- Location errorLocation = Location.Create(file.Path, TextSpan.FromBounds(0, 1), new LinePositionSpan(new LinePosition(error.StartLine, error.StartColumn), new LinePosition(error.EndLine, error.EndColumn)));
- sourceContext.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("PSG004", "PythonStaticGenerator", error.Message, "PythonStaticGenerator", DiagnosticSeverity.Error, true), errorLocation));
- }
-
- if (result)
- {
- IEnumerable methods = ModuleReflection.MethodsFromFunctionDefinitions(functions, fileName);
- string source = FormatClassFromMethods(@namespace, pascalFileName, methods, fileName, functions);
- sourceContext.AddSource($"{pascalFileName}.py.cs", source);
- sourceContext.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("PSG002", "PythonStaticGenerator", $"Generated {pascalFileName}.py.cs", "PythonStaticGenerator", DiagnosticSeverity.Info, true), Location.None));
- }
- }
- });
- }
-
- public static string FormatClassFromMethods(string @namespace, string pascalFileName, IEnumerable methods, string fileName, PythonFunctionDefinition[] functions)
- {
- var paramGenericArgs = methods
- .Select(m => m.ParameterGenericArgs)
- .Where(l => l is not null && l.Any());
-
- return $$"""
- //
- #nullable enable
- using CSnakes.Runtime;
- using CSnakes.Runtime.Python;
-
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
-
- using Microsoft.Extensions.Logging;
-
- namespace {{@namespace}};
- public static class {{pascalFileName}}Extensions
- {
- private static I{{pascalFileName}}? instance;
-
- public static I{{pascalFileName}} {{pascalFileName}}(this IPythonEnvironment env)
- {
- if (instance is null)
- {
- instance = new {{pascalFileName}}Internal(env.Logger);
- }
- Debug.Assert(!env.IsDisposed());
- return instance;
- }
-
- private class {{pascalFileName}}Internal : I{{pascalFileName}}
- {
- private readonly PyObject module;
-
- private readonly ILogger logger;
- private readonly IDictionary functions;
-
- internal {{pascalFileName}}Internal(ILogger logger)
- {
- this.logger = logger;
- using (GIL.Acquire())
- {
- logger.LogDebug("Importing module {ModuleName}", "{{fileName}}");
- module = Import.ImportModule("{{fileName}}");
- functions = new Dictionary();
- {{ string.Join(Environment.NewLine, functions.Select(f => $"functions[\"{f.Name}\"] = module.GetAttr(\"{f.Name}\");")) }}
- }
- }
-
- public void Dispose()
- {
- logger.LogDebug("Disposing module {ModuleName}", "{{fileName}}");
- foreach (var function in functions)
- {
- function.Value.Dispose();
- }
- module.Dispose();
- }
-
- {{methods.Select(m => m.Syntax).Compile()}}
- }
- }
- public interface I{{pascalFileName}}
- {
- {{string.Join(Environment.NewLine, methods.Select(m => m.Syntax).Select(m => $"{m.ReturnType.NormalizeWhitespace()} {m.Identifier.Text}{m.ParameterList.NormalizeWhitespace()};"))}}
- }
- """;
- }
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using CSnakes.Parser;
+using CSnakes.Parser.Types;
+using CSnakes.Reflection;
+
+namespace CSnakes;
+
+[Generator(LanguageNames.CSharp)]
+public class PythonStaticGenerator : IIncrementalGenerator
+{
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ // System.Diagnostics.Debugger.Launch();
+ var pythonFilesPipeline = context.AdditionalTextsProvider
+ .Where(static text => Path.GetExtension(text.Path) == ".py")
+ .Collect();
+
+ context.RegisterSourceOutput(pythonFilesPipeline, static (sourceContext, inputFiles) =>
+ {
+ foreach (var file in inputFiles)
+ {
+ // Add environment path
+ var @namespace = "CSnakes.Runtime";
+
+ var fileName = Path.GetFileNameWithoutExtension(file.Path);
+
+ // Convert snakecase to pascal case
+ var pascalFileName = string.Join("", fileName.Split('_').Select(s => char.ToUpperInvariant(s[0]) + s.Substring(1)));
+ // Read the file
+ var code = file.GetText(sourceContext.CancellationToken);
+
+ if (code is null) continue;
+
+ // Parse the Python file
+ var result = PythonParser.TryParseFunctionDefinitions(code, out PythonFunctionDefinition[] functions, out GeneratorError[]? errors);
+
+ foreach (var error in errors)
+ {
+ // Update text span
+ Location errorLocation = Location.Create(file.Path, TextSpan.FromBounds(0, 1), new LinePositionSpan(new LinePosition(error.StartLine, error.StartColumn), new LinePosition(error.EndLine, error.EndColumn)));
+ sourceContext.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("PSG004", "PythonStaticGenerator", error.Message, "PythonStaticGenerator", DiagnosticSeverity.Error, true), errorLocation));
+ }
+
+ if (result)
+ {
+ IEnumerable methods = ModuleReflection.MethodsFromFunctionDefinitions(functions, fileName);
+ string source = FormatClassFromMethods(@namespace, pascalFileName, methods, fileName, functions);
+ sourceContext.AddSource($"{pascalFileName}.py.cs", source);
+ sourceContext.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("PSG002", "PythonStaticGenerator", $"Generated {pascalFileName}.py.cs", "PythonStaticGenerator", DiagnosticSeverity.Info, true), Location.None));
+ }
+ }
+ });
+ }
+
+ public static string FormatClassFromMethods(string @namespace, string pascalFileName, IEnumerable methods, string fileName, PythonFunctionDefinition[] functions)
+ {
+ var paramGenericArgs = methods
+ .Select(m => m.ParameterGenericArgs)
+ .Where(l => l is not null && l.Any());
+
+ return $$"""
+ //
+ #nullable enable
+ using CSnakes.Runtime;
+ using CSnakes.Runtime.Python;
+
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+
+ using Microsoft.Extensions.Logging;
+
+ namespace {{@namespace}};
+ public static class {{pascalFileName}}Extensions
+ {
+ private static I{{pascalFileName}}? instance;
+
+ public static I{{pascalFileName}} {{pascalFileName}}(this IPythonEnvironment env)
+ {
+ if (instance is null)
+ {
+ instance = new {{pascalFileName}}Internal(env.Logger);
+ }
+ Debug.Assert(!env.IsDisposed());
+ return instance;
+ }
+
+ private class {{pascalFileName}}Internal : I{{pascalFileName}}
+ {
+ private readonly PyObject module;
+
+ private readonly ILogger logger;
+ private readonly IDictionary functions;
+
+ internal {{pascalFileName}}Internal(ILogger logger)
+ {
+ this.logger = logger;
+ using (GIL.Acquire())
+ {
+ logger.LogDebug("Importing module {ModuleName}", "{{fileName}}");
+ module = Import.ImportModule("{{fileName}}");
+ functions = new Dictionary();
+ {{ string.Join(Environment.NewLine, functions.Select(f => $"functions[\"{f.Name}\"] = module.GetAttr(\"{f.Name}\");")) }}
+ }
+ }
+
+ public void Dispose()
+ {
+ logger.LogDebug("Disposing module {ModuleName}", "{{fileName}}");
+ foreach (var function in functions)
+ {
+ function.Value.Dispose();
+ }
+ module.Dispose();
+ }
+
+ {{methods.Select(m => m.Syntax).Compile()}}
+ }
+ }
+ public interface I{{pascalFileName}}
+ {
+ {{string.Join(Environment.NewLine, methods.Select(m => m.Syntax).Select(m => $"{m.ReturnType.NormalizeWhitespace()} {m.Identifier.Text}{m.ParameterList.NormalizeWhitespace()};"))}}
+ }
+ """;
+ }
}
\ No newline at end of file
diff --git a/src/CSnakes/Reflection/ArgumentReflection.cs b/src/CSnakes.SourceGeneration/Reflection/ArgumentReflection.cs
similarity index 97%
rename from src/CSnakes/Reflection/ArgumentReflection.cs
rename to src/CSnakes.SourceGeneration/Reflection/ArgumentReflection.cs
index 19de7598..507cb472 100644
--- a/src/CSnakes/Reflection/ArgumentReflection.cs
+++ b/src/CSnakes.SourceGeneration/Reflection/ArgumentReflection.cs
@@ -1,4 +1,4 @@
-using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using CSnakes.Parser.Types;
diff --git a/src/CSnakes/Reflection/MethodReflection.cs b/src/CSnakes.SourceGeneration/Reflection/MethodReflection.cs
similarity index 97%
rename from src/CSnakes/Reflection/MethodReflection.cs
rename to src/CSnakes.SourceGeneration/Reflection/MethodReflection.cs
index 597df350..10ae8092 100644
--- a/src/CSnakes/Reflection/MethodReflection.cs
+++ b/src/CSnakes.SourceGeneration/Reflection/MethodReflection.cs
@@ -1,4 +1,4 @@
-using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using CSnakes.Parser.Types;
diff --git a/src/CSnakes/Reflection/ModuleReflection.cs b/src/CSnakes.SourceGeneration/Reflection/ModuleReflection.cs
similarity index 96%
rename from src/CSnakes/Reflection/ModuleReflection.cs
rename to src/CSnakes.SourceGeneration/Reflection/ModuleReflection.cs
index 7edb41bf..bf6e0af5 100644
--- a/src/CSnakes/Reflection/ModuleReflection.cs
+++ b/src/CSnakes.SourceGeneration/Reflection/ModuleReflection.cs
@@ -1,24 +1,24 @@
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using CSnakes.Parser.Types;
-
-namespace CSnakes.Reflection;
-
-public static class ModuleReflection
-{
- public static IEnumerable MethodsFromFunctionDefinitions(PythonFunctionDefinition[] functions, string moduleName)
- {
- return functions.Select(function => MethodReflection.FromMethod(function, moduleName));
- }
-
- public static string Compile(this IEnumerable methods)
- {
- using StringWriter sw = new();
- foreach (var method in methods)
- {
- method.NormalizeWhitespace().WriteTo(sw);
- }
- return sw.ToString();
- }
-}
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using CSnakes.Parser.Types;
+
+namespace CSnakes.Reflection;
+
+public static class ModuleReflection
+{
+ public static IEnumerable MethodsFromFunctionDefinitions(PythonFunctionDefinition[] functions, string moduleName)
+ {
+ return functions.Select(function => MethodReflection.FromMethod(function, moduleName));
+ }
+
+ public static string Compile(this IEnumerable methods)
+ {
+ using StringWriter sw = new();
+ foreach (var method in methods)
+ {
+ method.NormalizeWhitespace().WriteTo(sw);
+ }
+ return sw.ToString();
+ }
+}
diff --git a/src/CSnakes/Reflection/TypeReflection.cs b/src/CSnakes.SourceGeneration/Reflection/TypeReflection.cs
similarity index 97%
rename from src/CSnakes/Reflection/TypeReflection.cs
rename to src/CSnakes.SourceGeneration/Reflection/TypeReflection.cs
index d2da044d..ab009225 100644
--- a/src/CSnakes/Reflection/TypeReflection.cs
+++ b/src/CSnakes.SourceGeneration/Reflection/TypeReflection.cs
@@ -1,4 +1,4 @@
-using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using CSnakes.Parser.Types;
diff --git a/src/CSnakes.Tests/CSnakes.Tests.csproj b/src/CSnakes.Tests/CSnakes.Tests.csproj
index 40202b34..aa9845ca 100644
--- a/src/CSnakes.Tests/CSnakes.Tests.csproj
+++ b/src/CSnakes.Tests/CSnakes.Tests.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/src/CSnakes.sln b/src/CSnakes.sln
index 6a415c85..c19a5c5d 100644
--- a/src/CSnakes.sln
+++ b/src/CSnakes.sln
@@ -10,7 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\README.md = ..\README.md
EndProjectSection
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSnakes", "CSnakes\CSnakes.csproj", "{3912E0F4-493B-4B26-A44D-B095F6CF7538}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSnakes.SourceGeneration", "CSnakes.SourceGeneration\CSnakes.SourceGeneration.csproj", "{3912E0F4-493B-4B26-A44D-B095F6CF7538}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSnakes.Runtime", "CSnakes.Runtime\CSnakes.Runtime.csproj", "{9BAB43D1-017F-4470-B898-42022328D17F}"
EndProject
@@ -22,6 +22,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSnakes.Runtime.Tests", "CS
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Profile", "Profile\Profile.csproj", "{93264FC1-2880-4959-9576-50D260039BC2}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4374E3D5-3A2C-47F8-B33E-F3C43A20B530}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E52DC71C-FB58-4E57-9CCA-8A78EAA49123}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -56,6 +60,14 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {3912E0F4-493B-4B26-A44D-B095F6CF7538} = {4374E3D5-3A2C-47F8-B33E-F3C43A20B530}
+ {9BAB43D1-017F-4470-B898-42022328D17F} = {4374E3D5-3A2C-47F8-B33E-F3C43A20B530}
+ {E01D5C27-CB38-4A0D-B297-33E1E10C1A82} = {E52DC71C-FB58-4E57-9CCA-8A78EAA49123}
+ {F179777C-913A-46FE-BE2F-15A59EAB7E88} = {E52DC71C-FB58-4E57-9CCA-8A78EAA49123}
+ {CB00C1F5-8C01-4FE7-82AD-7F9B4398E3F8} = {E52DC71C-FB58-4E57-9CCA-8A78EAA49123}
+ {93264FC1-2880-4959-9576-50D260039BC2} = {E52DC71C-FB58-4E57-9CCA-8A78EAA49123}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4ACC77F9-1BB8-42DE-B647-01C458922F49}
EndGlobalSection
diff --git a/src/Integration.Tests/Integration.Tests.csproj b/src/Integration.Tests/Integration.Tests.csproj
index 342922ac..cd9280cb 100644
--- a/src/Integration.Tests/Integration.Tests.csproj
+++ b/src/Integration.Tests/Integration.Tests.csproj
@@ -24,7 +24,7 @@
-
+
diff --git a/src/Profile/Profile.csproj b/src/Profile/Profile.csproj
index 33edd747..d224de7d 100644
--- a/src/Profile/Profile.csproj
+++ b/src/Profile/Profile.csproj
@@ -22,7 +22,7 @@
-
+