Skip to content

Commit

Permalink
Merge pull request #37 from tonybaloney/5-fix-compiler-warnings-for-n…
Browse files Browse the repository at this point in the history
…ullable-attributes

Fixing compiler null warnings
  • Loading branch information
tonybaloney authored Jul 22, 2024
2 parents d0d63c0 + b4d28aa commit 73dea94
Show file tree
Hide file tree
Showing 25 changed files with 368 additions and 386 deletions.
8 changes: 8 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>
3 changes: 0 additions & 3 deletions ExamplePythonDependency/ExamplePythonDependency.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>

Expand Down
4 changes: 0 additions & 4 deletions Integration.Tests/Integration.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
Expand Down
3 changes: 2 additions & 1 deletion PythonCodeGen.sln
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{24D80700-8F01-41C7-B858-2C1B242C4EE0}"
ProjectSection(SolutionItems) = preProject
AutoUpdateAssemblyName.txt = AutoUpdateAssemblyName.txt
Directory.Build.props = Directory.Build.props
README.md = README.md
EndProjectSection
EndProject
Expand All @@ -19,7 +20,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PythonEnvironments", "Pytho
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PythonSourceGenerator.Tests", "PythonSourceGenerator.Tests\PythonSourceGenerator.Tests.csproj", "{E01D5C27-CB38-4A0D-B297-33E1E10C1A82}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Tests", "Integration.Tests\Integration.Tests.csproj", "{F179777C-913A-46FE-BE2F-15A59EAB7E88}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Integration.Tests", "Integration.Tests\Integration.Tests.csproj", "{F179777C-913A-46FE-BE2F-15A59EAB7E88}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
2 changes: 1 addition & 1 deletion PythonEnvironments/CustomConverters/TupleConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public bool TryDecode<T>(PyObject pyObj, out T value)
// We have to convert the Python values to CLR values, as if we just tried As<object>() it would
// not parse the Python type to a CLR type, only to a new Python type.
Type[] types = typeof(T).GetGenericArguments();
object[] clrValues = pyValues.Select((p, i) => p.AsManagedObject(types[i])).ToArray();
object?[] clrValues = pyValues.Select((p, i) => p.AsManagedObject(types[i])).ToArray();

ConstructorInfo ctor = typeof(T).GetConstructors().First(c => c.GetParameters().Count() == clrValues.Length);
value = (T)ctor.Invoke(clrValues);
Expand Down
21 changes: 8 additions & 13 deletions PythonEnvironments/PythonEnvironment.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
using Python.Runtime;
using PythonEnvironments.CustomConverters;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

namespace PythonEnvironments;

public class PythonEnvironment(string pythonLocation, string version = "3.10.0")
{
private readonly string versionPath = MapVersion(version);
private PythonEnvironmentInternal env;
private PythonEnvironmentInternal? env;
private string[] extraPaths = [];

private static string MapVersion(string version, string sep = "")
Expand All @@ -33,7 +28,7 @@ public PythonEnvironment WithVirtualEnvironment(string path)

public IPythonEnvironment Build(string home)
{
if (PythonEngine.IsInitialized)
if (PythonEngine.IsInitialized && env is not null)
{
// Raise exception?
return env;
Expand All @@ -55,15 +50,15 @@ private static string TryLocatePython(string version)
{
return windowsStorePath;
}
else if (Directory.Exists(officialInstallerPath))

if (Directory.Exists(officialInstallerPath))
{
return officialInstallerPath;
}
else
{
// TODO : Use nuget package path?
throw new Exception("Python not found");
}

// TODO : Use nuget package path?
throw new Exception("Python not found");

}

public override bool Equals(object obj)
Expand Down
1 change: 0 additions & 1 deletion PythonEnvironments/PythonEnvironments.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
Expand Down
30 changes: 30 additions & 0 deletions PythonSourceGenerator/Parser/PythonSignatureParser.Args.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using PythonSourceGenerator.Parser.Types;
using Superpower;
using Superpower.Parsers;

namespace PythonSourceGenerator.Parser;
public static partial class PythonSignatureParser
{
public static TokenListParser<PythonSignatureTokens.PythonSignatureToken, PythonParameterType> PythonStarArgTokenizer { get; } =
(from star in Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.Asterisk)
from name in Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.Identifier).Optional()
select new PythonParameterType(name.HasValue ? name.Value.ToStringValue() : "args", PythonFunctionParameterType.Star))
.Named("Star Arg");

public static TokenListParser<PythonSignatureTokens.PythonSignatureToken, PythonParameterType> PythonDoubleStarArgTokenizer { get; } =
(from star in Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.DoubleAsterisk)
from name in Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.Identifier)
select new PythonParameterType(name.ToStringValue(), PythonFunctionParameterType.DoubleStar))
.Named("Double Star Arg");

