Skip to content

Commit

Permalink
allow more than 16 arguments #110
Browse files Browse the repository at this point in the history
  • Loading branch information
neuecc committed Jun 12, 2024
1 parent fe2877e commit 1e1229a
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 32 deletions.
40 changes: 31 additions & 9 deletions sandbox/GeneratorSandbox/Program.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
using ConsoleAppFramework;

var app = ConsoleApp.Create();

app.Add("aaa", () =>
{
});
var t = new Test();

var app = ConsoleApp.Create();

app.Add("foo", t.Handle);

app.Run(args);

app.Add("aabcdefg", int (int x, string y) =>
{
return default!;
});

app.Run(args);
public partial class Test
{
public void Handle(
bool a1,
bool a2,
bool a3,
bool a4,
bool a5,
bool a6,
bool a7,
bool a8,
bool a9,
bool a10,
bool a11,
bool a12,
bool a13,
bool a14,
bool a15,
bool a16,
bool a17,
bool a18,
bool a19
)
{
Console.Write("ok");
}
}
34 changes: 19 additions & 15 deletions src/ConsoleAppFramework/Command.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ public enum MethodKind

public enum DelegateBuildType
{
MakeDelegateWhenHasDefaultValue,
OnlyActionFunc,
MakeCustomDelegateWhenHasDefaultValueOrTooLarge,
None
}

Expand All @@ -31,25 +30,30 @@ public record class Command
public required EquatableArray<FilterInfo> Filters { get; init; }
public bool HasFilter => Filters.Length != 0;

