Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Variadic Source Generator #166

Closed
wants to merge 17 commits into from
Closed
12 changes: 6 additions & 6 deletions src/Arch.Benchmarks/QueryBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ public void WorldEntityQuery()
{
var refs = _world.Get<Transform, Velocity>(entity);

refs.t0.X += refs.t1.X;
refs.t0.Y += refs.t1.Y;
refs.Component_T0.X += refs.Component_T1.X;
refs.Component_T0.Y += refs.Component_T1.Y;
});
}

Expand All @@ -49,8 +49,8 @@ public void EntityExtensionQuery()
_world.Query(in _queryDescription, (Entity entity) =>
{
var refs = entity.Get<Transform, Velocity>();
refs.t0.X += refs.t1.X;
refs.t0.Y += refs.t1.Y;
refs.Component_T0.X += refs.Component_T1.X;
refs.Component_T0.Y += refs.Component_T1.Y;
});
}
#endif
Expand Down Expand Up @@ -84,8 +84,8 @@ public void Iterator()
var refs = chunk.GetFirst<Transform, Velocity>();
foreach (var entity in chunk)
{
ref var pos = ref Unsafe.Add(ref refs.t0, entity);
ref var vel = ref Unsafe.Add(ref refs.t1, entity);
ref var pos = ref Unsafe.Add(ref refs.Component_T0, entity);
ref var vel = ref Unsafe.Add(ref refs.Component_T1, entity);

pos.X += vel.X;
pos.Y += vel.Y;
Expand Down
8 changes: 7 additions & 1 deletion src/Arch.SourceGen/Arch.SourceGen.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

<!--<AllowUnsafeBlocks>true</AllowUnsafeBlocks>-->
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<Nullable>enable</Nullable>
<Configurations>Debug;Release;Release-Pure;Release-PureECS;Release-Events;Debug-PureECS;Debug-Events</Configurations>
<!--<TreatWarningsAsErrors>true</TreatWarningsAsErrors>-->
Expand All @@ -25,7 +26,12 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.1.0" PrivateAssets="analyzers" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
</ItemGroup>

<ItemGroup>
Expand Down
36 changes: 36 additions & 0 deletions src/Arch.SourceGen/CopyArgsAlgorithm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Text.RegularExpressions;

namespace Arch.SourceGen;

/// <summary>
/// <see cref="CopyArgsAlgorithm"/> repeats a given argument of structure <c>{name}_{variadicType}</c>, separated by commas. It properly copies any ref, in, out, etc modifiers.
/// It will additionally copy generic type arguments matching the variadic type, only if the variadic type is the last in the type parameter list.
/// </summary>
/// <example>
/// With type parameter T0:
/// <code>
/// MyMethod&lt;T0&gt;(in component_T0)
/// </code>
/// ... will convert to:
/// <code>
/// MyMethod&lt;T0, T1&gt;(in component_T0, in component_T1)
/// </code>
/// </example>
[VariadicAlgorithm]
internal class CopyArgsAlgorithm : LineAlgorithm
{
public override string Name { get => "CopyArgs"; }
public override int ExpectedParameterCount { get => 1; }

public override string Transform(string line, string type, int lastVariadic, string[] parameters)
{
// Expand the args
line = Utils.ExpandUntypedArguments(line, type, lastVariadic, parameters[0]);

// Expand any remaining generics
// Note that this'll break if the user uses Span<T> instead of var or something....
line = Utils.ExpandGenericTypes(line, type, lastVariadic);

return line;
}
}
39 changes: 39 additions & 0 deletions src/Arch.SourceGen/CopyLinesAlgorithm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
namespace Arch.SourceGen;

/// <summary>
/// <see cref="CopyLinesAlgorithm"/> is a very simple algorithm that will simply repeat the line, replacing any occurances of the variadic type with its variant for each iteration.
/// </summary>
/// <example>
/// With type parameter T0:
/// <code>
/// /// [Variadic: CopyLines]
/// somethingSomething_T0 = new T0(); // look! T0!
/// </code>
/// ... will expand to:
/// <code>
/// somethingSomething_T0 = new T0(); // look! T0!
/// somethingSomething_T1 = new T1(); // look! T1!
/// ...
/// </code>
/// </example>
[VariadicAlgorithm]
internal class CopyLinesAlgorithm : LineAlgorithm
{
public override string Name { get => "CopyLines"; }
public override int ExpectedParameterCount { get => 0; }

public override string Transform(string line, string type, int lastVariadic, string[] parameters)
{
var (typeName, typeNum) = Utils.ExtractTypeInfo(type);

var transformed = new StringBuilder();
transformed.AppendLine(line);

for (int i = typeNum + 1; i <= lastVariadic; i++)
{
transformed.AppendLine(Utils.ReplaceType(line, type, i));
}

return transformed.ToString();
}
}
46 changes: 46 additions & 0 deletions src/Arch.SourceGen/CopyParamsAlgorithm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Text.RegularExpressions;

namespace Arch.SourceGen;

/// <summary>
/// <para>
/// The <see cref="CopyParamsAlgorithm"/> is a special algorithm for more complicated method headers that can't be handled by the basic <see cref="DefaultAlgorithm"/>.
/// In particular, it handles cases of a type other than directly the variadic type, that needs to be transformed and copied. It also handles variable initializers.
/// For example, it will expand <c>ref Span&lt;T0&gt;? component_T0 = default</c> to
/// <c>ref Span&lt;T0&gt;? component_T0 = default, ref Span&lt;T1&gt;? component_T1 = default ...</c>.
/// </para>
/// <para>
/// Basically, use this algorithm if your method has any of these: Initializers (<c>= default</c>), nullables (<c>T0?</c>), or wrapped type arguments
/// (<c>MyWrapper&lt;T, T0&gt;</c>).
/// </para>
/// <para>
/// Of note, it won't expand generic parameters within arguments like the default algorithm will, which is often not desired.
/// </para>
/// </summary>
[VariadicAlgorithm]
internal class CopyParamsAlgorithm : LineAlgorithm
{
public override int ExpectedParameterCount { get => 1; }
public override string Name { get => "CopyParams"; }

public override string Transform(string line, string type, int lastVariadic, string[] parameters)
{
// Expand the "where" constraints (like where T0 : ISomething)
line = Utils.ExpandConstraints(line, type, lastVariadic);

// Expand params in header (i.e. Span<T0> component_T0 -> Span<T0> component_T0, Span<T1> component_T1...
line = Utils.ExpandTypedParameters(line, type, parameters[0], lastVariadic, out int transformedStart, out int transformedEnd);

// Expand any generic type groups (like <A, T0>). We need to explicitly ONLY do this on the part of the header outside of the params, or Span<T0>
// will turn into Span<T0...> which we don't want.
// So, we exclude the transformed range from the typed parameter extension.

var beforeParams = line.Substring(0, transformedStart);
var @params = line.Substring(transformedStart, transformedEnd - transformedStart);
var afterParams = line.Substring(transformedEnd, line.Length - transformedEnd);

// Composite the parts back together
return Utils.ExpandGenericTypes(beforeParams, type, lastVariadic) + @params
+ Utils.ExpandGenericTypes(afterParams, type, lastVariadic);
}
}
51 changes: 51 additions & 0 deletions src/Arch.SourceGen/DefaultAlgorithm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System.Text.RegularExpressions;

namespace Arch.SourceGen;

/// <summary>
/// <para>
/// The <see cref="DefaultAlgorithm"/> runs by default on any lines where a variadic comment like "// [Variadic: MethodName(param1...)]" is NOT present.
/// </para>
/// <para>
/// The behavior of the algorithm is as such:
/// </para>
/// <para>
/// Anywhere in the string, if there is a type sequence ending in the specified variadic type, it fills in the variadics. For example, <c>&lt;T, T0&gt;</c> will
/// expand to <c>&lt;T, T0, T1...&gt;</c>. The generic sequence must end in the specified variadic type, otherwise nothing will be changed.
/// </para>
/// <para>
/// If any type constraints are found with the specified variadic type, it copies the constraints to each new type. For example, with type <c>T0</c>,
/// <c>where T0 : struct, new()</c> will expand to <c>where T0 : struct, new() where T1 : struct, new() ...</c>. Of note, nested constraints of the variadic
/// types are not currently processed correctly, i.e. <c>where T0 : ISomething&lt;T0&gt;</c>. If that behavior is needed, either edit this algorithm or introduce a new one.
/// </para>
/// <para>
/// For method headers, it copies simple type parameters of the given variadic type, with the form <c>{paramName}_{type}</c>. For example, <c>in T0 component_T0</c>
/// would expand to <c>in T0 component_T0, in T1 component_T1</c>. Note that this algorithm isn't smart enough to recognize wrapped parameters, such as
/// <c>in Span&lt;T0&gt; component_T0</c>; it will expand it to an invalid <c>Span&lt;T0, T1, ...&gt;</c>. To use the former behavior, or if initializers like
/// <c>component_T0 = default</c> is needed, or nullables are needed, specify <see cref="CopyParamsAlgorithm"/> instead with an explicit type to copy.
/// </para>
/// <para>
/// Nothing else is changed.
/// </para>
/// </summary>
[VariadicAlgorithm]
internal class DefaultAlgorithm : LineAlgorithm
{
public override int ExpectedParameterCount { get => 0; }
public override string Name { get => string.Empty; }
public override string Transform(string line, string type, int lastVariadic, string[] parameters)
{
// Expand the "where" constraints (like where T0 : ISomething)
line = Utils.ExpandConstraints(line, type, lastVariadic);

// Expand any generic type groups (like <A, T0>)
line = Utils.ExpandGenericTypes(line, type, lastVariadic);

// Expand params in header (i.e. T0 component_T0 -> T0 component_T0, T1 component_T1...
// This is the 90% case. Occasionally there needs to be special handling for a method header with wrapped types, like Span<T0>.
// Those cases are handled by CopyParamsAlgorithm; this algorithm incorrectly produces Span<T0, T1...> instead of copying the typed params.
line = Utils.ExpandTypedParameters(line, type, type, lastVariadic, out var _, out var _);

return line;
}
}
131 changes: 0 additions & 131 deletions src/Arch.SourceGen/Fundamentals/Components.cs

This file was deleted.

Loading
Loading