public static TokenListParser<PythonSignatureTokens.PythonSignatureToken, PythonParameterType> PythonNormalArgTokenizer { get; } =
(from name in Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.Identifier)
select new PythonParameterType(name.ToStringValue(), PythonFunctionParameterType.Normal))
.Named("Normal Arg");

public static TokenListParser<PythonSignatureTokens.PythonSignatureToken, PythonParameterType?> PythonArgTokenizer { get; } =
PythonStarArgTokenizer.AsNullable()
.Or(PythonDoubleStarArgTokenizer.AsNullable())
.Or(PythonNormalArgTokenizer.AsNullable())
.Named("Arg");
}
124 changes: 124 additions & 0 deletions PythonSourceGenerator/Parser/PythonSignatureParser.Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
using PythonSourceGenerator.Parser.Types;
using Superpower;
using Superpower.Model;
using Superpower.Parsers;

namespace PythonSourceGenerator.Parser;
public static partial class PythonSignatureParser
{
public static TextParser<Unit> IntegerConstantToken { get; } =
from sign in Character.EqualTo('-').OptionalOrDefault()
from digits in Character.Digit.AtLeastOnce()
select Unit.Value;

public static TextParser<Unit> DecimalConstantToken { get; } =
from sign in Character.EqualTo('-').OptionalOrDefault()
from digits in Character.Digit.Many().OptionalOrDefault(['0'])
from decimal_ in Character.EqualTo('.')
from rest in Character.Digit.Many()
select Unit.Value;

public static TextParser<Unit> DoubleQuotedStringConstantToken { get; } =
from open in Character.EqualTo('"')
from chars in Character.ExceptIn('"').Many()
from close in Character.EqualTo('"')
select Unit.Value;

public static TextParser<Unit> SingleQuotedStringConstantToken { get; } =
from open in Character.EqualTo('\'')
from chars in Character.ExceptIn('\'').Many()
from close in Character.EqualTo('\'')
select Unit.Value;

public static TokenListParser<PythonSignatureTokens.PythonSignatureToken, PythonConstant> DoubleQuotedStringConstantTokenizer { get; } =
Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.DoubleQuotedString)
.Apply(ConstantParsers.DoubleQuotedString)
.Select(s => new PythonConstant { IsString = true, StringValue = s })
.Named("Double Quoted String Constant");

public static TokenListParser<PythonSignatureTokens.PythonSignatureToken, PythonConstant> SingleQuotedStringConstantTokenizer { get; } =
Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.SingleQuotedString)
.Apply(ConstantParsers.SingleQuotedString)
.Select(s => new PythonConstant { IsString = true, StringValue = s })
.Named("Single Quoted String Constant");

public static TokenListParser<PythonSignatureTokens.PythonSignatureToken, PythonConstant> DecimalConstantTokenizer { get; } =
Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.Decimal)
.Apply(ConstantParsers.Decimal)
.Select(d => new PythonConstant { IsFloat = true, FloatValue = d })
.Named("Decimal Constant");

public static TokenListParser<PythonSignatureTokens.PythonSignatureToken, PythonConstant> IntegerConstantTokenizer { get; } =
Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.Integer)
.Apply(ConstantParsers.Integer)
.Select(d => new PythonConstant { IsInteger = true, IntegerValue = d })
.Named("Integer Constant");

public static TokenListParser<PythonSignatureTokens.PythonSignatureToken, PythonConstant> BoolConstantTokenizer { get; } =
Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.True).Or(Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.False))
.Select(d => new PythonConstant { IsBool = true, BoolValue = d.Kind == PythonSignatureTokens.PythonSignatureToken.True })
.Named("Bool Constant");

public static TokenListParser<PythonSignatureTokens.PythonSignatureToken, PythonConstant> NoneConstantTokenizer { get; } =
Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.None)
.Select(d => new PythonConstant { IsNone = true })
.Named("None Constant");

// Any constant value
public static TokenListParser<PythonSignatureTokens.PythonSignatureToken, PythonConstant?> ConstantValueTokenizer { get; } =
DecimalConstantTokenizer.AsNullable()
.Or(IntegerConstantTokenizer.AsNullable())
.Or(BoolConstantTokenizer.AsNullable())
.Or(NoneConstantTokenizer.AsNullable())
.Or(DoubleQuotedStringConstantTokenizer.AsNullable())
.Or(SingleQuotedStringConstantTokenizer.AsNullable())
.Named("Constant");

static class ConstantParsers
{
public static TextParser<string> 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<string> 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);

public static TextParser<int> Integer { get; } =
from sign in Character.EqualTo('-').Value(-1).OptionalOrDefault(1)
from whole in Numerics.Natural.Select(n => int.Parse(n.ToStringValue()))
select whole * sign;

