diff --git a/src/CSnakes.Tests/GeneratedSignatureTests.cs b/src/CSnakes.Tests/GeneratedSignatureTests.cs index fd0bcf33..be87090d 100644 --- a/src/CSnakes.Tests/GeneratedSignatureTests.cs +++ b/src/CSnakes.Tests/GeneratedSignatureTests.cs @@ -35,6 +35,7 @@ public class GeneratedSignatureTests(TestEnvironment testEnv) : IClassFixture None:\n ...\n", "void Hello(long a = 0b10101010)")] [InlineData("def hello(a: int = 2147483648) -> None:\n ...\n", "void Hello(long a = 2147483648L)")] [InlineData("def hello(a: Optional[int] = None) -> None:\n ...\n", "void Hello(long? a = null)")] + [InlineData("def hello(a: typing.List[int], b: typing.Dict[str, int]) -> typing.Tuple[str, int]:\n ...\n", "public (string, long) Hello(IReadOnlyList a, IReadOnlyDictionary b)")] public void TestGeneratedSignature(string code, string expected) { diff --git a/src/CSnakes.Tests/TokenizerTests.cs b/src/CSnakes.Tests/TokenizerTests.cs index 3da3b79a..f6172230 100644 --- a/src/CSnakes.Tests/TokenizerTests.cs +++ b/src/CSnakes.Tests/TokenizerTests.cs @@ -230,6 +230,28 @@ public void ParseFunctionParameterListDoubleGeneric() Assert.Equal("list[int]", result.Value[0].Type.ToString()); } + [Fact] + public void ParseFunctionParameterListQualifiedGenericType() + { + var code = "(a: typing.List[int], b)"; + var tokens = PythonTokenizer.Instance.Tokenize(code); + var result = PythonParser.PythonParameterListTokenizer.TryParse(tokens); + Assert.True(result.HasValue); + Assert.Equal("a", result.Value[0].Name); + Assert.Equal("typing.List[int]", result.Value[0].Type.ToString()); + } + + [Fact] + public void ParseFunctionParameterListQualifiedBasicType() + { + var code = "(a: np.ndarray, b)"; + var tokens = PythonTokenizer.Instance.Tokenize(code); + var result = PythonParser.PythonParameterListTokenizer.TryParse(tokens); + Assert.True(result.HasValue); + Assert.Equal("a", result.Value[0].Name); + Assert.Equal("np.ndarray", result.Value[0].Type.ToString()); + } + [Fact] public void ParseFunctionParameterListEasy() { diff --git a/src/CSnakes/Parser/PythonParser.TypeDef.cs b/src/CSnakes/Parser/PythonParser.TypeDef.cs index 06cd204d..2e832496 100644 --- a/src/CSnakes/Parser/PythonParser.TypeDef.cs +++ b/src/CSnakes/Parser/PythonParser.TypeDef.cs @@ -1,10 +1,18 @@ 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)) #pragma warning disable CS8620 diff --git a/src/CSnakes/Parser/PythonTokenizer.cs b/src/CSnakes/Parser/PythonTokenizer.cs index d498ab0a..a4d580f2 100644 --- a/src/CSnakes/Parser/PythonTokenizer.cs +++ b/src/CSnakes/Parser/PythonTokenizer.cs @@ -26,7 +26,7 @@ public static class PythonTokenizer .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(Identifier.CStyle, PythonToken.Identifier, requireDelimiters: true) // TODO: (track) Does this require delimiters? + .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) diff --git a/src/CSnakes/Parser/PythonTokens.cs b/src/CSnakes/Parser/PythonTokens.cs index 1529a5cf..7816c2bc 100644 --- a/src/CSnakes/Parser/PythonTokens.cs +++ b/src/CSnakes/Parser/PythonTokens.cs @@ -29,6 +29,7 @@ public enum PythonToken DoubleAsterisk, Identifier, + QualifiedIdentifier, [Token(Example = "->")] Arrow, diff --git a/src/CSnakes/Reflection/TypeReflection.cs b/src/CSnakes/Reflection/TypeReflection.cs index a21fdcd7..c1ac2204 100644 --- a/src/CSnakes/Reflection/TypeReflection.cs +++ b/src/CSnakes/Reflection/TypeReflection.cs @@ -13,7 +13,7 @@ public static TypeSyntax AsPredefinedType(PythonTypeSpec pythonType) if (pythonType.HasArguments()) { // Get last occurrence of ] in pythonType - return pythonType.Name switch + return pythonType.Name.Replace("typing.", "") switch { "list" => CreateListType(pythonType.Arguments[0]), "List" => CreateListType(pythonType.Arguments[0]),