public string? BuildDelegateSignature(out string? delegateType)
// return is delegateType(Name).
public string? BuildDelegateSignature(string customDelegateTypeName, out string? customDelegateDefinition)
{
if (DelegateBuildType == DelegateBuildType.MakeDelegateWhenHasDefaultValue)
{
if (MethodKind == MethodKind.Lambda && Parameters.Any(x => x.HasDefaultValue || x.IsParams))
{
delegateType = BuildDelegateType("RunCommand");
return "RunCommand";
}
}

delegateType = null;
customDelegateDefinition = null;

if (DelegateBuildType == DelegateBuildType.None)
{
return null;
}

if (MethodKind == MethodKind.FunctionPointer) return BuildFunctionPointerDelegateSignature();
if (MethodKind == MethodKind.FunctionPointer)
{
customDelegateDefinition = null;
return BuildFunctionPointerDelegateSignature();
}

if (DelegateBuildType == DelegateBuildType.MakeCustomDelegateWhenHasDefaultValueOrTooLarge)
{
if (Parameters.Length > 16 || (MethodKind == MethodKind.Lambda && Parameters.Any(x => x.HasDefaultValue || x.IsParams)))
{
customDelegateDefinition = BuildDelegateTypeDefinition(customDelegateTypeName);
return customDelegateTypeName;
}
}

if (IsAsync)
{
Expand Down Expand Up @@ -126,7 +130,7 @@ public string BuildFunctionPointerDelegateSignature()
return $"delegate* managed<{parameters}{comma}{retType}>";
}

public string BuildDelegateType(string delegateName)
public string BuildDelegateTypeDefinition(string delegateName)
{
var retType = (IsAsync, IsVoid) switch
{
Expand Down
20 changes: 16 additions & 4 deletions src/ConsoleAppFramework/ConsoleAppGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
var reporter = new DiagnosticReporter();
var node = (InvocationExpressionSyntax)context.Node;
var wellknownTypes = new WellKnownTypes(context.SemanticModel.Compilation);
var parser = new Parser(reporter, node, context.SemanticModel, wellknownTypes, DelegateBuildType.MakeDelegateWhenHasDefaultValue, []);
var parser = new Parser(reporter, node, context.SemanticModel, wellknownTypes, DelegateBuildType.MakeCustomDelegateWhenHasDefaultValueOrTooLarge, []);
var isRunAsync = (node.Expression as MemberAccessExpressionSyntax)?.Name.Identifier.Text == "RunAsync";

var command = parser.ParseAndValidateForRun();
Expand Down Expand Up @@ -624,20 +624,32 @@ static void EmitConsoleAppBuilder(SourceProductionContext sourceProductionContex
var sb = new SourceBuilder(0);
sb.AppendLine(GeneratedCodeHeader);

var delegateSignatures = new List<string>();

// with id number
var commandIds = collectBuilderContext.Commands
.Select((x, i) =>
{
return new Emitter.CommandWithId(
FieldType: x!.BuildDelegateSignature(out _), // for builder, always generate Action/Func so ok to ignore out var.
var command = new Emitter.CommandWithId(
FieldType: x!.BuildDelegateSignature(Emitter.CommandWithId.BuildCustomDelegateTypeName(i), out var delegateDef),
Command: x!,
Id: i
);
if (delegateDef != null)
{
delegateSignatures.Add(delegateDef);
}
return command;
})
.ToArray();

using (sb.BeginBlock("internal static partial class ConsoleApp"))
{
foreach (var d in delegateSignatures)
{
sb.AppendLine(d);
}

var emitter = new Emitter();
emitter.EmitBuilder(sb, commandIds, hasRun, hasRunAsync);
}
Expand Down Expand Up @@ -748,7 +760,7 @@ public CollectBuilderContext(ImmutableArray<BuilderContext> contexts, Cancellati
.Select(x =>
{
var wellKnownTypes = new WellKnownTypes(x.Model.Compilation);
var parser = new Parser(DiagnosticReporter, x.Node, x.Model, wellKnownTypes, DelegateBuildType.OnlyActionFunc, globalFilters);
var parser = new Parser(DiagnosticReporter, x.Node, x.Model, wellKnownTypes, DelegateBuildType.MakeCustomDelegateWhenHasDefaultValueOrTooLarge, globalFilters);
var command = parser.ParseAndValidateForBuilderDelegateRegistration();

// validation command name duplicate
Expand Down
18 changes: 15 additions & 3 deletions src/ConsoleAppFramework/Emitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void EmitRun(SourceBuilder sb, CommandWithId commandWithId, bool isRunAsy
methodName = methodName ?? (isRunAsync ? "RunAsync" : "Run");
var unsafeCode = (command.MethodKind == MethodKind.FunctionPointer) ? "unsafe " : "";

var commandMethodType = command.BuildDelegateSignature(out var delegateType);
var commandMethodType = command.BuildDelegateSignature(commandWithId.BuildCustomDelegateTypeName(), out var delegateType);
if (commandMethodType != null)
{
commandMethodType = $", {commandMethodType} command";
Expand Down Expand Up @@ -539,7 +539,7 @@ void EmitLeafCommand(CommandWithId? command)

void EmitFilterInvoker(CommandWithId command)
{
var commandType = command.Command.BuildDelegateSignature(out _);
var commandType = command.Command.BuildDelegateSignature(command.BuildCustomDelegateTypeName(), out _);
var needsCommand = commandType != null;
if (needsCommand) commandType = $", {commandType} command";

Expand Down Expand Up @@ -605,5 +605,17 @@ public void EmitHelp(SourceBuilder sb, CommandWithId[] commands)
}
}

internal record CommandWithId(string? FieldType, Command Command, int Id);
internal record CommandWithId(string? FieldType, Command Command, int Id)
{
public static string BuildCustomDelegateTypeName(int id)
{
if (id < 0) return "DelegateCommand";
return $"DelegateCommand{id}";
}

public string BuildCustomDelegateTypeName()
{
return BuildCustomDelegateTypeName(Id);
}
}
}
174 changes: 174 additions & 0 deletions tests/ConsoleAppFramework.GeneratorTests/BuildCustomDelegateTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleAppFramework.GeneratorTests;

public class BuildCustomDelegateTest(ITestOutputHelper output)
{
VerifyHelper verifier = new VerifyHelper(output, "CAF");

[Fact]
public void Run()
{
var code = """
ConsoleApp.Run(args, (
bool a1,
bool a2,
bool a3,
bool a4,
bool a5,
bool a6,
bool a7,
bool a8,
bool a9,
bool a10,
bool a11,
bool a12,
bool a13,
bool a14,
bool a15,
bool a16 // ok it is Action
) => { Console.Write("ok"); });
""";

verifier.Execute(code, "", "ok");

var code2 = """
ConsoleApp.Run(args, (
bool a1,
bool a2,
bool a3,
bool a4,
bool a5,
bool a6,
bool a7,
bool a8,
bool a9,
bool a10,
bool a11,
bool a12,
bool a13,
bool a14,
bool a15,
bool a16,
bool a17 // custom delegate
) => { Console.Write("ok"); });
""";

verifier.Execute(code2, "", "ok");


verifier.Execute("""
var t = new Test();
ConsoleApp.Run(args, t.Handle);
public partial class Test
{
public void Handle(
bool a1,
bool a2,
bool a3,
bool a4,
bool a5,
bool a6,
bool a7,
bool a8,
bool a9,
bool a10,
bool a11,
bool a12,
bool a13,
bool a14,
bool a15,
bool a16,
bool a17,
bool a18,
bool a19
)
{
Console.Write("ok");
}
}
""", "", "ok");



verifier.Execute("""
unsafe
{
ConsoleApp.Run(args, &Test.Handle);
}
public partial class Test
{
public static void Handle(
bool a1,
bool a2,
bool a3,
bool a4,
bool a5,
bool a6,
bool a7,
bool a8,
bool a9,
bool a10,
bool a11,
bool a12,
bool a13,
bool a14,
bool a15,
bool a16,
bool a17,
bool a18,
bool a19
)
{
Console.Write("ok");
}
}
""", "", "ok");
}

[Fact]
public void Builder()
{
verifier.Execute("""
var t = new Test();
var app = ConsoleApp.Create();
app.Add("", t.Handle);
app.Run(args);
public partial class Test
{
public void Handle(
bool a1,
bool a2,
bool a3,
bool a4,
bool a5,
bool a6,
bool a7,
bool a8,
bool a9,
bool a10,
bool a11,
bool a12,
bool a13,
bool a14,
bool a15,
bool a16,
bool a17,
bool a18,
bool a19
)
{
Console.Write("ok");
}
}
""", "", "ok");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public static void InitializeCompilation()
var compilation = CSharpCompilation.Create("generatortest",
references: references,
syntaxTrees: [CSharpSyntaxTree.ParseText(globalUsings, path: "GlobalUsings.cs")],
options: new CSharpCompilationOptions(OutputKind.ConsoleApplication)); // .exe
options: new CSharpCompilationOptions(OutputKind.ConsoleApplication, allowUnsafe: true)); // .exe

baseCompilation = compilation;
}
Expand Down

0 comments on commit 1e1229a

Please sign in to comment.