// TODO: (track) This a copy from the JSON spec and probably doesn't reflect Python's other numeric literals like Hex and Real
public static TextParser<double> Decimal { get; } =
from sign in Character.EqualTo('-').Value(-1.0).OptionalOrDefault(1.0)
from whole in Numerics.Natural.Select(n => double.Parse(n.ToStringValue()))
from frac in Character.EqualTo('.')
.IgnoreThen(Numerics.Natural)
.Select(n => double.Parse(n.ToStringValue()) * Math.Pow(10, -n.Length))
.OptionalOrDefault()
from exp in Character.EqualToIgnoreCase('e')
.IgnoreThen(Character.EqualTo('+').Value(1.0)
.Or(Character.EqualTo('-').Value(-1.0))
.OptionalOrDefault(1.0))
.Then(expsign => Numerics.Natural.Select(n => double.Parse(n.ToStringValue()) * expsign))
.OptionalOrDefault()
select (whole + frac) * sign * Math.Pow(10, exp);
}
}
100 changes: 100 additions & 0 deletions PythonSourceGenerator/Parser/PythonSignatureParser.Function.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using PythonSourceGenerator.Parser.Types;
using Superpower;
using Superpower.Parsers;

namespace PythonSourceGenerator.Parser;
public static partial class PythonSignatureParser
{
public static TokenListParser<PythonSignatureTokens.PythonSignatureToken, PythonFunctionDefinition> PythonFunctionDefinitionTokenizer { get; } =
(from def in Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.Def)
from name in Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.Identifier)
from parameters in PythonParameterListTokenizer
from arrow in Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.Arrow).Optional().Then(returnType => PythonTypeDefinitionTokenizer.OptionalOrDefault())
from colon in Token.EqualTo(PythonSignatureTokens.PythonSignatureToken.Colon)
select new PythonFunctionDefinition(name.ToStringValue(), arrow, parameters))
.Named("Function Definition");

/// <summary>
/// Checks if the line starts with def.
/// </summary>
/// <param name="line">Line to check</param>
/// <returns></returns>
static bool IsFunctionSignature(string line) =>
line.StartsWith("def ") || line.StartsWith("async def");

public static bool TryParseFunctionDefinitions(string source, out PythonFunctionDefinition[] pythonSignatures, out GeneratorError[] errors)
{
List<PythonFunctionDefinition> functionDefinitions = [];

// Go line by line
var lines = source.Split(["\r\n", "\n"], StringSplitOptions.None);
var currentErrors = new List<GeneratorError>();
List<(int startLine, int endLine, string code)> functionLines = [];
List<string> currentBuffer = [];
int currentBufferStartLine = -1;
bool unfinishedFunctionSpec = false;
for (int i = 0; i < lines.Length; i++)
{
if (IsFunctionSignature(lines[i]) || unfinishedFunctionSpec)
{
currentBuffer.Add(lines[i]);
if (currentBufferStartLine == -1)
{
currentBufferStartLine = i;
}
// Parse the function signature
var result = PythonSignatureTokenizer.Instance.TryTokenize(lines[i]);
if (!result.HasValue)
{
// TODO: Work out end column and add to the other places in this function where it's raised
currentErrors.Add(new GeneratorError(i, i, result.ErrorPosition.Column, result.ErrorPosition.Column, result.FormatErrorMessageFragment()));

// Reset buffer
currentBuffer = [];
currentBufferStartLine = -1;
unfinishedFunctionSpec = false;
continue;
}

// If this is a function definition on one line..
if (result.Value.Last().Kind == PythonSignatureTokens.PythonSignatureToken.Colon)
{
// TODO: (track) Is an empty string the right joining character?
functionLines.Add((currentBufferStartLine, i, string.Join("", currentBuffer)));
currentBuffer = [];
currentBufferStartLine = -1;
unfinishedFunctionSpec = false;
continue;
}
else
{
unfinishedFunctionSpec = true;
}
}
}
foreach (var line in functionLines)
{
// TODO: (track) This means we end up tokenizing the lines twice (one individually and again merged). Optimize.
var result = PythonSignatureTokenizer.Instance.TryTokenize(line.code);
if (!result.HasValue)
{
currentErrors.Add(new GeneratorError(line.startLine, line.endLine, result.ErrorPosition.Column, result.ErrorPosition.Column, result.FormatErrorMessageFragment()));
continue;
}
var functionDefinition = PythonFunctionDefinitionTokenizer.TryParse(result.Value);
if (functionDefinition.HasValue)
{
functionDefinitions.Add(functionDefinition.Value);
}
else
{
// Error parsing the function definition
currentErrors.Add(new GeneratorError(line.startLine, line.endLine, functionDefinition.ErrorPosition.Column, functionDefinition.ErrorPosition.Column + 1, functionDefinition.FormatErrorMessageFragment()));
}
}

pythonSignatures = [.. functionDefinitions];
errors = [.. currentErrors];
return errors.Length == 0;
}
}
Loading

0 comments on commit 73dea94

Please sign in to comment.